From 79c5d3b305af947949689365801773a3aaa38eb9 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 25 Mar 2006 16:35:33 -0600 Subject: Tweaks to pvrusb2 init sequence From: Mike Isely Rename a pvrusb2 function to reflect its true meaning, and tweak the driver initialization sequence so that the I2C adapter isn't started until after the hardware has been given a powerup command. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c | 2 +- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 11 +++++++---- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index bcfe468eb..f5c267123 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -362,7 +362,7 @@ int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, } else if (debugifc_match_keyword(wptr,wlen,"bus")) { pvr2_hdw_device_reset(hdw); } else if (debugifc_match_keyword(wptr,wlen,"soft")) { - return pvr2_hdw_cmd_soft_reset(hdw); + return pvr2_hdw_cmd_powerup(hdw); } else if (debugifc_match_keyword(wptr,wlen,"deep")) { return pvr2_hdw_cmd_deep_reset(hdw); } else if (debugifc_match_keyword(wptr,wlen,"firmware")) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index b87651e5a..6a729c4c7 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -617,7 +617,6 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) ret = 0; /* First prepare firmware loading */ - ret |= pvr2_hdw_cmd_soft_reset(hdw); ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/ ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/ ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/ @@ -1093,7 +1092,7 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) } if (!pvr2_hdw_dev_ok(hdw)) return; - pvr2_i2c_core_init(hdw); + pvr2_hdw_cmd_powerup(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; if (pvr2_upload_firmware2(hdw)){ @@ -1102,6 +1101,10 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) return; } + // This step MUST happen after the earlier powerup step. + pvr2_i2c_core_init(hdw); + if (!pvr2_hdw_dev_ok(hdw)) return; + for (idx = 0; idx < PVR2_CID_COUNT; idx++) { if (control_defs[idx].skip_init) continue; pvr2_hdw_set_ctl_value_internal( @@ -2405,11 +2408,11 @@ int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw) } -int pvr2_hdw_cmd_soft_reset(struct pvr2_hdw *hdw) +int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw) { int status; LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT,"Requesting uproc soft reset"); + pvr2_trace(PVR2_TRACE_INIT,"Requesting powerup"); hdw->cmd_buffer[0] = 0xde; status = pvr2_send_request(hdw,hdw->cmd_buffer,1,0,0); } while (0); LOCK_GIVE(hdw->ctl_lock); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 667c95a27..69ad5e1b1 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -358,7 +358,7 @@ void pvr2_hdw_device_reset(struct pvr2_hdw *); int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *); /* Execute simple reset command */ -int pvr2_hdw_cmd_soft_reset(struct pvr2_hdw *); +int pvr2_hdw_cmd_powerup(struct pvr2_hdw *); /* Stop / start video stream transport */ int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl); -- cgit v1.2.3 From 099cf0ca3267af67d3db7c4587001e7f5a3877b3 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 25 Mar 2006 16:36:18 -0600 Subject: Use hex for printing I2C addresses in pvrusb2 From: Mike Isely Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 8ec637e5e..35224c678 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -88,7 +88,7 @@ static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */ } } #if 0 - trace_i2c("i2c_write(%d) len=%d ret=%d stat=%d",i2c_addr,length,ret, + trace_i2c("i2c_write(0x%x) len=%d ret=%d stat=%d",i2c_addr,length,ret, hdw->cmd_buffer[0]); #endif @@ -146,7 +146,7 @@ static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */ } #if 0 - trace_i2c("i2c_read(%d) wlen=%d rlen=%d ret=%d stat=%d", + trace_i2c("i2c_read(0x%x) wlen=%d rlen=%d ret=%d stat=%d", i2c_addr,dlen,rlen,ret,hdw->cmd_buffer[0]); #endif /* Copy back the result */ -- cgit v1.2.3 From 251aaed52d08d4fc485802f3a800c53de5a945ba Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 25 Mar 2006 16:37:07 -0600 Subject: Fix screwed up indentation in pvrusb2-sysfs.c From: Mike Isely Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c | 88 +++++++++++------------ 1 file changed, 44 insertions(+), 44 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index a9710b52c..f82fd643c 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -364,50 +364,50 @@ CREATE_STORE_INSTANCE(store_val_int,ctl_id) \ CREATE_STORE_INSTANCE(store_val_enum,ctl_id) CREATE_BATCH(0) - CREATE_BATCH(1) - CREATE_BATCH(2) - CREATE_BATCH(3) - CREATE_BATCH(4) - CREATE_BATCH(5) - CREATE_BATCH(6) - CREATE_BATCH(7) - CREATE_BATCH(8) - CREATE_BATCH(9) - CREATE_BATCH(10) - CREATE_BATCH(11) - CREATE_BATCH(12) - CREATE_BATCH(13) - CREATE_BATCH(14) - CREATE_BATCH(15) - CREATE_BATCH(16) - CREATE_BATCH(17) - CREATE_BATCH(18) - CREATE_BATCH(19) - CREATE_BATCH(20) - CREATE_BATCH(21) - CREATE_BATCH(22) - CREATE_BATCH(23) - CREATE_BATCH(24) - CREATE_BATCH(25) - CREATE_BATCH(26) - CREATE_BATCH(27) - CREATE_BATCH(28) - CREATE_BATCH(29) - CREATE_BATCH(30) - CREATE_BATCH(31) - - struct pvr2_sysfs_func_set { - ssize_t (*show_name)(struct class_device *,char *); - ssize_t (*show_min)(struct class_device *,char *); - ssize_t (*show_max)(struct class_device *,char *); - ssize_t (*show_enum)(struct class_device *,char *); - ssize_t (*show_val_int)(struct class_device *,char *); - ssize_t (*show_val_enum)(struct class_device *,char *); - ssize_t (*store_val_int)(struct class_device *, - const char *,size_t); - ssize_t (*store_val_enum)(struct class_device *, - const char *,size_t); - }; +CREATE_BATCH(1) +CREATE_BATCH(2) +CREATE_BATCH(3) +CREATE_BATCH(4) +CREATE_BATCH(5) +CREATE_BATCH(6) +CREATE_BATCH(7) +CREATE_BATCH(8) +CREATE_BATCH(9) +CREATE_BATCH(10) +CREATE_BATCH(11) +CREATE_BATCH(12) +CREATE_BATCH(13) +CREATE_BATCH(14) +CREATE_BATCH(15) +CREATE_BATCH(16) +CREATE_BATCH(17) +CREATE_BATCH(18) +CREATE_BATCH(19) +CREATE_BATCH(20) +CREATE_BATCH(21) +CREATE_BATCH(22) +CREATE_BATCH(23) +CREATE_BATCH(24) +CREATE_BATCH(25) +CREATE_BATCH(26) +CREATE_BATCH(27) +CREATE_BATCH(28) +CREATE_BATCH(29) +CREATE_BATCH(30) +CREATE_BATCH(31) + +struct pvr2_sysfs_func_set { + ssize_t (*show_name)(struct class_device *,char *); + ssize_t (*show_min)(struct class_device *,char *); + ssize_t (*show_max)(struct class_device *,char *); + ssize_t (*show_enum)(struct class_device *,char *); + ssize_t (*show_val_int)(struct class_device *,char *); + ssize_t (*show_val_enum)(struct class_device *,char *); + ssize_t (*store_val_int)(struct class_device *, + const char *,size_t); + ssize_t (*store_val_enum)(struct class_device *, + const char *,size_t); +}; #define INIT_BATCH(ctl_id) \ [ctl_id] = { \ -- cgit v1.2.3 From acea6b43b12c290328f857fa981b2c8b3d598b45 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 25 Mar 2006 16:37:54 -0600 Subject: Fix pvrusb2 compilation warning when building for amd64 From: Mike Isely Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 35224c678..a89f3574f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -56,7 +56,7 @@ static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */ "Killing an I2C write to %u that is too large" " (desired=%u limit=%u)", i2c_addr, - length,(sizeof(hdw->cmd_buffer) - 3)); + length,(unsigned int)(sizeof(hdw->cmd_buffer) - 3)); return -ENOTSUPP; } -- cgit v1.2.3 From 22184ee779d37f2c64b08ea427d939f0e2d58b37 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 25 Mar 2006 16:40:16 -0600 Subject: Implement method in pvrusb2 to force the decoder to reset From: Mike Isely This change threads logic through the pvrusb2 to make it possible to command the decoder chip to reset itself. The method is decoder-agnostic; the part of the pvrusb2 which control's that chip's module has to provide the final hook. This just lays the foundation. Signed-off-by: Mike Isely --- .../drivers/media/video/pvrusb2/pvrusb2-debugifc.c | 2 ++ .../media/video/pvrusb2/pvrusb2-hdw-internal.h | 1 + linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 21 +++++++++++++++++++++ linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h | 3 +++ 4 files changed, 27 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index f5c267123..16e6ea317 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -367,6 +367,8 @@ int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, return pvr2_hdw_cmd_deep_reset(hdw); } else if (debugifc_match_keyword(wptr,wlen,"firmware")) { return pvr2_upload_firmware2(hdw); + } else if (debugifc_match_keyword(wptr,wlen,"decoder")) { + return pvr2_hdw_cmd_decoder_reset(hdw); } return -EINVAL; } else if (debugifc_match_keyword(wptr,wlen,"subsys_flags")) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 1b161773b..05e44385b 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -74,6 +74,7 @@ struct pvr2_decoder_ctrl { void (*detach)(void *); void (*enable)(void *,int); int (*tuned)(void *); + void (*force_reset)(void *); }; #define PVR2_I2C_PEND_DETECT 0x01 /* Need to detect a client type */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 6a729c4c7..16757a8fb 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -2420,6 +2420,27 @@ int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw) } +int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) +{ + if (!hdw->decoder_ctrl) { + pvr2_trace(PVR2_TRACE_INIT, + "Unable to reset decoder: nothing attached"); + return -ENOTTY; + } + + if (!hdw->decoder_ctrl->force_reset) { + pvr2_trace(PVR2_TRACE_INIT, + "Unable to reset decoder: not implemented"); + return -ENOTTY; + } + + pvr2_trace(PVR2_TRACE_INIT, + "Requesting decoder reset"); + hdw->decoder_ctrl->force_reset(hdw->decoder_ctrl->ctxt); + return 0; +} + + int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) { int status; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 69ad5e1b1..cf5231ee9 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -360,6 +360,9 @@ int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *); /* Execute simple reset command */ int pvr2_hdw_cmd_powerup(struct pvr2_hdw *); +/* Order decoder to reset */ +int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *); + /* Stop / start video stream transport */ int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl); -- cgit v1.2.3 From 6aa6af1e4b4ce66a75dc8b20b4bd6670c4ecd0cf Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 25 Mar 2006 16:41:46 -0600 Subject: New debug code in pvrusb2 for tracking requests From: Mike Isely Implement pvrusb2 code normally compiled-out which can print useful information about commands issued to the hardware. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 42 +++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 16757a8fb..ed5e81224 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -2064,6 +2064,28 @@ int pvr2_send_request_ex(struct pvr2_hdw *hdw, return -EINVAL; } +#if 0 + printk(KERN_INFO "pvrusb2: REQUEST BEGIN writeCnt=%u readCnt=%u", + write_len,read_len); + if (probe_fl) { + printk(" "); + } + for (idx = 0; idx < write_len; idx++) { + if (idx > 5) { + printk(" ..."); + break; + } + if (idx) { + printk(" "); + } else { + printk(" ["); + } + printk("%02x",((unsigned char *)write_data)[idx]); + } + if (write_len) printk("]"); + printk("\n"); +#endif + hdw->cmd_debug_state = 1; if (write_len) { hdw->cmd_debug_code = ((unsigned char *)write_data)[0]; @@ -2233,6 +2255,26 @@ int pvr2_send_request_ex(struct pvr2_hdw *hdw, } done: +#if 0 + printk(KERN_INFO "pvrusb2: REQUEST END status=%d",status); + if (status >= 0) { + for (idx = 0; idx < read_len; idx++) { + if (idx > 5) { + printk(" ..."); + break; + } + if (idx) { + printk(" "); + } else { + printk(" ["); + } + printk("%02x",((unsigned char *)read_data)[idx]); + } + if (read_len) printk("]"); + } + printk("\n"); +#endif + hdw->cmd_debug_state = 0; if ((status < 0) && (!probe_fl)) { pvr2_hdw_render_useless_unlocked(hdw); -- cgit v1.2.3 From ba27877ce2c324e04a518371da0bf976308a314f Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 25 Mar 2006 16:43:17 -0600 Subject: Fix pvrusb2 bug which prevented proper operation of tda9887 From: Mike Isely The pvrusb2 driver was not correctly identifying and handling the tda9887 module. This is the bug fix (code had been ifdef'ed out which should not have been). Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c index 2d97653d5..d537e380f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c @@ -74,13 +74,11 @@ void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) return; } } -#ifdef PVR2_ENABLE_DRIVERID_TDA9887 if (id == I2C_DRIVERID_TDA9887) { if (pvr2_i2c_demod_setup(hdw,cp)) { return; } } -#endif } -- cgit v1.2.3 From 23fb60e03b25fa009ed530d83c5cb7463b96bdc9 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 25 Mar 2006 16:45:28 -0600 Subject: pvrusb2 control commit handling fix From: Mike Isely When a control change is committed to the driver, certain other parts of the driver must be updated to track the change and deal with the effects of that change. Some controls in the driver use a function pointer to implement the commit, however we weren't doing the tracking there and we should have been. This is the fix. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index ed5e81224..231c314f4 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1490,11 +1490,15 @@ int pvr2_hdw_get_ctl_max_value(struct pvr2_hdw *hdw,unsigned int ctl_id) int pvr2_hdw_set_ctl_value_internal(struct pvr2_hdw *hdw, unsigned int ctl_id,int value) { + int ret; if (ctl_id >= PVR2_CID_COUNT) return -EINVAL; if (value < control_defs[ctl_id].min_value) return -EINVAL; if (value > control_defs[ctl_id].max_value) return -EINVAL; if (control_defs[ctl_id].set_func) { - return control_defs[ctl_id].set_func(hdw,ctl_id,value); + ret = control_defs[ctl_id].set_func(hdw,ctl_id,value); + pvr2_i2c_core_check_stale(hdw); + pvr2_i2c_core_sync(hdw); + return ret; } else if (control_defs[ctl_id].get_func) { /* If there's no "set" function yet there is still a "get" function, then treat this as a read-only value. */ -- cgit v1.2.3 From 8b44741ac5b22de76954864b0db14a8d4ed5db07 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 25 Mar 2006 16:47:08 -0600 Subject: Adjust pvrusb2 stream on/off control handling From: Mike Isely Code in pvrusb2 which issues stream on/off commands was previously in a place specific to the saa7115. This change moves that function to a place where it can be used for other things (like controlling a future cx25840). Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c | 7 +++++++ linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h | 2 ++ linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c | 4 ++-- 3 files changed, 11 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c index 69864782b..7721e405e 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c @@ -239,6 +239,13 @@ const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log = { }; +void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *cp,int fl) +{ + pvr2_i2c_client_cmd(cp, + (fl ? VIDIOC_STREAMON : VIDIOC_STREAMOFF),0); +} + + /* Stuff for Emacs to see, in order to encourage consistent editing style: *** Local Variables: *** diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h index fa0a3af7e..1666a3287 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h @@ -33,6 +33,8 @@ extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log; +void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *,int); + #endif /* __PVRUSB2_CMD_V4L2_H */ /* diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c index d0bc3682b..7078ac3ad 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c @@ -29,6 +29,7 @@ */ #include "pvrusb2-video-v4l.h" +#include "pvrusb2-i2c-cmd-v4l2.h" #include "pvrusb2-hdw-internal.h" @@ -173,8 +174,7 @@ static int decoder_detect(struct pvr2_i2c_client *cp) static void decoder_enable(struct pvr2_v4l_decoder *ctxt,int fl) { pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 decoder_enable(%d)",fl); - pvr2_i2c_client_cmd(ctxt->client, - (fl ? VIDIOC_STREAMON : VIDIOC_STREAMOFF),0); + pvr2_v4l2_cmd_stream(ctxt->client,fl); } -- cgit v1.2.3 From d0b804387be596a0e4ea8177236a9d8126ac3f81 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 25 Mar 2006 16:48:19 -0600 Subject: Add more trace print for central I2C module commands in pvrusb2 From: Mike Isely This just adds some more trace print to make possible tracking of I2C commands being broadcast to support modules. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index a89f3574f..84903ccf5 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -322,15 +322,26 @@ static int pvr2_i2c_core_singleton(struct i2c_client *cp, int pvr2_i2c_client_cmd(struct pvr2_i2c_client *cp,unsigned int cmd,void *arg) { + int stat; if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) { char buf[100]; unsigned int cnt; cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG, buf,sizeof(buf)); pvr2_trace(PVR2_TRACE_I2C_CMD, - "i2c COMMAND to %.*s",cnt,buf); + "i2c COMMAND (code=%u 0x%x) to %.*s", + cmd,cmd,cnt,buf); } - return pvr2_i2c_core_singleton(cp->client,cmd,arg); + stat = pvr2_i2c_core_singleton(cp->client,cmd,arg); + if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) { + char buf[100]; + unsigned int cnt; + cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG, + buf,sizeof(buf)); + pvr2_trace(PVR2_TRACE_I2C_CMD, + "i2c COMMAND to %.*s (ret=%d)",cnt,buf,stat); + } + return stat; } int pvr2_i2c_core_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg) -- cgit v1.2.3 From 441ebaf82f5bb501cb77fe2316de11be5d90952e Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 25 Mar 2006 16:49:23 -0600 Subject: Advertise I2C functionality in pvrusb2 that's friendly to wm8775 From: Mike Isely Adjust pvrusb2 functionality bits so that wm8775 will attach to the adapter. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 84903ccf5..2f7946195 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -296,7 +296,7 @@ static int pvr2_i2c_control(struct i2c_adapter *adapter, static u32 pvr2_i2c_functionality(struct i2c_adapter *adap) { - return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA; } static int pvr2_i2c_core_singleton(struct i2c_client *cp, -- cgit v1.2.3 From 91ae35c1daa731973eb99fd8d422e60a6cb4ea5d Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 25 Mar 2006 16:53:15 -0600 Subject: Adjust cx23416 operation in pvrusb2 to work with model 24xxx hardware From: Mike Isely This applies some changes to the logic which controls the cx23416 in pvrusb2. This is required in order for newer model 24xxx hardware to operate correctly and is backwards compatible with older hardware. Signed-off-by: Mike Isely --- .../drivers/media/video/pvrusb2/pvrusb2-encoder.c | 27 +++++++++++----------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 97931d3c4..553bd2d7b 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -294,24 +294,25 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw) /* set stream output port. */ ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_OUTPUT_PORT, 2, - 0x01, 0x01); + 0x01, 0x02); /* set the Program Index Information. We want I,P,B frames (max 400) */ ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, 0x07, 0x0190); - /* NOTE : windows driver sends some 0xdc */ - - /* Mike Isely 19-Jun-2005 I've confirmed - that the Windows driver seems to issue these commands, but - right now I have no idea what these do (and neither does - the ivtv driver). But, if I leave them in, then mplayer - goes nuts with xrun errors. So for now we don't do this. - It sure would be nice to know what these are for. */ -#if 0 - ret |= pvr2_write_encoder_vcmd(hdw, 0xdc, 1, 5); - ret |= pvr2_write_encoder_vcmd(hdw, 0xdc, 2, 3, 1); - ret |= pvr2_write_encoder_vcmd(hdw, 0xdc, 1, 8); + /* NOTE : windows driver sends these */ + /* Mike Isely 7-Mar-2006 The windows driver + sends the following commands but if we do the same then + many apps are no longer able to read the video stream. + Leaving these out seems to do no harm at all, so they're + commented out for that reason. */ +#ifdef notdef + ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0); + ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3,1,0,0); + ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 8,0,0,0); + ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0); + ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 0,3,0,0); + ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4,15,0,0,0); #endif /* Strange compared to ivtv data. */ -- cgit v1.2.3 From b7be9b51e990f64ff667725533d5403660cbdac1 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 25 Mar 2006 16:55:50 -0600 Subject: Implement operation of cx2584x from within pvrusb2 From: Mike Isely This adds new logic to integrate the pvrusb2 driver with the V4L cx25840 module. Signed-off-by: Mike Isely --- .../media/video/pvrusb2/pvrusb2-cx2584x-v4l.c | 276 +++++++++++++++++++++ .../media/video/pvrusb2/pvrusb2-cx2584x-v4l.h | 54 ++++ .../media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c | 6 + 3 files changed, 336 insertions(+) create mode 100644 linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c create mode 100644 linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c new file mode 100644 index 000000000..df1eeefd5 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -0,0 +1,276 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * 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 + * + */ + +/* + + This source file is specifically designed to interface with the + cx2584x, in kernels 2.6.16 or newer. + +*/ + +#include "pvrusb2-cx2584x-v4l.h" +#include "pvrusb2-video-v4l.h" +#include "pvrusb2-i2c-cmd-v4l2.h" + + +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" +#include +#include +#include +#include +#include + +struct pvr2_v4l_cx2584x { + struct pvr2_i2c_handler handler; + struct pvr2_decoder_ctrl ctrl; + struct pvr2_i2c_client *client; + struct pvr2_hdw *hdw; + unsigned long stale_mask; +}; + + +static void set_input(struct pvr2_v4l_cx2584x *ctxt) +{ + struct pvr2_hdw *hdw = ctxt->hdw; + struct v4l2_audio inp; + int msk = 0; + + int v = 0; + + switch(hdw->controls[PVR2_CID_INPUT].value) { + case PVR2_CVAL_INPUT_TV: + msk = (8 << 16) | 7; + break; + case PVR2_CVAL_INPUT_COMPOSITE: + msk = (0 << 16) | 3; + break; + case PVR2_CVAL_INPUT_SVIDEO: + msk = (0 << 16) | 0x510; + break; + case PVR2_CVAL_INPUT_RADIO: + msk = 0; /* FIXME: Need to figure out radio */ + break; + default: + break; + } + + pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_input(val=%d msk=0x%x)", + hdw->controls[PVR2_CID_INPUT].value,msk); + + v = msk & 0x0000ffffu; + pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x S_INPUT val=0x%x",v); + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_INPUT,&v); + v = (msk >> 16) & 0x0000ffffu; + pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x S_AUDIO val=0x%x",v); + memset(&inp,0,sizeof(inp)); + inp.index = v; + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_AUDIO,&inp); +} + + +static int check_input(struct pvr2_v4l_cx2584x *ctxt) +{ + struct pvr2_hdw *hdw = ctxt->hdw; + return hdw->controls[PVR2_CID_INPUT].dirty != 0; +} + + +static void set_audio(struct pvr2_v4l_cx2584x *ctxt) +{ + u32 val; + struct pvr2_hdw *hdw = ctxt->hdw; + + pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_audio %d", + hdw->controls[PVR2_CID_SRATE].value); + switch (hdw->controls[PVR2_CID_SRATE].value) { + default: + case PVR2_CVAL_SRATE_48: + val = 48000; + break; + case PVR2_CVAL_SRATE_44_1: + val = 44100; + break; + } + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val); +} + + +static int check_audio(struct pvr2_v4l_cx2584x *ctxt) +{ + struct pvr2_hdw *hdw = ctxt->hdw; + return hdw->controls[PVR2_CID_SRATE].dirty != 0; +} + + +struct pvr2_v4l_cx2584x_ops { + void (*update)(struct pvr2_v4l_cx2584x *); + int (*check)(struct pvr2_v4l_cx2584x *); +}; + + +static const struct pvr2_v4l_cx2584x_ops decoder_ops[] = { + { .update = set_input, .check = check_input}, + { .update = set_audio, .check = check_audio}, +}; + + +static void decoder_detach(struct pvr2_v4l_cx2584x *ctxt) +{ + ctxt->client->handler = 0; + ctxt->hdw->decoder_ctrl = 0; + kfree(ctxt); +} + + +static int decoder_check(struct pvr2_v4l_cx2584x *ctxt) +{ + unsigned long msk; + unsigned int idx; + + for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]); + idx++) { + msk = 1 << idx; + if (ctxt->stale_mask & msk) continue; + if (decoder_ops[idx].check(ctxt)) { + ctxt->stale_mask |= msk; + } + } + return ctxt->stale_mask != 0; +} + + +static void decoder_update(struct pvr2_v4l_cx2584x *ctxt) +{ + unsigned long msk; + unsigned int idx; + + for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]); + idx++) { + msk = 1 << idx; + if (!(ctxt->stale_mask & msk)) continue; + ctxt->stale_mask &= ~msk; + decoder_ops[idx].update(ctxt); + } +} + + +static void decoder_enable(struct pvr2_v4l_cx2584x *ctxt,int fl) +{ + pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_enable(%d)",fl); + pvr2_v4l2_cmd_stream(ctxt->client,fl); +} + + +static int decoder_detect(struct pvr2_i2c_client *cp) +{ + int ret; + /* Attempt to query the decoder - let's see if it will answer */ + struct v4l2_queryctrl qc; + + memset(&qc,0,sizeof(qc)); + + qc.id = V4L2_CID_BRIGHTNESS; + + ret = pvr2_i2c_client_cmd(cp,VIDIOC_QUERYCTRL,&qc); + return ret == 0; /* Return true if it answered */ +} + + +static int decoder_is_tuned(struct pvr2_v4l_cx2584x *ctxt) +{ + struct v4l2_tuner vt; + int ret; + + memset(&vt,0,sizeof(vt)); + ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt); + if (ret < 0) return -EINVAL; + return vt.signal ? 1 : 0; +} + + +static unsigned int decoder_describe(struct pvr2_v4l_cx2584x *ctxt, + char *buf,unsigned int cnt) +{ + return scnprintf(buf,cnt,"handler: pvrusb2-cx2584x-v4l"); +} + + +static void decoder_reset(struct pvr2_v4l_cx2584x *ctxt) +{ + int ret; + ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_RESET,0); + pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_reset (ret=%d)",ret); +} + + +const static struct pvr2_i2c_handler_functions hfuncs = { + .detach = (void (*)(void *))decoder_detach, + .check = (int (*)(void *))decoder_check, + .update = (void (*)(void *))decoder_update, + .describe = (unsigned int (*)(void *,char *,unsigned int))decoder_describe, +}; + + +int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw, + struct pvr2_i2c_client *cp) +{ + struct pvr2_v4l_cx2584x *ctxt; + + if (hdw->decoder_ctrl) return 0; + if (cp->handler) return 0; + if (!decoder_detect(cp)) return 0; + + ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + if (!ctxt) return 0; + memset(ctxt,0,sizeof(*ctxt)); + + ctxt->handler.func_data = ctxt; + ctxt->handler.func_table = &hfuncs; + ctxt->ctrl.ctxt = ctxt; + ctxt->ctrl.detach = (void (*)(void *))decoder_detach; + ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable; + ctxt->ctrl.tuned = (int (*)(void *))decoder_is_tuned; + ctxt->ctrl.force_reset = (void (*)(void*))decoder_reset; + ctxt->client = cp; + ctxt->hdw = hdw; + ctxt->stale_mask = (1 << (sizeof(decoder_ops)/ + sizeof(decoder_ops[0]))) - 1; + hdw->decoder_ctrl = &ctxt->ctrl; + cp->handler = &ctxt->handler; + pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x cx2584x V4L2 handler set up", + cp->client->addr); + return !0; +} + + + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h new file mode 100644 index 000000000..5dea8d7b3 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h @@ -0,0 +1,54 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * 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 + * + */ + +#ifndef __PVRUSB2_CX2584X_V4L_H +#define __PVRUSB2_CX2584X_V4L_H + +/* + + This module connects the pvrusb2 driver to the I2C chip level + driver which handles combined device audio & video processing. + This interface is used internally by the driver; higher level code + should only interact through the interface provided by + pvrusb2-hdw.h. + +*/ + +#include "compat.h" + + +#include "pvrusb2-i2c-core.h" + +int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); + + +#endif /* __PVRUSB2_CX2584X_V4L_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c index d537e380f..897e74d42 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c @@ -28,6 +28,7 @@ #include "pvrusb2-tuner.h" #include "pvrusb2-demod.h" #include "pvrusb2-video-v4l.h" +#include "pvrusb2-cx2584x-v4l.h" #define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) @@ -69,6 +70,11 @@ void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) return; } } + if (id == I2C_DRIVERID_CX25840) { + if (pvr2_i2c_cx2584x_v4l_setup(hdw,cp)) { + return; + } + } if (id == I2C_DRIVERID_SAA711X) { if (pvr2_i2c_decoder_v4l_setup(hdw,cp)) { return; -- cgit v1.2.3 From b07b47fc9fabe18a4073a47397d4bad39ada93b9 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 25 Mar 2006 17:01:11 -0600 Subject: Integrate pvrusb2 with wm8775 From: Mike Isely Implement new logic in pvrusb2 to operate wm8775, which is needed for new model 24xxx hardware which has a wm8775 inside it (which is used to digitize audio from composite and s-video input). Signed-off-by: Mike Isely --- .../media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c | 6 + linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c | 171 +++++++++++++++++++++ linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h | 54 +++++++ 3 files changed, 231 insertions(+) create mode 100644 linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c create mode 100644 linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c index 897e74d42..fc9d76792 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c @@ -29,6 +29,7 @@ #include "pvrusb2-demod.h" #include "pvrusb2-video-v4l.h" #include "pvrusb2-cx2584x-v4l.h" +#include "pvrusb2-wm8775.h" #define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) @@ -75,6 +76,11 @@ void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) return; } } + if (id == I2C_DRIVERID_WM8775) { + if (pvr2_i2c_wm8775_setup(hdw,cp)) { + return; + } + } if (id == I2C_DRIVERID_SAA711X) { if (pvr2_i2c_decoder_v4l_setup(hdw,cp)) { return; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c new file mode 100644 index 000000000..79334980c --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c @@ -0,0 +1,171 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * 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 + * + */ + +/* + + This source file is specifically designed to interface with the + wm8775. + +*/ + +#include "pvrusb2-wm8775.h" +#include "pvrusb2-i2c-cmd-v4l2.h" + + +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" +#include +#include +#include +#include + +struct pvr2_v4l_wm8775 { + struct pvr2_i2c_handler handler; + struct pvr2_i2c_client *client; + struct pvr2_hdw *hdw; + unsigned long stale_mask; +}; + + +static void set_input(struct pvr2_v4l_wm8775 *ctxt) +{ + struct v4l2_audio inp; + struct pvr2_hdw *hdw = ctxt->hdw; + int msk = 0; + + memset(&inp,0,sizeof(inp)); + + pvr2_trace(PVR2_TRACE_CHIPS,"i2c wm8775 set_input(val=%d msk=0x%x)", + hdw->controls[PVR2_CID_INPUT].value,msk); + + // Always point to input #1 no matter what + inp.index = 2; + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_AUDIO,&inp); +} + + +static int check_input(struct pvr2_v4l_wm8775 *ctxt) +{ + struct pvr2_hdw *hdw = ctxt->hdw; + return hdw->controls[PVR2_CID_INPUT].dirty != 0; +} + + +struct pvr2_v4l_wm8775_ops { + void (*update)(struct pvr2_v4l_wm8775 *); + int (*check)(struct pvr2_v4l_wm8775 *); +}; + + +static const struct pvr2_v4l_wm8775_ops wm8775_ops[] = { + { .update = set_input, .check = check_input}, +}; + + +static unsigned int wm8775_describe(struct pvr2_v4l_wm8775 *ctxt, + char *buf,unsigned int cnt) +{ + return scnprintf(buf,cnt,"handler: pvrusb2-wm8775"); +} + + +static void wm8775_detach(struct pvr2_v4l_wm8775 *ctxt) +{ + ctxt->client->handler = 0; + kfree(ctxt); +} + + +static int wm8775_check(struct pvr2_v4l_wm8775 *ctxt) +{ + unsigned long msk; + unsigned int idx; + + for (idx = 0; idx < sizeof(wm8775_ops)/sizeof(wm8775_ops[0]); + idx++) { + msk = 1 << idx; + if (ctxt->stale_mask & msk) continue; + if (wm8775_ops[idx].check(ctxt)) { + ctxt->stale_mask |= msk; + } + } + return ctxt->stale_mask != 0; +} + + +static void wm8775_update(struct pvr2_v4l_wm8775 *ctxt) +{ + unsigned long msk; + unsigned int idx; + + for (idx = 0; idx < sizeof(wm8775_ops)/sizeof(wm8775_ops[0]); + idx++) { + msk = 1 << idx; + if (!(ctxt->stale_mask & msk)) continue; + ctxt->stale_mask &= ~msk; + wm8775_ops[idx].update(ctxt); + } +} + + +const static struct pvr2_i2c_handler_functions hfuncs = { + .detach = (void (*)(void *))wm8775_detach, + .check = (int (*)(void *))wm8775_check, + .update = (void (*)(void *))wm8775_update, + .describe = (unsigned int (*)(void *,char *,unsigned int))wm8775_describe, +}; + + +int pvr2_i2c_wm8775_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) +{ + struct pvr2_v4l_wm8775 *ctxt; + + if (cp->handler) return 0; + + ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + if (!ctxt) return 0; + memset(ctxt,0,sizeof(*ctxt)); + + ctxt->handler.func_data = ctxt; + ctxt->handler.func_table = &hfuncs; + ctxt->client = cp; + ctxt->hdw = hdw; + ctxt->stale_mask = (1 << (sizeof(wm8775_ops)/ + sizeof(wm8775_ops[0]))) - 1; + cp->handler = &ctxt->handler; + pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x wm8775 V4L2 handler set up", + cp->client->addr); + return !0; +} + + + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h new file mode 100644 index 000000000..15ee1e215 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h @@ -0,0 +1,54 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely + * Copyright (C) 2004 Aurelien Alleaume + * + * 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 + * + */ + +#ifndef __PVRUSB2_WM8775_H +#define __PVRUSB2_WM8775_H + +/* + + This module connects the pvrusb2 driver to the I2C chip level + driver which performs analog -> digital audio conversion for + external audio inputs. This interface is used internally by the + driver; higher level code should only interact through the + interface provided by pvrusb2-hdw.h. + +*/ + +#include "compat.h" + + +#include "pvrusb2-i2c-core.h" + +int pvr2_i2c_wm8775_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); + + +#endif /* __PVRUSB2_WM8775_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ -- cgit v1.2.3 From 52e19741ce48048ada5cc3b87c065dec5b100b8d Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 25 Mar 2006 17:02:46 -0600 Subject: pvrusb2 request_module() adjustments From: Mike Isely Request new modules that pvrusb2 may need. This is a stop-gap; we need to do a better job of this. Also reorder the requests in the extremely feeble hope that we can avoid some initialization races in the hardware. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c index c554671af..c070fc195 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c @@ -130,11 +130,13 @@ static int __init pvr_init(void) /* Auto-load various support modules (with which we may indirectly interact) */ - request_module("tuner"); - request_module("tveeprom"); request_module("msp3400"); + request_module("cx25840"); request_module("saa7115"); + request_module("tuner"); + request_module("tveeprom"); request_module("tda9887"); + request_module("wm8775"); class_ptr = pvr2_sysfs_class_create(); -- cgit v1.2.3 From aba6cef68fb994d3bea36f80619c35c676de0cdb Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 25 Mar 2006 17:43:14 -0600 Subject: Reduce FWSEND in cx25840 module due to certain I2C bus adapter limits From: Mike Isely The FWSEND parameter controls the size of the firmware chunks sent down the I2C bus to the chip. Previously this had been set to 1024 but unfortunately some I2C implementations can't transfer data in such big gulps. Specifically, the pvrusb2 driver has a hard limit of around 60 bytes, due to the encapsulation there of I2C traffic into USB messages. So we have to significantly reduce this parameter. Acked-by: Hans Verkuil Signed-off-by: Mike Isely --- linux/drivers/media/video/cx25840/cx25840-firmware.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx25840/cx25840-firmware.c b/linux/drivers/media/video/cx25840/cx25840-firmware.c index 96a45a4be..539de43dd 100644 --- a/linux/drivers/media/video/cx25840/cx25840-firmware.c +++ b/linux/drivers/media/video/cx25840/cx25840-firmware.c @@ -26,7 +26,17 @@ #include "cx25840-core.h" #define FWFILE "v4l-cx25840.fw" -#define FWSEND 1024 + +/* + * Mike Isely - The FWSEND parameter controls the + * size of the firmware chunks sent down the I2C bus to the chip. + * Previously this had been set to 1024 but unfortunately some I2C + * implementations can't transfer data in such big gulps. + * Specifically, the pvrusb2 driver has a hard limit of around 60 + * bytes, due to the encapsulation there of I2C traffic into USB + * messages. So we have to significantly reduce this parameter. + */ +#define FWSEND 48 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) #define FWDEV(x) &((x)->adapter->dev) -- cgit v1.2.3 From 6de66fc86dee706f81addaad380002538f65219c Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 25 Mar 2006 22:18:49 -0600 Subject: Include additional pvrusb2 object files in build From: Mike Isely Some new sources have been added into pvrusb2 to deal with cx25840 and wm8775 modules in V4L. Need to add them to the build. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/Makefile | 1 + 1 file changed, 1 insertion(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/Makefile b/linux/drivers/media/video/pvrusb2/Makefile index c83742fb1..4a37fe723 100644 --- a/linux/drivers/media/video/pvrusb2/Makefile +++ b/linux/drivers/media/video/pvrusb2/Makefile @@ -4,6 +4,7 @@ pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ pvrusb2-eeprom.o pvrusb2-tuner.o pvrusb2-demod.o \ pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \ pvrusb2-sysfs.o pvrusb2-context.o pvrusb2-io.o \ + pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \ pvrusb2-ioread.o pvrusb2-debugifc.o obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o -- cgit v1.2.3 From 2fc29f23f48a916f556d40ce8498ed27a0b1f0fc Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 25 Mar 2006 22:19:47 -0600 Subject: Update pvrusb2's cx25840 module handling to use new routing API From: Mike Isely Signed-off-by: Mike Isely --- .../media/video/pvrusb2/pvrusb2-cx2584x-v4l.c | 40 +++++++++++----------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index df1eeefd5..3cbce8eba 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -34,7 +34,7 @@ #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" -#include +#include #include #include #include @@ -52,39 +52,39 @@ struct pvr2_v4l_cx2584x { static void set_input(struct pvr2_v4l_cx2584x *ctxt) { struct pvr2_hdw *hdw = ctxt->hdw; - struct v4l2_audio inp; - int msk = 0; + struct v4l2_routing route; + enum cx25840_video_input vid_input; + enum cx25840_audio_input aud_input; - int v = 0; + memset(&route,0,sizeof(route)); switch(hdw->controls[PVR2_CID_INPUT].value) { case PVR2_CVAL_INPUT_TV: - msk = (8 << 16) | 7; + vid_input = CX25840_COMPOSITE7; + aud_input = CX25840_AUDIO8; break; case PVR2_CVAL_INPUT_COMPOSITE: - msk = (0 << 16) | 3; + vid_input = CX25840_COMPOSITE3; + aud_input = CX25840_AUDIO_SERIAL; break; case PVR2_CVAL_INPUT_SVIDEO: - msk = (0 << 16) | 0x510; + vid_input = CX25840_SVIDEO1; + aud_input = CX25840_AUDIO_SERIAL; break; case PVR2_CVAL_INPUT_RADIO: - msk = 0; /* FIXME: Need to figure out radio */ - break; default: + // Just set it to be composite input for now... + vid_input = CX25840_COMPOSITE3; + aud_input = CX25840_AUDIO_SERIAL; break; } - pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_input(val=%d msk=0x%x)", - hdw->controls[PVR2_CID_INPUT].value,msk); - - v = msk & 0x0000ffffu; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x S_INPUT val=0x%x",v); - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_INPUT,&v); - v = (msk >> 16) & 0x0000ffffu; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x S_AUDIO val=0x%x",v); - memset(&inp,0,sizeof(inp)); - inp.index = v; - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_AUDIO,&inp); + pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_input vid=0x%x aud=0x%x", + vid_input,aud_input); + route.input = (u32)vid_input; + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route); + route.input = (u32)aud_input; + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route); } -- cgit v1.2.3 From 9d7a8568c08e90097778aeac7bb66db7ddb183b8 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 26 Mar 2006 03:43:36 -0500 Subject: cxusb: add support for FusionHDTV USB portable remote control From: Michael Krufky - Added keycodes for the DViCO FusionHDTV portable remote control. - Enabled the remote control for both versions of FusionHDTV DVB-T USB and the FusionHDTV 5 USB Gold (ATSC) Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/dvb-usb/cxusb.c | 58 +++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index f20b2bda4..56ea5c6dc 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -233,6 +233,49 @@ static struct dvb_usb_rc_key dvico_mce_rc_keys[] = { { 0xfe, 0x4e, KEY_POWER }, }; +static struct dvb_usb_rc_key dvico_portable_rc_keys[] = { + { 0xfc, 0x02, KEY_SETUP }, /* Profile */ + { 0xfc, 0x43, KEY_POWER2 }, + { 0xfc, 0x06, KEY_EPG }, + { 0xfc, 0x5a, KEY_BACK }, + { 0xfc, 0x05, KEY_MENU }, + { 0xfc, 0x47, KEY_INFO }, + { 0xfc, 0x01, KEY_TAB }, + { 0xfc, 0x42, KEY_PREVIOUSSONG },/* Replay */ + { 0xfc, 0x49, KEY_VOLUMEUP }, + { 0xfc, 0x09, KEY_VOLUMEDOWN }, + { 0xfc, 0x54, KEY_CHANNELUP }, + { 0xfc, 0x0b, KEY_CHANNELDOWN }, + /* + { 0xfc, 0x56, KEY_CAMERA }, + Camera and Record keys both send the same code, + but the camera key doesn't repeat. + */ + { 0xfc, 0x40, KEY_TUNER }, /* ATV/DTV */ + { 0xfc, 0x45, KEY_OPEN }, + { 0xfc, 0x19, KEY_1 }, + { 0xfc, 0x18, KEY_2 }, + { 0xfc, 0x1b, KEY_3 }, + { 0xfc, 0x1a, KEY_4 }, + { 0xfc, 0x58, KEY_5 }, + { 0xfc, 0x59, KEY_6 }, + { 0xfc, 0x15, KEY_7 }, + { 0xfc, 0x14, KEY_8 }, + { 0xfc, 0x17, KEY_9 }, + { 0xfc, 0x44, KEY_ANGLE }, /* Aspect */ + { 0xfc, 0x55, KEY_0 }, + { 0xfc, 0x07, KEY_ZOOM }, + { 0xfc, 0x0a, KEY_REWIND }, + { 0xfc, 0x08, KEY_PLAYPAUSE }, + { 0xfc, 0x4b, KEY_FASTFORWARD }, + { 0xfc, 0x5b, KEY_MUTE }, + { 0xfc, 0x04, KEY_STOP }, + { 0xfc, 0x56, KEY_RECORD }, + { 0xfc, 0x57, KEY_POWER }, + { 0xfc, 0x41, KEY_UNKNOWN }, /* INPUT */ + { 0xfc, 0x00, KEY_UNKNOWN }, /* HD */ +}; + static int cxusb_dee1601_demod_init(struct dvb_frontend* fe) { static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x28 }; @@ -511,6 +554,11 @@ static struct dvb_usb_properties cxusb_bluebird_lgh064f_properties = { .i2c_algo = &cxusb_i2c_algo, + .rc_interval = 100, + .rc_key_map = dvico_portable_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dvico_portable_rc_keys), + .rc_query = cxusb_rc_query, + .generic_bulk_ctrl_endpoint = 0x01, /* parameter for the MPEG2-data transfer */ .urb = { @@ -600,6 +648,11 @@ static struct dvb_usb_properties cxusb_bluebird_lgz201_properties = { .i2c_algo = &cxusb_i2c_algo, + .rc_interval = 100, + .rc_key_map = dvico_portable_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dvico_portable_rc_keys), + .rc_query = cxusb_rc_query, + .generic_bulk_ctrl_endpoint = 0x01, /* parameter for the MPEG2-data transfer */ .urb = { @@ -640,6 +693,11 @@ static struct dvb_usb_properties cxusb_bluebird_dtt7579_properties = { .i2c_algo = &cxusb_i2c_algo, + .rc_interval = 100, + .rc_key_map = dvico_portable_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dvico_portable_rc_keys), + .rc_query = cxusb_rc_query, + .generic_bulk_ctrl_endpoint = 0x01, /* parameter for the MPEG2-data transfer */ .urb = { -- cgit v1.2.3 From 1513c16af8fe4dd3f0afa6548cb39331c24bd01d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 26 Mar 2006 08:53:06 -0300 Subject: Some cleanups at make xconfig From: Mauro Carvalho Chehab Now, it doesn't depend on kernel Kconfig stuff: - It generates a Kconfig.kern based on unsolved dependencies. - It assumes that all needed dependencies were compiled as module. Only Multimedia tree is showed to the user. To work, user should have runned xconfig before at the normal kernel tree. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/Kconfig | 68 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 linux/drivers/media/Kconfig (limited to 'linux/drivers') diff --git a/linux/drivers/media/Kconfig b/linux/drivers/media/Kconfig new file mode 100644 index 000000000..baa9f58be --- /dev/null +++ b/linux/drivers/media/Kconfig @@ -0,0 +1,68 @@ +# +# Multimedia device configuration +# + +menu "Multimedia devices" + +config VIDEO_DEV + tristate "Video For Linux" + ---help--- + Support for audio/video capture and overlay devices and FM radio + cards. The exact capabilities of each device vary. User tools for + this are available from + . + + This kernel includes support for the new Video for Linux Two API, + (V4L2) as well as the original system. Drivers and applications + need to be rewritten to use V4L2, but drivers for popular cards + and applications for most video capture functions already exist. + + Documentation for the original API is included in the file + . Documentation for V4L2 is + available on the web at . + + To compile this driver as a module, choose M here: the + module will be called videodev. + +source "drivers/media/video/Kconfig" + +source "drivers/media/radio/Kconfig" + +source "drivers/media/dvb/Kconfig" + +source "drivers/media/common/Kconfig" + +config VIDEO_TUNER + tristate + +config VIDEO_BUF + tristate + +config VIDEO_BUF_DVB + tristate + +config VIDEO_BTCX + tristate + +config VIDEO_IR + tristate + +config VIDEO_TVEEPROM + tristate + +config USB_DABUSB + tristate "DABUSB driver" + depends on USB + ---help--- + A Digital Audio Broadcasting (DAB) Receiver for USB and Linux + brought to you by the DAB-Team + . This driver can be taken + as an example for URB-based bulk, control, and isochronous + transactions. URB's are explained in + . + + To compile this driver as a module, choose M here: the + module will be called dabusb. + +endmenu + -- cgit v1.2.3 From 666910f34117901b1db8e6baf030d84451d6e4ab Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 26 Mar 2006 08:54:09 -0300 Subject: Whitespace cleanup From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/Kconfig | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/Kconfig b/linux/drivers/media/Kconfig index baa9f58be..fffc711c2 100644 --- a/linux/drivers/media/Kconfig +++ b/linux/drivers/media/Kconfig @@ -51,18 +51,18 @@ config VIDEO_TVEEPROM tristate config USB_DABUSB - tristate "DABUSB driver" - depends on USB - ---help--- - A Digital Audio Broadcasting (DAB) Receiver for USB and Linux - brought to you by the DAB-Team - . This driver can be taken - as an example for URB-based bulk, control, and isochronous - transactions. URB's are explained in - . - - To compile this driver as a module, choose M here: the - module will be called dabusb. + tristate "DABUSB driver" + depends on USB + ---help--- + A Digital Audio Broadcasting (DAB) Receiver for USB and Linux + brought to you by the DAB-Team + . This driver can be taken + as an example for URB-based bulk, control, and isochronous + transactions. URB's are explained in + . + + To compile this driver as a module, choose M here: the + module will be called dabusb. endmenu -- cgit v1.2.3 From f44c0bc49cbb5ee3bd7d89c602873840d40c8b25 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 26 Mar 2006 15:45:31 -0600 Subject: Get rid of static PVR2_CID_COUNT in pvrusb2 From: Mike Isely Eliminate the need to track the number pvrusb2 CIDs at compile time from within the pvrusb2 driver. This is part of a control structure cleanup. Signed-off-by: Mike Isely --- .../media/video/pvrusb2/pvrusb2-hdw-internal.h | 2 +- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 80 ++++++++++++++++++---- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h | 6 +- linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c | 2 +- 4 files changed, 73 insertions(+), 17 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 05e44385b..25854375b 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -211,7 +211,7 @@ struct pvr2_hdw { struct pvr2_audio_stat *audio_stat; /* Every last bit of controllable state */ - struct pvr2_ctl_state controls[PVR2_CID_COUNT]; + struct pvr2_ctl_state *controls; }; int pvr2_hdw_set_ctl_value_internal(struct pvr2_hdw *hdw, diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index ec2b5eeae..81a35a8e2 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -97,6 +97,7 @@ struct pvr2_ctl_def { int min_value; int skip_init; int default_value; + int is_valid; const char **value_defs_ptr; unsigned int value_defs_count; }; @@ -184,63 +185,73 @@ static int pvr2_ctl_get_subsys_stream_mask(struct pvr2_hdw *hdw,int ctl_id); static int pvr2_ctl_set_subsys_stream_mask(struct pvr2_hdw *hdw,int ctl_id, int val); -static struct pvr2_ctl_def control_defs[PVR2_CID_COUNT] = +static struct pvr2_ctl_def control_defs[] = { [PVR2_CID_BRIGHTNESS] = { + .is_valid = !0, .name = "Brightness", .min_value = 0, .max_value = 255, .default_value = 128, }, [PVR2_CID_CONTRAST] = { + .is_valid = !0, .name = "Contrast", .min_value = 0, .max_value = 127, .default_value = 68, }, [PVR2_CID_SATURATION] = { + .is_valid = !0, .name = "Saturation", .min_value = 0, .max_value = 127, .default_value = 64, }, [PVR2_CID_HUE] = { + .is_valid = !0, .name = "Hue", .min_value = -128, .max_value = 127, .default_value = 0, }, [PVR2_CID_VOLUME] = { + .is_valid = !0, .name = "Volume", .min_value = 0, .max_value = 65535, .default_value = 65535, }, [PVR2_CID_BALANCE] = { + .is_valid = !0, .name = "Balance", .min_value = -32768, .max_value = 32767, .default_value = 0, }, [PVR2_CID_BASS] = { + .is_valid = !0, .name = "Bass", .min_value = -32768, .max_value = 32767, .default_value = 0, }, [PVR2_CID_TREBLE] = { + .is_valid = !0, .name = "Treble", .min_value = -32768, .max_value = 32767, .default_value = 0, }, [PVR2_CID_MUTE] = { + .is_valid = !0, .name = "Mute", .min_value = 0, .max_value = 1, .default_value = 0, }, [PVR2_CID_SRATE] = { + .is_valid = !0, .name = "Sample rate", .min_value = PVR2_CVAL_SRATE_MIN, .max_value = PVR2_CVAL_SRATE_MAX, @@ -248,6 +259,7 @@ static struct pvr2_ctl_def control_defs[PVR2_CID_COUNT] = VDEF(control_values_srate), }, [PVR2_CID_AUDIOBITRATE] = { + .is_valid = !0, .name = "Audio Bitrate", .min_value = PVR2_CVAL_AUDIOBITRATE_MIN, .max_value = PVR2_CVAL_AUDIOBITRATE_MAX, @@ -255,12 +267,14 @@ static struct pvr2_ctl_def control_defs[PVR2_CID_COUNT] = VDEF(control_values_audiobitrate), }, [PVR2_CID_AUDIOCRC] = { + .is_valid = !0, .name = "Audio CRC", .min_value = 0, .max_value = 1, .default_value = 1, }, [PVR2_CID_AUDIOEMPHASIS] = { + .is_valid = !0, .name = "Audio Emphasis", .min_value = PVR2_CVAL_AUDIOEMPHASIS_MIN, .max_value = PVR2_CVAL_AUDIOEMPHASIS_MAX, @@ -268,24 +282,28 @@ static struct pvr2_ctl_def control_defs[PVR2_CID_COUNT] = VDEF(control_values_audioemphasis), }, [PVR2_CID_VBR] = { + .is_valid = !0, .name = "Variable video bitrate", .min_value = 0, .max_value = 1, .default_value = 0, }, [PVR2_CID_AVERAGEVIDEOBITRATE] = { + .is_valid = !0, .name = "Average video bitrate", .min_value = 1, .max_value = 20000000, .default_value = 6000000, }, [PVR2_CID_PEAKVIDEOBITRATE] = { + .is_valid = !0, .name = "Peak video bitrate", .min_value = 1, .max_value = 20000000, .default_value = 6000000, }, [PVR2_CID_VIDEOSTANDARD] = { + .is_valid = !0, .name = "Video Standard", .min_value = PVR2_CVAL_VIDEOSTANDARD_MIN, .max_value = PVR2_CVAL_VIDEOSTANDARD_MAX, @@ -293,6 +311,7 @@ static struct pvr2_ctl_def control_defs[PVR2_CID_COUNT] = VDEF(control_values_videostandard), }, [PVR2_CID_INPUT] = { + .is_valid = !0, .name = "Video Source", .min_value = PVR2_CVAL_INPUT_MIN, .max_value = PVR2_CVAL_INPUT_MAX, @@ -300,6 +319,7 @@ static struct pvr2_ctl_def control_defs[PVR2_CID_COUNT] = VDEF(control_values_input), }, [PVR2_CID_AUDIOMODE] = { + .is_valid = !0, .name = "Audio Mode", .min_value = PVR2_CVAL_AUDIOMODE_MIN, .max_value = PVR2_CVAL_AUDIOMODE_MAX, @@ -307,48 +327,56 @@ static struct pvr2_ctl_def control_defs[PVR2_CID_COUNT] = VDEF(control_values_audiomode), }, [PVR2_CID_FREQUENCY] = { + .is_valid = !0, .name = "Tuner Frequency (Hz)", .min_value = 55250000L, .max_value = 850000000L, .default_value = 175250000L, }, [PVR2_CID_HRES] = { + .is_valid = !0, .name = "Horizontal capture resolution", .min_value = 320, .max_value = 720, .default_value = 720, }, [PVR2_CID_VRES] = { + .is_valid = !0, .name = "Vertical capture resolution", .min_value = 200, .max_value = 625, .default_value = 480, }, [PVR2_CID_INTERLACE] = { + .is_valid = !0, .name = "Interlace mode", .min_value = 0, .max_value = 1, .default_value = 0, }, [PVR2_CID_AUDIOLAYER] = { + .is_valid = !0, .name = "Audio Layer", .min_value = 0, /* This is all a wild guess */ .max_value = 3, .default_value = 2, /* Appears to be all that is supported */ }, [PVR2_CID_CHANNEL] = { + .is_valid = !0, .name = "Channel", .min_value = 0, .max_value = FREQTABLE_SIZE, .default_value = 0, }, [PVR2_CID_CHANPROG_ID] = { + .is_valid = !0, .name = "Channel Program ID", .min_value = 0, .max_value = FREQTABLE_SIZE, .default_value = 0, }, [PVR2_CID_CHANPROG_FREQ] = { + .is_valid = !0, .name = "Channel Program Frequency", .min_value = 55250000L, .max_value = 850000000L, @@ -357,18 +385,21 @@ static struct pvr2_ctl_def control_defs[PVR2_CID_COUNT] = .get_func = pvr2_ctl_get_chanprog_id, }, [PVR2_CID_SIGNAL_PRESENT] = { + .is_valid = !0, .name = "Signal Present", .min_value = 0, .max_value = 1, .get_func = pvr2_ctl_get_signal, }, [PVR2_CID_STREAMING_ENABLED] = { + .is_valid = !0, .name = "Streaming Enabled", .min_value = 0, .max_value = 1, .get_func = pvr2_ctl_get_streaming, }, [PVR2_CID_HSM] = { + .is_valid = !0, .name = "USB Speed", .min_value = PVR2_CVAL_HSM_MIN, .max_value = PVR2_CVAL_HSM_MAX, @@ -376,6 +407,7 @@ static struct pvr2_ctl_def control_defs[PVR2_CID_COUNT] = VDEF(control_values_hsm), }, [PVR2_CID_SUBSYS_MASK] = { + .is_valid = !0, .name = "Subsystem enabled mask", .min_value = 0, .max_value = 0x7fffffff, @@ -384,6 +416,7 @@ static struct pvr2_ctl_def control_defs[PVR2_CID_COUNT] = .set_func = pvr2_ctl_set_subsys_mask, }, [PVR2_CID_SUBSYS_STREAM_MASK] = { + .is_valid = !0, .name = "Subsystem stream mask", .min_value = 0, .max_value = 0x7fffffff, @@ -395,6 +428,7 @@ static struct pvr2_ctl_def control_defs[PVR2_CID_COUNT] = #undef VDEF +#define CTRL_COUNT (sizeof(control_defs)/sizeof(control_defs[0])) const char *pvr2_config_get_name(enum pvr2_config cfg) { @@ -1106,7 +1140,7 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) pvr2_i2c_core_init(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; - for (idx = 0; idx < PVR2_CID_COUNT; idx++) { + for (idx = 0; idx < CTRL_COUNT; idx++) { if (control_defs[idx].skip_init) continue; pvr2_hdw_set_ctl_value_internal( hdw,idx,control_defs[idx].default_value); @@ -1259,6 +1293,9 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw,pvr2_device_names[hdw_type]); if (!hdw) goto fail; memset(hdw,0,sizeof(*hdw)); + hdw->controls = kmalloc(sizeof(struct pvr2_ctl_state) * CTRL_COUNT, + GFP_KERNEL); + if (!hdw->controls) goto fail; hdw->hdw_type = hdw_type; hdw->eeprom_addr = -1; @@ -1322,6 +1359,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, if (hdw->ctl_write_urb) usb_free_urb(hdw->ctl_write_urb); if (hdw->ctl_read_buffer) kfree(hdw->ctl_read_buffer); if (hdw->ctl_write_buffer) kfree(hdw->ctl_write_buffer); + if (hdw->controls) kfree(hdw->controls); kfree(hdw); } return 0; @@ -1386,6 +1424,7 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) unit_pointers[hdw->unit_number] = 0; } } while (0); up(&pvr2_unit_sem); + kfree(hdw->controls); kfree(hdw); } @@ -1447,7 +1486,9 @@ int pvr2_hdw_get_ctl_value(struct pvr2_hdw *hdw,unsigned int ctl_id) { int ret = 0; - if (ctl_id >= PVR2_CID_COUNT) return 0; + if (ctl_id >= CTRL_COUNT) return 0; + if (!control_defs[ctl_id].is_valid) return 0; + LOCK_TAKE(hdw->big_lock); do { if (control_defs[ctl_id].get_func) { ret = control_defs[ctl_id].get_func(hdw,ctl_id); @@ -1462,7 +1503,8 @@ int pvr2_hdw_get_ctl_value(struct pvr2_hdw *hdw,unsigned int ctl_id) /* Return true if control is writable */ int pvr2_hdw_get_ctl_rw(struct pvr2_hdw *hdw,unsigned int ctl_id) { - if (ctl_id >= PVR2_CID_COUNT) return 0; + if (ctl_id >= CTRL_COUNT) return 0; + if (!control_defs[ctl_id].is_valid) return 0; if (control_defs[ctl_id].get_func && !control_defs[ctl_id].set_func) { return 0; } @@ -1473,7 +1515,8 @@ int pvr2_hdw_get_ctl_rw(struct pvr2_hdw *hdw,unsigned int ctl_id) /* Retrieve legal minimum value for a given control */ int pvr2_hdw_get_ctl_min_value(struct pvr2_hdw *hdw,unsigned int ctl_id) { - if (ctl_id >= PVR2_CID_COUNT) return 0; + if (ctl_id >= CTRL_COUNT) return 0; + if (!control_defs[ctl_id].is_valid) return 0; return control_defs[ctl_id].min_value; } @@ -1481,7 +1524,8 @@ int pvr2_hdw_get_ctl_min_value(struct pvr2_hdw *hdw,unsigned int ctl_id) /* Retrieve legal maximum value for a given control */ int pvr2_hdw_get_ctl_max_value(struct pvr2_hdw *hdw,unsigned int ctl_id) { - if (ctl_id >= PVR2_CID_COUNT) return 0; + if (ctl_id >= CTRL_COUNT) return 0; + if (!control_defs[ctl_id].is_valid) return 0; return control_defs[ctl_id].max_value; } @@ -1492,7 +1536,8 @@ int pvr2_hdw_set_ctl_value_internal(struct pvr2_hdw *hdw, unsigned int ctl_id,int value) { int ret; - if (ctl_id >= PVR2_CID_COUNT) return -EINVAL; + if (ctl_id >= CTRL_COUNT) return -EINVAL; + if (!control_defs[ctl_id].is_valid) return -EINVAL; if (value < control_defs[ctl_id].min_value) return -EINVAL; if (value > control_defs[ctl_id].max_value) return -EINVAL; if (control_defs[ctl_id].set_func) { @@ -1532,7 +1577,8 @@ const char *pvr2_hdw_get_ctl_value_name(struct pvr2_hdw *hdw, int value) { struct pvr2_ctl_def *cdef; - if (ctl_id >= PVR2_CID_COUNT) return 0; + if (ctl_id >= CTRL_COUNT) return 0; + if (!control_defs[ctl_id].is_valid) return 0; cdef = control_defs + ctl_id; if (! cdef->value_defs_ptr) return 0; if (value >= cdef->value_defs_count) return 0; @@ -1543,11 +1589,21 @@ const char *pvr2_hdw_get_ctl_value_name(struct pvr2_hdw *hdw, /* Retrieve string name for given control */ const char *pvr2_hdw_get_ctl_name(struct pvr2_hdw *hdw,unsigned int ctl_id) { - if (ctl_id >= PVR2_CID_COUNT) return 0; + if (ctl_id >= CTRL_COUNT) return 0; + if (!control_defs[ctl_id].is_valid) return 0; return control_defs[ctl_id].name; } +/* Return true if control ID is a valid id */ +int pvr2_hdw_get_ctl_valid(struct pvr2_hdw *hdw,unsigned int ctl_id) +{ + if (ctl_id >= CTRL_COUNT) return 0; + if (!control_defs[ctl_id].is_valid) return 0; + return !0; +} + + /* Commit all control changes made up to this point. Subsystems can be indirectly affected by these changes. For a given set of things being committed, we'll clear the affected subsystem bits and then once we're @@ -1595,7 +1651,7 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) } } - for (idx = 0; idx < PVR2_CID_COUNT; idx++) { + for (idx = 0; idx < CTRL_COUNT; idx++) { if (!hdw->controls[idx].dirty) continue; if (!commit_flag) { commit_flag = !0; @@ -1671,7 +1727,7 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) the client drivers in order to keep everything in sync */ pvr2_i2c_core_check_stale(hdw); - for (idx = 0; idx < PVR2_CID_COUNT; idx++) { + for (idx = 0; idx < CTRL_COUNT; idx++) { hdw->controls[idx].dirty = 0; } @@ -1733,7 +1789,7 @@ void pvr2_hdw_poll_trigger(struct pvr2_hdw *hdw) through this value. */ unsigned int pvr2_hdw_get_ctl_count(struct pvr2_hdw *hdw) { - return PVR2_CID_COUNT; + return CTRL_COUNT; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h index aa6d9029a..6cc841b56 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -59,9 +59,6 @@ #define PVR2_CID_SUBSYS_MASK 30 #define PVR2_CID_SUBSYS_STREAM_MASK 31 -/* Number of state variables */ -#define PVR2_CID_COUNT 32 - /* Legal values for the SRATE state variable */ #define PVR2_CVAL_SRATE_48 0 #define PVR2_CVAL_SRATE_44_1 1 @@ -234,6 +231,9 @@ int pvr2_hdw_set_ctl_value(struct pvr2_hdw *,unsigned int ctl_id,int value); /* Retrieve string name for given control */ const char *pvr2_hdw_get_ctl_name(struct pvr2_hdw *,unsigned int ctl_id); +/* Return true if control id is a valid id */ +int pvr2_hdw_get_ctl_valid(struct pvr2_hdw *,unsigned int ctl_id); + /* Retrieve string name for a given control value (returns a null pointer for any invalid combinations). */ const char *pvr2_hdw_get_ctl_value_name(struct pvr2_hdw *, diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index f82fd643c..6b81def26 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -30,7 +30,7 @@ #define pvr2_sysfs_trace(...) pvr2_trace(PVR2_TRACE_SYSFS,__VA_ARGS__) -static char *item_names[PVR2_CID_COUNT] = { +static char *item_names[] = { [PVR2_CID_BRIGHTNESS] = "ctl_brightness", [PVR2_CID_CONTRAST] = "ctl_contrast", [PVR2_CID_SATURATION] = "ctl_saturation", -- cgit v1.2.3 From da1de2e42e2a5c0290c15edbbd4017176ff0b1c2 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 26 Mar 2006 15:46:35 -0600 Subject: Propagate default control values out to V4L apps from within pvrusb2 From: Mike Isely When a V4L app queries a pvrusb2 control, make sure we also tell it what the control's default value is. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 9 +++++++++ linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h | 3 +++ linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 2 ++ 3 files changed, 14 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 81a35a8e2..a0b0cd8aa 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1530,6 +1530,15 @@ int pvr2_hdw_get_ctl_max_value(struct pvr2_hdw *hdw,unsigned int ctl_id) } +/* Retrieve default value for a given control */ +int pvr2_hdw_get_ctl_default_value(struct pvr2_hdw *hdw,unsigned int ctl_id) +{ + if (ctl_id >= CTRL_COUNT) return 0; + if (!control_defs[ctl_id].is_valid) return 0; + return control_defs[ctl_id].default_value; +} + + /* Set current value for given control - normally this is just stored and the hardware isn't updated until the commit function is called. */ int pvr2_hdw_set_ctl_value_internal(struct pvr2_hdw *hdw, diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 6cc841b56..d7749138c 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -224,6 +224,9 @@ int pvr2_hdw_get_ctl_min_value(struct pvr2_hdw *,unsigned int ctl_id); /* Retrieve legal maximum value for a given control */ int pvr2_hdw_get_ctl_max_value(struct pvr2_hdw *,unsigned int ctl_id); +/* Retrieve legal maximum value for a given control */ +int pvr2_hdw_get_ctl_default_value(struct pvr2_hdw *,unsigned int ctl_id); + /* Set current value for given control - this is just stored; the hardware isn't updated until the commit function is called. */ int pvr2_hdw_set_ctl_value(struct pvr2_hdw *,unsigned int ctl_id,int value); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index dd207fae4..4cdd08708 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -788,6 +788,8 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, sizeof(vc->name)); vc->minimum = pvr2_hdw_get_ctl_min_value(hdw,pvr2_id); vc->maximum = pvr2_hdw_get_ctl_max_value(hdw,pvr2_id); + vc->default_value = + pvr2_hdw_get_ctl_default_value(hdw,pvr2_id); vc->step = 1; ret = 0; break; -- cgit v1.2.3 From d8d74e1971146106ca651efbbac772a93f968a41 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 26 Mar 2006 16:59:45 -0500 Subject: Fix camera key on FusionHDTV portable remote control From: Michael Krufky Thanks to: Andrew Cohen Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/dvb-usb/cxusb.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index 56ea5c6dc..d63f6ec4f 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -246,11 +246,7 @@ static struct dvb_usb_rc_key dvico_portable_rc_keys[] = { { 0xfc, 0x09, KEY_VOLUMEDOWN }, { 0xfc, 0x54, KEY_CHANNELUP }, { 0xfc, 0x0b, KEY_CHANNELDOWN }, - /* - { 0xfc, 0x56, KEY_CAMERA }, - Camera and Record keys both send the same code, - but the camera key doesn't repeat. - */ + { 0xfc, 0x16, KEY_CAMERA }, { 0xfc, 0x40, KEY_TUNER }, /* ATV/DTV */ { 0xfc, 0x45, KEY_OPEN }, { 0xfc, 0x19, KEY_1 }, -- cgit v1.2.3 From af3723d04cc119034880f365c1e71edf0b0cde83 Mon Sep 17 00:00:00 2001 From: Ricardo Cerqueira Date: Mon, 27 Mar 2006 09:16:31 -0300 Subject: Fix video-buf PCI wrappers From: Ricardo Cerqueira After the recent video-buf "generic" adaptation, the PCI wrappers got completely broken, and all of the DMA sound modules stopped working (and failed with an oops) Signed-off-by: Ricardo Cerqueira --- linux/drivers/media/video/video-buf.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/video-buf.c b/linux/drivers/media/video/video-buf.c index 715feea6b..4ae4e1e7a 100644 --- a/linux/drivers/media/video/video-buf.c +++ b/linux/drivers/media/video/video-buf.c @@ -406,19 +406,25 @@ void videobuf_queue_pci(struct videobuf_queue* q) int videobuf_pci_dma_map(struct pci_dev *pci,struct videobuf_dmabuf *dma) { struct videobuf_queue q; + struct videobuf_queue_ops qops; q.dev=pci; - q.ops->vb_map_sg=(vb_map_sg_t *)pci_unmap_sg; + qops.vb_map_sg=(vb_map_sg_t *)pci_map_sg; + qops.vb_unmap_sg=(vb_map_sg_t *)pci_unmap_sg; + q.ops = &qops; - return (videobuf_dma_unmap(&q,dma)); + return (videobuf_dma_map(&q,dma)); } int videobuf_pci_dma_unmap(struct pci_dev *pci,struct videobuf_dmabuf *dma) { struct videobuf_queue q; + struct videobuf_queue_ops qops; q.dev=pci; - q.ops->vb_map_sg=(vb_map_sg_t *)pci_unmap_sg; + qops.vb_map_sg=(vb_map_sg_t *)pci_map_sg; + qops.vb_unmap_sg=(vb_map_sg_t *)pci_unmap_sg; + q.ops = &qops; return (videobuf_dma_unmap(&q,dma)); } -- cgit v1.2.3 From d5b86b18b3c5bbd2b7f6ab6333ba4b858f48d656 Mon Sep 17 00:00:00 2001 From: Hartmut Hackmann Date: Tue, 28 Mar 2006 00:04:48 +0200 Subject: Added PCI IDs of 2 LifeView Cards From: Hartmut Hackmann Added ID entries for the Genius VideoWonder DVB-T and the LifeView FlyTV Platinum Gold Signed-off-by: Hartmut Hackmann --- linux/drivers/media/video/saa7134/saa7134-cards.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index 45eef4148..0e9ee8715 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -210,7 +210,7 @@ struct saa7134_board saa7134_boards[] = { [SAA7134_BOARD_FLYTVPLATINUM_FM] = { /* LifeView FlyTV Platinum FM (LR214WF) */ /* "Peter Missel */ - .name = "LifeView FlyTV Platinum FM", + .name = "LifeView FlyTV Platinum FM / Gold", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, @@ -2673,7 +2673,7 @@ struct saa7134_board saa7134_boards[] = { [SAA7134_BOARD_FLYDVBT_LR301] = { /* LifeView FlyDVB-T */ /* Giampiero Giancipoli */ - .name = "LifeView FlyDVB-T", + .name = "LifeView FlyDVB-T / Genius VideoWonder DVB-T", .audio_clock = 0x00200000, .tuner_type = TUNER_ABSENT, .radio_type = UNSET, @@ -3373,6 +3373,18 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x16be, .subdevice = 0x0005, .driver_data = SAA7134_BOARD_MD7134_BRIDGE_2, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1489, + .subdevice = 0x0301, + .driver_data = SAA7134_BOARD_FLYDVBT_LR301, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5168, /* Animation Technologies (LifeView) */ + .subdevice = 0x0304, + .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, -- cgit v1.2.3 From 40af1c784bed034e3c7dbfa7e804146dc8052e6a Mon Sep 17 00:00:00 2001 From: Hartmut Hackmann Date: Tue, 28 Mar 2006 00:15:47 +0200 Subject: Corrected CVBS input for the AVERMEDIA 777 DVB-T From: Hartmut Hackmann The .vmux entry needs to be 1 instead of 0 Signed-off-by: Hartmut Hackmann --- linux/drivers/media/video/saa7134/saa7134-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index 0e9ee8715..efdfc23db 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -2662,7 +2662,7 @@ struct saa7134_board saa7134_boards[] = { .mpeg = SAA7134_MPEG_DVB, .inputs = {{ .name = name_comp1, - .vmux = 0, + .vmux = 1, .amux = LINE1, },{ .name = name_svideo, -- cgit v1.2.3 From 071e2e8bc4c57532926a7a6e2b502cc065f33fbd Mon Sep 17 00:00:00 2001 From: Hartmut Hackmann Date: Tue, 28 Mar 2006 00:39:30 +0200 Subject: Added support for the new Lifeview hybrid cardbus modules From: Hartmut Hackmann There seem to be many variants of this cards with different feature sets. This entry supports analog TV, CVBS and s-video input, FM radio and DVB-T if they are supported by the hardware. Signed-off-by: Hartmut Hackmann --- linux/drivers/media/video/saa7134/saa7134-cards.c | 48 +++++++++++++++++++++++ linux/drivers/media/video/saa7134/saa7134-dvb.c | 4 ++ linux/drivers/media/video/saa7134/saa7134.h | 1 + 3 files changed, 53 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index efdfc23db..20d0327c4 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -2849,6 +2849,40 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, + [SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS] = { + .name = "LifeView FlyDVB-T Hybrid Cardbus", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x00600000, /* Bit 21 0=Radio, Bit 22 0=TV */ + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .gpio = 0x200000, /* GPIO21=High for TV input */ + .tv = 1, + },{ + .name = name_svideo, /* S-Video signal on S-Video input */ + .vmux = 8, + .amux = LINE2, + },{ + .name = name_comp1, /* Composite signal on S-Video input */ + .vmux = 0, + .amux = LINE2, + },{ + .name = name_comp2, /* Composite input */ + .vmux = 3, + .amux = LINE2, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x000000, /* GPIO21=Low for FM radio antenna */ + }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -3385,6 +3419,18 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x5168, /* Animation Technologies (LifeView) */ .subdevice = 0x0304, .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5168, + .subdevice = 0x3306, + .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5168, + .subdevice = 0x3502, /* whats the difference to 0x3306 ?*/ + .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, @@ -3552,6 +3598,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_writeb(SAA7134_GPIO_GPSTATUS3, 0x06); break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: + case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: saa_writeb(SAA7134_GPIO_GPMODE3, 0x08); saa_writeb(SAA7134_GPIO_GPSTATUS3, 0x00); break; @@ -3723,6 +3770,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) } break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: + case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: /* make the tda10046 find its eeprom */ { u8 data[] = { 0x3c, 0x33, 0x62}; diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index 1697fe6f8..9cbc8343d 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -1066,6 +1066,10 @@ static int dvb_init(struct saa7134_dev *dev) dev->dvb.frontend = tda10046_attach(&tevion_dvbt220rf_config, &dev->i2c_adap); break; + case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: + dev->dvb.frontend = tda10046_attach(&ads_tech_duo_config, + &dev->i2c_adap); + break; #endif #ifdef HAVE_NXT200X case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180: diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index cbcf9adf7..703b28731 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -230,6 +230,7 @@ struct saa7134_format { #define SAA7134_BOARD_AVERMEDIA_A169_B 91 #define SAA7134_BOARD_AVERMEDIA_A169_B1 92 #define SAA7134_BOARD_MD7134_BRIDGE_2 93 +#define SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS 94 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 -- cgit v1.2.3 From 51f36ae06143e1dd3b668635d02831f0e3d617f9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Mar 2006 10:02:28 -0300 Subject: Fix default values for tvp5150 controls From: Mauro Carvalho Chehab Default values were wrong. Fixing it. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tvp5150.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/tvp5150.c b/linux/drivers/media/video/tvp5150.c index 571ed11dd..8a6c5c0d0 100644 --- a/linux/drivers/media/video/tvp5150.c +++ b/linux/drivers/media/video/tvp5150.c @@ -80,7 +80,7 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = { .minimum = 0, .maximum = 255, .step = 1, - .default_value = 0, + .default_value = 128, .flags = 0, }, { .id = V4L2_CID_CONTRAST, @@ -89,7 +89,7 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = { .minimum = 0, .maximum = 255, .step = 0x1, - .default_value = 0x10, + .default_value = 128, .flags = 0, }, { .id = V4L2_CID_SATURATION, @@ -98,7 +98,7 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = { .minimum = 0, .maximum = 255, .step = 0x1, - .default_value = 0x10, + .default_value = 128, .flags = 0, }, { .id = V4L2_CID_HUE, @@ -107,7 +107,7 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = { .minimum = -128, .maximum = 127, .step = 0x1, - .default_value = 0x10, + .default_value = 0, .flags = 0, } }; -- cgit v1.2.3 From 5dd31c09dfb61c90f4d5e8a8f51824647cc9a3ad Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Mar 2006 11:33:49 -0300 Subject: Only saa7134-dvb depends on FW_LOADER. Fixing. From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/saa7134/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/saa7134/Kconfig b/linux/drivers/media/video/saa7134/Kconfig index 263498660..e1c1805df 100644 --- a/linux/drivers/media/video/saa7134/Kconfig +++ b/linux/drivers/media/video/saa7134/Kconfig @@ -5,7 +5,6 @@ config VIDEO_SAA7134 select VIDEO_IR select VIDEO_TUNER select CRC32 - select FW_LOADER ---help--- This is a video4linux driver for Philips SAA713x based TV cards. @@ -40,6 +39,7 @@ config VIDEO_SAA7134_DVB tristate "DVB/ATSC Support for saa7134 based TV cards" depends on VIDEO_SAA7134 && DVB_CORE select VIDEO_BUF_DVB + select FW_LOADER ---help--- This adds support for DVB cards based on the Philips saa7134 chip. -- cgit v1.2.3 From 69325b06271b237759dcca0a35907568b8700ac8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Mar 2006 13:35:41 -0300 Subject: Those files were modified only at -git. Updating v4l-dvb From: Mauro Carvalho Chehab kernel-sync Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/Kconfig | 234 ++++++++++++++++++++++++++++++++++++- linux/drivers/media/video/Makefile | 20 +++- 2 files changed, 249 insertions(+), 5 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index 762e35670..f31a19890 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -291,10 +291,6 @@ config VIDEO_HEXIUM_GEMINI source "drivers/media/video/cx88/Kconfig" -source "drivers/media/video/em28xx/Kconfig" - -source "drivers/media/video/pvrusb2/Kconfig" - config VIDEO_OVCAMCHIP tristate "OmniVision Camera Chip support" depends on VIDEO_DEV && I2C @@ -369,4 +365,234 @@ config VIDEO_SAA7127 To compile this driver as a module, choose M here: the module will be called saa7127 +# +# USB Multimedia device configuration +# + +menu "V4L USB devices" + depends on USB && VIDEO_DEV + +source "drivers/media/video/em28xx/Kconfig" + +config USB_VICAM + tristate "USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)" + depends on USB && VIDEO_DEV && EXPERIMENTAL + ---help--- + Say Y here if you have 3com homeconnect camera (vicam). + + This driver uses the Video For Linux API. You must say Y or M to + "Video For Linux" (under Multimedia Devices) to use this driver. + Information on this API and pointers to "v4l" programs may be found + at . + + To compile this driver as a module, choose M here: the + module will be called vicam. + +config USB_DSBR + tristate "D-Link USB FM radio support (EXPERIMENTAL)" + depends on USB && VIDEO_DEV && EXPERIMENTAL + ---help--- + Say Y here if you want to connect this type of radio to your + computer's USB port. Note that the audio is not digital, and + you must connect the line out connector to a sound card or a + set of speakers. + + This driver uses the Video For Linux API. You must enable + (Y or M in config) Video For Linux (under Character Devices) + to use this driver. Information on this API and pointers to + "v4l" programs may be found at + . + + To compile this driver as a module, choose M here: the + module will be called dsbr100. + +config USB_ET61X251 + tristate "USB ET61X[12]51 PC Camera Controller support" + depends on USB && VIDEO_DEV + ---help--- + Say Y here if you want support for cameras based on Etoms ET61X151 + or ET61X251 PC Camera Controllers. + + See for more informations. + + This driver uses the Video For Linux API. You must say Y or M to + "Video For Linux" to use this driver. + + To compile this driver as a module, choose M here: the + module will be called et61x251. + +config USB_IBMCAM + tristate "USB IBM (Xirlink) C-it Camera support" + depends on USB && VIDEO_DEV + ---help--- + Say Y here if you want to connect a IBM "C-It" camera, also known as + "Xirlink PC Camera" to your computer's USB port. For more + information, read . + + This driver uses the Video For Linux API. You must enable + (Y or M in config) Video For Linux (under Character Devices) + to use this driver. Information on this API and pointers to + "v4l" programs may be found at + . + + To compile this driver as a module, choose M here: the + module will be called ibmcam. + + This camera has several configuration options which + can be specified when you load the module. Read + to learn more. + +config USB_KONICAWC + tristate "USB Konica Webcam support" + depends on USB && VIDEO_DEV + ---help--- + Say Y here if you want support for webcams based on a Konica + chipset. This is known to work with the Intel YC76 webcam. + + This driver uses the Video For Linux API. You must enable + (Y or M in config) Video For Linux (under Character Devices) + to use this driver. Information on this API and pointers to + "v4l" programs may be found at + . + + To compile this driver as a module, choose M here: the + module will be called konicawc. + +config USB_OV511 + tristate "USB OV511 Camera support" + depends on USB && VIDEO_DEV + ---help--- + Say Y here if you want to connect this type of camera to your + computer's USB port. See for more + information and for a list of supported cameras. + + This driver uses the Video For Linux API. You must say Y or M to + "Video For Linux" (under Character Devices) to use this driver. + Information on this API and pointers to "v4l" programs may be found + at . + + To compile this driver as a module, choose M here: the + module will be called ov511. + +config USB_SE401 + tristate "USB SE401 Camera support" + depends on USB && VIDEO_DEV + ---help--- + Say Y here if you want to connect this type of camera to your + computer's USB port. See for more + information and for a list of supported cameras. + + This driver uses the Video For Linux API. You must say Y or M to + "Video For Linux" (under Multimedia Devices) to use this driver. + Information on this API and pointers to "v4l" programs may be found + at . + + To compile this driver as a module, choose M here: the + module will be called se401. + +config USB_SN9C102 + tristate "USB SN9C10x PC Camera Controller support" + depends on USB && VIDEO_DEV + ---help--- + Say Y here if you want support for cameras based on SONiX SN9C101, + SN9C102 or SN9C103 PC Camera Controllers. + + See for more informations. + + This driver uses the Video For Linux API. You must say Y or M to + "Video For Linux" to use this driver. + + To compile this driver as a module, choose M here: the + module will be called sn9c102. + +config USB_STV680 + tristate "USB STV680 (Pencam) Camera support" + depends on USB && VIDEO_DEV + ---help--- + Say Y here if you want to connect this type of camera to your + computer's USB port. This includes the Pencam line of cameras. + See for more information and for + a list of supported cameras. + + This driver uses the Video For Linux API. You must say Y or M to + "Video For Linux" (under Multimedia Devices) to use this driver. + Information on this API and pointers to "v4l" programs may be found + at . + + To compile this driver as a module, choose M here: the + module will be called stv680. + +config USB_W9968CF + tristate "USB W996[87]CF JPEG Dual Mode Camera support" + depends on USB && VIDEO_DEV && I2C && VIDEO_OVCAMCHIP + ---help--- + Say Y here if you want support for cameras based on OV681 or + Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips. + + This driver has an optional plugin, which is distributed as a + separate module only (released under GPL). It allows to use higher + resolutions and framerates, but cannot be included in the official + Linux kernel for performance purposes. + + See for more informations. + + This driver uses the Video For Linux and the I2C APIs. It needs the + OmniVision Camera Chip support as well. You must say Y or M to + "Video For Linux", "I2C Support" and "OmniVision Camera Chip + support" to use this driver. + + To compile this driver as a module, choose M here: the + module will be called w9968cf. + +config USB_ZC0301 + tristate "USB ZC0301 Image Processor and Control Chip support" + depends on USB && VIDEO_DEV + ---help--- + Say Y here if you want support for cameras based on the ZC0301 + Image Processor and Control Chip. + + See for more informations. + + This driver uses the Video For Linux API. You must say Y or M to + "Video For Linux" to use this driver. + + To compile this driver as a module, choose M here: the + module will be called zc0301. + +config USB_PWC + tristate "USB Philips Cameras" + depends on USB && VIDEO_DEV + ---help--- + Say Y or M here if you want to use one of these Philips & OEM + webcams: + * Philips PCA645, PCA646 + * Philips PCVC675, PCVC680, PCVC690 + * Philips PCVC720/40, PCVC730, PCVC740, PCVC750 + * Askey VC010 + * Logitech QuickCam Pro 3000, 4000, 'Zoom', 'Notebook Pro' + and 'Orbit'/'Sphere' + * Samsung MPC-C10, MPC-C30 + * Creative Webcam 5, Pro Ex + * SOTEC Afina Eye + * Visionite VCS-UC300, VCS-UM100 + + The PCA635, PCVC665 and PCVC720/20 are not supported by this driver + and never will be, but the 665 and 720/20 are supported by other + drivers. + + See for more information and + installation instructions. + + The built-in microphone is enabled by selecting USB Audio support. + + This driver uses the Video For Linux API. You must say Y or M to + "Video For Linux" (under Character Devices) to use this driver. + Information on this API and pointers to "v4l" programs may be found + at . + + To compile this driver as a module, choose M here: the + module will be called pwc. + +endmenu # V4L USB devices + endmenu diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index 27e7b37b1..1c0e72e5a 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -42,7 +42,6 @@ obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ obj-$(CONFIG_VIDEO_EM28XX) += tvp5150.o -obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o obj-$(CONFIG_VIDEO_WM8775) += wm8775.o @@ -66,4 +65,23 @@ obj-$(CONFIG_VIDEO_CX25840) += cx25840/ obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o +et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o +zc0301-objs := zc0301_core.o zc0301_pas202bcb.o + +obj-$(CONFIG_USB_DABUSB) += dabusb.o +obj-$(CONFIG_USB_DSBR) += dsbr100.o +obj-$(CONFIG_USB_OV511) += ov511.o +obj-$(CONFIG_USB_SE401) += se401.o +obj-$(CONFIG_USB_STV680) += stv680.o +obj-$(CONFIG_USB_W9968CF) += w9968cf.o + +obj-$(CONFIG_USB_SN9C102) += sn9c102/ +obj-$(CONFIG_USB_ET61X251) += et61x251/ +obj-$(CONFIG_USB_PWC) += pwc/ +obj-$(CONFIG_USB_ZC0301) += zc0301/ + +obj-$(CONFIG_USB_IBMCAM) += usbvideo/ +obj-$(CONFIG_USB_KONICAWC) += usbvideo/ +obj-$(CONFIG_USB_VICAM) += usbvideo/ + EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/dvb-core -- cgit v1.2.3 From 86cdf6980ce363417a611ef9a6f689875fb1b79e Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 28 Mar 2006 12:13:08 -0500 Subject: fix pvrusb2 build regression caused by last commit (changeset 4b95340cd669) From: Michael Krufky Signed-off-by: Michael Krufky --- linux/drivers/media/video/Kconfig | 2 ++ linux/drivers/media/video/Makefile | 1 + 2 files changed, 3 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index f31a19890..62e71b527 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -372,6 +372,8 @@ config VIDEO_SAA7127 menu "V4L USB devices" depends on USB && VIDEO_DEV +source "drivers/media/video/pvrusb2/Kconfig" + source "drivers/media/video/em28xx/Kconfig" config USB_VICAM diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index 1c0e72e5a..289ef1abf 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ obj-$(CONFIG_VIDEO_EM28XX) += tvp5150.o +obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o obj-$(CONFIG_VIDEO_WM8775) += wm8775.o -- cgit v1.2.3 From c599755e8210ed32a69a488d22dac0aca121fef5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Mar 2006 14:22:59 -0300 Subject: Make code sync with recent ALSA changes on -git From: Mauro Carvalho Chehab kernel-sync Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/cx88/cx88-alsa.c | 98 ++++++++++++++++++ linux/drivers/media/video/saa7134/saa7134-alsa.c | 120 +++++++++++++++++++++++ linux/drivers/media/video/saa7134/saa7134.h | 6 +- 3 files changed, 223 insertions(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-alsa.c b/linux/drivers/media/video/cx88/cx88-alsa.c index 8ecdb41af..cd3fa72fc 100644 --- a/linux/drivers/media/video/cx88/cx88-alsa.c +++ b/linux/drivers/media/video/cx88/cx88-alsa.c @@ -65,7 +65,11 @@ struct cx88_audio_dev { /* audio controls */ int irq; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) snd_card_t *card; +#else + struct snd_card *card; +#endif spinlock_t reg_lock; @@ -84,7 +88,11 @@ struct cx88_audio_dev { struct cx88_buffer *buf; long opened; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) snd_pcm_substream_t *substream; +#else + struct snd_pcm_substream *substream; +#endif }; typedef struct cx88_audio_dev snd_cx88_card_t; @@ -101,7 +109,11 @@ typedef struct cx88_audio_dev snd_cx88_card_t; static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static snd_card_t *snd_cx88_cards[SNDRV_CARDS]; +#else +static struct snd_card *snd_cx88_cards[SNDRV_CARDS]; +#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) static unsigned int dummy; @@ -334,7 +346,11 @@ static int dsp_buffer_free(snd_cx88_card_t *chip) /* * Digital hardware definition */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static snd_pcm_hardware_t snd_cx88_digital_hw = { +#else +static struct snd_pcm_hardware snd_cx88_digital_hw = { +#endif .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -356,16 +372,28 @@ static snd_pcm_hardware_t snd_cx88_digital_hw = { /* * audio pcm capture runtime free */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static void snd_card_cx88_runtime_free(snd_pcm_runtime_t *runtime) +#else +static void snd_card_cx88_runtime_free(struct snd_pcm_runtime *runtime) +#endif { } /* * audio pcm capture open callback */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_cx88_pcm_open(snd_pcm_substream_t *substream) +#else +static int snd_cx88_pcm_open(struct snd_pcm_substream *substream) +#endif { snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) snd_pcm_runtime_t *runtime = substream->runtime; +#else + struct snd_pcm_runtime *runtime = substream->runtime; +#endif int err; if (test_and_set_bit(0, &chip->opened)) @@ -394,7 +422,11 @@ _error: /* * audio close callback */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_cx88_close(snd_pcm_substream_t *substream) +#else +static int snd_cx88_close(struct snd_pcm_substream *substream) +#endif { snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); @@ -407,8 +439,13 @@ static int snd_cx88_close(snd_pcm_substream_t *substream) /* * hw_params callback */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_cx88_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params) +#else +static int snd_cx88_hw_params(struct snd_pcm_substream * substream, + struct snd_pcm_hw_params * hw_params) +#endif { snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); struct cx88_buffer *buf; @@ -467,7 +504,11 @@ static int snd_cx88_hw_params(snd_pcm_substream_t * substream, /* * hw free callback */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_cx88_hw_free(snd_pcm_substream_t * substream) +#else +static int snd_cx88_hw_free(struct snd_pcm_substream * substream) +#endif { snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); @@ -483,7 +524,11 @@ static int snd_cx88_hw_free(snd_pcm_substream_t * substream) /* * prepare callback */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_cx88_prepare(snd_pcm_substream_t *substream) +#else +static int snd_cx88_prepare(struct snd_pcm_substream *substream) +#endif { return 0; } @@ -492,7 +537,11 @@ static int snd_cx88_prepare(snd_pcm_substream_t *substream) /* * trigger callback */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_cx88_card_trigger(snd_pcm_substream_t *substream, int cmd) +#else +static int snd_cx88_card_trigger(struct snd_pcm_substream *substream, int cmd) +#endif { snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); int err; @@ -519,10 +568,18 @@ static int snd_cx88_card_trigger(snd_pcm_substream_t *substream, int cmd) /* * pointer callback */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static snd_pcm_uframes_t snd_cx88_pointer(snd_pcm_substream_t *substream) +#else +static snd_pcm_uframes_t snd_cx88_pointer(struct snd_pcm_substream *substream) +#endif { snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) snd_pcm_runtime_t *runtime = substream->runtime; +#else + struct snd_pcm_runtime *runtime = substream->runtime; +#endif if (chip->read_count) { chip->read_count -= snd_pcm_lib_period_bytes(substream); @@ -539,7 +596,11 @@ static snd_pcm_uframes_t snd_cx88_pointer(snd_pcm_substream_t *substream) /* * operators */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static snd_pcm_ops_t snd_cx88_pcm_ops = { +#else +static struct snd_pcm_ops snd_cx88_pcm_ops = { +#endif .open = snd_cx88_pcm_open, .close = snd_cx88_close, .ioctl = snd_pcm_lib_ioctl, @@ -556,7 +617,11 @@ static snd_pcm_ops_t snd_cx88_pcm_ops = { static int __devinit snd_cx88_pcm(snd_cx88_card_t *chip, int device, char *name) { int err; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) snd_pcm_t *pcm; +#else + struct snd_pcm *pcm; +#endif err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm); if (err < 0) @@ -571,7 +636,12 @@ static int __devinit snd_cx88_pcm(snd_cx88_card_t *chip, int device, char *name) /**************************************************************************** CONTROL INTERFACE ****************************************************************************/ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_cx88_capture_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *info) +#else +static int snd_cx88_capture_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *info) +#endif { info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; info->count = 1; @@ -582,7 +652,12 @@ static int snd_cx88_capture_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_i } /* OK - TODO: test it */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_cx88_capture_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value) +#else +static int snd_cx88_capture_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +#endif { snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); struct cx88_core *core=chip->core; @@ -593,7 +668,12 @@ static int snd_cx88_capture_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_va } /* OK - TODO: test it */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_cx88_capture_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value) +#else +static int snd_cx88_capture_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +#endif { snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); struct cx88_core *core=chip->core; @@ -609,7 +689,11 @@ static int snd_cx88_capture_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_va return v != old_control; } +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static snd_kcontrol_new_t snd_cx88_capture_volume = { +#else +static struct snd_kcontrol_new snd_cx88_capture_volume = { +#endif .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Volume", .info = snd_cx88_capture_volume_info, @@ -655,7 +739,11 @@ static int snd_cx88_free(snd_cx88_card_t *chip) /* * Component Destructor */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static void snd_cx88_dev_free(snd_card_t * card) +#else +static void snd_cx88_dev_free(struct snd_card * card) +#endif { snd_cx88_card_t *chip = card->private_data; @@ -668,8 +756,14 @@ static void snd_cx88_dev_free(snd_card_t * card) */ static int devno; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int __devinit snd_cx88_create(snd_card_t *card, struct pci_dev *pci, snd_cx88_card_t **rchip) +#else +static int __devinit snd_cx88_create(struct snd_card *card, + struct pci_dev *pci, + snd_cx88_card_t **rchip) +#endif { snd_cx88_card_t *chip; struct cx88_core *core; @@ -740,7 +834,11 @@ static int __devinit snd_cx88_create(snd_card_t *card, struct pci_dev *pci, static int __devinit cx88_audio_initdev(struct pci_dev *pci, const struct pci_device_id *pci_id) { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) snd_card_t *card; +#else + struct snd_card *card; +#endif snd_cx88_card_t *chip; int err; diff --git a/linux/drivers/media/video/saa7134/saa7134-alsa.c b/linux/drivers/media/video/saa7134/saa7134-alsa.c index 393a3a3bd..f670f4c8c 100644 --- a/linux/drivers/media/video/saa7134/saa7134-alsa.c +++ b/linux/drivers/media/video/saa7134/saa7134-alsa.c @@ -80,7 +80,11 @@ MODULE_PARM_DESC(enable, "Enable (or not) the SAA7134 capture interface(s)."); */ typedef struct snd_card_saa7134 { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) snd_card_t *card; +#else + struct snd_card *card; +#endif spinlock_t mixer_lock; int mixer_volume[MIXER_ADDR_LAST+1][2]; int capture_source[MIXER_ADDR_LAST+1][2]; @@ -107,10 +111,18 @@ typedef struct snd_card_saa7134_pcm { spinlock_t lock; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) snd_pcm_substream_t *substream; +#else + struct snd_pcm_substream *substream; +#endif } snd_card_saa7134_pcm_t; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static snd_card_t *snd_saa7134_cards[SNDRV_CARDS]; +#else +static struct snd_card *snd_saa7134_cards[SNDRV_CARDS]; +#endif /* @@ -263,10 +275,18 @@ out: * */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_card_saa7134_capture_trigger(snd_pcm_substream_t * substream, +#else +static int snd_card_saa7134_capture_trigger(struct snd_pcm_substream * substream, +#endif int cmd) { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) snd_pcm_runtime_t *runtime = substream->runtime; +#else + struct snd_pcm_runtime *runtime = substream->runtime; +#endif snd_card_saa7134_pcm_t *pcm = runtime->private_data; struct saa7134_dev *dev=pcm->dev; int err = 0; @@ -344,9 +364,17 @@ static int dsp_buffer_free(struct saa7134_dev *dev) * */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_card_saa7134_capture_prepare(snd_pcm_substream_t * substream) +#else +static int snd_card_saa7134_capture_prepare(struct snd_pcm_substream * substream) +#endif { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) snd_pcm_runtime_t *runtime = substream->runtime; +#else + struct snd_pcm_runtime *runtime = substream->runtime; +#endif int bswap, sign; u32 fmt, control; snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); @@ -433,9 +461,18 @@ static int snd_card_saa7134_capture_prepare(snd_pcm_substream_t * substream) * */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static snd_pcm_uframes_t snd_card_saa7134_capture_pointer(snd_pcm_substream_t * substream) +#else +static snd_pcm_uframes_t +snd_card_saa7134_capture_pointer(struct snd_pcm_substream * substream) +#endif { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) snd_pcm_runtime_t *runtime = substream->runtime; +#else + struct snd_pcm_runtime *runtime = substream->runtime; +#endif snd_card_saa7134_pcm_t *pcm = runtime->private_data; struct saa7134_dev *dev=pcm->dev; @@ -453,7 +490,11 @@ static snd_pcm_uframes_t snd_card_saa7134_capture_pointer(snd_pcm_substream_t * * ALSA hardware capabilities definition */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static snd_pcm_hardware_t snd_card_saa7134_capture = +#else +static struct snd_pcm_hardware snd_card_saa7134_capture = +#endif { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -476,7 +517,11 @@ static snd_pcm_hardware_t snd_card_saa7134_capture = .periods_max = 1024, }; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static void snd_card_saa7134_runtime_free(snd_pcm_runtime_t *runtime) +#else +static void snd_card_saa7134_runtime_free(struct snd_pcm_runtime *runtime) +#endif { snd_card_saa7134_pcm_t *pcm = runtime->private_data; @@ -493,8 +538,13 @@ static void snd_card_saa7134_runtime_free(snd_pcm_runtime_t *runtime) * */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_card_saa7134_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params) +#else +static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, + struct snd_pcm_hw_params * hw_params) +#endif { snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); struct saa7134_dev *dev; @@ -573,7 +623,11 @@ static int snd_card_saa7134_hw_params(snd_pcm_substream_t * substream, * */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_card_saa7134_hw_free(snd_pcm_substream_t * substream) +#else +static int snd_card_saa7134_hw_free(struct snd_pcm_substream * substream) +#endif { snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); struct saa7134_dev *dev; @@ -599,7 +653,11 @@ static int snd_card_saa7134_hw_free(snd_pcm_substream_t * substream) * */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_card_saa7134_capture_close(snd_pcm_substream_t * substream) +#else +static int snd_card_saa7134_capture_close(struct snd_pcm_substream * substream) +#endif { return 0; } @@ -614,9 +672,17 @@ static int snd_card_saa7134_capture_close(snd_pcm_substream_t * substream) * */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_card_saa7134_capture_open(snd_pcm_substream_t * substream) +#else +static int snd_card_saa7134_capture_open(struct snd_pcm_substream * substream) +#endif { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) snd_pcm_runtime_t *runtime = substream->runtime; +#else + struct snd_pcm_runtime *runtime = substream->runtime; +#endif snd_card_saa7134_pcm_t *pcm; snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); struct saa7134_dev *dev = saa7134->dev; @@ -652,7 +718,11 @@ static int snd_card_saa7134_capture_open(snd_pcm_substream_t * substream) * ALSA capture callbacks definition */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static snd_pcm_ops_t snd_card_saa7134_capture_ops = { +#else +static struct snd_pcm_ops snd_card_saa7134_capture_ops = { +#endif .open = snd_card_saa7134_capture_open, .close = snd_card_saa7134_capture_close, .ioctl = snd_pcm_lib_ioctl, @@ -673,7 +743,11 @@ static snd_pcm_ops_t snd_card_saa7134_capture_ops = { static int snd_card_saa7134_pcm(snd_card_saa7134_t *saa7134, int device) { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) snd_pcm_t *pcm; +#else + struct snd_pcm *pcm; +#endif int err; if ((err = snd_pcm_new(saa7134->card, "SAA7134 PCM", device, 0, 1, &pcm)) < 0) @@ -691,7 +765,12 @@ static int snd_card_saa7134_pcm(snd_card_saa7134_t *saa7134, int device) .get = snd_saa7134_volume_get, .put = snd_saa7134_volume_put, \ .private_value = addr } +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_saa7134_volume_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +#else +static int snd_saa7134_volume_info(struct snd_kcontrol * kcontrol, + struct snd_ctl_elem_info * uinfo) +#endif { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; @@ -700,7 +779,12 @@ static int snd_saa7134_volume_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_ return 0; } +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_saa7134_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +#else +static int snd_saa7134_volume_get(struct snd_kcontrol * kcontrol, + struct snd_ctl_elem_value * ucontrol) +#endif { snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); int addr = kcontrol->private_value; @@ -710,7 +794,12 @@ static int snd_saa7134_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_ return 0; } +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_saa7134_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +#else +static int snd_saa7134_volume_put(struct snd_kcontrol * kcontrol, + struct snd_ctl_elem_value * ucontrol) +#endif { snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); int change, addr = kcontrol->private_value; @@ -741,7 +830,12 @@ static int snd_saa7134_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_ .get = snd_saa7134_capsrc_get, .put = snd_saa7134_capsrc_put, \ .private_value = addr } +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_saa7134_capsrc_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) +#else +static int snd_saa7134_capsrc_info(struct snd_kcontrol * kcontrol, + struct snd_ctl_elem_info * uinfo) +#endif { uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 2; @@ -750,7 +844,12 @@ static int snd_saa7134_capsrc_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_ return 0; } +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_saa7134_capsrc_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +#else +static int snd_saa7134_capsrc_get(struct snd_kcontrol * kcontrol, + struct snd_ctl_elem_value * ucontrol) +#endif { snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); int addr = kcontrol->private_value; @@ -763,7 +862,12 @@ static int snd_saa7134_capsrc_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_ return 0; } +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static int snd_saa7134_capsrc_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +#else +static int snd_saa7134_capsrc_put(struct snd_kcontrol * kcontrol, + struct snd_ctl_elem_value * ucontrol) +#endif { snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); int change, addr = kcontrol->private_value; @@ -840,7 +944,11 @@ static int snd_saa7134_capsrc_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_ return change; } +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static snd_kcontrol_new_t snd_saa7134_controls[] = { +#else +static struct snd_kcontrol_new snd_saa7134_controls[] = { +#endif SAA713x_VOLUME("Video Volume", 0, MIXER_ADDR_TVTUNER), SAA713x_CAPSRC("Video Capture Switch", 0, MIXER_ADDR_TVTUNER), SAA713x_VOLUME("Line Volume", 1, MIXER_ADDR_LINE1), @@ -859,7 +967,11 @@ SAA713x_CAPSRC("Line Capture Switch", 2, MIXER_ADDR_LINE2), static int snd_card_saa7134_new_mixer(snd_card_saa7134_t * chip) { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) snd_card_t *card = chip->card; +#else + struct snd_card *card = chip->card; +#endif unsigned int idx; int err; @@ -873,7 +985,11 @@ static int snd_card_saa7134_new_mixer(snd_card_saa7134_t * chip) return 0; } +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) static void snd_saa7134_free(snd_card_t * card) +#else +static void snd_saa7134_free(struct snd_card * card) +#endif { snd_card_saa7134_t *chip = card->private_data; @@ -900,7 +1016,11 @@ static void snd_saa7134_free(snd_card_t * card) static int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum) { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) snd_card_t *card; +#else + struct snd_card *card; +#endif snd_card_saa7134_t *chip; int err; diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index 703b28731..54070d135 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -412,7 +412,11 @@ struct saa7134_dmasound { unsigned int read_count; void * priv_data; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) - snd_pcm_substream_t *substream; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) + snd_pcm_substream_t *substream; +#else + struct snd_pcm_substream *substream; +#endif #endif }; -- cgit v1.2.3 From f4ab1974fbec51307a7a929e99203da3e4251596 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Mar 2006 14:31:06 -0300 Subject: Wrong version testing... fixed. From: Mauro Carvalho Chehab kernel-sync. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/cx88/cx88-alsa.c | 46 +++++++++---------- linux/drivers/media/video/saa7134/saa7134-alsa.c | 56 ++++++++++++------------ linux/drivers/media/video/saa7134/saa7134.h | 2 +- 3 files changed, 52 insertions(+), 52 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-alsa.c b/linux/drivers/media/video/cx88/cx88-alsa.c index cd3fa72fc..80aef2421 100644 --- a/linux/drivers/media/video/cx88/cx88-alsa.c +++ b/linux/drivers/media/video/cx88/cx88-alsa.c @@ -65,7 +65,7 @@ struct cx88_audio_dev { /* audio controls */ int irq; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) snd_card_t *card; #else struct snd_card *card; @@ -88,7 +88,7 @@ struct cx88_audio_dev { struct cx88_buffer *buf; long opened; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) snd_pcm_substream_t *substream; #else struct snd_pcm_substream *substream; @@ -109,7 +109,7 @@ typedef struct cx88_audio_dev snd_cx88_card_t; static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static snd_card_t *snd_cx88_cards[SNDRV_CARDS]; #else static struct snd_card *snd_cx88_cards[SNDRV_CARDS]; @@ -346,7 +346,7 @@ static int dsp_buffer_free(snd_cx88_card_t *chip) /* * Digital hardware definition */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static snd_pcm_hardware_t snd_cx88_digital_hw = { #else static struct snd_pcm_hardware snd_cx88_digital_hw = { @@ -372,7 +372,7 @@ static struct snd_pcm_hardware snd_cx88_digital_hw = { /* * audio pcm capture runtime free */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static void snd_card_cx88_runtime_free(snd_pcm_runtime_t *runtime) #else static void snd_card_cx88_runtime_free(struct snd_pcm_runtime *runtime) @@ -382,14 +382,14 @@ static void snd_card_cx88_runtime_free(struct snd_pcm_runtime *runtime) /* * audio pcm capture open callback */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_cx88_pcm_open(snd_pcm_substream_t *substream) #else static int snd_cx88_pcm_open(struct snd_pcm_substream *substream) #endif { snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) snd_pcm_runtime_t *runtime = substream->runtime; #else struct snd_pcm_runtime *runtime = substream->runtime; @@ -422,7 +422,7 @@ _error: /* * audio close callback */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_cx88_close(snd_pcm_substream_t *substream) #else static int snd_cx88_close(struct snd_pcm_substream *substream) @@ -439,7 +439,7 @@ static int snd_cx88_close(struct snd_pcm_substream *substream) /* * hw_params callback */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_cx88_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params) #else @@ -504,7 +504,7 @@ static int snd_cx88_hw_params(struct snd_pcm_substream * substream, /* * hw free callback */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_cx88_hw_free(snd_pcm_substream_t * substream) #else static int snd_cx88_hw_free(struct snd_pcm_substream * substream) @@ -524,7 +524,7 @@ static int snd_cx88_hw_free(struct snd_pcm_substream * substream) /* * prepare callback */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_cx88_prepare(snd_pcm_substream_t *substream) #else static int snd_cx88_prepare(struct snd_pcm_substream *substream) @@ -537,7 +537,7 @@ static int snd_cx88_prepare(struct snd_pcm_substream *substream) /* * trigger callback */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_cx88_card_trigger(snd_pcm_substream_t *substream, int cmd) #else static int snd_cx88_card_trigger(struct snd_pcm_substream *substream, int cmd) @@ -568,14 +568,14 @@ static int snd_cx88_card_trigger(struct snd_pcm_substream *substream, int cmd) /* * pointer callback */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static snd_pcm_uframes_t snd_cx88_pointer(snd_pcm_substream_t *substream) #else static snd_pcm_uframes_t snd_cx88_pointer(struct snd_pcm_substream *substream) #endif { snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) snd_pcm_runtime_t *runtime = substream->runtime; #else struct snd_pcm_runtime *runtime = substream->runtime; @@ -596,7 +596,7 @@ static snd_pcm_uframes_t snd_cx88_pointer(struct snd_pcm_substream *substream) /* * operators */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static snd_pcm_ops_t snd_cx88_pcm_ops = { #else static struct snd_pcm_ops snd_cx88_pcm_ops = { @@ -617,7 +617,7 @@ static struct snd_pcm_ops snd_cx88_pcm_ops = { static int __devinit snd_cx88_pcm(snd_cx88_card_t *chip, int device, char *name) { int err; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) snd_pcm_t *pcm; #else struct snd_pcm *pcm; @@ -636,7 +636,7 @@ static int __devinit snd_cx88_pcm(snd_cx88_card_t *chip, int device, char *name) /**************************************************************************** CONTROL INTERFACE ****************************************************************************/ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_cx88_capture_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *info) #else static int snd_cx88_capture_volume_info(struct snd_kcontrol *kcontrol, @@ -652,7 +652,7 @@ static int snd_cx88_capture_volume_info(struct snd_kcontrol *kcontrol, } /* OK - TODO: test it */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_cx88_capture_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value) #else static int snd_cx88_capture_volume_get(struct snd_kcontrol *kcontrol, @@ -668,7 +668,7 @@ static int snd_cx88_capture_volume_get(struct snd_kcontrol *kcontrol, } /* OK - TODO: test it */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_cx88_capture_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value) #else static int snd_cx88_capture_volume_put(struct snd_kcontrol *kcontrol, @@ -689,7 +689,7 @@ static int snd_cx88_capture_volume_put(struct snd_kcontrol *kcontrol, return v != old_control; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static snd_kcontrol_new_t snd_cx88_capture_volume = { #else static struct snd_kcontrol_new snd_cx88_capture_volume = { @@ -739,7 +739,7 @@ static int snd_cx88_free(snd_cx88_card_t *chip) /* * Component Destructor */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static void snd_cx88_dev_free(snd_card_t * card) #else static void snd_cx88_dev_free(struct snd_card * card) @@ -756,7 +756,7 @@ static void snd_cx88_dev_free(struct snd_card * card) */ static int devno; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int __devinit snd_cx88_create(snd_card_t *card, struct pci_dev *pci, snd_cx88_card_t **rchip) #else @@ -834,7 +834,7 @@ static int __devinit snd_cx88_create(struct snd_card *card, static int __devinit cx88_audio_initdev(struct pci_dev *pci, const struct pci_device_id *pci_id) { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) snd_card_t *card; #else struct snd_card *card; diff --git a/linux/drivers/media/video/saa7134/saa7134-alsa.c b/linux/drivers/media/video/saa7134/saa7134-alsa.c index f670f4c8c..54fc877ba 100644 --- a/linux/drivers/media/video/saa7134/saa7134-alsa.c +++ b/linux/drivers/media/video/saa7134/saa7134-alsa.c @@ -80,7 +80,7 @@ MODULE_PARM_DESC(enable, "Enable (or not) the SAA7134 capture interface(s)."); */ typedef struct snd_card_saa7134 { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) snd_card_t *card; #else struct snd_card *card; @@ -111,14 +111,14 @@ typedef struct snd_card_saa7134_pcm { spinlock_t lock; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) snd_pcm_substream_t *substream; #else struct snd_pcm_substream *substream; #endif } snd_card_saa7134_pcm_t; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static snd_card_t *snd_saa7134_cards[SNDRV_CARDS]; #else static struct snd_card *snd_saa7134_cards[SNDRV_CARDS]; @@ -275,14 +275,14 @@ out: * */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_card_saa7134_capture_trigger(snd_pcm_substream_t * substream, #else static int snd_card_saa7134_capture_trigger(struct snd_pcm_substream * substream, #endif int cmd) { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) snd_pcm_runtime_t *runtime = substream->runtime; #else struct snd_pcm_runtime *runtime = substream->runtime; @@ -364,13 +364,13 @@ static int dsp_buffer_free(struct saa7134_dev *dev) * */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_card_saa7134_capture_prepare(snd_pcm_substream_t * substream) #else static int snd_card_saa7134_capture_prepare(struct snd_pcm_substream * substream) #endif { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) snd_pcm_runtime_t *runtime = substream->runtime; #else struct snd_pcm_runtime *runtime = substream->runtime; @@ -461,14 +461,14 @@ static int snd_card_saa7134_capture_prepare(struct snd_pcm_substream * substream * */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static snd_pcm_uframes_t snd_card_saa7134_capture_pointer(snd_pcm_substream_t * substream) #else static snd_pcm_uframes_t snd_card_saa7134_capture_pointer(struct snd_pcm_substream * substream) #endif { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) snd_pcm_runtime_t *runtime = substream->runtime; #else struct snd_pcm_runtime *runtime = substream->runtime; @@ -490,7 +490,7 @@ snd_card_saa7134_capture_pointer(struct snd_pcm_substream * substream) * ALSA hardware capabilities definition */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static snd_pcm_hardware_t snd_card_saa7134_capture = #else static struct snd_pcm_hardware snd_card_saa7134_capture = @@ -517,7 +517,7 @@ static struct snd_pcm_hardware snd_card_saa7134_capture = .periods_max = 1024, }; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static void snd_card_saa7134_runtime_free(snd_pcm_runtime_t *runtime) #else static void snd_card_saa7134_runtime_free(struct snd_pcm_runtime *runtime) @@ -538,7 +538,7 @@ static void snd_card_saa7134_runtime_free(struct snd_pcm_runtime *runtime) * */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_card_saa7134_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params) #else @@ -623,7 +623,7 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, * */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_card_saa7134_hw_free(snd_pcm_substream_t * substream) #else static int snd_card_saa7134_hw_free(struct snd_pcm_substream * substream) @@ -653,7 +653,7 @@ static int snd_card_saa7134_hw_free(struct snd_pcm_substream * substream) * */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_card_saa7134_capture_close(snd_pcm_substream_t * substream) #else static int snd_card_saa7134_capture_close(struct snd_pcm_substream * substream) @@ -672,13 +672,13 @@ static int snd_card_saa7134_capture_close(struct snd_pcm_substream * substream) * */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_card_saa7134_capture_open(snd_pcm_substream_t * substream) #else static int snd_card_saa7134_capture_open(struct snd_pcm_substream * substream) #endif { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) snd_pcm_runtime_t *runtime = substream->runtime; #else struct snd_pcm_runtime *runtime = substream->runtime; @@ -718,7 +718,7 @@ static int snd_card_saa7134_capture_open(struct snd_pcm_substream * substream) * ALSA capture callbacks definition */ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static snd_pcm_ops_t snd_card_saa7134_capture_ops = { #else static struct snd_pcm_ops snd_card_saa7134_capture_ops = { @@ -743,7 +743,7 @@ static struct snd_pcm_ops snd_card_saa7134_capture_ops = { static int snd_card_saa7134_pcm(snd_card_saa7134_t *saa7134, int device) { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) snd_pcm_t *pcm; #else struct snd_pcm *pcm; @@ -765,7 +765,7 @@ static int snd_card_saa7134_pcm(snd_card_saa7134_t *saa7134, int device) .get = snd_saa7134_volume_get, .put = snd_saa7134_volume_put, \ .private_value = addr } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_saa7134_volume_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) #else static int snd_saa7134_volume_info(struct snd_kcontrol * kcontrol, @@ -779,7 +779,7 @@ static int snd_saa7134_volume_info(struct snd_kcontrol * kcontrol, return 0; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_saa7134_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) #else static int snd_saa7134_volume_get(struct snd_kcontrol * kcontrol, @@ -794,7 +794,7 @@ static int snd_saa7134_volume_get(struct snd_kcontrol * kcontrol, return 0; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_saa7134_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) #else static int snd_saa7134_volume_put(struct snd_kcontrol * kcontrol, @@ -830,7 +830,7 @@ static int snd_saa7134_volume_put(struct snd_kcontrol * kcontrol, .get = snd_saa7134_capsrc_get, .put = snd_saa7134_capsrc_put, \ .private_value = addr } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_saa7134_capsrc_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) #else static int snd_saa7134_capsrc_info(struct snd_kcontrol * kcontrol, @@ -844,7 +844,7 @@ static int snd_saa7134_capsrc_info(struct snd_kcontrol * kcontrol, return 0; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_saa7134_capsrc_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) #else static int snd_saa7134_capsrc_get(struct snd_kcontrol * kcontrol, @@ -862,7 +862,7 @@ static int snd_saa7134_capsrc_get(struct snd_kcontrol * kcontrol, return 0; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static int snd_saa7134_capsrc_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) #else static int snd_saa7134_capsrc_put(struct snd_kcontrol * kcontrol, @@ -944,7 +944,7 @@ static int snd_saa7134_capsrc_put(struct snd_kcontrol * kcontrol, return change; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static snd_kcontrol_new_t snd_saa7134_controls[] = { #else static struct snd_kcontrol_new snd_saa7134_controls[] = { @@ -967,7 +967,7 @@ SAA713x_CAPSRC("Line Capture Switch", 2, MIXER_ADDR_LINE2), static int snd_card_saa7134_new_mixer(snd_card_saa7134_t * chip) { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) snd_card_t *card = chip->card; #else struct snd_card *card = chip->card; @@ -985,7 +985,7 @@ static int snd_card_saa7134_new_mixer(snd_card_saa7134_t * chip) return 0; } -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) static void snd_saa7134_free(snd_card_t * card) #else static void snd_saa7134_free(struct snd_card * card) @@ -1016,7 +1016,7 @@ static void snd_saa7134_free(struct snd_card * card) static int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum) { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) snd_card_t *card; #else struct snd_card *card; diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index 54070d135..8600c57b1 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -412,7 +412,7 @@ struct saa7134_dmasound { unsigned int read_count; void * priv_data; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,17) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16) snd_pcm_substream_t *substream; #else struct snd_pcm_substream *substream; -- cgit v1.2.3 From fe57e9e343b68651e0666447d96d2db4fbb0a83d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Mar 2006 14:54:46 -0300 Subject: Removing proper __stringfy according with kernel ver From: Mauro Carvalho Chehab kernel-sync Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tda9840.c | 5 +++++ linux/drivers/media/video/tea6415c.c | 5 +++++ linux/drivers/media/video/tea6420.c | 6 +++++- 3 files changed, 15 insertions(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/tda9840.c b/linux/drivers/media/video/tda9840.c index 42a842aec..cc6de147c 100644 --- a/linux/drivers/media/video/tda9840.c +++ b/linux/drivers/media/video/tda9840.c @@ -35,8 +35,13 @@ static int debug = 0; /* insmod parameter */ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) #define dprintk(args...) \ do { if (debug) { printk("%s: %s()[%d]: ",__stringify(KBUILD_MODNAME), __FUNCTION__, __LINE__); printk(args); } } while (0) +#else +#define dprintk(args...) \ + do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __FUNCTION__, __LINE__); printk(args); } } while (0) +#endif #define SWITCH 0x00 #define LEVEL_ADJUST 0x02 diff --git a/linux/drivers/media/video/tea6415c.c b/linux/drivers/media/video/tea6415c.c index 98434efb0..4e6038324 100644 --- a/linux/drivers/media/video/tea6415c.c +++ b/linux/drivers/media/video/tea6415c.c @@ -37,8 +37,13 @@ static int debug = 0; /* insmod parameter */ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) #define dprintk(args...) \ do { if (debug) { printk("%s: %s()[%d]: ",__stringify(KBUILD_MODNAME), __FUNCTION__, __LINE__); printk(args); } } while (0) +#else +#define dprintk(args...) \ + do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __FUNCTION__, __LINE__); printk(args); } } while (0) +#endif #define TEA6415C_NUM_INPUTS 8 #define TEA6415C_NUM_OUTPUTS 6 diff --git a/linux/drivers/media/video/tea6420.c b/linux/drivers/media/video/tea6420.c index b5e41f49b..74af061b2 100644 --- a/linux/drivers/media/video/tea6420.c +++ b/linux/drivers/media/video/tea6420.c @@ -37,8 +37,12 @@ static int debug = 0; /* insmod parameter */ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); -#define dprintk(args...) \ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) do { if (debug) { printk("%s: %s()[%d]: ",__stringify(KBUILD_MODNAME), __FUNCTION__, __LINE__); printk(args); } } while (0) +#else +#define dprintk(args...) \ + do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __FUNCTION__, __LINE__); printk(args); } } while (0) +#endif /* addresses to scan, found only at 0x4c and/or 0x4d (7-Bit) */ static unsigned short normal_i2c[] = { I2C_ADDR_TEA6420_1, I2C_ADDR_TEA6420_2, I2C_CLIENT_END }; -- cgit v1.2.3 From b9581adab1cc68bac8b1ee446845b0e12a6691a1 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Tue, 28 Mar 2006 21:15:05 +0200 Subject: Support for a new revision of the WT220U-stick From: Patrick Boettcher There seems to be a new version of the USB DVB-T stick from WideView with a new demod-revision inside and thus a new firwmare. This patch enables support for that. Thanks to Mikel Martin for early testing. Signed-off-by: Patrick Boettcher --- linux/drivers/media/dvb/dvb-usb/dtt200u.c | 47 ++++++++++++++++++++++++++- linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 2 ++ 2 files changed, 48 insertions(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-usb/dtt200u.c b/linux/drivers/media/dvb/dvb-usb/dtt200u.c index b25f65238..b225297d7 100644 --- a/linux/drivers/media/dvb/dvb-usb/dtt200u.c +++ b/linux/drivers/media/dvb/dvb-usb/dtt200u.c @@ -94,12 +94,14 @@ static int dtt200u_frontend_attach(struct dvb_usb_device *d) static struct dvb_usb_properties dtt200u_properties; static struct dvb_usb_properties wt220u_properties; +static struct dvb_usb_properties wt220u_zl0353_properties; static int dtt200u_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { if (dvb_usb_device_init(intf,&dtt200u_properties,THIS_MODULE,NULL) == 0 || - dvb_usb_device_init(intf,&wt220u_properties,THIS_MODULE,NULL) == 0) + dvb_usb_device_init(intf,&wt220u_properties,THIS_MODULE,NULL) == 0 || + dvb_usb_device_init(intf,&wt220u_zl0353_properties,THIS_MODULE,NULL) == 0) return 0; return -ENODEV; @@ -110,6 +112,8 @@ static struct usb_device_id dtt200u_usb_table [] = { { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_DTT200U_WARM) }, { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_COLD) }, { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_WARM) }, + { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZL0353_COLD) }, + { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZL0353_WARM) }, { 0 }, }; MODULE_DEVICE_TABLE(usb, dtt200u_usb_table); @@ -196,6 +200,47 @@ static struct dvb_usb_properties wt220u_properties = { } }; +static struct dvb_usb_properties wt220u_zl0353_properties = { + .caps = DVB_USB_HAS_PID_FILTER | DVB_USB_NEED_PID_FILTERING, + .pid_filter_count = 15, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-wt220u-zl0353-01.fw", + + .power_ctrl = dtt200u_power_ctrl, + .streaming_ctrl = dtt200u_streaming_ctrl, + .pid_filter = dtt200u_pid_filter, + .frontend_attach = dtt200u_frontend_attach, + + .rc_interval = 300, + .rc_key_map = dtt200u_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dtt200u_rc_keys), + .rc_query = dtt200u_rc_query, + + .generic_bulk_ctrl_endpoint = 0x01, + + /* parameter for the MPEG2-data transfer */ + .urb = { + .type = DVB_USB_BULK, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + + .num_device_descs = 1, + .devices = { + { .name = "WideView WT-220U PenType Receiver (based on ZL353)", + .cold_ids = { &dtt200u_usb_table[4], NULL }, + .warm_ids = { &dtt200u_usb_table[5], NULL }, + }, + { NULL }, + } +}; + /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver dtt200u_usb_driver = { #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 4a1b9e77e..cb239049b 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -83,6 +83,8 @@ #define USB_PID_DTT200U_WARM 0x0301 #define USB_PID_WT220U_COLD 0x0222 #define USB_PID_WT220U_WARM 0x0221 +#define USB_PID_WT220U_ZL0353_COLD 0x022a +#define USB_PID_WT220U_ZL0353_WARM 0x022b #define USB_PID_WINTV_NOVA_T_USB2_COLD 0x9300 #define USB_PID_WINTV_NOVA_T_USB2_WARM 0x9301 #define USB_PID_NEBULA_DIGITV 0x0201 -- cgit v1.2.3 From 903c36ebf540db31e1dc1348e3b13d91ba79faee Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 28 Mar 2006 23:23:48 +0200 Subject: Add wm8739 stereo audio ADC i2c driver From: Hans Verkuil Add support for the Wolfson Microelectronics WM8739 stereo A/D converter from the ivtv driver. Many thanks to Takahiro Adachi for writing the original driver. Signed-off-by: Takahiro Adachi Signed-off-by: Hans Verkuil --- linux/drivers/media/video/Kconfig | 16 +- linux/drivers/media/video/Makefile | 1 + linux/drivers/media/video/wm8739.c | 396 +++++++++++++++++++++++++++++++++++++ 3 files changed, 410 insertions(+), 3 deletions(-) create mode 100644 linux/drivers/media/video/wm8739.c (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index 62e71b527..ba64440c2 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -336,15 +336,25 @@ config VIDEO_CS53L32A module will be called cs53l32a config VIDEO_WM8775 - tristate "Wolfson Microelectronics WM8775 audio ADC" + tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer" depends on VIDEO_DEV && I2C && EXPERIMENTAL ---help--- - Support for the Wolfson Microelectronics WM8775 - high performance stereo A/D Converter. + Support for the Wolfson Microelectronics WM8775 high + performance stereo A/D Converter with a 4 channel input mixer. To compile this driver as a module, choose M here: the module will be called wm8775 +config VIDEO_WM8739 + tristate "Wolfson Microelectronics WM8739 stereo audio ADC" + depends on VIDEO_DEV && I2C && EXPERIMENTAL + ---help--- + Support for the Wolfson Microelectronics WM8739 + stereo A/D Converter. + + To compile this driver as a module, choose M here: the + module will be called wm8739 + source "drivers/media/video/cx25840/Kconfig" config VIDEO_SAA711X diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index 289ef1abf..896619c43 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o obj-$(CONFIG_VIDEO_WM8775) += wm8775.o +obj-$(CONFIG_VIDEO_WM8739) += wm8739.o obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip/ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ obj-$(CONFIG_VIDEO_MXB) += saa7111.o tda9840.o tea6415c.o tea6420.o mxb.o diff --git a/linux/drivers/media/video/wm8739.c b/linux/drivers/media/video/wm8739.c new file mode 100644 index 000000000..79394f205 --- /dev/null +++ b/linux/drivers/media/video/wm8739.c @@ -0,0 +1,396 @@ +/* + * wm8739 + * + * Copyright (C) 2005 T. Adachi + * + * Copyright (C) 2005 Hans Verkuil + * - Cleanup + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "compat.h" +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +#include "i2c-compat.h" +#include +#endif + +MODULE_DESCRIPTION("wm8739 driver"); +MODULE_AUTHOR("T. Adachi, Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static int debug = 0; +static unsigned short normal_i2c[] = { 0x34 >> 1, 0x36 >> 1, I2C_CLIENT_END }; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) +module_param(debug, int, 0644); +#else +MODULE_PARM(debug, "i"); +#endif + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) +static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; +#endif + +I2C_CLIENT_INSMOD; + +/* ------------------------------------------------------------------------ */ + +enum { + R0 = 0, R1, + R5 = 5, R6, R7, R8, R9, R15 = 15, + TOT_REGS +}; + +struct wm8739_state { + u32 clock_freq; + u8 muted; + u16 volume; + u16 balance; + u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ + u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ +}; + +/* ------------------------------------------------------------------------ */ + +static int wm8739_write(struct i2c_client *client, int reg, u16 val) +{ + int i; + + if (reg < 0 || reg >= TOT_REGS) { + v4l_err(client, "Invalid register R%d\n", reg); + return -1; + } + + v4l_dbg(1, debug, client, "write: %02x %02x\n", reg, val); + + for (i = 0; i < 3; i++) { + if (i2c_smbus_write_byte_data(client, (reg << 1) | + (val >> 8), val & 0xff) == 0) { + return 0; + } + } + v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg); + return -1; +} + +/* write regs to set audio volume etc */ +static void wm8739_set_audio(struct i2c_client *client) +{ + struct wm8739_state *state = i2c_get_clientdata(client); + u16 mute = state->muted ? 0x80 : 0; + + /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB + * Default setting: 0x17 = 0 dB + */ + wm8739_write(client, R0, (state->vol_l & 0x1f) | mute); + wm8739_write(client, R1, (state->vol_r & 0x1f) | mute); +} + +static int wm8739_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct wm8739_state *state = i2c_get_clientdata(client); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = state->muted; + break; + + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = state->volume; + break; + + case V4L2_CID_AUDIO_BALANCE: + ctrl->value = state->balance; + break; + + default: + return -EINVAL; + } + return 0; +} + +static int wm8739_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct wm8739_state *state = i2c_get_clientdata(client); + unsigned int work_l, work_r; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + state->muted = ctrl->value; + break; + + case V4L2_CID_AUDIO_VOLUME: + state->volume = ctrl->value; + break; + + case V4L2_CID_AUDIO_BALANCE: + state->balance = ctrl->value; + break; + + default: + return -EINVAL; + } + + /* normalize ( 65535 to 0 -> 31 to 0 (12dB to -34.5dB) ) */ + work_l = (min(65536 - state->balance, 32768) * state->volume) / 32768; + work_r = (min(state->balance, (u16)32768) * state->volume) / 32768; + + state->vol_l = (long)work_l * 31 / 65535; + state->vol_r = (long)work_r * 31 / 65535; + + /* set audio volume etc. */ + wm8739_set_audio(client); + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static struct v4l2_queryctrl wm8739_qctrl[] = { + { + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 58880, + .flags = 0, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = 0, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_AUDIO_BALANCE, + .name = "Balance", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 32768, + .flags = 0, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; + +/* ------------------------------------------------------------------------ */ + +static int wm8739_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + struct wm8739_state *state = i2c_get_clientdata(client); + + switch (cmd) { + case VIDIOC_INT_AUDIO_CLOCK_FREQ: + { + u32 audiofreq = *(u32 *)arg; + + state->clock_freq = audiofreq; + wm8739_write(client, R9, 0x000); /* de-activate */ + switch (audiofreq) { + case 44100: + wm8739_write(client, R8, 0x020); /* 256fps, fs=44.1k */ + break; + case 48000: + wm8739_write(client, R8, 0x000); /* 256fps, fs=48k */ + break; + case 32000: + wm8739_write(client, R8, 0x018); /* 256fps, fs=32k */ + break; + default: + break; + } + wm8739_write(client, R9, 0x001); /* activate */ + break; + } + + case VIDIOC_G_CTRL: + return wm8739_get_ctrl(client, arg); + + case VIDIOC_S_CTRL: + return wm8739_set_ctrl(client, arg); + + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++) + if (qc->id && qc->id == wm8739_qctrl[i].id) { + memcpy(qc, &wm8739_qctrl[i], sizeof(*qc)); + return 0; + } + return -EINVAL; + } + + case VIDIOC_LOG_STATUS: + v4l_info(client, "Frequency: %u Hz\n", state->clock_freq); + v4l_info(client, "Volume L: %02x%s\n", state->vol_l & 0x1f, + state->muted ? " (muted)" : ""); + v4l_info(client, "Volume R: %02x%s\n", state->vol_r & 0x1f, + state->muted ? " (muted)" : ""); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ------------------------------------------------------------------------ */ + +/* i2c implementation */ + +static struct i2c_driver i2c_driver; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +static int wm8739_attach(struct i2c_adapter *adapter, int address, int kind) +#else +static int wm8739_attach(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +#endif +{ + struct i2c_client *client; + struct wm8739_state *state; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == NULL) + return -ENOMEM; + + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) + client->flags = I2C_CLIENT_ALLOW_USE; +#endif + snprintf(client->name, sizeof(client->name) - 1, "wm8739"); + + v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name); + + state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL); + if (state == NULL) { + kfree(client); + return -ENOMEM; + } + state->vol_l = 0x17; /* 0dB */ + state->vol_r = 0x17; /* 0dB */ + state->muted = 0; + state->balance = 32768; + /* normalize (12dB(31) to -34.5dB(0) [0dB(23)] -> 65535 to 0) */ + state->volume = ((long)state->vol_l + 1) * 65535 / 31; + state->clock_freq = 48000; + i2c_set_clientdata(client, state); + + /* initialize wm8739 */ + wm8739_write(client, R15, 0x00); /* reset */ + wm8739_write(client, R5, 0x000); /* filter setting, high path, offet clear */ + wm8739_write(client, R6, 0x000); /* ADC, OSC, Power Off mode Disable */ + wm8739_write(client, R7, 0x049); /* Digital Audio interface format */ + /* Enable Master mode */ + /* 24 bit, MSB first/left justified */ + wm8739_write(client, R8, 0x000); /* sampling control */ + /* normal, 256fs, 48KHz sampling rate */ + wm8739_write(client, R9, 0x001); /* activate */ + wm8739_set_audio(client); /* set volume/mute */ + + i2c_attach_client(client); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + MOD_INC_USE_COUNT; +#endif + return 0; +} + +static int wm8739_probe(struct i2c_adapter *adapter) +{ +#ifdef I2C_CLASS_TV_ANALOG + if (adapter->class & I2C_CLASS_TV_ANALOG) +#else + if (adapter->id == I2C_HW_B_CX2341X) +#endif + return i2c_probe(adapter, &addr_data, wm8739_attach); + return 0; +} + +static int wm8739_detach(struct i2c_client *client) +{ + int err; + + err = i2c_detach_client(client); + if (err) + return err; + + kfree(client); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ +static struct i2c_driver i2c_driver = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) && LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) + .owner = THIS_MODULE, +#endif +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) + .name = "wm8739", + .flags = I2C_DF_NOTIFY, +#else + .driver = { + .name = "wm8739", + }, +#endif + .id = I2C_DRIVERID_WM8739, + .attach_adapter = wm8739_probe, + .detach_client = wm8739_detach, + .command = wm8739_command, +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +EXPORT_NO_SYMBOLS; +#endif + +static int __init wm8739_init_module(void) +{ + return i2c_add_driver(&i2c_driver); +} + +static void __exit wm8739_cleanup_module(void) +{ + i2c_del_driver(&i2c_driver); +} + +module_init(wm8739_init_module); +module_exit(wm8739_cleanup_module); -- cgit v1.2.3 From a6e71fa22c0c223dd7fd3507737228c848f3bfb0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 28 Mar 2006 23:32:52 +0200 Subject: Don't set msp3400c-non-existent register From: Hans Verkuil The driver tried to set a register that is not present on msp3400c devices. Add the missing test. Signed-off-by: Hans Verkuil --- linux/drivers/media/video/msp3400-driver.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/msp3400-driver.c b/linux/drivers/media/video/msp3400-driver.c index 65c99edd8..b52e5bdea 100644 --- a/linux/drivers/media/video/msp3400-driver.c +++ b/linux/drivers/media/video/msp3400-driver.c @@ -298,7 +298,8 @@ void msp_set_scart(struct i2c_client *client, int in, int out) msp_write_dsp(client, 0x13, state->acb); /* Sets I2S speed 0 = 1.024 Mbps, 1 = 2.048 Mbps */ - msp_write_dem(client, 0x40, state->i2s_mode); + if (state->has_i2s_conf) + msp_write_dem(client, 0x40, state->i2s_mode); } void msp_set_mute(struct i2c_client *client) -- cgit v1.2.3 From d213c7fd61512ba8605208f6b785829ff1da017b Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Tue, 28 Mar 2006 22:05:31 -0600 Subject: Major pvrusb2 rework to remove translations From: Mike Isely Rework entire internal controls interface to eliminate the need for visibly defined control IDs which must otherwise be translated by the V4L2 public interface. As part of this work, internal structures which mimiced various V4L2 structures (video standards, audio modes) have been reworked to actually use the native structures. This triggered a _significant_ rework for how video standards are dealt with (and what is in place now should be much more flexible and forgiving for various handling less-common video standards). Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-audio.c | 23 +- linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c | 44 +- linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.h | 1 - .../drivers/media/video/pvrusb2/pvrusb2-encoder.c | 12 +- .../media/video/pvrusb2/pvrusb2-hdw-internal.h | 123 ++- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 956 ++++++++++++++++----- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h | 199 ++--- .../media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c | 30 +- linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c | 120 ++- linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 402 +++------ 10 files changed, 1134 insertions(+), 776 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c index 79395d540..ae34ff9e4 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -37,26 +37,6 @@ struct pvr2_msp3400_handler { }; -static int xlat_audiomode_to_v4l2(int id) -{ - switch (id) { - case PVR2_CVAL_AUDIOMODE_MONO: - return V4L2_TUNER_MODE_MONO; - case PVR2_CVAL_AUDIOMODE_STEREO: - return V4L2_TUNER_MODE_STEREO; - case PVR2_CVAL_AUDIOMODE_SAP: - return V4L2_TUNER_MODE_SAP; - case PVR2_CVAL_AUDIOMODE_LANG1: - return V4L2_TUNER_MODE_LANG1; - case PVR2_CVAL_AUDIOMODE_LANG2: - return V4L2_TUNER_MODE_LANG2; - case PVR2_CVAL_AUDIOMODE_LANG1_LANG2: - return V4L2_TUNER_MODE_LANG1_LANG2; - } - return V4L2_TUNER_MODE_STEREO; -} - - /* This function selects the correct audio input source */ static void set_stereo(struct pvr2_msp3400_handler *ctxt) { @@ -68,8 +48,7 @@ static void set_stereo(struct pvr2_msp3400_handler *ctxt) if (hdw->controls[PVR2_CID_INPUT].value == PVR2_CVAL_INPUT_TV) { struct v4l2_tuner vt; memset(&vt,0,sizeof(vt)); - vt.audmode = xlat_audiomode_to_v4l2( - hdw->controls[PVR2_CID_AUDIOMODE].value); + vt.audmode = hdw->controls[PVR2_CID_AUDIOMODE].value; pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_TUNER,&vt); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c index 60ee45ca2..634095efc 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c @@ -204,7 +204,7 @@ int pvr2_eeprom_analyze(struct pvr2_hdw *hdw) trace_eeprom("rev_str=%s",tvdata.rev_str); hdw->tuner_type = tvdata.tuner_type; hdw->serial_number = tvdata.serial_number; - hdw->video_standards = tvdata.tuner_formats; + pvr2_hdw_internal_set_std_avail(hdw,tvdata.tuner_formats); kfree(eeprom); @@ -222,48 +222,6 @@ int pvr2_eeprom_analyze(struct pvr2_hdw *hdw) -static v4l2_std_id std_choices[] = { - [PVR2_CVAL_VIDEOSTANDARD_NTSC_M] = V4L2_STD_NTSC_M, - [PVR2_CVAL_VIDEOSTANDARD_PAL_BG] = V4L2_STD_PAL_BG, - [PVR2_CVAL_VIDEOSTANDARD_PAL_I] = V4L2_STD_PAL_I, - [PVR2_CVAL_VIDEOSTANDARD_PAL_DK] = V4L2_STD_PAL_DK, - [PVR2_CVAL_VIDEOSTANDARD_SECAM_L] = V4L2_STD_SECAM_L, - [PVR2_CVAL_VIDEOSTANDARD_PAL_M] = V4L2_STD_PAL_M, -}; - -void pvr2_eeprom_set_default_standard(struct pvr2_hdw *hdw) -{ - int vstd_value = 0; - int vstd_found = 0; - unsigned int idx; - v4l2_std_id vs = (v4l2_std_id)hdw->video_standards; - - for (idx = 0; idx < sizeof(std_choices)/sizeof(std_choices[0]); - idx++) { - if (!(vs & std_choices[idx])) continue; - trace_eeprom("Detected video standard %s (from eeprom)", - pvr2_hdw_get_ctl_value_name( - hdw,PVR2_CID_VIDEOSTANDARD,idx)); - if (vstd_found) continue; - vstd_value = idx; - vstd_found = !0; - } - - if (!vstd_found) { - trace_eeprom("eeprom unable to recognize" - " a known video standard"); - return; - } - - trace_eeprom("Setting initial video standard to %s" - " (detected from eeprom)", - pvr2_hdw_get_ctl_value_name(hdw, - PVR2_CID_VIDEOSTANDARD, - vstd_value)); - pvr2_hdw_set_ctl_value_internal(hdw,PVR2_CID_VIDEOSTANDARD,vstd_value); -} - - /* Stuff for Emacs to see, in order to encourage consistent editing style: *** Local Variables: *** diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.h b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.h index 061cecd91..84242975d 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.h @@ -26,7 +26,6 @@ struct pvr2_hdw; int pvr2_eeprom_analyze(struct pvr2_hdw *); -void pvr2_eeprom_set_default_standard(struct pvr2_hdw *); #endif /* __PVRUSB2_EEPROM_H */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 553bd2d7b..2de5b6c8f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -268,26 +268,22 @@ static int pvr2_write_encoder_vcmd (struct pvr2_hdw *hdw, u8 cmd, int pvr2_encoder_configure(struct pvr2_hdw *hdw) { int ret = 0, audio, i; - int vd_std = hdw->controls[PVR2_CID_VIDEOSTANDARD].value; + int vd_std = hdw->controls[PVR2_CID_STDCUR].value; int height = hdw->controls[PVR2_CID_VRES].value; int width = hdw->controls[PVR2_CID_HRES].value; int height_full = !hdw->controls[PVR2_CID_INTERLACE].value; int is_30fps, is_ntsc; - switch (vd_std) { - case PVR2_CVAL_VIDEOSTANDARD_NTSC_M: + if (vd_std & V4L2_STD_NTSC) { is_ntsc=1; is_30fps=1; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_M: + } else if (vd_std & V4L2_STD_PAL_M) { is_ntsc=0; is_30fps=1; - break; - default: + } else { is_ntsc=0; is_30fps=0; - break; } pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure"); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 25854375b..189075bbe 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -45,6 +45,83 @@ #include "pvrusb2-hdw.h" #include "pvrusb2-io.h" + +/* Definition of state variables that we can inspect & change. Numbers are + assigned from zero counting up with no gaps. */ +#define PVR2_CID_BRIGHTNESS 0 +#define PVR2_CID_CONTRAST 1 +#define PVR2_CID_SATURATION 2 +#define PVR2_CID_HUE 3 +#define PVR2_CID_VOLUME 4 +#define PVR2_CID_BALANCE 5 +#define PVR2_CID_BASS 6 +#define PVR2_CID_TREBLE 7 +#define PVR2_CID_MUTE 8 +#define PVR2_CID_SRATE 9 +#define PVR2_CID_AUDIOBITRATE 10 +#define PVR2_CID_AUDIOCRC 11 +#define PVR2_CID_AUDIOEMPHASIS 12 +#define PVR2_CID_VBR 13 +#define PVR2_CID_AVERAGEVIDEOBITRATE 14 +#define PVR2_CID_PEAKVIDEOBITRATE 15 +#define PVR2_CID_STDAVAIL 16 // V4L2 video standard bit mask +#define PVR2_CID_INPUT 17 +#define PVR2_CID_AUDIOMODE 18 // V4L2 standard audio mode enum +#define PVR2_CID_FREQUENCY 19 // Units of Hz +#define PVR2_CID_HRES 20 +#define PVR2_CID_VRES 21 +#define PVR2_CID_INTERLACE 22 +#define PVR2_CID_AUDIOLAYER 23 +#define PVR2_CID_CHANNEL 24 +#define PVR2_CID_CHANPROG_ID 25 +#define PVR2_CID_CHANPROG_FREQ 26 +#define PVR2_CID_SIGNAL_PRESENT 27 +#define PVR2_CID_STREAMING_ENABLED 28 +#define PVR2_CID_HSM 29 +#define PVR2_CID_SUBSYS_MASK 30 +#define PVR2_CID_SUBSYS_STREAM_MASK 31 +#define PVR2_CID_STDCUR 32 // V4L2 video standard bit mask +#define PVR2_CID_STDNAME 33 // Enumeration of available standards + +/* Legal values for the SRATE state variable */ +#define PVR2_CVAL_SRATE_48 0 +#define PVR2_CVAL_SRATE_44_1 1 +#define PVR2_CVAL_SRATE_MIN PVR2_CVAL_SRATE_48 +#define PVR2_CVAL_SRATE_MAX PVR2_CVAL_SRATE_44_1 + +/* Legal values for the AUDIOBITRATE state variable */ +#define PVR2_CVAL_AUDIOBITRATE_384 0 +#define PVR2_CVAL_AUDIOBITRATE_320 1 +#define PVR2_CVAL_AUDIOBITRATE_256 2 +#define PVR2_CVAL_AUDIOBITRATE_224 3 +#define PVR2_CVAL_AUDIOBITRATE_192 4 +#define PVR2_CVAL_AUDIOBITRATE_160 5 +#define PVR2_CVAL_AUDIOBITRATE_128 6 +#define PVR2_CVAL_AUDIOBITRATE_112 7 +#define PVR2_CVAL_AUDIOBITRATE_96 8 +#define PVR2_CVAL_AUDIOBITRATE_80 9 +#define PVR2_CVAL_AUDIOBITRATE_64 10 +#define PVR2_CVAL_AUDIOBITRATE_56 11 +#define PVR2_CVAL_AUDIOBITRATE_48 12 +#define PVR2_CVAL_AUDIOBITRATE_32 13 +#define PVR2_CVAL_AUDIOBITRATE_VBR 14 +#define PVR2_CVAL_AUDIOBITRATE_MIN PVR2_CVAL_AUDIOBITRATE_384 +#define PVR2_CVAL_AUDIOBITRATE_MAX PVR2_CVAL_AUDIOBITRATE_VBR + +/* Legal values for the AUDIOEMPHASIS state variable */ +#define PVR2_CVAL_AUDIOEMPHASIS_NONE 0 +#define PVR2_CVAL_AUDIOEMPHASIS_50_15 1 +#define PVR2_CVAL_AUDIOEMPHASIS_CCITT 2 +#define PVR2_CVAL_AUDIOEMPHASIS_MIN PVR2_CVAL_AUDIOEMPHASIS_NONE +#define PVR2_CVAL_AUDIOEMPHASIS_MAX PVR2_CVAL_AUDIOEMPHASIS_CCITT + +/* Legal values for PVR2_CID_HSM */ +#define PVR2_CVAL_HSM_FAIL 0 +#define PVR2_CVAL_HSM_FULL 1 +#define PVR2_CVAL_HSM_HIGH 2 +#define PVR2_CVAL_HSM_MIN PVR2_CVAL_HSM_FAIL +#define PVR2_CVAL_HSM_MAX PVR2_CVAL_HSM_HIGH + #define PVR2_VID_ENDPOINT 0x84 #define PVR2_UNK_ENDPOINT 0x86 /* maybe raw yuv ? */ #define PVR2_VBI_ENDPOINT 0x88 @@ -58,7 +135,33 @@ struct pvr2_decoder; -struct pvr2_ctl_state { +struct pvr2_ctl_def; +struct pvr2_ctrl; + +typedef int (*pvr2_ctl_set_func)(struct pvr2_ctrl *,int val); +typedef int (*pvr2_ctl_get_func)(struct pvr2_ctrl *); + +struct pvr2_ctl_def { + int id; + const char *name; + const char *desc; + pvr2_ctl_set_func set_func; + pvr2_ctl_get_func get_func; + int mask_value; + int max_value; + int min_value; + int skip_init; + int default_value; + int is_valid; + const char **value_defs_ptr; + unsigned int value_defs_count; +}; + + +struct pvr2_ctrl { + struct pvr2_hdw *hdw; + const struct pvr2_ctl_def *ctl_def; + int is_valid; int value; int dirty; }; @@ -191,7 +294,13 @@ struct pvr2_hdw { /* Tuner / frequency control stuff */ unsigned int tuner_type; int tuner_updated; - unsigned long video_standards; + v4l2_std_id video_std_avail; + v4l2_std_id video_std_cur; + struct pvr2_ctl_def video_std_enum; + struct v4l2_standard *std_defs; + const char **video_std_names; + unsigned int std_cnt; + int std_id; int unit_number; /* ID for driver instance */ unsigned long serial_number; /* ID for hardware itself */ @@ -211,13 +320,17 @@ struct pvr2_hdw { struct pvr2_audio_stat *audio_stat; /* Every last bit of controllable state */ - struct pvr2_ctl_state *controls; + struct pvr2_ctrl *controls; }; -int pvr2_hdw_set_ctl_value_internal(struct pvr2_hdw *hdw, - unsigned int ctl_id,int value); int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw); +int pvr2_ctrl_internal_set_value(struct pvr2_ctrl *cptr,int val); +int pvr2_ctrl_internal_get_value(struct pvr2_ctrl *cptr); + +int pvr2_hdw_internal_set_stdenum_cur(struct pvr2_hdw *hdw,int val); +void pvr2_hdw_internal_set_std_cur(struct pvr2_hdw *hdw,int msk); +void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw,int msk); #endif /* __PVRUSB2_HDW_INTERNAL_H */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index a0b0cd8aa..585f4ee0b 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "pvrusb2.h" #include "pvrusb2-util.h" @@ -86,23 +87,6 @@ MODULE_PARM_DESC(tolerance,"specify stream error tolerance"); /* size of a firmware chunk */ #define FIRMWARE_CHUNK_SIZE 0x2000 -typedef int (*pvr2_ctl_set_func)(struct pvr2_hdw *,int ctl_id,int val); -typedef int (*pvr2_ctl_get_func)(struct pvr2_hdw *,int ctl_id); - -struct pvr2_ctl_def { - const char *name; - pvr2_ctl_set_func set_func; - pvr2_ctl_get_func get_func; - int max_value; - int min_value; - int skip_init; - int default_value; - int is_valid; - const char **value_defs_ptr; - unsigned int value_defs_count; -}; - - static const char *control_values_srate[] = { [PVR2_CVAL_SRATE_48] = "48KHz", [PVR2_CVAL_SRATE_44_1] = "44.1KHz", @@ -136,12 +120,35 @@ static const char *control_values_audioemphasis[] = { static const char *control_values_videostandard[] = { - [PVR2_CVAL_VIDEOSTANDARD_NTSC_M] = "NTSC-M", - [PVR2_CVAL_VIDEOSTANDARD_SECAM_L] = "SECAM-L", - [PVR2_CVAL_VIDEOSTANDARD_PAL_BG] = "PAL-BG", - [PVR2_CVAL_VIDEOSTANDARD_PAL_I] = "PAL-I", - [PVR2_CVAL_VIDEOSTANDARD_PAL_DK] = "PAL-DK", - [PVR2_CVAL_VIDEOSTANDARD_PAL_M] = "PAL-M", + "PAL-B", + "PAL-B1", + "PAL-G", + "PAL-H", + + "PAL-I", + "PAL-D", + "PAL-D1", + "PAL-K", + + "PAL-M", + "PAL-N", + "PAL-Nc", + "PAL-60", + + "NTSC-M", + "NTSC-M-JP", + "NTSC-443", + 0, + + "SECAM-B", + "SECAM-D", + "SECAM-G", + "SECAM-H", + + "SECAM-K", + "SECAM-K1", + "SECAM-L", + "SECAM-LC", }; @@ -154,12 +161,11 @@ static const char *control_values_input[] = { static const char *control_values_audiomode[] = { - [PVR2_CVAL_AUDIOMODE_MONO] = "Mono", - [PVR2_CVAL_AUDIOMODE_STEREO] = "Stereo", - [PVR2_CVAL_AUDIOMODE_SAP] = "SAP", - [PVR2_CVAL_AUDIOMODE_LANG1] = "Lang1", - [PVR2_CVAL_AUDIOMODE_LANG2] = "Lang2", - [PVR2_CVAL_AUDIOMODE_LANG1_LANG2] = "Lang1+Lang2", + [V4L2_TUNER_MODE_MONO] = "Mono", + [V4L2_TUNER_MODE_STEREO] = "Stereo", + [V4L2_TUNER_MODE_LANG1] = "Lang1", + [V4L2_TUNER_MODE_LANG2] = "Lang2", + [V4L2_TUNER_MODE_LANG1_LANG2] = "Lang1+Lang2", }; @@ -174,210 +180,265 @@ static const char *control_values_hsm[] = { .value_defs_ptr = x, \ .value_defs_count = (sizeof(x)/sizeof(x[0])) -static int pvr2_ctl_set_chanprog_id(struct pvr2_hdw *hdw,int ctl_id,int value); -static int pvr2_ctl_get_chanprog_id(struct pvr2_hdw *hdw,int ctl_id); -static int pvr2_ctl_get_signal(struct pvr2_hdw *hdw,int ctl_id); -static int pvr2_ctl_get_streaming(struct pvr2_hdw *hdw,int ctl_id); -static int pvr2_ctl_get_hsm(struct pvr2_hdw *hdw,int ctl_id); -static int pvr2_ctl_get_subsys_mask(struct pvr2_hdw *hdw,int ctl_id); -static int pvr2_ctl_set_subsys_mask(struct pvr2_hdw *hdw,int ctl_id,int val); -static int pvr2_ctl_get_subsys_stream_mask(struct pvr2_hdw *hdw,int ctl_id); -static int pvr2_ctl_set_subsys_stream_mask(struct pvr2_hdw *hdw,int ctl_id, - int val); +static int pvr2_ctl_set_chanprog_id(struct pvr2_ctrl *cptr,int value); +static int pvr2_ctl_get_chanprog_id(struct pvr2_ctrl *cptr); +static int pvr2_ctl_get_signal(struct pvr2_ctrl *cptr); +static int pvr2_ctl_get_streaming(struct pvr2_ctrl *cptr); +static int pvr2_ctl_get_hsm(struct pvr2_ctrl *cptr); +static int pvr2_ctl_get_subsys_mask(struct pvr2_ctrl *cptr); +static int pvr2_ctl_set_subsys_mask(struct pvr2_ctrl *cptr,int val); +static int pvr2_ctl_get_subsys_stream_mask(struct pvr2_ctrl *cptr); +static int pvr2_ctl_set_subsys_stream_mask(struct pvr2_ctrl *cptr,int val); +static int pvr2_ctl_set_stdcur(struct pvr2_ctrl *cptr,int val); +static int pvr2_ctl_get_stdcur(struct pvr2_ctrl *cptr); +static int pvr2_ctl_get_stdavail(struct pvr2_ctrl *cptr); +static int pvr2_ctl_set_stdenumcur(struct pvr2_ctrl *cptr,int val); +static int pvr2_ctl_get_stdenumcur(struct pvr2_ctrl *cptr); static struct pvr2_ctl_def control_defs[] = { [PVR2_CID_BRIGHTNESS] = { + .id = V4L2_CID_BRIGHTNESS, .is_valid = !0, - .name = "Brightness", + .desc = "Brightness", + .name = "brightness", .min_value = 0, .max_value = 255, .default_value = 128, }, [PVR2_CID_CONTRAST] = { + .id = V4L2_CID_CONTRAST, .is_valid = !0, - .name = "Contrast", + .desc = "Contrast", + .name = "contrast", .min_value = 0, .max_value = 127, .default_value = 68, }, [PVR2_CID_SATURATION] = { + .id = V4L2_CID_SATURATION, .is_valid = !0, - .name = "Saturation", + .desc = "Saturation", + .name = "saturation", .min_value = 0, .max_value = 127, .default_value = 64, }, [PVR2_CID_HUE] = { + .id = V4L2_CID_HUE, .is_valid = !0, - .name = "Hue", + .desc = "Hue", + .name = "hue", .min_value = -128, .max_value = 127, .default_value = 0, }, [PVR2_CID_VOLUME] = { + .id = V4L2_CID_AUDIO_VOLUME, .is_valid = !0, - .name = "Volume", + .desc = "Volume", + .name = "volume", .min_value = 0, .max_value = 65535, .default_value = 65535, }, [PVR2_CID_BALANCE] = { + .id = V4L2_CID_AUDIO_BALANCE, .is_valid = !0, - .name = "Balance", + .desc = "Balance", + .name = "balance", .min_value = -32768, .max_value = 32767, .default_value = 0, }, [PVR2_CID_BASS] = { + .id = V4L2_CID_AUDIO_BASS, .is_valid = !0, - .name = "Bass", + .desc = "Bass", + .name = "bass", .min_value = -32768, .max_value = 32767, .default_value = 0, }, [PVR2_CID_TREBLE] = { + .id = V4L2_CID_AUDIO_TREBLE, .is_valid = !0, - .name = "Treble", + .desc = "Treble", + .name = "treble", .min_value = -32768, .max_value = 32767, .default_value = 0, }, [PVR2_CID_MUTE] = { + .id = V4L2_CID_AUDIO_MUTE, .is_valid = !0, - .name = "Mute", + .desc = "Mute", + .name = "mute", .min_value = 0, .max_value = 1, .default_value = 0, }, [PVR2_CID_SRATE] = { + .id = V4L2_CID_PVR_SRATE, .is_valid = !0, - .name = "Sample rate", + .desc = "Sample rate", + .name = "srate", .min_value = PVR2_CVAL_SRATE_MIN, .max_value = PVR2_CVAL_SRATE_MAX, .default_value = PVR2_CVAL_SRATE_48, VDEF(control_values_srate), }, [PVR2_CID_AUDIOBITRATE] = { + .id = V4L2_CID_PVR_AUDIOBITRATE, .is_valid = !0, - .name = "Audio Bitrate", + .desc = "Audio Bitrate", + .name = "audio_bitrate", .min_value = PVR2_CVAL_AUDIOBITRATE_MIN, .max_value = PVR2_CVAL_AUDIOBITRATE_MAX, .default_value = PVR2_CVAL_AUDIOBITRATE_224, VDEF(control_values_audiobitrate), }, [PVR2_CID_AUDIOCRC] = { + .id = V4L2_CID_PVR_AUDIOCRC, .is_valid = !0, - .name = "Audio CRC", + .desc = "Audio CRC", + .name = "audio_crc", .min_value = 0, .max_value = 1, .default_value = 1, }, [PVR2_CID_AUDIOEMPHASIS] = { + .id = V4L2_CID_PVR_AUDIOEMPHASIS, .is_valid = !0, - .name = "Audio Emphasis", + .desc = "Audio Emphasis", + .name = "audio_emphasis", .min_value = PVR2_CVAL_AUDIOEMPHASIS_MIN, .max_value = PVR2_CVAL_AUDIOEMPHASIS_MAX, .default_value = PVR2_CVAL_AUDIOEMPHASIS_NONE, VDEF(control_values_audioemphasis), }, [PVR2_CID_VBR] = { + .id = V4L2_CID_PVR_VBR, .is_valid = !0, - .name = "Variable video bitrate", + .desc = "Variable video bitrate", + .name = "vbr", .min_value = 0, .max_value = 1, .default_value = 0, }, [PVR2_CID_AVERAGEVIDEOBITRATE] = { + .id = V4L2_CID_PVR_VIDEOBITRATE, .is_valid = !0, - .name = "Average video bitrate", + .desc = "Average video bitrate", + .name = "video_average_bitrate", .min_value = 1, .max_value = 20000000, .default_value = 6000000, }, [PVR2_CID_PEAKVIDEOBITRATE] = { + .id = V4L2_CID_PVR_VIDEOPEAK, .is_valid = !0, - .name = "Peak video bitrate", + .desc = "Peak video bitrate", + .name = "video_peak_bitrate", .min_value = 1, .max_value = 20000000, .default_value = 6000000, }, - [PVR2_CID_VIDEOSTANDARD] = { + [PVR2_CID_STDAVAIL] = { .is_valid = !0, - .name = "Video Standard", - .min_value = PVR2_CVAL_VIDEOSTANDARD_MIN, - .max_value = PVR2_CVAL_VIDEOSTANDARD_MAX, - .default_value = PVR2_CVAL_VIDEOSTANDARD_NTSC_M, + .desc = "Video Standards Available Mask", + .name = "video_standard_mask_available", + .min_value = 0, + .max_value = 0, + .default_value = (int)V4L2_STD_UNKNOWN, + .mask_value = (int)V4L2_STD_ALL, + .get_func = pvr2_ctl_get_stdavail, VDEF(control_values_videostandard), }, [PVR2_CID_INPUT] = { + .id = V4L2_CID_PVR_INPUT, .is_valid = !0, - .name = "Video Source", + .desc = "Video Source", + .name = "input", .min_value = PVR2_CVAL_INPUT_MIN, .max_value = PVR2_CVAL_INPUT_MAX, .default_value = PVR2_CVAL_INPUT_TV, VDEF(control_values_input), }, [PVR2_CID_AUDIOMODE] = { + .id = V4L2_CID_PVR_AUDIOMODE, .is_valid = !0, - .name = "Audio Mode", - .min_value = PVR2_CVAL_AUDIOMODE_MIN, - .max_value = PVR2_CVAL_AUDIOMODE_MAX, - .default_value = PVR2_CVAL_AUDIOMODE_STEREO, + .desc = "Audio Mode", + .name = "audio_mode", + .min_value = V4L2_TUNER_MODE_MONO, + .max_value = V4L2_TUNER_MODE_LANG1_LANG2, + .default_value = V4L2_TUNER_MODE_STEREO, VDEF(control_values_audiomode), }, [PVR2_CID_FREQUENCY] = { + .id = V4L2_CID_PVR_FREQUENCY, .is_valid = !0, - .name = "Tuner Frequency (Hz)", + .desc = "Tuner Frequency (Hz)", + .name = "frequency", .min_value = 55250000L, .max_value = 850000000L, .default_value = 175250000L, }, [PVR2_CID_HRES] = { + .id = V4L2_CID_PVR_HRES, .is_valid = !0, - .name = "Horizontal capture resolution", + .desc = "Horizontal capture resolution", + .name = "resolution_hor", .min_value = 320, .max_value = 720, .default_value = 720, }, [PVR2_CID_VRES] = { + .id = V4L2_CID_PVR_VRES, .is_valid = !0, - .name = "Vertical capture resolution", + .desc = "Vertical capture resolution", + .name = "resolution_ver", .min_value = 200, .max_value = 625, .default_value = 480, }, [PVR2_CID_INTERLACE] = { + .id = V4L2_CID_PVR_INTERLACE, .is_valid = !0, - .name = "Interlace mode", + .desc = "Interlace mode", + .name = "interlace", .min_value = 0, .max_value = 1, .default_value = 0, }, [PVR2_CID_AUDIOLAYER] = { .is_valid = !0, - .name = "Audio Layer", + .desc = "Audio Layer", + .name = "audio_layer", .min_value = 0, /* This is all a wild guess */ .max_value = 3, .default_value = 2, /* Appears to be all that is supported */ }, [PVR2_CID_CHANNEL] = { .is_valid = !0, - .name = "Channel", + .desc = "Channel", + .name = "channel", .min_value = 0, .max_value = FREQTABLE_SIZE, .default_value = 0, }, [PVR2_CID_CHANPROG_ID] = { .is_valid = !0, - .name = "Channel Program ID", + .desc = "Channel Program ID", + .name = "freq_table_channel", .min_value = 0, .max_value = FREQTABLE_SIZE, .default_value = 0, }, [PVR2_CID_CHANPROG_FREQ] = { .is_valid = !0, - .name = "Channel Program Frequency", + .desc = "Channel Program Frequency", + .name = "freq_table_value", .min_value = 55250000L, .max_value = 850000000L, .skip_init = !0, @@ -386,21 +447,24 @@ static struct pvr2_ctl_def control_defs[] = }, [PVR2_CID_SIGNAL_PRESENT] = { .is_valid = !0, - .name = "Signal Present", + .desc = "Signal Present", + .name = "signal_present", .min_value = 0, .max_value = 1, .get_func = pvr2_ctl_get_signal, }, [PVR2_CID_STREAMING_ENABLED] = { .is_valid = !0, - .name = "Streaming Enabled", + .desc = "Streaming Enabled", + .name = "streaming_enabled", .min_value = 0, .max_value = 1, .get_func = pvr2_ctl_get_streaming, }, [PVR2_CID_HSM] = { .is_valid = !0, - .name = "USB Speed", + .desc = "USB Speed", + .name = "usb_speed", .min_value = PVR2_CVAL_HSM_MIN, .max_value = PVR2_CVAL_HSM_MAX, .get_func = pvr2_ctl_get_hsm, @@ -408,7 +472,8 @@ static struct pvr2_ctl_def control_defs[] = }, [PVR2_CID_SUBSYS_MASK] = { .is_valid = !0, - .name = "Subsystem enabled mask", + .desc = "Subsystem enabled mask", + .name = "debug_subsys_mask", .min_value = 0, .max_value = 0x7fffffff, .skip_init = !0, @@ -417,18 +482,33 @@ static struct pvr2_ctl_def control_defs[] = }, [PVR2_CID_SUBSYS_STREAM_MASK] = { .is_valid = !0, - .name = "Subsystem stream mask", + .desc = "Subsystem stream mask", + .name = "debug_subsys_stream_mask", .min_value = 0, .max_value = 0x7fffffff, .skip_init = !0, .get_func = pvr2_ctl_get_subsys_stream_mask, .set_func = pvr2_ctl_set_subsys_stream_mask, }, + [PVR2_CID_STDCUR] = { + .id = V4L2_CID_PVR_STDCUR, + .is_valid = !0, + .desc = "Video Standard In Use Mask", + .name = "video_standard_mask_active", + .min_value = 0, + .max_value = 0, + .default_value = (int)V4L2_STD_UNKNOWN, + .mask_value = (int)V4L2_STD_ALL, + .set_func = pvr2_ctl_set_stdcur, + .get_func = pvr2_ctl_get_stdcur, + VDEF(control_values_videostandard), + }, }; #undef VDEF -#define CTRL_COUNT (sizeof(control_defs)/sizeof(control_defs[0])) +#define CTRL_DEF_COUNT (sizeof(control_defs)/sizeof(control_defs[0])) +#define CTRL_COUNT CTRL_DEF_COUNT+1 const char *pvr2_config_get_name(enum pvr2_config cfg) { @@ -978,9 +1058,9 @@ void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw, } -static int pvr2_ctl_get_streaming(struct pvr2_hdw *hdw,int ctl_id) +static int pvr2_ctl_get_streaming(struct pvr2_ctrl *cptr) { - return hdw->flag_streaming_enabled != 0; + return cptr->hdw->flag_streaming_enabled != 0; } @@ -1095,6 +1175,7 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) { int ret; unsigned int idx; + struct pvr2_ctrl *cptr; int reloadFl = 0; if (!reloadFl) { reloadFl = (hdw->usb_intf->cur_altsetting->desc.bNumEndpoints @@ -1141,9 +1222,11 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) if (!pvr2_hdw_dev_ok(hdw)) return; for (idx = 0; idx < CTRL_COUNT; idx++) { - if (control_defs[idx].skip_init) continue; - pvr2_hdw_set_ctl_value_internal( - hdw,idx,control_defs[idx].default_value); + cptr = hdw->controls + idx; + if (!pvr2_ctrl_is_valid(cptr)) continue; + if (cptr->ctl_def->skip_init) continue; + pvr2_ctrl_internal_set_value(cptr, + cptr->ctl_def->default_value); } pvr2_reset_ctl_endpoints(hdw); @@ -1172,7 +1255,21 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) if (!pvr2_hdw_dev_ok(hdw)) return; - pvr2_eeprom_set_default_standard(hdw); + for (idx = 0; idx < hdw->std_cnt; idx++) { + pvr2_trace(PVR2_TRACE_EEPROM, + "Detected video standard %s (from eeprom)", + hdw->std_defs[idx].name); + } + if (hdw->std_cnt) { + pvr2_trace(PVR2_TRACE_EEPROM, + "Initial video standard set to %s" + " (detected from eeprom)", + hdw->std_defs[hdw->std_id].name); + } else { + pvr2_trace(PVR2_TRACE_EEPROM, + "Unable to select a viable video standard"); + } + if (!pvr2_hdw_dev_ok(hdw)) return; pvr2_hdw_commit_ctl_internal(hdw); @@ -1293,11 +1390,35 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw,pvr2_device_names[hdw_type]); if (!hdw) goto fail; memset(hdw,0,sizeof(*hdw)); - hdw->controls = kmalloc(sizeof(struct pvr2_ctl_state) * CTRL_COUNT, + + // Initialize video standard enum dynamic control + hdw->video_std_enum.name = "video_standard"; + hdw->video_std_enum.desc = "Video Standard Name"; + hdw->video_std_enum.id = 0; // ????? + hdw->video_std_enum.set_func = pvr2_ctl_set_stdenumcur; + hdw->video_std_enum.get_func = pvr2_ctl_get_stdenumcur; + hdw->video_std_enum.mask_value = 0; + hdw->video_std_enum.max_value = 0; + hdw->video_std_enum.min_value = 0; + hdw->video_std_enum.default_value = 0; + hdw->video_std_enum.is_valid = !0; + + hdw->controls = kmalloc(sizeof(struct pvr2_ctrl) * CTRL_COUNT, GFP_KERNEL); if (!hdw->controls) goto fail; + memset(hdw->controls,0,sizeof(struct pvr2_ctrl) * CTRL_COUNT); hdw->hdw_type = hdw_type; + for (idx = 0; idx < CTRL_DEF_COUNT; idx++) { + hdw->controls[idx].hdw = hdw; + hdw->controls[idx].ctl_def = control_defs + idx; + hdw->controls[idx].is_valid = + hdw->controls[idx].ctl_def->is_valid; + } + hdw->controls[PVR2_CID_STDNAME].hdw = hdw; + hdw->controls[PVR2_CID_STDNAME].ctl_def = &hdw->video_std_enum; + hdw->controls[PVR2_CID_STDNAME].is_valid = !0; + hdw->eeprom_addr = -1; hdw->unit_number = -1; hdw->v4l_minor_number = -1; @@ -1425,6 +1546,8 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) } } while (0); up(&pvr2_unit_sem); kfree(hdw->controls); + if (hdw->std_defs) kfree(hdw->std_defs); + if (hdw->video_std_names) kfree(hdw->video_std_names); kfree(hdw); } @@ -1453,10 +1576,11 @@ void pvr2_hdw_disconnect(struct pvr2_hdw *hdw) } -static int pvr2_ctl_set_chanprog_id(struct pvr2_hdw *hdw,int ctl_id,int value) +static int pvr2_ctl_set_chanprog_id(struct pvr2_ctrl *cptr,int value) { /* This is a special case; the value to store is to an array, and the element to select is determined by PVR_CID_CHANPROG_ID. */ + struct pvr2_hdw *hdw = cptr->hdw; int id = hdw->controls[PVR2_CID_CHANPROG_ID].value; if ((id < 1) || (id > FREQTABLE_SIZE)) return 0; hdw->freqTable[id-1] = value; @@ -1470,149 +1594,584 @@ static int pvr2_ctl_set_chanprog_id(struct pvr2_hdw *hdw,int ctl_id,int value) } -static int pvr2_ctl_get_chanprog_id(struct pvr2_hdw *hdw,int ctl_id) +static int pvr2_ctl_get_chanprog_id(struct pvr2_ctrl *cptr) { /* This is a special case; the value to return is from an array, and the element to select is determined by PVR_CID_CHANPROG_ID. */ + struct pvr2_hdw *hdw = cptr->hdw; int id = hdw->controls[PVR2_CID_CHANPROG_ID].value; if ((id < 1) || (id > FREQTABLE_SIZE)) return 0; return hdw->freqTable[id-1]; } +// Template data for possible enumerated video standards +static struct v4l2_standard pvr_standards[] = { + { + .id = V4L2_STD_PAL_BG, + .frameperiod = + { + .numerator = 1, + .denominator= 25 + }, + .framelines = 625, + .reserved = {0,0,0,0} + }, { + .id = V4L2_STD_PAL_I, + .frameperiod = + { + .numerator = 1, + .denominator= 25 + }, + .framelines = 625, + .reserved = {0,0,0,0} + }, { + .id = V4L2_STD_PAL_DK, + .frameperiod = + { + .numerator = 1, + .denominator= 25 + }, + .framelines = 625, + .reserved = {0,0,0,0} + }, { + .id = V4L2_STD_SECAM, + .frameperiod = + { + .numerator = 1, + .denominator= 25 + }, + .framelines = 625, + .reserved = {0,0,0,0} + }, { + .id = V4L2_STD_NTSC_M, + .frameperiod = + { + .numerator = 1001, + .denominator= 30000 + }, + .framelines = 525, + .reserved = {0,0,0,0} + }, { + .id = V4L2_STD_PAL_M, + .frameperiod = + { + .numerator = 1001, + .denominator= 30000 + }, + .framelines = 525, + .reserved = {0,0,0,0} + } +}; + +#define pvr_standards_cnt (sizeof(pvr_standards)/sizeof(pvr_standards[0])) + -/* Retrieve current value for a given control */ -int pvr2_hdw_get_ctl_value(struct pvr2_hdw *hdw,unsigned int ctl_id) +struct name_data { + struct v4l2_standard *std; + unsigned int bcnt; + unsigned int scnt; +}; + +static void name_build(struct name_data *dp,const char *str) { - int ret = 0; + if (!dp->bcnt) { + dp->bcnt = scnprintf(dp->std->name, + sizeof(dp->std->name)-1,"%s",str); + dp->scnt = 0; + return; + } + + dp->bcnt += scnprintf(dp->std->name+dp->bcnt, + sizeof(dp->std->name)-(1+dp->bcnt), + "%s%s", + (dp->scnt ? "/" : "-"),str); + (dp->scnt)++; +} + +// Generate a descriptive name for a given standard +static void name_bucket(struct v4l2_standard *std) +{ + struct name_data nd; + nd.std = std; + nd.bcnt = 0; + if (std->id & (V4L2_STD_PAL_B| + V4L2_STD_PAL_B1| + V4L2_STD_PAL_G| + V4L2_STD_PAL_H| + V4L2_STD_PAL_I| + V4L2_STD_PAL_D| + V4L2_STD_PAL_D1| + V4L2_STD_PAL_K)) { + name_build(&nd,"PAL"); + if (std->id & V4L2_STD_PAL_B) name_build(&nd,"B"); + if (std->id & V4L2_STD_PAL_B1) name_build(&nd,"B1"); + if (std->id & V4L2_STD_PAL_D) name_build(&nd,"D"); + if (std->id & V4L2_STD_PAL_D1) name_build(&nd,"D1"); + if (std->id & V4L2_STD_PAL_G) name_build(&nd,"G"); + if (std->id & V4L2_STD_PAL_H) name_build(&nd,"H"); + if (std->id & V4L2_STD_PAL_I) name_build(&nd,"I"); + if (std->id & V4L2_STD_PAL_K) name_build(&nd,"K"); + if (std->id & V4L2_STD_PAL_M) name_build(&nd,"M"); + if (std->id & V4L2_STD_PAL_N) name_build(&nd,"N"); + if (std->id & V4L2_STD_PAL_Nc) name_build(&nd,"Nc"); + if (std->id & V4L2_STD_PAL_60) name_build(&nd,"60"); + std->name[nd.bcnt] = 0; + return; + } + if (std->id & (V4L2_STD_NTSC_M| + V4L2_STD_NTSC_M_JP| + V4L2_STD_NTSC_443)) { + name_build(&nd,"NTSC"); + if (std->id & V4L2_STD_NTSC_M) name_build(&nd,"M"); + if (std->id & V4L2_STD_NTSC_M_JP) name_build(&nd,"Mjp"); + if (std->id & V4L2_STD_NTSC_443) name_build(&nd,"443"); + std->name[nd.bcnt] = 0; + return; + } + if (std->id & (V4L2_STD_SECAM_B| + V4L2_STD_SECAM_D| + V4L2_STD_SECAM_G| + V4L2_STD_SECAM_H| + V4L2_STD_SECAM_K| + V4L2_STD_SECAM_K1| + V4L2_STD_SECAM_L| + V4L2_STD_SECAM_LC)) { + name_build(&nd,"SECAM"); + if (std->id & V4L2_STD_SECAM_B) name_build(&nd,"B"); + if (std->id & V4L2_STD_SECAM_D) name_build(&nd,"D"); + if (std->id & V4L2_STD_SECAM_G) name_build(&nd,"G"); + if (std->id & V4L2_STD_SECAM_H) name_build(&nd,"H"); + if (std->id & V4L2_STD_SECAM_K) name_build(&nd,"K"); + if (std->id & V4L2_STD_SECAM_K1) name_build(&nd,"K1"); + if (std->id & V4L2_STD_SECAM_L) name_build(&nd,"L"); + if (std->id & V4L2_STD_SECAM_LC) name_build(&nd,"LC"); + std->name[nd.bcnt] = 0; + return; + } + std->name[0] = 0; +} - if (ctl_id >= CTRL_COUNT) return 0; - if (!control_defs[ctl_id].is_valid) return 0; - LOCK_TAKE(hdw->big_lock); do { - if (control_defs[ctl_id].get_func) { - ret = control_defs[ctl_id].get_func(hdw,ctl_id); - break; +// Given a mask of viable video standards to choose from, generate an +// appropriate array of v4l2_standard data that corresponds to it and set +// up related state in the driver to match. +void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw,int arg) +{ + v4l2_std_id buckets[pvr_standards_cnt]; + unsigned int idx1,idx2,std_cnt; + v4l2_std_id mmsk,amsk; + + amsk = (v4l2_std_id)arg; + + // Figure out which standard groups we can work with + std_cnt = 0; + mmsk = 0; + for (idx1 = 0; idx1 < pvr_standards_cnt; idx1++) { + buckets[idx1] = pvr_standards[idx1].id & amsk; + if (!buckets[idx1]) continue; + mmsk |= buckets[idx1]; + amsk &= ~buckets[idx1]; + std_cnt++; + } + + if (amsk) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Failed to bucketize the following standards: 0x%llx", + amsk); + } + + if (hdw->std_defs) { + kfree(hdw->std_defs); + hdw->std_defs = 0; + } + if (hdw->video_std_names) { + kfree(hdw->video_std_names); + hdw->video_std_names = 0; + } + hdw->std_cnt = 0; + + if (!std_cnt) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Failed to identify any viable standard groups"); + hdw->video_std_avail = 0; + pvr2_hdw_internal_set_std_cur(hdw,0); + return; + } + + if (std_cnt) { + // Allocate new video standard array + hdw->std_defs = kmalloc(sizeof(struct v4l2_standard) * std_cnt, + GFP_KERNEL); + hdw->std_cnt = std_cnt; + memset(hdw->std_defs,0,sizeof(struct v4l2_standard) * std_cnt); + hdw->video_std_names = kmalloc(sizeof(char *) * std_cnt, + GFP_KERNEL); + memset(hdw->video_std_names,0,sizeof(char *) * std_cnt); + idx2 = 0; + + // Initialize video standard array + for (idx1 = 0; idx1 < pvr_standards_cnt; idx1++) { + if (!buckets[idx1]) continue; + memcpy(hdw->std_defs + idx2, + pvr_standards + idx1, + sizeof(struct v4l2_standard)); + hdw->std_defs[idx2].id = buckets[idx1]; + idx2++; } - ret = hdw->controls[ctl_id].value; - } while(0); LOCK_GIVE(hdw->big_lock); - return ret; + + // Generate a name for each known video standard + for (idx1 = 0; idx1 < std_cnt; idx1++) { + name_bucket(hdw->std_defs + idx1); + hdw->video_std_names[idx1] = + hdw->std_defs[idx1].name; + } + + // Set up the dynamic control for this standard + hdw->video_std_enum.value_defs_ptr = hdw->video_std_names; + hdw->video_std_enum.value_defs_count = std_cnt; + } + + hdw->video_std_avail = mmsk; + if (!(hdw->video_std_avail & hdw->video_std_cur)) { + // Reselect standard if there isn't one that matches... + pvr2_hdw_internal_set_stdenum_cur(hdw,0); + } } -/* Return true if control is writable */ -int pvr2_hdw_get_ctl_rw(struct pvr2_hdw *hdw,unsigned int ctl_id) +unsigned int pvr2_hdw_get_stdenum_count(struct pvr2_hdw *hdw) { - if (ctl_id >= CTRL_COUNT) return 0; - if (!control_defs[ctl_id].is_valid) return 0; - if (control_defs[ctl_id].get_func && !control_defs[ctl_id].set_func) { - return 0; + return hdw->std_cnt; +} + + +const struct v4l2_standard *pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw, + unsigned int idx) +{ + if (idx >= hdw->std_cnt) return 0; + return hdw->std_defs + idx; +} + + +int pvr2_hdw_internal_set_stdenum_cur(struct pvr2_hdw *hdw,int val) +{ + if (val < 0) return -EINVAL; + if (val >= hdw->std_cnt) return -EINVAL; + pvr2_hdw_internal_set_std_cur(hdw,hdw->std_defs[val].id); + return 0; +} + + +void pvr2_hdw_internal_set_std_cur(struct pvr2_hdw *hdw,int val) +{ + unsigned int idx; + v4l2_std_id msk,id; + + id = (v4l2_std_id)val; + // Only select from available standards + id &= hdw->video_std_avail; + + // Only select a single bit + for (idx = 0, msk = 1; msk; idx++, msk <<= 1) { + if (!(id & msk)) continue; + id = msk; + break; } - return !0; + + // Get out if nothing found + if (!msk) return; + + // Fix up standard group now + hdw->video_std_cur = id; + hdw->controls[PVR2_CID_STDCUR].value = id; + hdw->controls[PVR2_CID_STDCUR].dirty = !0; + for (idx = 0; idx < hdw->std_cnt; idx++) { + if (hdw->std_defs[idx].id & id) { + hdw->std_id = idx; + return; + } + } + + // Should never really get here, but just in case... + hdw->std_id = 0; } -/* Retrieve legal minimum value for a given control */ -int pvr2_hdw_get_ctl_min_value(struct pvr2_hdw *hdw,unsigned int ctl_id) +static int pvr2_ctl_set_stdcur(struct pvr2_ctrl *cptr,int val) { - if (ctl_id >= CTRL_COUNT) return 0; - if (!control_defs[ctl_id].is_valid) return 0; - return control_defs[ctl_id].min_value; + pvr2_hdw_internal_set_std_cur(cptr->hdw,val); + return 0; +} + + +static int pvr2_ctl_get_stdcur(struct pvr2_ctrl *cptr) +{ + return (int)(cptr->hdw->video_std_cur); +} + + +static int pvr2_ctl_set_stdenumcur(struct pvr2_ctrl *cptr,int val) +{ + if (val < 0) return -EINVAL; + if (val >= cptr->hdw->std_cnt) return -EINVAL; + cptr->hdw->std_id = val; + pvr2_hdw_internal_set_std_cur(cptr->hdw, + cptr->hdw->std_id); + return 0; } -/* Retrieve legal maximum value for a given control */ -int pvr2_hdw_get_ctl_max_value(struct pvr2_hdw *hdw,unsigned int ctl_id) +static int pvr2_ctl_get_stdenumcur(struct pvr2_ctrl *cptr) { - if (ctl_id >= CTRL_COUNT) return 0; - if (!control_defs[ctl_id].is_valid) return 0; - return control_defs[ctl_id].max_value; + return cptr->hdw->std_id; } -/* Retrieve default value for a given control */ -int pvr2_hdw_get_ctl_default_value(struct pvr2_hdw *hdw,unsigned int ctl_id) +static int pvr2_ctl_get_stdavail(struct pvr2_ctrl *cptr) { - if (ctl_id >= CTRL_COUNT) return 0; - if (!control_defs[ctl_id].is_valid) return 0; - return control_defs[ctl_id].default_value; + return (int)(cptr->hdw->video_std_avail); } -/* Set current value for given control - normally this is just stored and - the hardware isn't updated until the commit function is called. */ -int pvr2_hdw_set_ctl_value_internal(struct pvr2_hdw *hdw, - unsigned int ctl_id,int value) +/* Get the number of defined controls */ +unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw) { + return CTRL_COUNT; +} + + +/* Retrieve a control handle given its index (0..count-1) */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *hdw, + unsigned int idx) +{ + if (idx < 0) return 0; + if (idx >= CTRL_COUNT) return 0; + return hdw->controls + idx; +} + + +/* Given an ID, retrieve the control structure associated with it. */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl(struct pvr2_hdw *hdw,unsigned int ctl_id) +{ + struct pvr2_ctrl *cptr; + unsigned int idx; + int i; + + /* This could be made a lot more efficient, but for now... */ + for (idx = 0; idx < CTRL_COUNT; idx++) { + cptr = hdw->controls + idx; + i = cptr->ctl_def->id; + if (i && (i == ctl_id)) return cptr; + } + + return 0; +} + + +/* Set the current value of a given control. This assumes we are already + inside our critical region. */ +int pvr2_ctrl_internal_set_value(struct pvr2_ctrl *cptr,int value) +{ + const struct pvr2_ctl_def *dptr; int ret; - if (ctl_id >= CTRL_COUNT) return -EINVAL; - if (!control_defs[ctl_id].is_valid) return -EINVAL; - if (value < control_defs[ctl_id].min_value) return -EINVAL; - if (value > control_defs[ctl_id].max_value) return -EINVAL; - if (control_defs[ctl_id].set_func) { - ret = control_defs[ctl_id].set_func(hdw,ctl_id,value); - pvr2_i2c_core_check_stale(hdw); - pvr2_i2c_core_sync(hdw); + if (!cptr) return -EINVAL; + if (!cptr->is_valid) return -EINVAL; + dptr = cptr->ctl_def; + if (!dptr->is_valid) return -EINVAL; + if (value < dptr->min_value) return -EINVAL; + if (value > dptr->max_value) return -EINVAL; + if (dptr->set_func) { + ret = dptr->set_func(cptr,value); + pvr2_i2c_core_check_stale(cptr->hdw); + pvr2_i2c_core_sync(cptr->hdw); return ret; - } else if (control_defs[ctl_id].get_func) { + } else if (dptr->get_func) { /* If there's no "set" function yet there is still a "get" function, then treat this as a read-only value. */ return -EINVAL; } - if ((hdw->controls[ctl_id].value != value) || (ctlchg != 0)) { - hdw->controls[ctl_id].value = value; - hdw->controls[ctl_id].dirty = !0; + if ((cptr->value != value) || (ctlchg != 0)) { + cptr->value = value; + cptr->dirty = !0; } return 0; } -/* Set current value for given control - this is just stored; the hardware - isn't updated until the commit function is called. */ -int pvr2_hdw_set_ctl_value(struct pvr2_hdw *hdw,unsigned int ctl_id,int value) +/* Get the current value of a given control. This assumes that we are + already inside our critical region. */ +int pvr2_ctrl_internal_get_value(struct pvr2_ctrl *cptr) +{ + const struct pvr2_ctl_def *dptr; + if (!cptr) return 0; + if (!cptr->is_valid) return 0; + dptr = cptr->ctl_def; + if (!dptr->is_valid) return 0; + if (dptr->get_func) { + return dptr->get_func(cptr); + } + + return cptr->value; +} + + +/* Set the current value of the given control. */ +int pvr2_ctrl_set_value(struct pvr2_ctrl *cptr,int val) { int ret; - LOCK_TAKE(hdw->big_lock); do { - ret = pvr2_hdw_set_ctl_value_internal(hdw,ctl_id,value); - } while (0); LOCK_GIVE(hdw->big_lock); + if (!cptr) return -EINVAL; + LOCK_TAKE(cptr->hdw->big_lock); do { + ret = pvr2_ctrl_internal_set_value(cptr,val); + } while(0); LOCK_GIVE(cptr->hdw->big_lock); return ret; } -/* Retrieve string name for a given control value (returns a null pointer - for any invalid combinations). */ -const char *pvr2_hdw_get_ctl_value_name(struct pvr2_hdw *hdw, - unsigned int ctl_id, - int value) +/* Get the current value of the given control. */ +int pvr2_ctrl_get_value(struct pvr2_ctrl *cptr) { - struct pvr2_ctl_def *cdef; - if (ctl_id >= CTRL_COUNT) return 0; - if (!control_defs[ctl_id].is_valid) return 0; - cdef = control_defs + ctl_id; - if (! cdef->value_defs_ptr) return 0; - if (value >= cdef->value_defs_count) return 0; - return cdef->value_defs_ptr[value]; + int ret; + if (!cptr) return -EINVAL; + LOCK_TAKE(cptr->hdw->big_lock); do { + ret = pvr2_ctrl_internal_get_value(cptr); + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; } -/* Retrieve string name for given control */ -const char *pvr2_hdw_get_ctl_name(struct pvr2_hdw *hdw,unsigned int ctl_id) +/* Return the type of the given control (int, enum, or bit mask). */ +int pvr2_ctrl_get_type(struct pvr2_ctrl *cptr) { - if (ctl_id >= CTRL_COUNT) return 0; - if (!control_defs[ctl_id].is_valid) return 0; - return control_defs[ctl_id].name; + const struct pvr2_ctl_def *dptr; + if (!cptr) return PVR2_CTRL_TYPE_INVALID; + dptr = cptr->ctl_def; + if (dptr->mask_value) { + return PVR2_CTRL_TYPE_BITMASK; + } + if (dptr->value_defs_ptr) { + return PVR2_CTRL_TYPE_ENUM; + } + return PVR2_CTRL_TYPE_INT; } -/* Return true if control ID is a valid id */ -int pvr2_hdw_get_ctl_valid(struct pvr2_hdw *hdw,unsigned int ctl_id) +/* Return the minimum legal value for a given control. This command is + only relevant for int or enum types. */ +int pvr2_ctrl_get_min_value(struct pvr2_ctrl *cptr) { - if (ctl_id >= CTRL_COUNT) return 0; - if (!control_defs[ctl_id].is_valid) return 0; + const struct pvr2_ctl_def *dptr; + if (!cptr) return 0; + dptr = cptr->ctl_def; + return dptr->min_value; +} + + +/* Return the maximum legal value for a given control. This command is + only relevant for int or enum types. */ +int pvr2_ctrl_get_max_value(struct pvr2_ctrl *cptr) +{ + const struct pvr2_ctl_def *dptr; + if (!cptr) return 0; + dptr = cptr->ctl_def; + return dptr->max_value; +} + + +/* Return the default value for a given control. */ +int pvr2_ctrl_get_default_value(struct pvr2_ctrl *cptr) +{ + const struct pvr2_ctl_def *dptr; + if (!cptr) return 0; + dptr = cptr->ctl_def; + return dptr->default_value; +} + + +/* Return a mask of which bits are used within the bit mask of a given + control. This command is only relevant for bit mask types. */ +int pvr2_ctrl_get_mask_value(struct pvr2_ctrl *cptr) +{ + const struct pvr2_ctl_def *dptr; + if (!cptr) return 0; + dptr = cptr->ctl_def; + return dptr->mask_value; +} + + +/* Return true if this is a valid control. */ +int pvr2_ctrl_is_valid(struct pvr2_ctrl *cptr) +{ + if (!cptr) return 0; + return cptr->is_valid; +} + + +/* Return true if the control can be set (otherwise it may only be read, + assuming that it is valid). */ +int pvr2_ctrl_is_writeable(struct pvr2_ctrl *cptr) +{ + const struct pvr2_ctl_def *dptr; + if (!cptr) return 0; + dptr = cptr->ctl_def; + if (!dptr->is_valid) return 0; + if (dptr->set_func) return !0; + if (dptr->get_func) return 0; return !0; } +/* Return the control's name, or null if there isn't a name or the control + isn't otherwise valid. */ +const char *pvr2_ctrl_get_name(struct pvr2_ctrl *cptr) +{ + const struct pvr2_ctl_def *dptr; + if (!cptr) return 0; + dptr = cptr->ctl_def; + return dptr->name; +} + + +/* Return the control's description, or null if there isn't a name or the + control isn't otherwise valid. */ +const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *cptr) +{ + const struct pvr2_ctl_def *dptr; + if (!cptr) return 0; + dptr = cptr->ctl_def; + return dptr->desc; +} + + +/* Return the name for an enumeration value or bit mask position for the + given control. If the control is not an enumeration or bit mask type, + then return null. */ +const char *pvr2_ctrl_get_value_name(struct pvr2_ctrl *cptr,int val) +{ + int msk,idx; + const struct pvr2_ctl_def *dptr; + if (!cptr) return 0; + dptr = cptr->ctl_def; + if (dptr->mask_value) { + for (idx = 0, msk = 1; + (idx < dptr->value_defs_count) && msk; + idx++, msk <<= 1) { + if (val & msk) { + return dptr->value_defs_ptr[idx]; + } + } + } else { + val -= dptr->min_value; + if (val < 0) return 0; + if (val >= dptr->value_defs_count) return 0; + return dptr->value_defs_ptr[val]; + } + return 0; +} + + /* Commit all control changes made up to this point. Subsystems can be indirectly affected by these changes. For a given set of things being committed, we'll clear the affected subsystem bits and then once we're @@ -1625,6 +2184,8 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) unsigned long saved_subsys_mask = hdw->subsys_enabled_mask; unsigned long stale_subsys_mask = 0; unsigned int idx; + const struct pvr2_ctl_def *dptr; + struct pvr2_ctrl *cptr; int value; const char *ctl_name; const char *ctl_value; @@ -1661,16 +2222,17 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) } for (idx = 0; idx < CTRL_COUNT; idx++) { - if (!hdw->controls[idx].dirty) continue; + cptr = hdw->controls + idx; + if (!cptr->dirty) continue; if (!commit_flag) { commit_flag = !0; } - value = hdw->controls[idx].value; - ctl_name = control_defs[idx].name; - if (control_defs[idx].value_defs_ptr) { - if (value < control_defs[idx].value_defs_count) { - ctl_value = - control_defs[idx].value_defs_ptr[value]; + value = cptr->value; + dptr = cptr->ctl_def; + ctl_name = dptr->name; + if (dptr->value_defs_ptr) { + if (value < dptr->value_defs_count) { + ctl_value = dptr->value_defs_ptr[value]; } else { ctl_value = ""; } @@ -1690,16 +2252,13 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) /* When video standard changes, reset the hres and vres values - but if the user has pending changes there, then let the changes take priority. */ - if (hdw->controls[PVR2_CID_VIDEOSTANDARD].dirty) { + if (hdw->controls[PVR2_CID_STDCUR].dirty) { /* Rewrite the vertical resolution to be appropriate to the video standard that has been selected. */ - int nvres = hdw->controls[PVR2_CID_VRES].value; - switch (hdw->controls[PVR2_CID_VIDEOSTANDARD].value) { - case PVR2_CVAL_VIDEOSTANDARD_NTSC_M: - case PVR2_CVAL_VIDEOSTANDARD_PAL_M: + int nvres; + if (hdw->video_std_cur & V4L2_STD_525_60) { nvres = 480; - break; - default: + } else { nvres = 576; } if (nvres != hdw->controls[PVR2_CID_VRES].value) { @@ -1712,7 +2271,7 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) } } - if (hdw->controls[PVR2_CID_VIDEOSTANDARD].dirty || + if (hdw->controls[PVR2_CID_STDCUR].dirty || hdw->controls[PVR2_CID_VRES].dirty || hdw->controls[PVR2_CID_HRES].dirty || hdw->controls[PVR2_CID_INTERLACE].dirty || @@ -1737,7 +2296,8 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) pvr2_i2c_core_check_stale(hdw); for (idx = 0; idx < CTRL_COUNT; idx++) { - hdw->controls[idx].dirty = 0; + cptr = hdw->controls + idx; + cptr->dirty = 0; } /* Now execute i2c core update */ @@ -1794,14 +2354,6 @@ void pvr2_hdw_poll_trigger(struct pvr2_hdw *hdw) } -/* Find out how many controls there are. Legal ids are numbered from 1 - through this value. */ -unsigned int pvr2_hdw_get_ctl_count(struct pvr2_hdw *hdw) -{ - return CTRL_COUNT; -} - - /* Return name for this driver instance */ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw) { @@ -1837,36 +2389,36 @@ unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *hdw) } -static int pvr2_ctl_get_subsys_mask(struct pvr2_hdw *hdw,int ctl_id) +static int pvr2_ctl_get_subsys_mask(struct pvr2_ctrl *cptr) { - return hdw->subsys_enabled_mask; + return cptr->hdw->subsys_enabled_mask; } -static int pvr2_ctl_set_subsys_mask(struct pvr2_hdw *hdw,int ctl_id,int val) +static int pvr2_ctl_set_subsys_mask(struct pvr2_ctrl *cptr,int val) { - pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,val); + pvr2_hdw_subsys_bit_chg_no_lock(cptr->hdw,~0,val); return 0; } -static int pvr2_ctl_get_subsys_stream_mask(struct pvr2_hdw *hdw,int ctl_id) +static int pvr2_ctl_get_subsys_stream_mask(struct pvr2_ctrl *cptr) { - return hdw->subsys_stream_mask; + return cptr->hdw->subsys_stream_mask; } -static int pvr2_ctl_set_subsys_stream_mask(struct pvr2_hdw *hdw,int ctl_id, +static int pvr2_ctl_set_subsys_stream_mask(struct pvr2_ctrl *cptr, int val) { - pvr2_hdw_subsys_stream_bit_chg_no_lock(hdw,~0,val); + pvr2_hdw_subsys_stream_bit_chg_no_lock(cptr->hdw,~0,val); return 0; } -static int pvr2_ctl_get_hsm(struct pvr2_hdw *hdw,int ctl_id) +static int pvr2_ctl_get_hsm(struct pvr2_ctrl *cptr) { - int result = pvr2_hdw_is_hsm(hdw); + int result = pvr2_hdw_is_hsm(cptr->hdw); if (result < 0) return PVR2_CVAL_HSM_FAIL; if (result) return PVR2_CVAL_HSM_HIGH; return PVR2_CVAL_HSM_FULL; @@ -1888,10 +2440,10 @@ int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw) } -static int pvr2_ctl_get_signal(struct pvr2_hdw *hdw,int ctl_id) +static int pvr2_ctl_get_signal(struct pvr2_ctrl *cptr) { - return ((pvr2_hdw_get_signal_status_internal(hdw) & PVR2_SIGNAL_OK) ? - 1 : 0); + return ((pvr2_hdw_get_signal_status_internal(cptr->hdw) & + PVR2_SIGNAL_OK) ? 1 : 0); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h index d7749138c..4101f4097 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -21,86 +21,37 @@ #ifndef __PVRUSB2_HDW_H #define __PVRUSB2_HDW_H +#include "compat.h" #include +#include #include "pvrusb2-io.h" -/* Definition of state variables that we can inspect & change. Numbers are - assigned from zero counting up with no gaps. */ -#define PVR2_CID_BRIGHTNESS 0 -#define PVR2_CID_CONTRAST 1 -#define PVR2_CID_SATURATION 2 -#define PVR2_CID_HUE 3 -#define PVR2_CID_VOLUME 4 -#define PVR2_CID_BALANCE 5 -#define PVR2_CID_BASS 6 -#define PVR2_CID_TREBLE 7 -#define PVR2_CID_MUTE 8 -#define PVR2_CID_SRATE 9 -#define PVR2_CID_AUDIOBITRATE 10 -#define PVR2_CID_AUDIOCRC 11 -#define PVR2_CID_AUDIOEMPHASIS 12 -#define PVR2_CID_VBR 13 -#define PVR2_CID_AVERAGEVIDEOBITRATE 14 -#define PVR2_CID_PEAKVIDEOBITRATE 15 -#define PVR2_CID_VIDEOSTANDARD 16 -#define PVR2_CID_INPUT 17 -#define PVR2_CID_AUDIOMODE 18 -#define PVR2_CID_FREQUENCY 19 // Units of Hz -#define PVR2_CID_HRES 20 -#define PVR2_CID_VRES 21 -#define PVR2_CID_INTERLACE 22 -#define PVR2_CID_AUDIOLAYER 23 -#define PVR2_CID_CHANNEL 24 -#define PVR2_CID_CHANPROG_ID 25 -#define PVR2_CID_CHANPROG_FREQ 26 -#define PVR2_CID_SIGNAL_PRESENT 27 -#define PVR2_CID_STREAMING_ENABLED 28 -#define PVR2_CID_HSM 29 -#define PVR2_CID_SUBSYS_MASK 30 -#define PVR2_CID_SUBSYS_STREAM_MASK 31 - -/* Legal values for the SRATE state variable */ -#define PVR2_CVAL_SRATE_48 0 -#define PVR2_CVAL_SRATE_44_1 1 -#define PVR2_CVAL_SRATE_MIN PVR2_CVAL_SRATE_48 -#define PVR2_CVAL_SRATE_MAX PVR2_CVAL_SRATE_44_1 - -/* Legal values for the AUDIOBITRATE state variable */ -#define PVR2_CVAL_AUDIOBITRATE_384 0 -#define PVR2_CVAL_AUDIOBITRATE_320 1 -#define PVR2_CVAL_AUDIOBITRATE_256 2 -#define PVR2_CVAL_AUDIOBITRATE_224 3 -#define PVR2_CVAL_AUDIOBITRATE_192 4 -#define PVR2_CVAL_AUDIOBITRATE_160 5 -#define PVR2_CVAL_AUDIOBITRATE_128 6 -#define PVR2_CVAL_AUDIOBITRATE_112 7 -#define PVR2_CVAL_AUDIOBITRATE_96 8 -#define PVR2_CVAL_AUDIOBITRATE_80 9 -#define PVR2_CVAL_AUDIOBITRATE_64 10 -#define PVR2_CVAL_AUDIOBITRATE_56 11 -#define PVR2_CVAL_AUDIOBITRATE_48 12 -#define PVR2_CVAL_AUDIOBITRATE_32 13 -#define PVR2_CVAL_AUDIOBITRATE_VBR 14 -#define PVR2_CVAL_AUDIOBITRATE_MIN PVR2_CVAL_AUDIOBITRATE_384 -#define PVR2_CVAL_AUDIOBITRATE_MAX PVR2_CVAL_AUDIOBITRATE_VBR - -/* Legal values for the AUDIOEMPHASIS state variable */ -#define PVR2_CVAL_AUDIOEMPHASIS_NONE 0 -#define PVR2_CVAL_AUDIOEMPHASIS_50_15 1 -#define PVR2_CVAL_AUDIOEMPHASIS_CCITT 2 -#define PVR2_CVAL_AUDIOEMPHASIS_MIN PVR2_CVAL_AUDIOEMPHASIS_NONE -#define PVR2_CVAL_AUDIOEMPHASIS_MAX PVR2_CVAL_AUDIOEMPHASIS_CCITT - -/* Legal values for the VIDEOSTANDARD state variable */ -#define PVR2_CVAL_VIDEOSTANDARD_NTSC_M 0 -#define PVR2_CVAL_VIDEOSTANDARD_PAL_BG 1 -#define PVR2_CVAL_VIDEOSTANDARD_PAL_I 2 -#define PVR2_CVAL_VIDEOSTANDARD_PAL_DK 3 -#define PVR2_CVAL_VIDEOSTANDARD_PAL_M 4 -#define PVR2_CVAL_VIDEOSTANDARD_SECAM_L 5 - -#define PVR2_CVAL_VIDEOSTANDARD_MIN PVR2_CVAL_VIDEOSTANDARD_NTSC_M -#define PVR2_CVAL_VIDEOSTANDARD_MAX PVR2_CVAL_VIDEOSTANDARD_SECAM_L +#define PVR2_CTRL_TYPE_INVALID 0 +#define PVR2_CTRL_TYPE_ENUM 1 +#define PVR2_CTRL_TYPE_INT 2 +#define PVR2_CTRL_TYPE_BITMASK 3 + +/* Private V4L2-compatible controls available in this driver */ +#define V4L2_CID_PVR_SRATE (V4L2_CID_PRIVATE_BASE) +#define V4L2_CID_PVR_AUDIOBITRATE (V4L2_CID_PRIVATE_BASE+1) +#define V4L2_CID_PVR_AUDIOCRC (V4L2_CID_PRIVATE_BASE+2) +#define V4L2_CID_PVR_AUDIOEMPHASIS (V4L2_CID_PRIVATE_BASE+3) +#define V4L2_CID_PVR_VBR (V4L2_CID_PRIVATE_BASE+4) +#define V4L2_CID_PVR_VIDEOBITRATE (V4L2_CID_PRIVATE_BASE+5) +#define V4L2_CID_PVR_VIDEOPEAK (V4L2_CID_PRIVATE_BASE+6) +#define V4L2_CID_PVR_VIDEOSTANDARD (V4L2_CID_PRIVATE_BASE+7) + +/* Deliberate gap for CIDs we don't want apps to discover */ +#define V4L2_CID_PVR_GAP (V4L2_CID_PRIVATE_BASE+8) + +/* Additional explicit controls needed by V4L2 ioctl implementation */ +#define V4L2_CID_PVR_STDCUR (V4L2_CID_PVR_GAP+1) +#define V4L2_CID_PVR_INPUT (V4L2_CID_PVR_GAP+2) +#define V4L2_CID_PVR_AUDIOMODE (V4L2_CID_PVR_GAP+3) +#define V4L2_CID_PVR_FREQUENCY (V4L2_CID_PVR_GAP+4) +#define V4L2_CID_PVR_HRES (V4L2_CID_PVR_GAP+5) +#define V4L2_CID_PVR_VRES (V4L2_CID_PVR_GAP+6) +#define V4L2_CID_PVR_INTERLACE (V4L2_CID_PVR_GAP+7) /* Legal values for the INPUT state variable */ #define PVR2_CVAL_INPUT_TV 0 @@ -110,27 +61,11 @@ #define PVR2_CVAL_INPUT_MIN PVR2_CVAL_INPUT_TV #define PVR2_CVAL_INPUT_MAX PVR2_CVAL_INPUT_RADIO -/* Legal values for the AUDIOMODE state variable */ -#define PVR2_CVAL_AUDIOMODE_MONO 0 -#define PVR2_CVAL_AUDIOMODE_STEREO 1 -#define PVR2_CVAL_AUDIOMODE_SAP 2 -#define PVR2_CVAL_AUDIOMODE_LANG1 3 -#define PVR2_CVAL_AUDIOMODE_LANG2 4 -#define PVR2_CVAL_AUDIOMODE_LANG1_LANG2 5 -#define PVR2_CVAL_AUDIOMODE_MIN PVR2_CVAL_AUDIOMODE_MONO -#define PVR2_CVAL_AUDIOMODE_MAX PVR2_CVAL_AUDIOMODE_LANG1_LANG2 - /* Values that pvr2_hdw_get_signal_status() returns */ #define PVR2_SIGNAL_OK 0x0001 #define PVR2_SIGNAL_STEREO 0x0002 #define PVR2_SIGNAL_SAP 0x0004 -/* Legal values for PVR2_CID_HSM */ -#define PVR2_CVAL_HSM_FAIL 0 -#define PVR2_CVAL_HSM_FULL 1 -#define PVR2_CVAL_HSM_HIGH 2 -#define PVR2_CVAL_HSM_MIN PVR2_CVAL_HSM_FAIL -#define PVR2_CVAL_HSM_MAX PVR2_CVAL_HSM_HIGH /* Subsystem definitions - these are various pieces that can be independently stopped / started. Usually you don't want to mess with @@ -164,6 +99,8 @@ const char *pvr2_config_get_name(enum pvr2_config); struct pvr2_hdw; +struct pvr2_ctrl; + /* Create and return a structure for interacting with the underlying hardware */ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, @@ -212,43 +149,69 @@ unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *); /* Called when hardware has been unplugged */ void pvr2_hdw_disconnect(struct pvr2_hdw *); -/* Retrieve current value for a given control */ -int pvr2_hdw_get_ctl_value(struct pvr2_hdw *,unsigned int ctl_id); +/* Get the number of defined controls */ +unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *); + +/* Retrieve a control handle given its index (0..count-1) */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *,unsigned int); + +/* Retrieve a control handle given its well known ID */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl(struct pvr2_hdw *,unsigned int ctl_id); + +/* Set the current value of the given control. */ +int pvr2_ctrl_set_value(struct pvr2_ctrl *,int val); + +/* Get the current value of the given control. */ +int pvr2_ctrl_get_value(struct pvr2_ctrl *); -/* Return true if control is writable */ -int pvr2_hdw_get_ctl_rw(struct pvr2_hdw *,unsigned int ctl_id); +/* Return the type of the given control (int, enum, or bit mask). */ +int pvr2_ctrl_get_type(struct pvr2_ctrl *); -/* Retrieve legal minimum value for a given control */ -int pvr2_hdw_get_ctl_min_value(struct pvr2_hdw *,unsigned int ctl_id); +/* Return the minimum legal value for a given control. This command is + only relevant for int or enum types. */ +int pvr2_ctrl_get_min_value(struct pvr2_ctrl *); -/* Retrieve legal maximum value for a given control */ -int pvr2_hdw_get_ctl_max_value(struct pvr2_hdw *,unsigned int ctl_id); +/* Return the maximum legal value for a given control. This command is + only relevant for int or enum types. */ +int pvr2_ctrl_get_max_value(struct pvr2_ctrl *); -/* Retrieve legal maximum value for a given control */ -int pvr2_hdw_get_ctl_default_value(struct pvr2_hdw *,unsigned int ctl_id); +/* Return a mask of which bits are used within the bit mask of a given + control. This command is only relevant for bit mask types. */ +int pvr2_ctrl_get_mask_value(struct pvr2_ctrl *); -/* Set current value for given control - this is just stored; the hardware - isn't updated until the commit function is called. */ -int pvr2_hdw_set_ctl_value(struct pvr2_hdw *,unsigned int ctl_id,int value); +/* Return the default value for a given control. */ +int pvr2_ctrl_get_default_value(struct pvr2_ctrl *); -/* Retrieve string name for given control */ -const char *pvr2_hdw_get_ctl_name(struct pvr2_hdw *,unsigned int ctl_id); +/* Return true if this is a valid control. */ +int pvr2_ctrl_is_valid(struct pvr2_ctrl *); -/* Return true if control id is a valid id */ -int pvr2_hdw_get_ctl_valid(struct pvr2_hdw *,unsigned int ctl_id); +/* Return true if the control can be set (otherwise it may only be read, + assuming that it is valid). */ +int pvr2_ctrl_is_writeable(struct pvr2_ctrl *); -/* Retrieve string name for a given control value (returns a null pointer - for any invalid combinations). */ -const char *pvr2_hdw_get_ctl_value_name(struct pvr2_hdw *, - unsigned int ctl_id,int value); +/* Return the control's name, or null if there isn't a name or the control + isn't otherwise valid. */ +const char *pvr2_ctrl_get_name(struct pvr2_ctrl *); + +/* Return the control's description, or null if there isn't a name or the + control isn't otherwise valid. */ +const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *); + +/* Return the name for an enumeration value or bit mask position for the + given control. If the control is not an enumeration or bit mask type, + then return null. */ +const char *pvr2_ctrl_get_value_name(struct pvr2_ctrl *,int val); + +/* Return the number of support standard groups */ +unsigned int pvr2_hdw_get_stdenum_count(struct pvr2_hdw *); + +/* Return a pointer to a v4l2 standard descriptor for a given group */ +const struct v4l2_standard *pvr2_hdw_get_stdenum_value(struct pvr2_hdw *, + unsigned int idx); /* Commit all control changes made up to this point */ int pvr2_hdw_commit_ctl(struct pvr2_hdw *); -/* Find out how many controls there are. Legal ids are numbered from 0 - through this value - 1. */ -unsigned int pvr2_hdw_get_ctl_count(struct pvr2_hdw *); - /* Return name for this driver instance */ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c index 000c26911..b3d2ce926 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c @@ -24,43 +24,23 @@ #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" #include +#include static void set_standard(struct pvr2_hdw *hdw) { - int cvstd = hdw->controls[PVR2_CID_VIDEOSTANDARD].value; v4l2_std_id vs; + vs = hdw->controls[PVR2_CID_STDCUR].value; pvr2_trace(PVR2_TRACE_CHIPS, - "i2c v4l2 set_standard(%d)",cvstd); - - switch (cvstd) { - default: - case PVR2_CVAL_VIDEOSTANDARD_NTSC_M: - vs = V4L2_STD_NTSC_M; - break; - case PVR2_CVAL_VIDEOSTANDARD_SECAM_L: - vs = V4L2_STD_SECAM; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_BG: - vs = V4L2_STD_PAL_BG; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_I: - vs = V4L2_STD_PAL_I; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_DK: - vs = V4L2_STD_PAL_DK; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_M: - vs = V4L2_STD_PAL_M; - break; - } + "i2c v4l2 set_standard(0x%llx)",(__u64)vs); + pvr2_i2c_core_cmd(hdw,VIDIOC_S_STD,&vs); } static int check_standard(struct pvr2_hdw *hdw) { - return hdw->controls[PVR2_CID_VIDEOSTANDARD].dirty != 0; + return hdw->controls[PVR2_CID_STDCUR].dirty != 0; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 6b81def26..0ded41fcb 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -30,41 +30,6 @@ #define pvr2_sysfs_trace(...) pvr2_trace(PVR2_TRACE_SYSFS,__VA_ARGS__) -static char *item_names[] = { - [PVR2_CID_BRIGHTNESS] = "ctl_brightness", - [PVR2_CID_CONTRAST] = "ctl_contrast", - [PVR2_CID_SATURATION] = "ctl_saturation", - [PVR2_CID_HUE] = "ctl_hue", - [PVR2_CID_VOLUME] = "ctl_volume", - [PVR2_CID_BALANCE] = "ctl_balance", - [PVR2_CID_BASS] = "ctl_bass", - [PVR2_CID_TREBLE] = "ctl_treble", - [PVR2_CID_MUTE] = "ctl_mute", - [PVR2_CID_SRATE] = "ctl_srate", - [PVR2_CID_AUDIOBITRATE] = "ctl_audio_bitrate", - [PVR2_CID_AUDIOCRC] = "ctl_audio_crc", - [PVR2_CID_AUDIOEMPHASIS] = "ctl_audio_emphasis", - [PVR2_CID_VBR] = "ctl_vbr", - [PVR2_CID_AVERAGEVIDEOBITRATE] = "ctl_video_average_bitrate", - [PVR2_CID_PEAKVIDEOBITRATE] = "ctl_video_peak_bitrate", - [PVR2_CID_VIDEOSTANDARD] = "ctl_video_standard", - [PVR2_CID_INPUT] = "ctl_input", - [PVR2_CID_AUDIOMODE] = "ctl_audio_mode", - [PVR2_CID_FREQUENCY] = "ctl_frequency", - [PVR2_CID_HRES] = "ctl_resolution_hor", - [PVR2_CID_VRES] = "ctl_resolution_ver", - [PVR2_CID_INTERLACE] = "ctl_interlace", - [PVR2_CID_AUDIOLAYER] = "ctl_audio_layer", - [PVR2_CID_CHANNEL] = "ctl_channel", - [PVR2_CID_CHANPROG_ID] = "ctl_freq_table_channel", - [PVR2_CID_CHANPROG_FREQ] = "ctl_freq_table_value", - [PVR2_CID_SIGNAL_PRESENT] = "ctl_signal_present", - [PVR2_CID_STREAMING_ENABLED] = "ctl_streaming_enabled", - [PVR2_CID_HSM] = "ctl_usb_speed", - [PVR2_CID_SUBSYS_MASK] = "ctl_debug_subsys_mask", - [PVR2_CID_SUBSYS_STREAM_MASK] = "ctl_debug_subsys_stream_mask", -}; - struct pvr2_sysfs { struct pvr2_channel channel; struct class_device *class_dev; @@ -88,11 +53,12 @@ struct pvr2_sysfs_ctl_item { struct class_device_attribute attr_max; struct class_device_attribute attr_enum; struct class_device_attribute attr_val; - int attr_id; + struct pvr2_ctrl *cptr; struct pvr2_sysfs *chptr; struct pvr2_sysfs_ctl_item *item_next; struct attribute *attr_gen[5]; struct attribute_group grp; + char name[25]; }; struct pvr2_sysfs_class { @@ -101,13 +67,16 @@ struct pvr2_sysfs_class { static ssize_t show_name(int id,struct class_device *class_dev,char *buf) { + struct pvr2_ctrl *cptr; struct pvr2_sysfs *sfp; const char *name; sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; - name = pvr2_hdw_get_ctl_name(sfp->channel.hdw,id); + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); + if (!pvr2_ctrl_is_valid(cptr)) return -EINVAL; + name = pvr2_ctrl_get_desc(cptr); pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s",sfp,id,name); if (!name) return -EINVAL; @@ -117,13 +86,15 @@ static ssize_t show_name(int id,struct class_device *class_dev,char *buf) static ssize_t show_min(int id,struct class_device *class_dev,char *buf) { + struct pvr2_ctrl *cptr; struct pvr2_sysfs *sfp; int val; sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; - val = pvr2_hdw_get_ctl_min_value(sfp->channel.hdw,id); - + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); + if (!pvr2_ctrl_is_valid(cptr)) return -EINVAL; + val = pvr2_ctrl_get_min_value(cptr); pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %d",sfp,id,val); return scnprintf(buf,PAGE_SIZE,"%d\n",val); @@ -131,12 +102,15 @@ static ssize_t show_min(int id,struct class_device *class_dev,char *buf) static ssize_t show_max(int id,struct class_device *class_dev,char *buf) { + struct pvr2_ctrl *cptr; struct pvr2_sysfs *sfp; int val; sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; - val = pvr2_hdw_get_ctl_max_value(sfp->channel.hdw,id); + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); + if (!pvr2_ctrl_is_valid(cptr)) return -EINVAL; + val = pvr2_ctrl_get_max_value(cptr); pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %d",sfp,id,val); @@ -145,12 +119,15 @@ static ssize_t show_max(int id,struct class_device *class_dev,char *buf) static ssize_t show_val_int(int id,struct class_device *class_dev,char *buf) { + struct pvr2_ctrl *cptr; struct pvr2_sysfs *sfp; int val; sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; - val = pvr2_hdw_get_ctl_value(sfp->channel.hdw,id); + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); + if (!pvr2_ctrl_is_valid(cptr)) return -EINVAL; + val = pvr2_ctrl_get_value(cptr); pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_int(cid=%d) is %d", sfp,id,val); @@ -160,15 +137,18 @@ static ssize_t show_val_int(int id,struct class_device *class_dev,char *buf) static ssize_t show_val_enum(int id,struct class_device *class_dev,char *buf) { + struct pvr2_ctrl *cptr; struct pvr2_sysfs *sfp; int val; const char *name; sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; - val = pvr2_hdw_get_ctl_value(sfp->channel.hdw,id); + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); + if (!pvr2_ctrl_is_valid(cptr)) return -EINVAL; - name = pvr2_hdw_get_ctl_value_name(sfp->channel.hdw,id,val); + val = pvr2_ctrl_get_value(cptr); + name = pvr2_ctrl_get_value_name(cptr,val); pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_enum(cid=%d) is %s (%d)", sfp,id,name,val); @@ -178,6 +158,7 @@ static ssize_t show_val_enum(int id,struct class_device *class_dev,char *buf) static ssize_t show_enum(int id,struct class_device *class_dev,char *buf) { + struct pvr2_ctrl *cptr; struct pvr2_sysfs *sfp; int minval,maxval,val; const char *name; @@ -185,10 +166,12 @@ static ssize_t show_enum(int id,struct class_device *class_dev,char *buf) sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; - minval = pvr2_hdw_get_ctl_min_value(sfp->channel.hdw,id); - maxval = pvr2_hdw_get_ctl_max_value(sfp->channel.hdw,id); + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); + if (!pvr2_ctrl_is_valid(cptr)) return -EINVAL; + minval = pvr2_ctrl_get_min_value(cptr); + maxval = pvr2_ctrl_get_max_value(cptr); for (val = minval; val <= maxval; val++) { - name = pvr2_hdw_get_ctl_value_name(sfp->channel.hdw,id,val); + name = pvr2_ctrl_get_value_name(cptr,val); cnt += scnprintf(buf+cnt,PAGE_SIZE-cnt,"%s\n",name); } pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)",sfp,id); @@ -198,6 +181,7 @@ static ssize_t show_enum(int id,struct class_device *class_dev,char *buf) static int store_val_any(int id,struct pvr2_sysfs *sfp, const char *buf,unsigned int count) { + struct pvr2_ctrl *cptr; int val,minval,maxval; int ch,ret; const char *nv; @@ -217,11 +201,14 @@ static int store_val_any(int id,struct pvr2_sysfs *sfp, count--; } + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); + if (!pvr2_ctrl_is_valid(cptr)) return -EINVAL; + /* Is this an enum? Look for a string value */ - minval = pvr2_hdw_get_ctl_min_value(sfp->channel.hdw,id); - maxval = pvr2_hdw_get_ctl_max_value(sfp->channel.hdw,id); + minval = pvr2_ctrl_get_min_value(cptr); + maxval = pvr2_ctrl_get_max_value(cptr); for (val = minval; val <= maxval; val++) { - nv = pvr2_hdw_get_ctl_value_name(sfp->channel.hdw,id,val); + nv = pvr2_ctrl_get_value_name(cptr,val); if ((!nv) && (val == minval)) break; /* Not an enum */ pvr2_sysfs_trace("pvr2_sysfs(%p) trying ctl_id %d val %d", sfp,id,val); @@ -246,7 +233,7 @@ static int store_val_any(int id,struct pvr2_sysfs *sfp, pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_any(cid=%d)" " is enum %s", sfp,id,nv); - ret = pvr2_hdw_set_ctl_value(sfp->channel.hdw,id,val); + ret = pvr2_ctrl_set_value(cptr,val); pvr2_hdw_commit_ctl(sfp->channel.hdw); return 0; } @@ -284,7 +271,7 @@ static int store_val_any(int id,struct pvr2_sysfs *sfp, " int is %d", sfp,id,val); - ret = pvr2_hdw_set_ctl_value(sfp->channel.hdw,id,val); + ret = pvr2_ctrl_set_value(cptr,val); pvr2_hdw_commit_ctl(sfp->channel.hdw); return ret; } @@ -395,6 +382,8 @@ CREATE_BATCH(28) CREATE_BATCH(29) CREATE_BATCH(30) CREATE_BATCH(31) +CREATE_BATCH(32) +CREATE_BATCH(33) struct pvr2_sysfs_func_set { ssize_t (*show_name)(struct class_device *,char *); @@ -454,6 +443,8 @@ static struct pvr2_sysfs_func_set funcs[] = { INIT_BATCH(29), INIT_BATCH(30), INIT_BATCH(31), + INIT_BATCH(32), + INIT_BATCH(33), }; @@ -461,19 +452,23 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) { struct pvr2_sysfs_ctl_item *cip; struct pvr2_sysfs_func_set *fp; + struct pvr2_ctrl *cptr; + unsigned int cnt; if ((ctl_id < 0) || (ctl_id >= (sizeof(funcs)/sizeof(funcs[0])))) { return; } fp = funcs + ctl_id; + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,ctl_id); + if (!cptr) return; cip = kmalloc(sizeof(*cip),GFP_KERNEL); if (!cip) return; memset(cip,0,sizeof(*cip)); pvr2_sysfs_trace("Creating pvr2_sysfs_ctl_item id=%p",cip); - cip->attr_id = ctl_id; + cip->cptr = cptr; cip->chptr = sfp; cip->item_next = 0; @@ -508,15 +503,13 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) cip->attr_enum.attr.mode = S_IRUGO; cip->attr_enum.show = fp->show_enum; - if (pvr2_hdw_get_ctl_rw(sfp->channel.hdw,ctl_id)) { + if (pvr2_ctrl_is_writeable(cptr)) { cip->attr_val.attr.mode |= S_IWUSR|S_IWGRP; } cip->attr_gen[0] = &cip->attr_name.attr; cip->attr_gen[1] = &cip->attr_val.attr; - if (pvr2_hdw_get_ctl_value_name( - sfp->channel.hdw,ctl_id, - pvr2_hdw_get_ctl_min_value(sfp->channel.hdw,ctl_id))) { + if (pvr2_ctrl_get_type(cptr) == PVR2_CTRL_TYPE_ENUM) { // Control is an enumeration cip->attr_gen[2] = &cip->attr_enum.attr; cip->attr_val.show = fp->show_val_enum; @@ -529,7 +522,10 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) cip->attr_gen[3] = &cip->attr_max.attr; } - cip->grp.name = item_names[ctl_id]; + cnt = scnprintf(cip->name,sizeof(cip->name)-1,"ctl_%s", + pvr2_ctrl_get_name(cptr)); + cip->name[cnt] = 0; + cip->grp.name = cip->name; cip->grp.attrs = cip->attr_gen; sysfs_create_group(&sfp->class_dev->kobj,&cip->grp); @@ -573,12 +569,10 @@ static void pvr2_sysfs_tear_down_debugifc(struct pvr2_sysfs *sfp) static void pvr2_sysfs_add_controls(struct pvr2_sysfs *sfp) { - unsigned int ctl_id; - - for (ctl_id = 0; - ctl_id < (sizeof(item_names)/sizeof(item_names[0])); ctl_id++) { - if (!item_names[ctl_id]) continue; - pvr2_sysfs_add_control(sfp,ctl_id); + unsigned int idx,cnt; + cnt = pvr2_hdw_get_ctrl_count(sfp->channel.hdw); + for (idx = 0; idx < cnt; idx++) { + pvr2_sysfs_add_control(sfp,idx); } } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 4cdd08708..fe7045e8f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -80,22 +80,6 @@ static int video_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; module_param_array(video_nr, int, NULL, 0444); MODULE_PARM_DESC(video_nr, "Offset for device's minor"); -#define V4L2_CID_PVR_SRATE (V4L2_CID_PRIVATE_BASE) -#define V4L2_CID_PVR_AUDIOBITRATE (V4L2_CID_PRIVATE_BASE+1) -#define V4L2_CID_PVR_AUDIOCRC (V4L2_CID_PRIVATE_BASE+2) -#define V4L2_CID_PVR_AUDIOEMPHASIS (V4L2_CID_PRIVATE_BASE+3) -#define V4L2_CID_PVR_VBR (V4L2_CID_PRIVATE_BASE+4) -#define V4L2_CID_PVR_VIDEOBITRATE (V4L2_CID_PRIVATE_BASE+5) -#define V4L2_CID_PVR_VIDEOPEAK (V4L2_CID_PRIVATE_BASE+6) -#define V4L2_CID_PVR_VIDEOSTANDARD (V4L2_CID_PRIVATE_BASE+7) -#define V4L2_CID_PVR_INPUT (V4L2_CID_PRIVATE_BASE+8) -#define V4L2_CID_PVR_AUDIOMODE (V4L2_CID_PRIVATE_BASE+9) -#define V4L2_CID_PVR_FREQUENCY (V4L2_CID_PRIVATE_BASE+10) -#define V4L2_CID_PVR_HRES (V4L2_CID_PRIVATE_BASE+11) -#define V4L2_CID_PVR_VRES (V4L2_CID_PRIVATE_BASE+12) - -#define V4L2_CID_PVR_MAX (V4L2_CID_PRIVATE_BASE+12) - struct v4l2_capability pvr_capability ={ .driver = "pvrusb2", .card = "Hauppauge WinTV pvr-usb2", @@ -141,69 +125,6 @@ static struct v4l2_tuner pvr_v4l2_tuners[]= { #endif }; -struct v4l2_standard pvr_standards[] = { - [PVR2_CVAL_VIDEOSTANDARD_PAL_BG] = { - .id = V4L2_STD_PAL_BG, - .frameperiod = - { - .numerator = 1, - .denominator= 25 - }, - .framelines = 625, - .reserved = {0,0,0,0} - }, - [PVR2_CVAL_VIDEOSTANDARD_PAL_I] = { - .id = V4L2_STD_PAL_I, - .frameperiod = - { - .numerator = 1, - .denominator= 25 - }, - .framelines = 625, - .reserved = {0,0,0,0} - }, - [PVR2_CVAL_VIDEOSTANDARD_PAL_DK] = { - .id = V4L2_STD_PAL_DK, - .frameperiod = - { - .numerator = 1, - .denominator= 25 - }, - .framelines = 625, - .reserved = {0,0,0,0} - }, - [PVR2_CVAL_VIDEOSTANDARD_SECAM_L] = { - .id = V4L2_STD_SECAM, - .frameperiod = - { - .numerator = 1, - .denominator= 25 - }, - .framelines = 625, - .reserved = {0,0,0,0} - }, - [PVR2_CVAL_VIDEOSTANDARD_NTSC_M] = { - .id = V4L2_STD_NTSC_M, - .frameperiod = - { - .numerator = 1001, - .denominator= 30000 - }, - .framelines = 525, - .reserved = {0,0,0,0} - }, - [PVR2_CVAL_VIDEOSTANDARD_PAL_M] = { - .id = V4L2_STD_PAL_M, - .frameperiod = - { - .numerator = 1001, - .denominator= 30000 - }, - .framelines = 525, - .reserved = {0,0,0,0} - } -}; - struct v4l2_fmtdesc pvr_fmtdesc [] = { { .index = 0, @@ -257,84 +178,6 @@ struct v4l2_format pvr_format [] = { } }; -static int cnv_cid_v4l2_pvr2(int id) -{ - switch (id) { - case V4L2_CID_BRIGHTNESS: - return PVR2_CID_BRIGHTNESS; - case V4L2_CID_SATURATION: - return PVR2_CID_SATURATION; - case V4L2_CID_CONTRAST: - return PVR2_CID_CONTRAST; - case V4L2_CID_HUE: - return PVR2_CID_HUE; - case V4L2_CID_AUDIO_VOLUME: - return PVR2_CID_VOLUME; - case V4L2_CID_AUDIO_BALANCE: - return PVR2_CID_BALANCE; - case V4L2_CID_AUDIO_BASS: - return PVR2_CID_BASS; - case V4L2_CID_AUDIO_TREBLE: - return PVR2_CID_TREBLE; - case V4L2_CID_AUDIO_MUTE: - return PVR2_CID_MUTE; - case V4L2_CID_PVR_SRATE: - return PVR2_CID_SRATE; - case V4L2_CID_PVR_AUDIOBITRATE: - return PVR2_CID_AUDIOBITRATE; - case V4L2_CID_PVR_AUDIOCRC: - return PVR2_CID_AUDIOCRC; - case V4L2_CID_PVR_AUDIOEMPHASIS: - return PVR2_CID_AUDIOEMPHASIS; - case V4L2_CID_PVR_VBR: - return PVR2_CID_VBR; - case V4L2_CID_PVR_VIDEOBITRATE: - return PVR2_CID_AVERAGEVIDEOBITRATE; - case V4L2_CID_PVR_VIDEOPEAK: - return PVR2_CID_PEAKVIDEOBITRATE; - case V4L2_CID_PVR_INPUT: - return PVR2_CID_INPUT; - case V4L2_CID_PVR_AUDIOMODE: - return PVR2_CID_AUDIOMODE; - case V4L2_CID_PVR_FREQUENCY: - return PVR2_CID_FREQUENCY; - case V4L2_CID_PVR_HRES: - return PVR2_CID_HRES; - case V4L2_CID_PVR_VRES: - return PVR2_CID_VRES; - } - return -1; -} - -#if 0 -static int cnv_cid_pvr2_v4l2(int id) -{ - switch (id) { - case PVR2_CID_BRIGHTNESS: - return V4L2_CID_BRIGHTNESS; - case PVR2_CID_SATURATION: - return V4L2_CID_SATURATION; - case PVR2_CID_CONTRAST: - return V4L2_CID_CONTRAST; - case PVR2_CID_HUE: - return V4L2_CID_HUE; - case PVR2_CID_VOLUME: - return V4L2_CID_AUDIO_VOLUME; - case PVR2_CID_BALANCE: - return V4L2_CID_AUDIO_BALANCE; - case PVR2_CID_BASS: - return V4L2_CID_AUDIO_BASS; - case PVR2_CID_TREBLE: - return V4L2_CID_AUDIO_TREBLE; - case PVR2_CID_MUTE: - return V4L2_CID_AUDIO_MUTE; - - return id + V4L2_CID_PRIVATE_BASE; - } - return -1; -} -#endif - /* * pvr_ioctl() * @@ -402,22 +245,15 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_ENUMSTD: { - struct v4l2_standard *vs = (struct v4l2_standard *)arg; + const struct v4l2_standard *src; int idx = vs->index; - if ((vs->index < PVR2_CVAL_VIDEOSTANDARD_MIN) || - (vs->index > PVR2_CVAL_VIDEOSTANDARD_MAX)) { - break; - } + src = pvr2_hdw_get_stdenum_value(hdw,idx); + if (!src) break; - memcpy(vs, &pvr_standards[idx], sizeof(struct v4l2_standard)); + memcpy(vs, src, sizeof(struct v4l2_standard)); vs->index = idx; - strlcpy(vs->name, - pvr2_hdw_get_ctl_value_name(hdw, - PVR2_CID_VIDEOSTANDARD, - idx), - sizeof(vs->name)); ret = 0; break; @@ -425,59 +261,37 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_G_STD: { + struct pvr2_ctrl *cptr; v4l2_std_id *vs = (v4l2_std_id *)arg; - - switch (pvr2_hdw_get_ctl_value(hdw,PVR2_CID_VIDEOSTANDARD)) { - default: - case PVR2_CVAL_VIDEOSTANDARD_NTSC_M: - *vs = V4L2_STD_NTSC_M; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_M: - *vs = V4L2_STD_PAL_M; - break; - case PVR2_CVAL_VIDEOSTANDARD_SECAM_L: - *vs = V4L2_STD_SECAM; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_BG: - *vs = V4L2_STD_PAL_BG; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_I: - *vs = V4L2_STD_PAL_I; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_DK: - *vs = V4L2_STD_PAL_DK; + cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_STDCUR); + if (!pvr2_ctrl_is_valid(cptr)) { + ret = -EINVAL; break; } + + *vs = pvr2_ctrl_get_value(cptr); ret = 0; break; } case VIDIOC_S_STD: { + struct pvr2_ctrl *cptr; v4l2_std_id *vs = (v4l2_std_id *)arg; - int val = PVR2_CVAL_VIDEOSTANDARD_NTSC_M; - - if (*vs & V4L2_STD_NTSC_M){ - val = PVR2_CVAL_VIDEOSTANDARD_NTSC_M; - } else if (*vs & V4L2_STD_PAL_BG){ - val = PVR2_CVAL_VIDEOSTANDARD_PAL_BG; - } else if (*vs & V4L2_STD_PAL_I){ - val = PVR2_CVAL_VIDEOSTANDARD_PAL_I; - } else if (*vs & V4L2_STD_PAL_DK){ - val = PVR2_CVAL_VIDEOSTANDARD_PAL_DK; - } else if (*vs & V4L2_STD_SECAM){ - val = PVR2_CVAL_VIDEOSTANDARD_SECAM_L; - } else if (*vs & V4L2_STD_PAL_M){ - val = PVR2_CVAL_VIDEOSTANDARD_PAL_M; + cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_STDCUR); + if (!pvr2_ctrl_is_valid(cptr)) { + ret = -EINVAL; + break; } - pvr2_hdw_set_ctl_value(hdw,PVR2_CID_VIDEOSTANDARD,val); - + pvr2_ctrl_set_value(cptr,*vs); + ret = 0; break; } case VIDIOC_ENUMINPUT: { + struct pvr2_ctrl *cptr; struct v4l2_input *vi = (struct v4l2_input *)arg; struct v4l2_input tmp; @@ -486,6 +300,8 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, break; } + cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_INPUT); + memset(&tmp,0,sizeof(tmp)); tmp.index = vi->index; switch (vi->index) { @@ -500,8 +316,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, } strlcpy(tmp.name, - pvr2_hdw_get_ctl_value_name(hdw,PVR2_CID_INPUT, - vi->index), + pvr2_ctrl_get_value_name(cptr,vi->index), sizeof(tmp.name)); /* Don't bother with audioset, since this driver currently @@ -522,17 +337,31 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_G_INPUT: { + struct pvr2_ctrl *cptr; struct v4l2_input *vi = (struct v4l2_input *)arg; - vi->index = pvr2_hdw_get_ctl_value(hdw,PVR2_CID_INPUT); + cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_INPUT); + if (!pvr2_ctrl_is_valid(cptr)) { + ret = -EINVAL; + break; + } + + vi->index = pvr2_ctrl_get_value(cptr); ret = 0; break; } case VIDIOC_S_INPUT: { + struct pvr2_ctrl *cptr; struct v4l2_input *vi = (struct v4l2_input *)arg; + cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_INPUT); + if (!pvr2_ctrl_is_valid(cptr)) { + ret = -EINVAL; + break; + } + ret = 0; - if (pvr2_hdw_set_ctl_value(hdw,PVR2_CID_INPUT,vi->index)) { + if (pvr2_ctrl_set_value(cptr,vi->index)) { ret = -EINVAL; } break; @@ -557,6 +386,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, } case VIDIOC_G_TUNER: { + struct pvr2_ctrl *cptr; struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; unsigned int status_mask; if (vt->index !=0) break; @@ -580,26 +410,8 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, vt->signal = 65535; } - switch (pvr2_hdw_get_ctl_value(hdw,PVR2_CID_AUDIOMODE)) { - case PVR2_CVAL_AUDIOMODE_MONO: - vt->audmode = V4L2_TUNER_MODE_MONO; - break; - case PVR2_CVAL_AUDIOMODE_STEREO: - vt->audmode = V4L2_TUNER_MODE_STEREO; - break; - case PVR2_CVAL_AUDIOMODE_LANG1: - vt->audmode = V4L2_TUNER_MODE_LANG1; - break; - case PVR2_CVAL_AUDIOMODE_LANG2: - vt->audmode = V4L2_TUNER_MODE_LANG2; - break; - case PVR2_CVAL_AUDIOMODE_LANG1_LANG2: - vt->audmode = V4L2_TUNER_MODE_LANG1_LANG2; - break; - case PVR2_CVAL_AUDIOMODE_SAP: - vt->audmode = V4L2_TUNER_MODE_SAP; - break; - } + cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_AUDIOMODE); + vt->audmode = pvr2_ctrl_get_value(cptr); ret = 0; break; @@ -607,40 +419,34 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_S_TUNER: { + struct pvr2_ctrl *cptr; struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; - int val = PVR2_CVAL_AUDIOMODE_STEREO; if (vt->index != 0) break; - switch (vt->audmode) { - case V4L2_TUNER_MODE_MONO: - val = PVR2_CVAL_AUDIOMODE_MONO; - break; - case V4L2_TUNER_MODE_STEREO: - val = PVR2_CVAL_AUDIOMODE_STEREO; - break; - case V4L2_TUNER_MODE_LANG1: - val = PVR2_CVAL_AUDIOMODE_LANG1; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - val = PVR2_CVAL_AUDIOMODE_LANG1_LANG2; - break; - case V4L2_TUNER_MODE_SAP: // Also LANG2 - val = PVR2_CVAL_AUDIOMODE_SAP; + cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_AUDIOMODE); + if (!pvr2_ctrl_is_valid(cptr)) { + ret = -EINVAL; break; } - pvr2_hdw_set_ctl_value(hdw,PVR2_CID_AUDIOMODE,val); + pvr2_ctrl_set_value(cptr,vt->audmode); ret = 0; } case VIDIOC_S_FREQUENCY: { + struct pvr2_ctrl *cptr; const struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; - pvr2_hdw_set_ctl_value(hdw,PVR2_CID_FREQUENCY, - vf->frequency * 62500); + cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_FREQUENCY); + if (!pvr2_ctrl_is_valid(cptr)) { + ret = -EINVAL; + break; + } + + pvr2_ctrl_set_value(cptr,vf->frequency * 62500); ret = 0; break; @@ -648,10 +454,17 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_G_FREQUENCY: { + struct pvr2_ctrl *cptr; struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; int val; - val = pvr2_hdw_get_ctl_value(hdw,PVR2_CID_FREQUENCY); + cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_FREQUENCY); + if (!pvr2_ctrl_is_valid(cptr)) { + ret = -EINVAL; + break; + } + + val = pvr2_ctrl_get_value(cptr); val /= 62500; vf->frequency = val; @@ -682,12 +495,18 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, memcpy(vf, &pvr_format[PVR_FORMAT_PIX], sizeof(struct v4l2_format)); vf->fmt.pix.width = - pvr2_hdw_get_ctl_value(hdw,PVR2_CID_HRES); - if (pvr2_hdw_get_ctl_value(hdw,PVR2_CID_INTERLACE)) { + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl(hdw, + V4L2_CID_PVR_HRES)); + if (pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl( + hdw,V4L2_CID_PVR_INTERLACE))) { vf->fmt.pix.width /= 2; } vf->fmt.pix.height = - pvr2_hdw_get_ctl_value(hdw,PVR2_CID_VRES); + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl(hdw, + V4L2_CID_PVR_VRES)); ret = 0; break; case V4L2_BUF_TYPE_VBI_CAPTURE: @@ -713,16 +532,12 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, int w = vf->fmt.pix.width; int vd_std, hf, hh; - vd_std = pvr2_hdw_get_ctl_value(hdw, - PVR2_CID_VIDEOSTANDARD); - switch (vd_std) { - case PVR2_CVAL_VIDEOSTANDARD_NTSC_M: - case PVR2_CVAL_VIDEOSTANDARD_PAL_M: + vd_std = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_STDCUR)); + if (vd_std & V4L2_STD_525_60) { hf=480; - break; - default: + } else { hf=576; - break; } hh = (int) (hf / 2); @@ -734,15 +549,18 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, vf->fmt.pix.height = (h > hh) ? hf : hh; if (cmd == VIDIOC_S_FMT){ - pvr2_hdw_set_ctl_value( - hdw,PVR2_CID_HRES, + pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl(hdw, + V4L2_CID_PVR_HRES), vf->fmt.pix.width); - pvr2_hdw_set_ctl_value( - hdw,PVR2_CID_VRES, + pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl(hdw, + V4L2_CID_PVR_VRES), vf->fmt.pix.height); - pvr2_hdw_set_ctl_value( - hdw,PVR2_CID_INTERLACE, - (vf->fmt.pix.height != hf)); + pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl( + hdw,V4L2_CID_PVR_INTERLACE), + vf->fmt.pix.height != hf); } } break; case V4L2_BUF_TYPE_VBI_CAPTURE: @@ -772,24 +590,31 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_QUERYCTRL: { + struct pvr2_ctrl *cptr; struct v4l2_queryctrl *vc = (struct v4l2_queryctrl *)arg; - int pvr2_id = cnv_cid_v4l2_pvr2(vc->id); - if (pvr2_id < 0) { + cptr = pvr2_hdw_get_ctrl(hdw,vc->id); + + if (!pvr2_ctrl_is_valid(cptr)) { ret = -EINVAL; break; } - if (pvr2_hdw_get_ctl_value_name(hdw,pvr2_id,0)) { + switch (pvr2_ctrl_get_type(cptr)) { + case PVR2_CTRL_TYPE_ENUM: vc->type = V4L2_CTRL_TYPE_MENU; - } else { + break; + case PVR2_CTRL_TYPE_INT: vc->type = V4L2_CTRL_TYPE_INTEGER; + break; + default: + ret = -EINVAL; + break; } - strlcpy(vc->name,pvr2_hdw_get_ctl_name(hdw,pvr2_id), - sizeof(vc->name)); - vc->minimum = pvr2_hdw_get_ctl_min_value(hdw,pvr2_id); - vc->maximum = pvr2_hdw_get_ctl_max_value(hdw,pvr2_id); - vc->default_value = - pvr2_hdw_get_ctl_default_value(hdw,pvr2_id); + + strlcpy(vc->name,pvr2_ctrl_get_desc(cptr),sizeof(vc->name)); + vc->minimum = pvr2_ctrl_get_min_value(cptr); + vc->maximum = pvr2_ctrl_get_max_value(cptr); + vc->default_value = pvr2_ctrl_get_default_value(cptr); vc->step = 1; ret = 0; break; @@ -797,16 +622,15 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_QUERYMENU: { + struct pvr2_ctrl *cptr; struct v4l2_querymenu *vm = (struct v4l2_querymenu *)arg; - int pvr2_id = cnv_cid_v4l2_pvr2(vm->id); const char *value_name; - if (pvr2_id < 0) { + cptr = pvr2_hdw_get_ctrl(hdw,vm->id); + if (!pvr2_ctrl_is_valid(cptr)) { ret = -EINVAL; break; } - - value_name = pvr2_hdw_get_ctl_value_name(hdw,pvr2_id, - vm->index); + value_name = pvr2_ctrl_get_value_name(cptr,vm->index); if (value_name) { strlcpy(vm->name,value_name,sizeof(vm->name)); ret = 0; @@ -819,31 +643,31 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_G_CTRL: { + struct pvr2_ctrl *cptr; struct v4l2_control *vc = (struct v4l2_control *)arg; - int pvr2_id; - pvr2_id = cnv_cid_v4l2_pvr2(vc->id); - if (pvr2_id < 0) { + cptr = pvr2_hdw_get_ctrl(hdw,vc->id); + if (!pvr2_ctrl_is_valid(cptr)) { ret = -EINVAL; break; } ret = 0; - vc->value = pvr2_hdw_get_ctl_value(hdw,pvr2_id); + vc->value = pvr2_ctrl_get_value(cptr); break; } case VIDIOC_S_CTRL: { + struct pvr2_ctrl *cptr; struct v4l2_control *vc = (struct v4l2_control *)arg; - int pvr2_id; - pvr2_id = cnv_cid_v4l2_pvr2(vc->id); - if (pvr2_id < 0) { + cptr = pvr2_hdw_get_ctrl(hdw,vc->id); + if (!pvr2_ctrl_is_valid(cptr)) { ret = -EINVAL; break; } - ret = pvr2_hdw_set_ctl_value(hdw,pvr2_id,vc->value); + ret = pvr2_ctrl_set_value(cptr,vc->value); break; } -- cgit v1.2.3 From 4c9a77ace93000ba7b4f10da0e6e6899da6d238b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 29 Mar 2006 01:17:00 -0300 Subject: Tea6420: missing #define (< 2.6.16) From: Darren Salt tea6420: fix missing #define for kernels < 2.6.16 Change set 3653:388da79ac33b broke compilation of tea6420.c on kernels older than 2.6.16. This patch fixes it. Signed-off-by: Darren Salt Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tea6420.c | 1 + 1 file changed, 1 insertion(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/tea6420.c b/linux/drivers/media/video/tea6420.c index 74af061b2..72ed34e08 100644 --- a/linux/drivers/media/video/tea6420.c +++ b/linux/drivers/media/video/tea6420.c @@ -38,6 +38,7 @@ static int debug = 0; /* insmod parameter */ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) +#define dprintk(args...) \ do { if (debug) { printk("%s: %s()[%d]: ",__stringify(KBUILD_MODNAME), __FUNCTION__, __LINE__); printk(args); } } while (0) #else #define dprintk(args...) \ -- cgit v1.2.3 From 0d7fd3cdc85eb7e7e64a735ee7242aad047f4d64 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Tue, 28 Mar 2006 23:36:32 -0600 Subject: Fix pvrusb2 kernel oops From: Mike Isely This problem was introduced by the previous change. It appears that if you try to create to sysfs entries of the same name in one directory, then sysfs gets very angry and causes a kernel oops when you later try to tear down those nodes. By not allocating enough space to hold the name, we were inadvertently causing a sysfs control file name collision... Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 0ded41fcb..c5d44aa6c 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -58,7 +58,7 @@ struct pvr2_sysfs_ctl_item { struct pvr2_sysfs_ctl_item *item_next; struct attribute *attr_gen[5]; struct attribute_group grp; - char name[25]; + char name[80]; }; struct pvr2_sysfs_class { -- cgit v1.2.3 From 896235d467ecdcaecf352dae6d1970fdda4caeec Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Wed, 29 Mar 2006 02:00:23 -0600 Subject: Enhance I2C debugging in pvrusb2 From: Mike Isely Enhance I2C debugging in pvrusb2 such that I2C data is printed when transactions are reported. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 2f7946195..9a96273f2 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -262,18 +262,29 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, done: if (pvrusb2_debug & PVR2_TRACE_I2C_TRAF) { - unsigned int idx; + unsigned int idx,offs,cnt; for (idx = 0; idx < num; idx++) { + cnt = msgs[idx].len; printk(KERN_INFO "pvrusb2 i2c xfer %u/%u:" " addr=0x%x len=%d %s%s", idx+1,num, msgs[idx].addr, - msgs[idx].len, + cnt, (msgs[idx].flags & I2C_M_RD ? "read" : "write"), (msgs[idx].flags & I2C_M_NOSTART ? " nostart" : "")); + if ((ret > 0) || !(msgs[idx].flags & I2C_M_RD)) { + if (cnt > 8) cnt = 8; + printk(" ["); + for (offs = 0; offs < (cnt>8?8:cnt); offs++) { + if (offs) printk(" "); + printk("%02x",msgs[idx].buf[offs]); + } + if (offs < cnt) printk(" ..."); + printk("]"); + } if (idx+1 == num) { printk(" result=%d",ret); } -- cgit v1.2.3 From df9faeeb73f7e2d40d144d32b07dbc8560b5e031 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Wed, 29 Mar 2006 02:01:58 -0600 Subject: Don't reset USB control state during pvrusb2 initialization From: Mike Isely Initialization failures in the pvrusb2 driver seem to coincide with attempts to reset parts of the USB core related to this device. This really should not be needed and I suspect it may be doing more harm than good. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 585f4ee0b..7b27d96ab 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1229,8 +1229,10 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) cptr->ctl_def->default_value); } +#if 0 // Suspect this is doing more harm than good pvr2_reset_ctl_endpoints(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; +#endif ret = pvr2_hdw_get_eeprom_addr(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; -- cgit v1.2.3 From 12836d25828428c8ba891392b0441c3963d0557a Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Wed, 29 Mar 2006 12:53:15 -0400 Subject: Kconfig: Add firmware download comments for or51211 and or51132 From: Trent Piepho - Added firmware download comments to or51211 and or51132 Kconfig help text. Signed-off-by: Trent Piepho Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/frontends/Kconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/Kconfig b/linux/drivers/media/dvb/frontends/Kconfig index 94233168d..37d5e0af1 100644 --- a/linux/drivers/media/dvb/frontends/Kconfig +++ b/linux/drivers/media/dvb/frontends/Kconfig @@ -181,6 +181,11 @@ config DVB_OR51211 help An ATSC 8VSB tuner module. Say Y when you want to support this frontend. + This driver needs external firmware. Please use the command + "/Documentation/dvb/get_dvb_firmware or51211" to + download it, and then copy it to /usr/lib/hotplug/firmware + or /lib/firmware (depending on configuration of firmware hotplug). + config DVB_OR51132 tristate "Oren OR51132 based" depends on DVB_CORE @@ -189,6 +194,13 @@ config DVB_OR51132 An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. + This driver needs external firmware. Please use the commands + "/Documentation/dvb/get_dvb_firmware or51132_vsb" and/or + "/Documentation/dvb/get_dvb_firmware or51132_qam" to + download firmwares for 8VSB and QAM64/256, respectively. Copy them to + /usr/lib/hotplug/firmware or /lib/firmware (depending on + configuration of firmware hotplug). + config DVB_BCM3510 tristate "Broadcom BCM3510" depends on DVB_CORE -- cgit v1.2.3 From a7aa675c92fa3d11e83bf8990e98fd06e2c95459 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Wed, 29 Mar 2006 12:53:49 -0400 Subject: Kconfig: Fix PCI ID typo in VIDEO_CX88_ALSA help text From: Trent Piepho - Fixed PCI ID typo in VIDEO_CX88_ALSA help text Signed-off-by: Trent Piepho Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/Kconfig b/linux/drivers/media/video/cx88/Kconfig index ff0f72340..2c586f903 100644 --- a/linux/drivers/media/video/cx88/Kconfig +++ b/linux/drivers/media/video/cx88/Kconfig @@ -25,7 +25,7 @@ config VIDEO_CX88_ALSA It only works with boards with function 01 enabled. To check if your board supports, use lspci -n. - If supported, you should see 1471:8801 or 1471:8811 + If supported, you should see 14f1:8801 or 14f1:8811 PCI device. To compile this driver as a module, choose M here: the -- cgit v1.2.3 From 8cb185c4aa68c43b8b1c9c964c7ab785ecfbb928 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 29 Mar 2006 19:31:44 +0200 Subject: Fix msp3400c wait time and better audio mode fallbacks From: Hans Verkuil - The wait time until the first time the audio mode was detected was 1+5=6 seconds instead of just 1 second (wrong statement order). msp3400c specific bug. - Implemented audio mode fallback for msp3400c/d just like the msp3400g does automatically. E.g. fallback to stereo if no second language exists, etc. Signed-off-by: Hans Verkuil --- linux/drivers/media/video/msp3400-kthreads.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/msp3400-kthreads.c b/linux/drivers/media/video/msp3400-kthreads.c index 0a9b4a2ba..4b0d6340a 100644 --- a/linux/drivers/media/video/msp3400-kthreads.c +++ b/linux/drivers/media/video/msp3400-kthreads.c @@ -234,6 +234,7 @@ static void msp3400c_set_audmode(struct i2c_client *client) char *modestr = (state->audmode >= 0 && state->audmode < 5) ? strmode[state->audmode] : "unknown"; int src = 0; /* channel source: FM/AM, nicam or SCART */ + int audmode = state->audmode; if (state->opmode == OPMODE_AUTOSELECT) { /* this method would break everything, let's make sure @@ -245,11 +246,22 @@ static void msp3400c_set_audmode(struct i2c_client *client) return; } + /* If no second language is available, switch to the first language */ + if ((audmode == V4L2_TUNER_MODE_LANG2 || + audmode == V4L2_TUNER_MODE_LANG1_LANG2) && + !(state->rxsubchans & V4L2_TUNER_SUB_LANG2)) + audmode = V4L2_TUNER_MODE_LANG1; + /* switch to stereo for stereo transmission, otherwise + keep first language */ + if (audmode == V4L2_TUNER_MODE_LANG1 && + (state->rxsubchans & V4L2_TUNER_SUB_STEREO)) + audmode = V4L2_TUNER_MODE_STEREO; + /* switch demodulator */ switch (state->mode) { case MSP_MODE_FM_TERRA: v4l_dbg(1, msp_debug, client, "FM set_audmode: %s\n", modestr); - switch (state->audmode) { + switch (audmode) { case V4L2_TUNER_MODE_STEREO: msp_write_dsp(client, 0x000e, 0x3001); break; @@ -263,7 +275,7 @@ static void msp3400c_set_audmode(struct i2c_client *client) break; case MSP_MODE_FM_SAT: v4l_dbg(1, msp_debug, client, "SAT set_audmode: %s\n", modestr); - switch (state->audmode) { + switch (audmode) { case V4L2_TUNER_MODE_MONO: msp3400c_set_carrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); break; @@ -302,7 +314,7 @@ static void msp3400c_set_audmode(struct i2c_client *client) } /* switch audio */ - switch (state->audmode) { + switch (audmode) { case V4L2_TUNER_MODE_STEREO: case V4L2_TUNER_MODE_LANG1_LANG2: src |= 0x0020; @@ -320,10 +332,6 @@ static void msp3400c_set_audmode(struct i2c_client *client) src = 0x0030; break; case V4L2_TUNER_MODE_LANG1: - /* switch to stereo for stereo transmission, otherwise - keep first language */ - if (state->rxsubchans & V4L2_TUNER_SUB_STEREO) - src |= 0x0020; break; case V4L2_TUNER_MODE_LANG2: src |= 0x0010; @@ -625,9 +633,9 @@ int msp3400c_thread(void *data) if (msp_sleep(state, 1000)) goto restart; while (state->watch_stereo) { + watch_stereo(client); if (msp_sleep(state, 5000)) goto restart; - watch_stereo(client); } } v4l_dbg(1, msp_debug, client, "thread: exit\n"); -- cgit v1.2.3 From 256280a989a5688161c66f97a3e054749875a723 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 29 Mar 2006 19:56:17 +0200 Subject: Add new NEC uPD64031A and uPD64083 i2c drivers From: Hans Verkuil - Add support for the uPD64031A NEC Electronics Ghost Reduction i2c device - Add support for the uPD6408x NEC Electronics 3-Dimensional Y/C separation i2c device. Signed-off-by: Takahiro Adachi Signed-off-by: Takeru Komoriya Signed-off-by: Hans Verkuil --- linux/drivers/media/video/Kconfig | 37 +++- linux/drivers/media/video/Makefile | 2 + linux/drivers/media/video/upd64031a.c | 324 ++++++++++++++++++++++++++++++++++ linux/drivers/media/video/upd64083.c | 299 +++++++++++++++++++++++++++++++ 4 files changed, 655 insertions(+), 7 deletions(-) create mode 100644 linux/drivers/media/video/upd64031a.c create mode 100644 linux/drivers/media/video/upd64083.c (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index ba64440c2..de8cfebd4 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -300,7 +300,7 @@ config VIDEO_OVCAMCHIP camera drivers. To compile this driver as a module, choose M here: the - module will be called ovcamchip + module will be called ovcamchip. config VIDEO_M32R_AR tristate "AR devices" @@ -323,7 +323,7 @@ config VIDEO_MSP3400 Support for the Micronas MSP34xx series of audio decoders. To compile this driver as a module, choose M here: the - module will be called msp3400 + module will be called msp3400. config VIDEO_CS53L32A tristate "Cirrus Logic CS53L32A audio ADC" @@ -333,7 +333,7 @@ config VIDEO_CS53L32A stereo A/D converter. To compile this driver as a module, choose M here: the - module will be called cs53l32a + module will be called cs53l32a. config VIDEO_WM8775 tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer" @@ -343,7 +343,7 @@ config VIDEO_WM8775 performance stereo A/D Converter with a 4 channel input mixer. To compile this driver as a module, choose M here: the - module will be called wm8775 + module will be called wm8775. config VIDEO_WM8739 tristate "Wolfson Microelectronics WM8739 stereo audio ADC" @@ -353,7 +353,7 @@ config VIDEO_WM8739 stereo A/D Converter. To compile this driver as a module, choose M here: the - module will be called wm8739 + module will be called wm8739. source "drivers/media/video/cx25840/Kconfig" @@ -364,7 +364,7 @@ config VIDEO_SAA711X Support for the Philips SAA7113/4/5 video decoders. To compile this driver as a module, choose M here: the - module will be called saa7115 + module will be called saa7115. config VIDEO_SAA7127 tristate "Philips SAA7127/9 digital video encoders" @@ -373,7 +373,30 @@ config VIDEO_SAA7127 Support for the Philips SAA7127/9 digital video encoders. To compile this driver as a module, choose M here: the - module will be called saa7127 + module will be called saa7127. + +config VIDEO_UPD64031A + tristate "NEC Electronics uPD64031A Ghost Reduction" + depends on VIDEO_DEV && I2C && EXPERIMENTAL + ---help--- + Support for the NEC Electronics uPD64031A Ghost Reduction + video chip. It is most often found in NTSC TV cards made for + Japan and is used to reduce the 'ghosting' effect that can + be present in analog TV broadcasts. + + To compile this driver as a module, choose M here: the + module will be called upd64031a. + +config VIDEO_UPD64083 + tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation" + depends on VIDEO_DEV && I2C && EXPERIMENTAL + ---help--- + Support for the NEC Electronics uPD64083 3-Dimensional Y/C + separation video chip. It is used to improve the quality of + the colors of a composite signal. + + To compile this driver as a module, choose M here: the + module will be called upd64083. # # USB Multimedia device configuration diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index 896619c43..593933b69 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -66,6 +66,8 @@ obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o obj-$(CONFIG_VIDEO_CX25840) += cx25840/ obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o +obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o +obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o zc0301-objs := zc0301_core.o zc0301_pas202bcb.o diff --git a/linux/drivers/media/video/upd64031a.c b/linux/drivers/media/video/upd64031a.c new file mode 100644 index 000000000..5ca6497cd --- /dev/null +++ b/linux/drivers/media/video/upd64031a.c @@ -0,0 +1,324 @@ +/* + * upd64031A - NEC Electronics Ghost Reduction for NTSC in Japan + * + * 2003 by T.Adachi + * 2003 by Takeru KOMORIYA + * 2006 by Hans Verkuil + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "compat.h" + +#include +#include +#include +#include +#include +#include +#include + +// --------------------- read registers functions define ----------------------- + +/* bit masks */ +#define GR_MODE_MASK 0xc0 +#define DIRECT_3DYCS_CONNECT_MASK 0xc0 +#define SYNC_CIRCUIT_MASK 0xa0 + +// ----------------------------------------------------------------------------- + +MODULE_DESCRIPTION("uPD64031A driver"); +MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static int debug = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +module_param(debug, int, 0644); +#else +MODULE_PARM(debug, "i"); +#endif + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +static unsigned short normal_i2c[] = { 0x24 >> 1, 0x26 >> 1, I2C_CLIENT_END }; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) +static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; +#endif + +I2C_CLIENT_INSMOD; + +enum { + R00 = 0, R01, R02, R03, R04, + R05, R06, R07, R08, R09, + R0A, R0B, R0C, R0D, R0E, R0F, + /* unused registers + R10, R11, R12, R13, R14, + R15, R16, R17, + */ + TOT_REGS +}; + +struct upd64031a_state { + u8 regs[TOT_REGS]; + u8 gr_mode; + u8 direct_3dycs_connect; + u8 ext_comp_sync; + u8 ext_vert_sync; +}; + +static u8 upd64031a_init[] = { + 0x00, 0xb8, 0x48, 0xd2, 0xe6, + 0x03, 0x10, 0x0b, 0xaf, 0x7f, + 0x00, 0x00, 0x1d, 0x5e, 0x00, + 0xd0 +}; + +/* ------------------------------------------------------------------------ */ + +static u8 upd64031a_read(struct i2c_client *client, u8 reg) +{ + u8 buf[2]; + + if (reg >= sizeof(buf)) + return 0xff; + i2c_master_recv(client, buf, 2); + return buf[reg]; +} + +/* ------------------------------------------------------------------------ */ + +static void upd64031a_write(struct i2c_client *client, u8 reg, u8 val) +{ + u8 buf[2]; + + buf[0] = reg; + buf[1] = val; + v4l_dbg(1, debug, client, "writing reg addr: %02X val: %02X\n", reg, val); + if (i2c_master_send(client, buf, 2) != 2) + v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val); +} + +/* ------------------------------------------------------------------------ */ + +/* The input changed due to new input or channel changed */ +static void upd64031a_change(struct i2c_client *client) +{ + struct upd64031a_state *state = i2c_get_clientdata(client); + u8 reg = state->regs[R00]; + + v4l_dbg(1, debug, client, "changed input or channel\n"); + upd64031a_write(client, R00, reg | 0x10); + upd64031a_write(client, R00, reg & ~0x10); +} + +/* ------------------------------------------------------------------------ */ + +static int upd64031a_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + struct upd64031a_state *state = i2c_get_clientdata(client); + struct v4l2_routing *route = arg; + + switch (cmd) { + case VIDIOC_S_FREQUENCY: + upd64031a_change(client); + break; + + case VIDIOC_INT_G_VIDEO_ROUTING: + route->input = (state->gr_mode >> 6) | + (state->direct_3dycs_connect >> 4) | + (state->ext_comp_sync >> 1) | + (state->ext_vert_sync >> 2); + route->output = 0; + break; + + case VIDIOC_INT_S_VIDEO_ROUTING: + { + u8 r00, r05, r08; + + state->gr_mode = (route->input & 3) << 6; + state->direct_3dycs_connect = (route->input & 0xc) << 4; + state->ext_comp_sync = (route->input & UPD64031A_COMPOSITE_EXTERNAL) << 1; + state->ext_vert_sync = (route->input & UPD64031A_VERTICAL_EXTERNAL) << 2; + r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode; + r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) | + state->ext_comp_sync | state->ext_vert_sync; + r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) | + state->direct_3dycs_connect; + upd64031a_write(client, R00, r00); + upd64031a_write(client, R05, r05); + upd64031a_write(client, R08, r08); + upd64031a_change(client); + break; + } + + case VIDIOC_LOG_STATUS: + v4l_info(client, "Status: SA00=0x%02x SA01=0x%02x\n", + upd64031a_read(client, 0), upd64031a_read(client, 1)); + break; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_INT_G_REGISTER: + { + struct v4l2_register *reg = arg; + + if (reg->i2c_id != I2C_DRIVERID_UPD64031A) + return -EINVAL; + reg->val = upd64031a_read(client, reg->reg & 0xff); + break; + } + + case VIDIOC_INT_S_REGISTER: + { + struct v4l2_register *reg = arg; + u8 addr = reg->reg & 0xff; + u8 val = reg->val & 0xff; + + if (reg->i2c_id != I2C_DRIVERID_UPD64031A) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + upd64031a_write(client, addr, val); + break; + } +#endif + + default: + break; + } + return 0; +} + +/* ------------------------------------------------------------------------ */ + +/* i2c implementation */ + +static struct i2c_driver i2c_driver; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +static int upd64031a_attach(struct i2c_adapter *adapter, int address, int kind) +#else +static int upd64031a_attach(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +#endif +{ + struct i2c_client *client; + struct upd64031a_state *state; + int i; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == NULL) { + return -ENOMEM; + } + + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) + client->flags = I2C_CLIENT_ALLOW_USE; +#endif + snprintf(client->name, sizeof(client->name) - 1, "uPD64031A"); + + v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name); + + state = kmalloc(sizeof(struct upd64031a_state), GFP_KERNEL); + if (state == NULL) { + kfree(client); + return -ENOMEM; + } + i2c_set_clientdata(client, state); + memcpy(state->regs, upd64031a_init, sizeof(state->regs)); + state->gr_mode = UPD64031A_GR_ON << 6; + state->direct_3dycs_connect = UPD64031A_3DYCS_COMPOSITE << 4; + state->ext_comp_sync = state->ext_vert_sync = 0; + for (i = 0; i < TOT_REGS; i++) { + upd64031a_write(client, i, state->regs[i]); + } + + i2c_attach_client(client); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + MOD_INC_USE_COUNT; +#endif + return 0; +} + +static int upd64031a_probe(struct i2c_adapter *adapter) +{ +#ifdef I2C_CLASS_TV_ANALOG + if (adapter->class & I2C_CLASS_TV_ANALOG) +#else + if (adapter->id == I2C_HW_B_BT848) +#endif + return i2c_probe(adapter, &addr_data, upd64031a_attach); + return 0; +} + +static int upd64031a_detach(struct i2c_client *client) +{ + int err; + + err = i2c_detach_client(client); + if (err) + return err; + + kfree(client); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ +static struct i2c_driver i2c_driver = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) && LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) + .owner = THIS_MODULE, +#endif +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) + .name = "upd64031a", + .flags = I2C_DF_NOTIFY, +#else + .driver = { + .name = "upd64031a", + }, +#endif + .id = I2C_DRIVERID_UPD64031A, + .attach_adapter = upd64031a_probe, + .detach_client = upd64031a_detach, + .command = upd64031a_command, +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +EXPORT_NO_SYMBOLS; +#endif + +static int __init upd64031a_init_module(void) +{ + return i2c_add_driver(&i2c_driver); +} + +static void __exit upd64031a_exit_module(void) +{ + i2c_del_driver(&i2c_driver); +} + +module_init(upd64031a_init_module); +module_exit(upd64031a_exit_module); + diff --git a/linux/drivers/media/video/upd64083.c b/linux/drivers/media/video/upd64083.c new file mode 100644 index 000000000..861d9b114 --- /dev/null +++ b/linux/drivers/media/video/upd64083.c @@ -0,0 +1,299 @@ +/* + * upd6408x - NEC Electronics 3-Dimensional Y/C separation driver + * + * 2003 by T.Adachi (tadachi@tadachi-net.com) + * 2003 by Takeru KOMORIYA + * 2006 by Hans Verkuil + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "compat.h" +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("uPD64083 driver"); +MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static int debug = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) +module_param(debug, bool, 0644); +#else +MODULE_PARM(debug, "i"); +#endif + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +static unsigned short normal_i2c[] = { 0xb8 >> 1, 0xba >> 1, I2C_CLIENT_END }; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) +static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; +#endif + +I2C_CLIENT_INSMOD; + +enum { + R00 = 0, R01, R02, R03, R04, + R05, R06, R07, R08, R09, + R0A, R0B, R0C, R0D, R0E, R0F, + R10, R11, R12, R13, R14, + R15, R16, + TOT_REGS +}; + +struct upd64083_state { + u8 mode; + u8 ext_y_adc; + u8 regs[TOT_REGS]; +}; + +/* Initial values when used in combination with the + NEC upd64031a ghost reduction chip. */ +static u8 upd64083_init[] = { + 0x1f, 0x01, 0xa0, 0x2d, 0x29, /* we use EXCSS=0 */ + 0x36, 0xdd, 0x05, 0x56, 0x48, + 0x00, 0x3a, 0xa0, 0x05, 0x08, + 0x44, 0x60, 0x08, 0x52, 0xf8, + 0x53, 0x60, 0x10 +}; + +/* ------------------------------------------------------------------------ */ + +static void upd64083_log_status(struct i2c_client *client) +{ + u8 buf[7]; + + i2c_master_recv(client, buf, 7); + v4l_info(client, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x " + "SA04=%02x SA05=%02x SA06=%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); +} + +/* ------------------------------------------------------------------------ */ + +static void upd64083_write(struct i2c_client *client, u8 reg, u8 val) +{ + u8 buf[2]; + + buf[0] = reg; + buf[1] = val; + v4l_dbg(1, debug, client, "writing reg addr: %02x val: %02x\n", reg, val); + if (i2c_master_send(client, buf, 2) != 2) + v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val); +} + +/* ------------------------------------------------------------------------ */ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static u8 upd64083_read(struct i2c_client *client, u8 reg) +{ + u8 buf[7]; + + if (reg >= sizeof(buf)) + return 0xff; + i2c_master_recv(client, buf, sizeof(buf)); + return buf[reg]; +} +#endif + +/* ------------------------------------------------------------------------ */ + +static int upd64083_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + struct upd64083_state *state = i2c_get_clientdata(client); + struct v4l2_routing *route = arg; + + switch (cmd) { + case VIDIOC_INT_G_VIDEO_ROUTING: + route->input = (state->mode >> 6) | (state->ext_y_adc >> 3); + route->output = 0; + break; + + case VIDIOC_INT_S_VIDEO_ROUTING: + { + u8 r00, r02; + + if (route->input > 7 || (route->input & 6) == 6) + return -EINVAL; + state->mode = (route->input & 3) << 6; + state->ext_y_adc = (route->input & UPD64083_EXT_Y_ADC) << 3; + r00 = (state->regs[R00] & ~(3 << 6)) | state->mode; + r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc; + upd64083_write(client, R00, r00); + upd64083_write(client, R02, r02); + break; + } + + case VIDIOC_LOG_STATUS: + upd64083_log_status(client); + break; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_INT_G_REGISTER: + { + struct v4l2_register *reg = arg; + + if (reg->i2c_id != I2C_DRIVERID_UPD64083) + return -EINVAL; + reg->val = upd64083_read(client, reg->reg & 0xff); + break; + } + + case VIDIOC_INT_S_REGISTER: + { + struct v4l2_register *reg = arg; + u8 addr = reg->reg & 0xff; + u8 val = reg->val & 0xff; + + if (reg->i2c_id != I2C_DRIVERID_UPD64083) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + upd64083_write(client, addr, val); + break; + } +#endif + default: + break; + } + + return 0; +} + +/* ------------------------------------------------------------------------ */ + +/* i2c implementation */ + +static struct i2c_driver i2c_driver; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +static int upd64083_attach(struct i2c_adapter *adapter, int address, int kind) +#else +static int upd64083_attach(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +#endif +{ + struct i2c_client *client; + struct upd64083_state *state; + int i; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == NULL) { + return -ENOMEM; + } + + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) + client->flags = I2C_CLIENT_ALLOW_USE; +#endif + snprintf(client->name, sizeof(client->name) - 1, "uPD64083"); + + v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name); + + state = kmalloc(sizeof(struct upd64083_state), GFP_KERNEL); + if (state == NULL) { + kfree(client); + return -ENOMEM; + } + i2c_set_clientdata(client, state); + /* Initially assume that a ghost reduction chip is present */ + state->mode = 0; /* YCS mode */ + state->ext_y_adc = (1 << 5); + memcpy(state->regs, upd64083_init, TOT_REGS); + for (i = 0; i < TOT_REGS; i++) { + upd64083_write(client, i, state->regs[i]); + } + i2c_attach_client(client); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + MOD_INC_USE_COUNT; +#endif + return 0; +} + +static int upd64083_probe(struct i2c_adapter *adapter) +{ +#ifdef I2C_CLASS_TV_ANALOG + if (adapter->class & I2C_CLASS_TV_ANALOG) +#else + if (adapter->id == I2C_HW_B_BT848) +#endif + return i2c_probe(adapter, &addr_data, upd64083_attach); + return 0; +} + +static int upd64083_detach(struct i2c_client *client) +{ + int err; + + err = i2c_detach_client(client); + if (err) + return err; + + kfree(client); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ +static struct i2c_driver i2c_driver = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) && LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) + .owner = THIS_MODULE, +#endif +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) + .name = "upd64083", + .flags = I2C_DF_NOTIFY, +#else + .driver = { + .name = "upd64083", + }, +#endif + .id = I2C_DRIVERID_UPD64083, + .attach_adapter = upd64083_probe, + .detach_client = upd64083_detach, + .command = upd64083_command, +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +EXPORT_NO_SYMBOLS; +#endif + +static int __init upd64083_init_module(void) +{ + return i2c_add_driver(&i2c_driver); +} + +static void __exit upd64083_exit_module(void) +{ + i2c_del_driver(&i2c_driver); +} + +module_init(upd64083_init_module); +module_exit(upd64083_exit_module); -- cgit v1.2.3 From f8f26c1536f4aa9e1329fb70979cdab63fdda0c0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 29 Mar 2006 20:16:08 +0200 Subject: Remove trailing newlines From: Hans Verkuil Signed-off-by: Hans Verkuil --- linux/drivers/media/video/upd64031a.c | 1 - 1 file changed, 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/upd64031a.c b/linux/drivers/media/video/upd64031a.c index 5ca6497cd..66254bad4 100644 --- a/linux/drivers/media/video/upd64031a.c +++ b/linux/drivers/media/video/upd64031a.c @@ -321,4 +321,3 @@ static void __exit upd64031a_exit_module(void) module_init(upd64031a_init_module); module_exit(upd64031a_exit_module); - -- cgit v1.2.3 From 6c7054ea82d8b4745c6293b852813671bbf8b5ba Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 29 Mar 2006 15:19:26 -0300 Subject: zmalloc changed to kzmalloc; semaphore to mutex From: Mauro Carvalho Chehab kernel-sync Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/dpc7146.c | 3 +-- linux/drivers/media/video/hexium_gemini.c | 3 +-- linux/drivers/media/video/hexium_orion.c | 5 ++--- linux/drivers/media/video/mxb.c | 9 ++++----- linux/drivers/media/video/tea6420.c | 3 +-- 5 files changed, 9 insertions(+), 14 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/dpc7146.c b/linux/drivers/media/video/dpc7146.c index 5cce40af6..3ee2cc6e3 100644 --- a/linux/drivers/media/video/dpc7146.c +++ b/linux/drivers/media/video/dpc7146.c @@ -95,12 +95,11 @@ static int dpc_probe(struct saa7146_dev* dev) struct i2c_client *client; struct list_head *item; - dpc = (struct dpc*)kmalloc(sizeof(struct dpc), GFP_KERNEL); + dpc = kzalloc(sizeof(struct dpc), GFP_KERNEL); if( NULL == dpc ) { printk("dpc_v4l2.o: dpc_probe: not enough kernel memory.\n"); return -ENOMEM; } - memset(dpc, 0x0, sizeof(struct dpc)); /* FIXME: enable i2c-port pins, video-port-pins video port pins should be enabled here ?! */ diff --git a/linux/drivers/media/video/hexium_gemini.c b/linux/drivers/media/video/hexium_gemini.c index 24ae47cff..6cc7ca99d 100644 --- a/linux/drivers/media/video/hexium_gemini.c +++ b/linux/drivers/media/video/hexium_gemini.c @@ -241,12 +241,11 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d DEB_EE((".\n")); - hexium = (struct hexium *) kmalloc(sizeof(struct hexium), GFP_KERNEL); + hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL); if (NULL == hexium) { printk("hexium_gemini: not enough kernel memory in hexium_attach().\n"); return -ENOMEM; } - memset(hexium, 0x0, sizeof(struct hexium)); dev->ext_priv = hexium; /* enable i2c-port pins */ diff --git a/linux/drivers/media/video/hexium_orion.c b/linux/drivers/media/video/hexium_orion.c index 776eeea1b..59ed0dfd2 100644 --- a/linux/drivers/media/video/hexium_orion.c +++ b/linux/drivers/media/video/hexium_orion.c @@ -225,12 +225,11 @@ static int hexium_probe(struct saa7146_dev *dev) return -EFAULT; } - hexium = (struct hexium *) kmalloc(sizeof(struct hexium), GFP_KERNEL); + hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL); if (NULL == hexium) { printk("hexium_orion: hexium_probe: not enough kernel memory.\n"); return -ENOMEM; } - memset(hexium, 0x0, sizeof(struct hexium)); /* enable i2c-port pins */ saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26)); @@ -486,7 +485,7 @@ static struct saa7146_ext_vv vv_data = { }; static struct saa7146_extension extension = { - .name = "hexium HV-PCI6/Orion", + .name = "hexium HV-PCI6 Orion", .flags = 0, // SAA7146_USE_I2C_IRQ, .pci_tbl = &pci_tbl[0], diff --git a/linux/drivers/media/video/mxb.c b/linux/drivers/media/video/mxb.c index 615cc0a1d..37d3f33b7 100644 --- a/linux/drivers/media/video/mxb.c +++ b/linux/drivers/media/video/mxb.c @@ -26,8 +26,8 @@ #include "compat.h" #include #include -#include #include +#include #include "mxb.h" #include "tea6415c.h" @@ -178,12 +178,11 @@ static int mxb_probe(struct saa7146_dev* dev) return -ENODEV; } - mxb = (struct mxb*)kmalloc(sizeof(struct mxb), GFP_KERNEL); + mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL); if( NULL == mxb ) { DEB_D(("not enough kernel memory.\n")); return -ENOMEM; } - memset(mxb, 0x0, sizeof(struct mxb)); mxb->i2c_adapter = (struct i2c_adapter) { .class = I2C_CLASS_TV_ANALOG, @@ -631,9 +630,9 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) } /* fixme: locke das setzen des inputs mit hilfe des mutexes - down(&dev->lock); + mutex_lock(&dev->lock); video_mux(dev,*i); - up(&dev->lock); + mutex_unlock(&dev->lock); */ /* fixme: check if streaming capture diff --git a/linux/drivers/media/video/tea6420.c b/linux/drivers/media/video/tea6420.c index 72ed34e08..9896661da 100644 --- a/linux/drivers/media/video/tea6420.c +++ b/linux/drivers/media/video/tea6420.c @@ -109,11 +109,10 @@ static int tea6420_detect(struct i2c_adapter *adapter, int address, int kind) } /* allocate memory for client structure */ - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (0 == client) { return -ENOMEM; } - memset(client, 0x0, sizeof(struct i2c_client)); /* fill client structure */ memcpy(client, &client_template, sizeof(struct i2c_client)); -- cgit v1.2.3 From d2f9849264786f56967fb28a66a295c79c29b322 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 29 Mar 2006 15:25:00 -0300 Subject: Several improvements at Kconfig menu by mkrufky From: Mauro Carvalho Chehab merge: from -git tree (changesets 3653a to 3653g) Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/Kconfig | 168 ++++--------------------------------- linux/drivers/media/video/Makefile | 3 - 2 files changed, 16 insertions(+), 155 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index 62e71b527..612096aec 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -316,6 +316,13 @@ config VIDEO_M32R_AR_M64278 Say Y here to use the Renesas M64278E-800 camera module, which supports VGA(640x480 pixcels) size of images. +# +# Encoder / Decoder module configuration +# + +menu "Encoders and Decoders" + depends on VIDEO_DEV + config VIDEO_MSP3400 tristate "Micronas MSP34xx audio decoders" depends on VIDEO_DEV && I2C @@ -365,6 +372,8 @@ config VIDEO_SAA7127 To compile this driver as a module, choose M here: the module will be called saa7127 +endmenu # encoder / decoder chips + # # USB Multimedia device configuration # @@ -376,20 +385,6 @@ source "drivers/media/video/pvrusb2/Kconfig" source "drivers/media/video/em28xx/Kconfig" -config USB_VICAM - tristate "USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)" - depends on USB && VIDEO_DEV && EXPERIMENTAL - ---help--- - Say Y here if you have 3com homeconnect camera (vicam). - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" (under Multimedia Devices) to use this driver. - Information on this API and pointers to "v4l" programs may be found - at . - - To compile this driver as a module, choose M here: the - module will be called vicam. - config USB_DSBR tristate "D-Link USB FM radio support (EXPERIMENTAL)" depends on USB && VIDEO_DEV && EXPERIMENTAL @@ -399,66 +394,12 @@ config USB_DSBR you must connect the line out connector to a sound card or a set of speakers. - This driver uses the Video For Linux API. You must enable - (Y or M in config) Video For Linux (under Character Devices) - to use this driver. Information on this API and pointers to - "v4l" programs may be found at - . - To compile this driver as a module, choose M here: the module will be called dsbr100. -config USB_ET61X251 - tristate "USB ET61X[12]51 PC Camera Controller support" - depends on USB && VIDEO_DEV - ---help--- - Say Y here if you want support for cameras based on Etoms ET61X151 - or ET61X251 PC Camera Controllers. +source "drivers/media/video/usbvideo/Kconfig" - See for more informations. - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" to use this driver. - - To compile this driver as a module, choose M here: the - module will be called et61x251. - -config USB_IBMCAM - tristate "USB IBM (Xirlink) C-it Camera support" - depends on USB && VIDEO_DEV - ---help--- - Say Y here if you want to connect a IBM "C-It" camera, also known as - "Xirlink PC Camera" to your computer's USB port. For more - information, read . - - This driver uses the Video For Linux API. You must enable - (Y or M in config) Video For Linux (under Character Devices) - to use this driver. Information on this API and pointers to - "v4l" programs may be found at - . - - To compile this driver as a module, choose M here: the - module will be called ibmcam. - - This camera has several configuration options which - can be specified when you load the module. Read - to learn more. - -config USB_KONICAWC - tristate "USB Konica Webcam support" - depends on USB && VIDEO_DEV - ---help--- - Say Y here if you want support for webcams based on a Konica - chipset. This is known to work with the Intel YC76 webcam. - - This driver uses the Video For Linux API. You must enable - (Y or M in config) Video For Linux (under Character Devices) - to use this driver. Information on this API and pointers to - "v4l" programs may be found at - . - - To compile this driver as a module, choose M here: the - module will be called konicawc. +source "drivers/media/video/et61x251/Kconfig" config USB_OV511 tristate "USB OV511 Camera support" @@ -468,11 +409,6 @@ config USB_OV511 computer's USB port. See for more information and for a list of supported cameras. - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" (under Character Devices) to use this driver. - Information on this API and pointers to "v4l" programs may be found - at . - To compile this driver as a module, choose M here: the module will be called ov511. @@ -484,28 +420,10 @@ config USB_SE401 computer's USB port. See for more information and for a list of supported cameras. - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" (under Multimedia Devices) to use this driver. - Information on this API and pointers to "v4l" programs may be found - at . - To compile this driver as a module, choose M here: the module will be called se401. -config USB_SN9C102 - tristate "USB SN9C10x PC Camera Controller support" - depends on USB && VIDEO_DEV - ---help--- - Say Y here if you want support for cameras based on SONiX SN9C101, - SN9C102 or SN9C103 PC Camera Controllers. - - See for more informations. - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" to use this driver. - - To compile this driver as a module, choose M here: the - module will be called sn9c102. +source "drivers/media/video/sn9c102/Kconfig" config USB_STV680 tristate "USB STV680 (Pencam) Camera support" @@ -516,17 +434,13 @@ config USB_STV680 See for more information and for a list of supported cameras. - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" (under Multimedia Devices) to use this driver. - Information on this API and pointers to "v4l" programs may be found - at . - To compile this driver as a module, choose M here: the module will be called stv680. config USB_W9968CF tristate "USB W996[87]CF JPEG Dual Mode Camera support" - depends on USB && VIDEO_DEV && I2C && VIDEO_OVCAMCHIP + depends on USB && VIDEO_DEV && I2C + select VIDEO_OVCAMCHIP ---help--- Say Y here if you want support for cameras based on OV681 or Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips. @@ -538,62 +452,12 @@ config USB_W9968CF See for more informations. - This driver uses the Video For Linux and the I2C APIs. It needs the - OmniVision Camera Chip support as well. You must say Y or M to - "Video For Linux", "I2C Support" and "OmniVision Camera Chip - support" to use this driver. - To compile this driver as a module, choose M here: the module will be called w9968cf. -config USB_ZC0301 - tristate "USB ZC0301 Image Processor and Control Chip support" - depends on USB && VIDEO_DEV - ---help--- - Say Y here if you want support for cameras based on the ZC0301 - Image Processor and Control Chip. - - See for more informations. - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" to use this driver. +source "drivers/media/video/zc0301/Kconfig" - To compile this driver as a module, choose M here: the - module will be called zc0301. - -config USB_PWC - tristate "USB Philips Cameras" - depends on USB && VIDEO_DEV - ---help--- - Say Y or M here if you want to use one of these Philips & OEM - webcams: - * Philips PCA645, PCA646 - * Philips PCVC675, PCVC680, PCVC690 - * Philips PCVC720/40, PCVC730, PCVC740, PCVC750 - * Askey VC010 - * Logitech QuickCam Pro 3000, 4000, 'Zoom', 'Notebook Pro' - and 'Orbit'/'Sphere' - * Samsung MPC-C10, MPC-C30 - * Creative Webcam 5, Pro Ex - * SOTEC Afina Eye - * Visionite VCS-UC300, VCS-UM100 - - The PCA635, PCVC665 and PCVC720/20 are not supported by this driver - and never will be, but the 665 and 720/20 are supported by other - drivers. - - See for more information and - installation instructions. - - The built-in microphone is enabled by selecting USB Audio support. - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" (under Character Devices) to use this driver. - Information on this API and pointers to "v4l" programs may be found - at . - - To compile this driver as a module, choose M here: the - module will be called pwc. +source "drivers/media/video/pwc/Kconfig" endmenu # V4L USB devices diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index 289ef1abf..14219217d 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -66,9 +66,6 @@ obj-$(CONFIG_VIDEO_CX25840) += cx25840/ obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o -et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o -zc0301-objs := zc0301_core.o zc0301_pas202bcb.o - obj-$(CONFIG_USB_DABUSB) += dabusb.o obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_USB_OV511) += ov511.o -- cgit v1.2.3 From ed85a3383b5c666eb560d2668f43ca85ed925e72 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 29 Mar 2006 23:02:51 +0200 Subject: Reverted sliced VBI API changes made in videodev2.h. From: Hans Verkuil The sliced VBI defines added in videodev2.h are removed. One define changed the meaning of a old define, thus breaking existing apps, another used more than 16 bits (the max size is u16), and many types were added that were untested and whose payload was undocumented. New types should be shown to exist 'in the wild' and the payload format must be documented in the V4L2 API spec. Signed-off-by: Hans Verkuil --- linux/drivers/media/video/cx25840/cx25840-vbi.c | 6 +++--- linux/drivers/media/video/saa7115.c | 6 +++--- linux/drivers/media/video/tvp5150.c | 20 ++++++++++---------- 3 files changed, 16 insertions(+), 16 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx25840/cx25840-vbi.c b/linux/drivers/media/video/cx25840/cx25840-vbi.c index e16be5b2e..658fcb260 100644 --- a/linux/drivers/media/video/cx25840/cx25840-vbi.c +++ b/linux/drivers/media/video/cx25840/cx25840-vbi.c @@ -153,7 +153,7 @@ int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg) case VIDIOC_G_FMT: { static u16 lcr2vbi[] = { - 0, V4L2_SLICED_TELETEXT_PAL_B, 0, /* 1 */ + 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ 0, V4L2_SLICED_WSS_625, 0, /* 4 */ V4L2_SLICED_CAPTION_525, /* 6 */ 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ @@ -233,7 +233,7 @@ int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg) for (i = 7; i <= 23; i++) { for (x = 0; x <= 1; x++) { switch (svbi->service_lines[1-x][i]) { - case V4L2_SLICED_TELETEXT_PAL_B: + case V4L2_SLICED_TELETEXT_B: lcr[i] |= 1 << (4 * x); break; case V4L2_SLICED_WSS_625: @@ -284,7 +284,7 @@ int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg) switch (id2) { case 1: - id2 = V4L2_SLICED_TELETEXT_PAL_B; + id2 = V4L2_SLICED_TELETEXT_B; break; case 4: id2 = V4L2_SLICED_WSS_625; diff --git a/linux/drivers/media/video/saa7115.c b/linux/drivers/media/video/saa7115.c index 8992de807..1ecfacea5 100644 --- a/linux/drivers/media/video/saa7115.c +++ b/linux/drivers/media/video/saa7115.c @@ -865,7 +865,7 @@ static void saa7115_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo case 0: lcr[i] |= 0xf << (4 * x); break; - case V4L2_SLICED_TELETEXT_PAL_B: + case V4L2_SLICED_TELETEXT_B: lcr[i] |= 1 << (4 * x); break; case V4L2_SLICED_CAPTION_525: @@ -894,7 +894,7 @@ static void saa7115_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo static int saa7115_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) { static u16 lcr2vbi[] = { - 0, V4L2_SLICED_TELETEXT_PAL_B, 0, /* 1 */ + 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ 0, V4L2_SLICED_CAPTION_525, /* 4 */ V4L2_SLICED_WSS_625, 0, /* 5 */ V4L2_SLICED_VPS, 0, 0, 0, 0, /* 7 */ @@ -1059,7 +1059,7 @@ static void saa7115_decode_vbi_line(struct i2c_client *client, /* decode payloads */ switch (id2) { case 1: - vbi->type = V4L2_SLICED_TELETEXT_PAL_B; + vbi->type = V4L2_SLICED_TELETEXT_B; break; case 4: if (!saa7115_odd_parity(p[0]) || !saa7115_odd_parity(p[1])) diff --git a/linux/drivers/media/video/tvp5150.c b/linux/drivers/media/video/tvp5150.c index 8a6c5c0d0..1a2cc873e 100644 --- a/linux/drivers/media/video/tvp5150.c +++ b/linux/drivers/media/video/tvp5150.c @@ -532,37 +532,37 @@ struct i2c_vbi_ram_value { static struct i2c_vbi_ram_value vbi_ram_default[] = { {0x010, /* Teletext, SECAM, WST System A */ - {V4L2_SLICED_TELETEXT_SECAM,6,23,1}, + {0,6,23,1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26, 0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 } }, {0x030, /* Teletext, PAL, WST System B */ - {V4L2_SLICED_TELETEXT_PAL_B,6,22,1}, + {V4L2_SLICED_TELETEXT_B,6,22,1}, { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b, 0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 } }, {0x050, /* Teletext, PAL, WST System C */ - {V4L2_SLICED_TELETEXT_PAL_C,6,22,1}, + {0,6,22,1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, 0xa6, 0x98, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } }, {0x070, /* Teletext, NTSC, WST System B */ - {V4L2_SLICED_TELETEXT_NTSC_B,10,21,1}, + {0,10,21,1}, { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x23, 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } }, {0x090, /* Tetetext, NTSC NABTS System C */ - {V4L2_SLICED_TELETEXT_NTSC_C,10,21,1}, + {0,10,21,1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00 } }, {0x0b0, /* Teletext, NTSC-J, NABTS System D */ - {V4L2_SLICED_TELETEXT_NTSC_D,10,21,1}, + {0,10,21,1}, { 0xaa, 0xaa, 0xff, 0xff, 0xa7, 0x2e, 0x20, 0x23, 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } }, {0x0d0, /* Closed Caption, PAL/SECAM */ - {V4L2_SLICED_CAPTION_625,22,22,1}, + {0,22,22,1}, { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, 0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } }, @@ -577,17 +577,17 @@ static struct i2c_vbi_ram_value vbi_ram_default[] = 0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 } }, {0x130, /* Wide Screen Signal, NTSC C */ - {V4L2_SLICED_WSS_525,20,20,1}, + {0,20,20,1}, { 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43, 0x69, 0x7c, 0x08, 0x00, 0x00, 0x00, 0x39, 0x00 } }, {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */ - {V4l2_SLICED_VITC_625,6,22,0}, + {0,6,22,0}, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, 0xa6, 0x85, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } }, {0x170, /* Vertical Interval Timecode (VITC), NTSC */ - {V4l2_SLICED_VITC_525,10,20,0}, + {0,10,20,0}, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, 0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } }, -- cgit v1.2.3 From f55b2be52b11947513cc6fc58b8ec2e5b3bea3e3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 29 Mar 2006 18:06:26 -0300 Subject: Kernel-sync changes to -git From: Mauro Carvalho Chehab kernel-sync Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/cpia2/cpia2.h | 2 +- linux/drivers/media/video/saa711x.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cpia2/cpia2.h b/linux/drivers/media/video/cpia2/cpia2.h index a53056c82..7480c1827 100644 --- a/linux/drivers/media/video/cpia2/cpia2.h +++ b/linux/drivers/media/video/cpia2/cpia2.h @@ -462,7 +462,7 @@ int cpia2_init_camera(struct camera_data *cam); int cpia2_allocate_buffers(struct camera_data *cam); void cpia2_free_buffers(struct camera_data *cam); long cpia2_read(struct camera_data *cam, - char *buf, unsigned long count, int noblock); + char __user *buf, unsigned long count, int noblock); unsigned int cpia2_poll(struct camera_data *cam, struct file *filp, poll_table *wait); int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma); diff --git a/linux/drivers/media/video/saa711x.c b/linux/drivers/media/video/saa711x.c index f51cd9442..59425506e 100644 --- a/linux/drivers/media/video/saa711x.c +++ b/linux/drivers/media/video/saa711x.c @@ -46,7 +46,6 @@ MODULE_AUTHOR("Dave Perks, Jose Ignacio Gijon, Joerg Heckenbach, Mark McClelland MODULE_LICENSE("GPL"); #include -#include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) #include "i2c-compat.h" #endif -- cgit v1.2.3 From 35c5ecc171a4a76c0a7a35e23e0ac2d25caa91ae Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Thu, 30 Mar 2006 03:05:16 +0200 Subject: Configurable dma buffer size for saa7146-based budget dvb cards From: Ingo Schneider - Issue a warning when more than 80% of the DMA buffer is being used (probably due to bad IRQ latency). Warnings are rate-limited. - Introduce a new parameter 'bufsize' (in KByte) which increases the default DMA buffer of 188 KByte up to 1410 KByte (Activy: 564 KByte). Signed-off-by: Ingo Schneider Signed-off-by: Oliver Endriss --- linux/drivers/media/dvb/ttpci/budget-core.c | 78 ++++++++++++++++++++++------ linux/drivers/media/dvb/ttpci/budget-patch.c | 24 ++++----- linux/drivers/media/dvb/ttpci/budget.h | 13 ++--- 3 files changed, 82 insertions(+), 33 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/ttpci/budget-core.c b/linux/drivers/media/dvb/ttpci/budget-core.c index 633e68c34..ea2066d46 100644 --- a/linux/drivers/media/dvb/ttpci/budget-core.c +++ b/linux/drivers/media/dvb/ttpci/budget-core.c @@ -39,9 +39,21 @@ #include "budget.h" #include "ttpci-eeprom.h" +#define TS_WIDTH (2 * TS_SIZE) +#define TS_WIDTH_ACTIVY TS_SIZE +#define TS_HEIGHT_MASK 0xf00 +#define TS_HEIGHT_MASK_ACTIVY 0xc00 +#define TS_MIN_BUFSIZE_K 188 +#define TS_MAX_BUFSIZE_K 1410 +#define TS_MAX_BUFSIZE_K_ACTIVY 564 +#define BUFFER_WARNING_WAIT (30*HZ) + int budget_debug; +static int dma_buffer_size = TS_MIN_BUFSIZE_K; module_param_named(debug, budget_debug, int, 0644); +module_param_named(bufsize, dma_buffer_size, int, 0444); MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off)."); +MODULE_PARM_DESC(bufsize, "DMA buffer size in KB, default: 188, min: 188, max: 1410 (Activy: 564)"); /**************************************************************************** * TT budget / WinTV Nova @@ -70,11 +82,10 @@ static int start_ts_capture(struct budget *budget) saa7146_write(dev, MC1, MASK_20); // DMA3 off - memset(budget->grabbing, 0x00, TS_HEIGHT * TS_WIDTH); + memset(budget->grabbing, 0x00, budget->buffer_size); saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); - budget->tsf = 0xff; budget->ttbp = 0; /* @@ -115,16 +126,12 @@ static int start_ts_capture(struct budget *budget) saa7146_write(dev, BASE_ODD3, 0); saa7146_write(dev, BASE_EVEN3, 0); - saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT); + saa7146_write(dev, PROT_ADDR3, budget->buffer_size); saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90); - if (budget->card->type == BUDGET_FS_ACTIVY) { - saa7146_write(dev, PITCH3, TS_WIDTH / 2); - saa7146_write(dev, NUM_LINE_BYTE3, ((TS_HEIGHT * 2) << 16) | (TS_WIDTH / 2)); - } else { - saa7146_write(dev, PITCH3, TS_WIDTH); - saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH); - } + saa7146_write(dev, PITCH3, budget->buffer_width); + saa7146_write(dev, NUM_LINE_BYTE3, + (budget->buffer_height << 16) | budget->buffer_width); saa7146_write(dev, MC2, (MASK_04 | MASK_20)); @@ -141,11 +148,12 @@ static void vpeirq(unsigned long data) u8 *mem = (u8 *) (budget->grabbing); u32 olddma = budget->ttbp; u32 newdma = saa7146_read(budget->dev, PCI_VDP3); + u32 count; /* nearest lower position divisible by 188 */ newdma -= newdma % 188; - if (newdma >= TS_BUFLEN) + if (newdma >= budget->buffer_size) return; budget->ttbp = newdma; @@ -154,11 +162,24 @@ static void vpeirq(unsigned long data) return; if (newdma > olddma) { /* no wraparound, dump olddma..newdma */ - dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, (newdma - olddma) / 188); + count = newdma - olddma; + dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188); } else { /* wraparound, dump olddma..buflen and 0..newdma */ - dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, (TS_BUFLEN - olddma) / 188); + count = budget->buffer_size - olddma; + dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188); + count += newdma; dvb_dmx_swfilter_packets(&budget->demux, mem, newdma / 188); } + + if (count > budget->buffer_warning_threshold) + budget->buffer_warnings++; + + if (budget->buffer_warnings && time_after(jiffies, budget->buffer_warning_time)) { + printk("%s %s: used %d times >80%% of buffer (%u bytes now)\n", + budget->dev->name, __FUNCTION__, budget->buffer_warnings, count); + budget->buffer_warning_time = jiffies + BUFFER_WARNING_WAIT; + budget->buffer_warnings = 0; + } } @@ -341,9 +362,10 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, struct saa7146_pci_extension_data *info, struct module *owner) { - int length = TS_WIDTH * TS_HEIGHT; int ret = 0; struct budget_info *bi = info->ext_priv; + int max_bufsize; + int height_mask; memset(budget, 0, sizeof(struct budget)); @@ -352,6 +374,32 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, budget->card = bi; budget->dev = (struct saa7146_dev *) dev; + if (budget->card->type == BUDGET_FS_ACTIVY) { + budget->buffer_width = TS_WIDTH_ACTIVY; + max_bufsize = TS_MAX_BUFSIZE_K_ACTIVY; + height_mask = TS_HEIGHT_MASK_ACTIVY; + } else { + budget->buffer_width = TS_WIDTH; + max_bufsize = TS_MAX_BUFSIZE_K; + height_mask = TS_HEIGHT_MASK; + } + + if (dma_buffer_size < TS_MIN_BUFSIZE_K) + dma_buffer_size = TS_MIN_BUFSIZE_K; + else if (dma_buffer_size > max_bufsize) + dma_buffer_size = max_bufsize; + + budget->buffer_height = dma_buffer_size * 1024 / budget->buffer_width; + budget->buffer_height &= height_mask; + budget->buffer_size = budget->buffer_height * budget->buffer_width; + budget->buffer_warning_threshold = budget->buffer_size * 80/100; + budget->buffer_warnings = 0; + budget->buffer_warning_time = jiffies; + + dprintk(2, "%s: width = %d, height = %d\n", + budget->dev->name, budget->buffer_width, budget->buffer_height); + printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size); + dvb_register_adapter(&budget->dvb_adapter, budget->card->name, owner); /* set dd1 stream a & b */ @@ -392,7 +440,7 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, ttpci_eeprom_parse_mac(&budget->i2c_adap, budget->dvb_adapter.proposed_mac); if (NULL == - (budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, length, &budget->pt))) { + (budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, budget->buffer_size, &budget->pt))) { ret = -ENOMEM; goto err; } diff --git a/linux/drivers/media/dvb/ttpci/budget-patch.c b/linux/drivers/media/dvb/ttpci/budget-patch.c index 5ea95da4f..25e40fc1f 100644 --- a/linux/drivers/media/dvb/ttpci/budget-patch.c +++ b/linux/drivers/media/dvb/ttpci/budget-patch.c @@ -577,6 +577,17 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // Set RPS1 Address register to point to RPS code (r108 p42) saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); + + if (!(budget = kmalloc (sizeof(struct budget_patch), GFP_KERNEL))) + return -ENOMEM; + + dprintk(2, "budget: %p\n", budget); + + if ((err = ttpci_budget_init (budget, dev, info, THIS_MODULE))) { + kfree (budget); + return err; + } + // Set Source Line Counter Threshold, using BRS (rCC p43) // It generates HS event every TS_HEIGHT lines // this is related to TS_WIDTH set in register @@ -585,24 +596,13 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte //,then RPS_THRESH1 // should be set to trigger every TS_HEIGHT (512) lines. // - saa7146_write(dev, RPS_THRESH1, (TS_HEIGHT*1) | MASK_12 ); + saa7146_write(dev, RPS_THRESH1, budget->buffer_height | MASK_12 ); // saa7146_write(dev, RPS_THRESH0, ((TS_HEIGHT/2)<<16) |MASK_28| (TS_HEIGHT/2) |MASK_12 ); // Enable RPS1 (rFC p33) saa7146_write(dev, MC1, (MASK_13 | MASK_29)); - if (!(budget = kmalloc (sizeof(struct budget_patch), GFP_KERNEL))) - return -ENOMEM; - - dprintk(2, "budget: %p\n", budget); - - if ((err = ttpci_budget_init (budget, dev, info, THIS_MODULE))) { - kfree (budget); - return err; - } - - dev->ext_priv = budget; budget->dvb_adapter.priv = budget; diff --git a/linux/drivers/media/dvb/ttpci/budget.h b/linux/drivers/media/dvb/ttpci/budget.h index 30f74b416..c5c226d4f 100644 --- a/linux/drivers/media/dvb/ttpci/budget.h +++ b/linux/drivers/media/dvb/ttpci/budget.h @@ -70,7 +70,13 @@ struct budget { int ci_present; int video_port; - u8 tsf; + u32 buffer_width; + u32 buffer_height; + u32 buffer_size; + u32 buffer_warning_threshold; + u32 buffer_warnings; + unsigned long buffer_warning_time; + u32 ttbp; int feeding; @@ -91,11 +97,6 @@ static struct saa7146_pci_extension_data x_var = { \ .ext_priv = &x_var ## _info, \ .ext = &budget_extension }; -#define TS_WIDTH (376) -#define TS_HEIGHT (512) -#define TS_BUFLEN (TS_WIDTH*TS_HEIGHT) -#define TS_MAX_PACKETS (TS_BUFLEN/TS_SIZE) - #define BUDGET_TT 0 #define BUDGET_TT_HW_DISEQC 1 #define BUDGET_PATCH 3 -- cgit v1.2.3 From 04b5c3627a782fb9773d5a538f2a4121ea546313 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Thu, 30 Mar 2006 03:46:12 +0200 Subject: fix typo in comment From: Adrian Bunk This patch fixes a typo in a comment. Signed-off-by: Adrian Bunk Signed-off-by: Oliver Endriss --- linux/drivers/media/dvb/ttpci/av7110.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/ttpci/av7110.c b/linux/drivers/media/dvb/ttpci/av7110.c index 87741cf17..cda21cab8 100644 --- a/linux/drivers/media/dvb/ttpci/av7110.c +++ b/linux/drivers/media/dvb/ttpci/av7110.c @@ -2123,7 +2123,7 @@ static int frontend_init(struct av7110 *av7110) read_pwm(av7110)); break; case 0x0003: - /* Haupauge DVB-C 2.1 VES1820/ALPS TDBE2 */ + /* Hauppauge DVB-C 2.1 VES1820/ALPS TDBE2 */ av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); break; -- cgit v1.2.3 From 3d2479ff04112f41bd9a231f1aed9e96fdd8d07d Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Thu, 30 Mar 2006 09:31:48 +0200 Subject: new module parameter 'tv_standard' (dvb-ttpci driver) From: C.Y.M This attached patch was originally proposed by Anssi Hannula to the dvb-kernel tree a while ago. I have recreated the patch for v4l-dvb. It will allow the user to choose the default broadcast mode when using the ttpci driver. NTSC users need to only add the following line to modprobe.d: options dvb-ttpci tv_standard=1 PAL users will not need to change anything, for this will be the default. Signed-off-by: C.Y.M Signed-off-by: Oliver Endriss --- linux/drivers/media/dvb/ttpci/av7110.c | 6 ++++++ linux/drivers/media/dvb/ttpci/av7110_av.c | 2 -- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/ttpci/av7110.c b/linux/drivers/media/dvb/ttpci/av7110.c index cda21cab8..940b05439 100644 --- a/linux/drivers/media/dvb/ttpci/av7110.c +++ b/linux/drivers/media/dvb/ttpci/av7110.c @@ -87,6 +87,7 @@ static int volume = 255; static int budgetpatch; static int wss_cfg_4_3 = 0x4008; static int wss_cfg_16_9 = 0x0007; +static int tv_standard; module_param_named(debug, av7110_debug, int, 0644); MODULE_PARM_DESC(debug, "debug level (bitmask, default 0)"); @@ -109,6 +110,8 @@ module_param(wss_cfg_4_3, int, 0444); MODULE_PARM_DESC(wss_cfg_4_3, "WSS 4:3 - default 0x4008 - bit 15: disable, 14: burst mode, 13..0: wss data"); module_param(wss_cfg_16_9, int, 0444); MODULE_PARM_DESC(wss_cfg_16_9, "WSS 16:9 - default 0x0007 - bit 15: disable, 14: burst mode, 13..0: wss data"); +module_param(tv_standard, int, 0444); +MODULE_PARM_DESC(tv_standard, "TV standard: 0 PAL (default), 1 NTSC"); static void restart_feeds(struct av7110 *av7110); @@ -2543,6 +2546,9 @@ static int __devinit av7110_attach(struct saa7146_dev* dev, av7110->osdwin = 1; mutex_init(&av7110->osd_mutex); + /* TV standard */ + av7110->vidmode = tv_standard == 1 ? VIDEO_MODE_NTSC : VIDEO_MODE_PAL; + /* ARM "watchdog" */ init_waitqueue_head(&av7110->arm_wait); av7110->arm_thread = NULL; diff --git a/linux/drivers/media/dvb/ttpci/av7110_av.c b/linux/drivers/media/dvb/ttpci/av7110_av.c index 400facec7..2eff09f63 100644 --- a/linux/drivers/media/dvb/ttpci/av7110_av.c +++ b/linux/drivers/media/dvb/ttpci/av7110_av.c @@ -1479,8 +1479,6 @@ int av7110_av_init(struct av7110 *av7110) void (*play[])(u8 *, int, void *) = { play_audio_cb, play_video_cb }; int i, ret; - av7110->vidmode = VIDEO_MODE_PAL; - for (i = 0; i < 2; i++) { struct ipack *ipack = av7110->ipack + i; -- cgit v1.2.3 From 5b513ea826a257bfa13e6083edd93815242f3730 Mon Sep 17 00:00:00 2001 From: "xyzzy@t2.domain.actdsltmp" Date: Thu, 30 Mar 2006 19:53:32 +0100 Subject: Fix memory leak in dvr open From: Trent Piepho The dvr device could be opened multiple times simultaneously in O_RDONLY mode. Each open after the first would allocate a new dvr buffer (1880 KB) and leak the old buffer. The first close would de-allocate the dvr buffer and cause all other open dvrs to stop working. This patch allows only a single O_RDONLY open of the drv device, as per the API specification. Multiple O_WRONLY opens are still allowed and don't appear to cause any problems. Signed-off-by: Trent Piepho Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/dvb-core/dmxdev.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-core/dmxdev.c b/linux/drivers/media/dvb/dvb-core/dmxdev.c index 09e96e9dd..04578df3f 100644 --- a/linux/drivers/media/dvb/dvb-core/dmxdev.c +++ b/linux/drivers/media/dvb/dvb-core/dmxdev.c @@ -141,12 +141,18 @@ static int dvb_dvr_open(struct inode *inode, struct file *file) } if ((file->f_flags & O_ACCMODE) == O_RDONLY) { - void *mem = vmalloc(DVR_BUFFER_SIZE); + void *mem; + if (!dvbdev->readers) { + mutex_unlock(&dmxdev->mutex); + return -EBUSY; + } + mem = vmalloc(DVR_BUFFER_SIZE); if (!mem) { mutex_unlock(&dmxdev->mutex); return -ENOMEM; } dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE); + dvbdev->readers--; } if ((file->f_flags & O_ACCMODE) == O_WRONLY) { @@ -184,6 +190,7 @@ static int dvb_dvr_release(struct inode *inode, struct file *file) dmxdev->dvr_orig_fe); } if ((file->f_flags & O_ACCMODE) == O_RDONLY) { + dvbdev->readers++; if (dmxdev->dvr_buffer.data) { void *mem = dmxdev->dvr_buffer.data; mb(); @@ -1029,8 +1036,7 @@ static struct file_operations dvb_dvr_fops = { static struct dvb_device dvbdev_dvr = { .priv = NULL, - .users = 1, - .writers = 1, + .readers = 1, .fops = &dvb_dvr_fops }; -- cgit v1.2.3 From ec2c10b2d67b9015a2f4e174d482a4b076d38507 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Thu, 30 Mar 2006 19:53:35 +0100 Subject: Fix budget-av CAM reset From: Andrew de Quincey Unfortunately on the budget-av board, the CAM reset line is tied to the frontend reset line, so resetting the CAM also zaps the frontend. This breaks the tda1004x at least, and causes it to fail to tune until the budget-av module is reloaded. This patch adds an exported function to dvb_frontend that allows a card to forcibly reinitialise a frontend. The budget-av now does this on CAM reset, which corrects this problem. Note: other CA interface implementations (e.g. budget-ci) do not need this since they do not tie the CAM reset line to the frontend reset line. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 18 +++++++++++++++++- linux/drivers/media/dvb/dvb-core/dvb_frontend.h | 2 ++ linux/drivers/media/dvb/frontends/tda1004x.c | 12 ------------ linux/drivers/media/dvb/ttpci/budget-av.c | 13 ++++++++----- 4 files changed, 27 insertions(+), 18 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 55a91bb18..875033330 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -105,6 +105,7 @@ struct dvb_frontend_private { fe_status_t status; unsigned long tune_mode_flags; unsigned int delay; + unsigned int reinitialise; /* swzigzag values */ unsigned int state; @@ -121,6 +122,7 @@ struct dvb_frontend_private { unsigned int check_wrapped; }; +static void dvb_frontend_wakeup(struct dvb_frontend *fe); static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status) { @@ -213,6 +215,15 @@ static void dvb_frontend_init(struct dvb_frontend *fe) fe->ops->init(fe); } +void dvb_frontend_reinitialise(struct dvb_frontend *fe) +{ + struct dvb_frontend_private *fepriv = fe->frontend_priv; + + fepriv->reinitialise = 1; + dvb_frontend_wakeup(fe); +} +EXPORT_SYMBOL(dvb_frontend_reinitialise); + static void dvb_frontend_swzigzag_update_delay(struct dvb_frontend_private *fepriv, int locked) { int q2; @@ -505,8 +516,8 @@ static int dvb_frontend_thread(void *data) fepriv->quality = 0; fepriv->delay = 3*HZ; fepriv->status = 0; - dvb_frontend_init(fe); fepriv->wakeup = 0; + fepriv->reinitialise = 1; while (1) { up(&fepriv->sem); /* is locked when we enter the thread... */ @@ -530,6 +541,11 @@ static int dvb_frontend_thread(void *data) if (down_interruptible(&fepriv->sem)) break; + if (fepriv->reinitialise) { + dvb_frontend_init(fe); + fepriv->reinitialise = 0; + } + /* do an iteration of the tuning loop */ if (fe->ops->tune) { /* have we been asked to retune? */ diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h index d5aee5ad6..5926a3b74 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -112,6 +112,8 @@ extern int dvb_register_frontend(struct dvb_adapter* dvb, extern int dvb_unregister_frontend(struct dvb_frontend* fe); +extern void dvb_frontend_reinitialise(struct dvb_frontend *fe); + extern void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec); extern s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime); diff --git a/linux/drivers/media/dvb/frontends/tda1004x.c b/linux/drivers/media/dvb/frontends/tda1004x.c index 8e8df7b4c..b83dafa4e 100644 --- a/linux/drivers/media/dvb/frontends/tda1004x.c +++ b/linux/drivers/media/dvb/frontends/tda1004x.c @@ -52,7 +52,6 @@ struct tda1004x_state { struct dvb_frontend frontend; /* private demod data */ - u8 initialised; enum tda1004x_demod demod_type; }; @@ -594,9 +593,6 @@ static int tda10045_init(struct dvb_frontend* fe) dprintk("%s\n", __FUNCTION__); - if (state->initialised) - return 0; - if (tda10045_fwupload(fe)) { printk("tda1004x: firmware upload failed\n"); return -EIO; @@ -626,7 +622,6 @@ static int tda10045_init(struct dvb_frontend* fe) tda1004x_write_mask(state, 0x1f, 0x01, state->config->invert_oclk); - state->initialised = 1; return 0; } @@ -635,9 +630,6 @@ static int tda10046_init(struct dvb_frontend* fe) struct tda1004x_state* state = fe->demodulator_priv; dprintk("%s\n", __FUNCTION__); - if (state->initialised) - return 0; - if (tda10046_fwupload(fe)) { printk("tda1004x: firmware upload failed\n"); return -EIO; @@ -697,7 +689,6 @@ static int tda10046_init(struct dvb_frontend* fe) // tda1004x_write_mask(state, 0x50, 0x80, 0x80); // handle out of guard echoes tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7); - state->initialised = 1; return 0; } @@ -1207,7 +1198,6 @@ static int tda1004x_sleep(struct dvb_frontend* fe) tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1); break; } - state->initialised = 0; return 0; } @@ -1271,7 +1261,6 @@ struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, state->config = config; state->i2c = i2c; memcpy(&state->ops, &tda10045_ops, sizeof(struct dvb_frontend_ops)); - state->initialised = 0; state->demod_type = TDA1004X_DEMOD_TDA10045; /* check if the demod is there */ @@ -1330,7 +1319,6 @@ struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, state->config = config; state->i2c = i2c; memcpy(&state->ops, &tda10046_ops, sizeof(struct dvb_frontend_ops)); - state->initialised = 0; state->demod_type = TDA1004X_DEMOD_TDA10046; /* check if the demod is there */ diff --git a/linux/drivers/media/dvb/ttpci/budget-av.c b/linux/drivers/media/dvb/ttpci/budget-av.c index 9dd4745f5..8efe3ce5f 100644 --- a/linux/drivers/media/dvb/ttpci/budget-av.c +++ b/linux/drivers/media/dvb/ttpci/budget-av.c @@ -60,11 +60,11 @@ struct budget_av { struct dvb_ca_en50221 ca; }; -/* GPIO CI Connections: - * 0 - Vcc/Reset (Reset is controlled by capacitor) - * 1 - Attribute Memory - * 2 - Card Enable (Active Low) - * 3 - Card Detect +/* GPIO Connections: + * 0 - Vcc/Reset (Reset is controlled by capacitor). Resets the frontend *AS WELL*! + * 1 - CI memory select 0=>IO memory, 1=>Attribute Memory + * 2 - CI Card Enable (Active Low) + * 3 - CI Card Detect */ /**************************************************************************** @@ -214,6 +214,9 @@ static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) while (--timeout > 0 && ciintf_read_attribute_mem(ca, slot, 0) != 0x1d) msleep(100); + /* reinitialise the frontend */ + dvb_frontend_reinitialise(budget_av->budget.dvb_frontend); + if (timeout <= 0) { printk(KERN_ERR "budget-av: cam reset failed (timeout).\n"); -- cgit v1.2.3 From f11510a3383a92e9158e7174c335e20707e5bc51 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 31 Mar 2006 00:50:34 +0200 Subject: Fix msp3400c and bttv stereo/mono/bilingual detection/handling From: Hans Verkuil - msp3400c did not detect the second carrier, thus being always mono. - properly mute the msp3400c while detecting the carrier. - fix checks on the presence of scart2/3 inputs and scart 2 output. - implement proper audio mode fallbacks for msp3400c/d, identical to the way msp3400g works. - MODE_STEREO no longer produces dual languages when set for a bilingual transmission, instead it falls back to LANG1. Use LANG1_LANG2 to hear both languages of a bilingual transmission. This is much more intuitive for the user and is in accordance with the preferred usage in the v4l2 specification. - bttv tried to implement v4l2 calls with v4l1 calls to the i2c devices, completely mangling the audmode/rxsubchans handling. v4l2 calls now do v4l2 calls to the i2c devices. - fixed broken i2c_vidiocschan in bttv. - add start/end lines to LOG_STATUS. Signed-off-by: Hans Verkuil --- linux/drivers/media/video/bt8xx/bttv-driver.c | 147 +++++++++++++------------- linux/drivers/media/video/msp3400-driver.c | 54 ++++------ linux/drivers/media/video/msp3400-driver.h | 5 +- linux/drivers/media/video/msp3400-kthreads.c | 71 +++++++------ 4 files changed, 138 insertions(+), 139 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/bt8xx/bttv-driver.c b/linux/drivers/media/video/bt8xx/bttv-driver.c index 0e1aae5d0..1e13ee966 100644 --- a/linux/drivers/media/video/bt8xx/bttv-driver.c +++ b/linux/drivers/media/video/bt8xx/bttv-driver.c @@ -1044,14 +1044,12 @@ audio_input(struct bttv *btv, int input) static void i2c_vidiocschan(struct bttv *btv) { - struct video_channel c; + v4l2_std_id std = bttv_tvnorms[btv->tvnorm].v4l2_id; - memset(&c,0,sizeof(c)); - c.norm = btv->tvnorm; - c.channel = btv->input; - bttv_call_i2c_clients(btv,VIDIOCSCHAN,&c); + bttv_call_i2c_clients(btv, VIDIOC_S_INPUT, &btv->input); + bttv_call_i2c_clients(btv, VIDIOC_S_STD, &std); if (btv->c.type == BTTV_BOARD_VOODOOTV_FM) - bttv_tda9880_setnorm(btv,c.norm); + bttv_tda9880_setnorm(btv,btv->tvnorm); } static int @@ -1210,11 +1208,27 @@ static int get_control(struct bttv *btv, struct v4l2_control *c) break; if (i == BTTV_CTLS) return -EINVAL; - if (i >= 4 && i <= 8) { + if (btv->audio_hook && i >= 4 && i <= 8) { memset(&va,0,sizeof(va)); - bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); - if (btv->audio_hook) - btv->audio_hook(btv,&va,0); + btv->audio_hook(btv,&va,0); + switch (c->id) { + case V4L2_CID_AUDIO_MUTE: + c->value = (VIDEO_AUDIO_MUTE & va.flags) ? 1 : 0; + break; + case V4L2_CID_AUDIO_VOLUME: + c->value = va.volume; + break; + case V4L2_CID_AUDIO_BALANCE: + c->value = va.balance; + break; + case V4L2_CID_AUDIO_BASS: + c->value = va.bass; + break; + case V4L2_CID_AUDIO_TREBLE: + c->value = va.treble; + break; + } + return 0; } switch (c->id) { case V4L2_CID_BRIGHTNESS: @@ -1231,19 +1245,11 @@ static int get_control(struct bttv *btv, struct v4l2_control *c) break; case V4L2_CID_AUDIO_MUTE: - c->value = (VIDEO_AUDIO_MUTE & va.flags) ? 1 : 0; - break; case V4L2_CID_AUDIO_VOLUME: - c->value = va.volume; - break; case V4L2_CID_AUDIO_BALANCE: - c->value = va.balance; - break; case V4L2_CID_AUDIO_BASS: - c->value = va.bass; - break; case V4L2_CID_AUDIO_TREBLE: - c->value = va.treble; + bttv_call_i2c_clients(btv,VIDIOC_G_CTRL,c); break; case V4L2_CID_PRIVATE_CHROMA_AGC: @@ -1295,11 +1301,35 @@ static int set_control(struct bttv *btv, struct v4l2_control *c) break; if (i == BTTV_CTLS) return -EINVAL; - if (i >= 4 && i <= 8) { + if (btv->audio_hook && i >= 4 && i <= 8) { memset(&va,0,sizeof(va)); - bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); - if (btv->audio_hook) - btv->audio_hook(btv,&va,0); + btv->audio_hook(btv,&va,0); + switch (c->id) { + case V4L2_CID_AUDIO_MUTE: + if (c->value) { + va.flags |= VIDEO_AUDIO_MUTE; + audio_mute(btv, 1); + } else { + va.flags &= ~VIDEO_AUDIO_MUTE; + audio_mute(btv, 0); + } + break; + + case V4L2_CID_AUDIO_VOLUME: + va.volume = c->value; + break; + case V4L2_CID_AUDIO_BALANCE: + va.balance = c->value; + break; + case V4L2_CID_AUDIO_BASS: + va.bass = c->value; + break; + case V4L2_CID_AUDIO_TREBLE: + va.treble = c->value; + break; + } + btv->audio_hook(btv,&va,1); + return 0; } switch (c->id) { case V4L2_CID_BRIGHTNESS: @@ -1315,26 +1345,13 @@ static int set_control(struct bttv *btv, struct v4l2_control *c) bt848_sat(btv,c->value); break; case V4L2_CID_AUDIO_MUTE: - if (c->value) { - va.flags |= VIDEO_AUDIO_MUTE; - audio_mute(btv, 1); - } else { - va.flags &= ~VIDEO_AUDIO_MUTE; - audio_mute(btv, 0); - } - break; - + audio_mute(btv, c->value); + /* fall through */ case V4L2_CID_AUDIO_VOLUME: - va.volume = c->value; - break; case V4L2_CID_AUDIO_BALANCE: - va.balance = c->value; - break; case V4L2_CID_AUDIO_BASS: - va.bass = c->value; - break; case V4L2_CID_AUDIO_TREBLE: - va.treble = c->value; + bttv_call_i2c_clients(btv,VIDIOC_S_CTRL,c); break; case V4L2_CID_PRIVATE_CHROMA_AGC: @@ -1390,11 +1407,6 @@ static int set_control(struct bttv *btv, struct v4l2_control *c) default: return -EINVAL; } - if (i >= 4 && i <= 8) { - bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va); - if (btv->audio_hook) - btv->audio_hook(btv,&va,1); - } return 0; } @@ -1853,33 +1865,26 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) return -EINVAL; mutex_lock(&btv->lock); memset(t,0,sizeof(*t)); + t->rxsubchans = V4L2_TUNER_SUB_MONO; + bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t); strcpy(t->name, "Television"); - t->type = V4L2_TUNER_ANALOG_TV; t->capability = V4L2_TUNER_CAP_NORM; - t->rxsubchans = V4L2_TUNER_SUB_MONO; + t->type = V4L2_TUNER_ANALOG_TV; if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) t->signal = 0xffff; - { - struct video_tuner tuner; - - memset(&tuner, 0, sizeof (tuner)); - tuner.rangehigh = 0xffffffffUL; - bttv_call_i2c_clients(btv, VIDIOCGTUNER, &tuner); - t->rangelow = tuner.rangelow; - t->rangehigh = tuner.rangehigh; - } - { + + if (btv->audio_hook) { /* Hmmm ... */ struct video_audio va; memset(&va, 0, sizeof(struct video_audio)); - bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); - if (btv->audio_hook) - btv->audio_hook(btv,&va,0); + btv->audio_hook(btv,&va,0); + t->audmode = V4L2_TUNER_MODE_MONO; + t->rxsubchans = V4L2_TUNER_SUB_MONO; if(va.mode & VIDEO_SOUND_STEREO) { - t->audmode = V4L2_TUNER_MODE_STEREO; - t->rxsubchans |= V4L2_TUNER_SUB_STEREO; + t->audmode = V4L2_TUNER_MODE_STEREO; + t->rxsubchans = V4L2_TUNER_SUB_STEREO; } - if(va.mode & VIDEO_SOUND_LANG1) { + if(va.mode & VIDEO_SOUND_LANG2) { t->audmode = V4L2_TUNER_MODE_LANG1; t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; @@ -1898,10 +1903,10 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) if (0 != t->index) return -EINVAL; mutex_lock(&btv->lock); - { + bttv_call_i2c_clients(btv, VIDIOC_S_TUNER, t); + if (btv->audio_hook) { struct video_audio va; memset(&va, 0, sizeof(struct video_audio)); - bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); if (t->audmode == V4L2_TUNER_MODE_MONO) va.mode = VIDEO_SOUND_MONO; else if (t->audmode == V4L2_TUNER_MODE_STEREO || @@ -1911,9 +1916,7 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) va.mode = VIDEO_SOUND_LANG1; else if (t->audmode == V4L2_TUNER_MODE_LANG2) va.mode = VIDEO_SOUND_LANG2; - bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va); - if (btv->audio_hook) - btv->audio_hook(btv,&va,1); + btv->audio_hook(btv,&va,1); } mutex_unlock(&btv->lock); return 0; @@ -1938,7 +1941,7 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) return -EINVAL; mutex_lock(&btv->lock); btv->freq = f->frequency; - bttv_call_i2c_clients(btv,VIDIOCSFREQ,&btv->freq); + bttv_call_i2c_clients(btv,VIDIOC_S_FREQUENCY,f); if (btv->has_matchbox && btv->radio_user) tea5757_set_freq(btv,btv->freq); mutex_unlock(&btv->lock); @@ -1946,7 +1949,9 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) } case VIDIOC_LOG_STATUS: { + printk(KERN_INFO "bttv%d: ================= START STATUS CARD #%d =================\n", btv->c.nr, btv->c.nr); bttv_call_i2c_clients(btv, VIDIOC_LOG_STATUS, NULL); + printk(KERN_INFO "bttv%d: ================== END STATUS CARD #%d ==================\n", btv->c.nr, btv->c.nr); return 0; } @@ -2896,12 +2901,10 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, return 0; } *c = bttv_ctls[i]; - if (i >= 4 && i <= 8) { + if (btv->audio_hook && i >= 4 && i <= 8) { struct video_audio va; memset(&va,0,sizeof(va)); - bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); - if (btv->audio_hook) - btv->audio_hook(btv,&va,0); + btv->audio_hook(btv,&va,0); switch (bttv_ctls[i].id) { case V4L2_CID_AUDIO_VOLUME: if (!(va.flags & VIDEO_AUDIO_VOLUME)) diff --git a/linux/drivers/media/video/msp3400-driver.c b/linux/drivers/media/video/msp3400-driver.c index b52e5bdea..6ef656765 100644 --- a/linux/drivers/media/video/msp3400-driver.c +++ b/linux/drivers/media/video/msp3400-driver.c @@ -302,19 +302,6 @@ void msp_set_scart(struct i2c_client *client, int in, int out) msp_write_dem(client, 0x40, state->i2s_mode); } -void msp_set_mute(struct i2c_client *client) -{ - struct msp_state *state = i2c_get_clientdata(client); - - v4l_dbg(1, msp_debug, client, "mute audio\n"); - msp_write_dsp(client, 0x0000, 0); - msp_write_dsp(client, 0x0007, 1); - if (state->has_scart2_out_volume) - msp_write_dsp(client, 0x0040, 1); - if (state->has_headphones) - msp_write_dsp(client, 0x0006, 0); -} - void msp_set_audio(struct i2c_client *client) { struct msp_state *state = i2c_get_clientdata(client); @@ -383,7 +370,6 @@ static void msp_wake_thread(struct i2c_client *client) if (NULL == state->kthread) return; - msp_set_mute(client); state->watch_stereo = 0; state->restart = 1; wake_up_interruptible(&state->wq); @@ -427,19 +413,15 @@ int msp_sleep(struct msp_state *state, int timeout) /* ------------------------------------------------------------------------ */ -static int msp_mode_v4l2_to_v4l1(int rxsubchans) +static int msp_mode_v4l2_to_v4l1(int rxsubchans, int audmode) { - int mode = 0; - - if (rxsubchans & V4L2_TUNER_SUB_STEREO) - mode |= VIDEO_SOUND_STEREO; - if (rxsubchans & V4L2_TUNER_SUB_LANG2) - mode |= VIDEO_SOUND_LANG2 | VIDEO_SOUND_STEREO; - if (rxsubchans & V4L2_TUNER_SUB_LANG1) - mode |= VIDEO_SOUND_LANG1 | VIDEO_SOUND_STEREO; - if (mode == 0) - mode |= VIDEO_SOUND_MONO; - return mode; + if (rxsubchans == V4L2_TUNER_SUB_MONO) + return VIDEO_SOUND_MONO; + if (rxsubchans == V4L2_TUNER_SUB_STEREO) + return VIDEO_SOUND_STEREO; + if (audmode == V4L2_TUNER_MODE_LANG2) + return VIDEO_SOUND_LANG2; + return VIDEO_SOUND_LANG1; } static int msp_mode_v4l1_to_v4l2(int mode) @@ -658,7 +640,7 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) break; if (state->opmode == OPMODE_AUTOSELECT) msp_detect_stereo(client); - va->mode = msp_mode_v4l2_to_v4l1(state->rxsubchans); + va->mode = msp_mode_v4l2_to_v4l1(state->rxsubchans, state->audmode); break; } @@ -673,7 +655,8 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) state->treble = va->treble; msp_set_audio(client); - if (va->mode != 0 && state->radio == 0) { + if (va->mode != 0 && state->radio == 0 && + state->audmode != msp_mode_v4l1_to_v4l2(va->mode)) { state->audmode = msp_mode_v4l1_to_v4l2(va->mode); msp_set_audmode(client); } @@ -779,6 +762,8 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) if (state->radio) /* TODO: add mono/stereo support for radio */ break; + if (state->audmode == vt->audmode) + break; state->audmode = vt->audmode; /* only set audmode */ msp_set_audmode(client); @@ -962,7 +947,7 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind) memset(state, 0, sizeof(*state)); state->v4l2_std = V4L2_STD_NTSC; - state->audmode = V4L2_TUNER_MODE_LANG1; + state->audmode = V4L2_TUNER_MODE_STEREO; state->volume = 58880; /* 0db gain */ state->balance = 32768; /* 0db gain */ state->bass = 32768; @@ -1010,13 +995,16 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind) state->has_radio = msp_revision >= 'G'; /* Has headphones output: not for stripped down products */ state->has_headphones = msp_prod_lo < 5; + /* Has scart2 input: not in stripped down products of the '3' family */ + state->has_scart2 = msp_family >= 4 || msp_prod_lo < 7; + /* Has scart3 input: not in stripped down products of the '3' family */ + state->has_scart3 = msp_family >= 4 || msp_prod_lo < 5; /* Has scart4 input: not in pre D revisions, not in stripped D revs */ state->has_scart4 = msp_family >= 4 || (msp_revision >= 'D' && msp_prod_lo < 5); - /* Has scart2 and scart3 inputs and scart2 output: not in stripped - down products of the '3' family */ - state->has_scart23_in_scart2_out = msp_family >= 4 || msp_prod_lo < 5; + /* Has scart2 output: not in stripped down products of the '3' family */ + state->has_scart2_out = msp_family >= 4 || msp_prod_lo < 5; /* Has scart2 a volume control? Not in pre-D revisions. */ - state->has_scart2_out_volume = msp_revision > 'C' && state->has_scart23_in_scart2_out; + state->has_scart2_out_volume = msp_revision > 'C' && state->has_scart2_out; /* Has a configurable i2s out? */ state->has_i2s_conf = msp_revision >= 'G' && msp_prod_lo < 7; /* Has subwoofer output: not in pre-D revs and not in stripped down products */ diff --git a/linux/drivers/media/video/msp3400-driver.h b/linux/drivers/media/video/msp3400-driver.h index 1b03cc5a9..90c2b1297 100644 --- a/linux/drivers/media/video/msp3400-driver.h +++ b/linux/drivers/media/video/msp3400-driver.h @@ -55,8 +55,10 @@ struct msp_state { u8 has_radio; u8 has_headphones; u8 has_ntsc_jp_d_k3; + u8 has_scart2; + u8 has_scart3; u8 has_scart4; - u8 has_scart23_in_scart2_out; + u8 has_scart2_out; u8 has_scart2_out_volume; u8 has_i2s_conf; u8 has_subwoofer; @@ -103,7 +105,6 @@ int msp_read_dem(struct i2c_client *client, int addr); int msp_read_dsp(struct i2c_client *client, int addr); int msp_reset(struct i2c_client *client); void msp_set_scart(struct i2c_client *client, int in, int out); -void msp_set_mute(struct i2c_client *client); void msp_set_audio(struct i2c_client *client); int msp_sleep(struct msp_state *state, int timeout); diff --git a/linux/drivers/media/video/msp3400-kthreads.c b/linux/drivers/media/video/msp3400-kthreads.c index 4b0d6340a..6a4137c81 100644 --- a/linux/drivers/media/video/msp3400-kthreads.c +++ b/linux/drivers/media/video/msp3400-kthreads.c @@ -176,7 +176,7 @@ static void msp_set_source(struct i2c_client *client, u16 src) msp_write_dsp(client, 0x000a, src); msp_write_dsp(client, 0x000b, src); msp_write_dsp(client, 0x000c, src); - if (state->has_scart23_in_scart2_out) + if (state->has_scart2_out) msp_write_dsp(client, 0x0041, src); } @@ -246,15 +246,22 @@ static void msp3400c_set_audmode(struct i2c_client *client) return; } - /* If no second language is available, switch to the first language */ - if ((audmode == V4L2_TUNER_MODE_LANG2 || - audmode == V4L2_TUNER_MODE_LANG1_LANG2) && - !(state->rxsubchans & V4L2_TUNER_SUB_LANG2)) - audmode = V4L2_TUNER_MODE_LANG1; - /* switch to stereo for stereo transmission, otherwise - keep first language */ - if (audmode == V4L2_TUNER_MODE_LANG1 && - (state->rxsubchans & V4L2_TUNER_SUB_STEREO)) + /* Note: for the C and D revs no NTSC stereo + SAP is possible as + the hardware does not support SAP. So the rxsubchans combination + of STEREO | LANG2 does not occur. */ + + /* switch to mono if only mono is available */ + if (state->rxsubchans == V4L2_TUNER_SUB_MONO) + audmode = V4L2_TUNER_MODE_MONO; + /* if bilingual */ + else if (state->rxsubchans & V4L2_TUNER_SUB_LANG2) { + /* and mono or stereo, then fallback to lang1 */ + if (audmode == V4L2_TUNER_MODE_MONO || + audmode == V4L2_TUNER_MODE_STEREO) + audmode = V4L2_TUNER_MODE_LANG1; + } + /* if stereo, and audmode is not mono, then switch to stereo */ + else if (audmode != V4L2_TUNER_MODE_MONO) audmode = V4L2_TUNER_MODE_STEREO; /* switch demodulator */ @@ -314,6 +321,7 @@ static void msp3400c_set_audmode(struct i2c_client *client) } /* switch audio */ + v4l_dbg(1, msp_debug, client, "set audmode %d\n", audmode); switch (audmode) { case V4L2_TUNER_MODE_STEREO: case V4L2_TUNER_MODE_LANG1_LANG2: @@ -489,8 +497,9 @@ int msp3400c_thread(void *data) continue; } - /* mute */ - msp_set_mute(client); + /* put into sane state (and mute) */ + msp_reset(client); + msp3400c_set_mode(client, MSP_MODE_AM_DETECT); val1 = val2 = 0; max1 = max2 = -1; @@ -573,7 +582,6 @@ int msp3400c_thread(void *data) /* B/G NICAM */ state->second = msp3400c_carrier_detect_55[max2].cdo; msp3400c_set_mode(client, MSP_MODE_FM_NICAM1); - msp3400c_set_carrier(client, state->second, state->main); state->nicam_on = 1; state->watch_stereo = 1; } else { @@ -584,7 +592,6 @@ int msp3400c_thread(void *data) /* PAL I NICAM */ state->second = MSP_CARRIER(6.552); msp3400c_set_mode(client, MSP_MODE_FM_NICAM2); - msp3400c_set_carrier(client, state->second, state->main); state->nicam_on = 1; state->watch_stereo = 1; break; @@ -598,13 +605,11 @@ int msp3400c_thread(void *data) /* L NICAM or AM-mono */ state->second = msp3400c_carrier_detect_65[max2].cdo; msp3400c_set_mode(client, MSP_MODE_AM_NICAM); - msp3400c_set_carrier(client, state->second, state->main); state->watch_stereo = 1; } else if (max2 == 0 && state->has_nicam) { /* D/K NICAM */ state->second = msp3400c_carrier_detect_65[max2].cdo; msp3400c_set_mode(client, MSP_MODE_FM_NICAM1); - msp3400c_set_carrier(client, state->second, state->main); state->nicam_on = 1; state->watch_stereo = 1; } else { @@ -616,13 +621,15 @@ int msp3400c_thread(void *data) no_second: state->second = msp3400c_carrier_detect_main[max1].cdo; msp3400c_set_mode(client, MSP_MODE_FM_TERRA); - msp3400c_set_carrier(client, state->second, state->main); state->rxsubchans = V4L2_TUNER_SUB_MONO; break; } + msp3400c_set_carrier(client, state->second, state->main); - /* unmute */ + /* unmute, restore misc registers */ msp_set_audio(client); + + msp_write_dsp(client, 0x13, state->acb); msp3400c_set_audmode(client); if (msp_debug) @@ -630,12 +637,12 @@ int msp3400c_thread(void *data) /* monitor tv audio mode, the first time don't wait so long to get a quick stereo/bilingual result */ - if (msp_sleep(state, 1000)) - goto restart; + count = 20; while (state->watch_stereo) { watch_stereo(client); - if (msp_sleep(state, 5000)) + if (msp_sleep(state, count ? 200 : 5000)) goto restart; + if (count) count--; } } v4l_dbg(1, msp_debug, client, "thread: exit\n"); @@ -653,7 +660,7 @@ int msp3410d_thread(void *data) { struct i2c_client *client = data; struct msp_state *state = i2c_get_clientdata(client); - int val, i, std; + int val, i, std, count; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) msp_setup_thread(state); @@ -801,12 +808,12 @@ int msp3410d_thread(void *data) /* monitor tv audio mode, the first time don't wait so long to get a quick stereo/bilingual result */ - if (msp_sleep(state, 1000)) - goto restart; + count = 20; while (state->watch_stereo) { watch_stereo(client); - if (msp_sleep(state, 5000)) + if (msp_sleep(state, count ? 200 : 5000)) goto restart; + if (count) count--; } } v4l_dbg(1, msp_debug, client, "thread: exit\n"); @@ -869,20 +876,20 @@ static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) source = 0; /* mono only */ matrix = 0x30; break; - case V4L2_TUNER_MODE_LANG1: - source = 3; /* stereo or A */ - matrix = 0x00; - break; case V4L2_TUNER_MODE_LANG2: source = 4; /* stereo or B */ matrix = 0x10; break; - case V4L2_TUNER_MODE_STEREO: case V4L2_TUNER_MODE_LANG1_LANG2: - default: source = 1; /* stereo or A|B */ matrix = 0x20; break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1: + default: + source = 3; /* stereo or A */ + matrix = 0x00; + break; } if (in == MSP_DSP_OUT_TUNER) @@ -909,7 +916,7 @@ static void msp34xxg_set_sources(struct i2c_client *client) msp34xxg_set_source(client, 0x000c, (in >> 4) & 0xf); msp34xxg_set_source(client, 0x0009, (in >> 8) & 0xf); msp34xxg_set_source(client, 0x000a, (in >> 12) & 0xf); - if (state->has_scart23_in_scart2_out) + if (state->has_scart2_out) msp34xxg_set_source(client, 0x0041, (in >> 16) & 0xf); msp34xxg_set_source(client, 0x000b, (in >> 20) & 0xf); } -- cgit v1.2.3 From 9875f670afe53a6edd3a2b7852b4ee592eef4160 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 31 Mar 2006 00:55:22 +0200 Subject: Move cx2341x.h to media/cx2341x.h From: Hans Verkuil This header is used by multiple drivers, so keep it in include/media so all drivers can access it. Signed-off-by: Hans Verkuil --- linux/drivers/media/video/cx2341x.h | 121 --------------------- .../drivers/media/video/pvrusb2/pvrusb2-encoder.c | 2 +- 2 files changed, 1 insertion(+), 122 deletions(-) delete mode 100644 linux/drivers/media/video/cx2341x.h (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx2341x.h b/linux/drivers/media/video/cx2341x.h deleted file mode 100644 index 7e7dcc072..000000000 --- a/linux/drivers/media/video/cx2341x.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - cx23415/6 header containing common defines. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef CX2341X_H -#define CX2341X_H - -/* Firmware names */ -#define CX2341X_FIRM_ENC_FILENAME "v4l-cx2341x-enc.fw" -/* Decoder firmware for the cx23415 only */ -#define CX2341X_FIRM_DEC_FILENAME "v4l-cx2341x-dec.fw" - -/* Firmware API commands */ - -/* MPEG decoder API, specific to the cx23415 */ -#define CX2341X_DEC_PING_FW 0x00 -#define CX2341X_DEC_START_PLAYBACK 0x01 -#define CX2341X_DEC_STOP_PLAYBACK 0x02 -#define CX2341X_DEC_SET_PLAYBACK_SPEED 0x03 -#define CX2341X_DEC_STEP_VIDEO 0x05 -#define CX2341X_DEC_SET_DMA_BLOCK_SIZE 0x08 -#define CX2341X_DEC_GET_XFER_INFO 0x09 -#define CX2341X_DEC_GET_DMA_STATUS 0x0a -#define CX2341X_DEC_SCHED_DMA_FROM_HOST 0x0b -#define CX2341X_DEC_PAUSE_PLAYBACK 0x0d -#define CX2341X_DEC_HALT_FW 0x0e -#define CX2341X_DEC_SET_STANDARD 0x10 -#define CX2341X_DEC_GET_VERSION 0x11 -#define CX2341X_DEC_SET_STREAM_INPUT 0x14 -#define CX2341X_DEC_GET_TIMING_INFO 0x15 -#define CX2341X_DEC_SET_AUDIO_MODE 0x16 -#define CX2341X_DEC_SET_EVENT_NOTIFICATION 0x17 -#define CX2341X_DEC_SET_DISPLAY_BUFFERS 0x18 -#define CX2341X_DEC_EXTRACT_VBI 0x19 -#define CX2341X_DEC_SET_DECODER_SOURCE 0x1a -#define CX2341X_DEC_SET_AUDIO_OUTPUT 0x1b -#define CX2341X_DEC_SET_AV_DELAY 0x1c -#define CX2341X_DEC_SET_PREBUFFERING 0x1e - -/* MPEG encoder API */ -#define CX2341X_ENC_PING_FW 0x80 -#define CX2341X_ENC_START_CAPTURE 0x81 -#define CX2341X_ENC_STOP_CAPTURE 0x82 -#define CX2341X_ENC_SET_AUDIO_ID 0x89 -#define CX2341X_ENC_SET_VIDEO_ID 0x8b -#define CX2341X_ENC_SET_PCR_ID 0x8d -#define CX2341X_ENC_SET_FRAME_RATE 0x8f -#define CX2341X_ENC_SET_FRAME_SIZE 0x91 -#define CX2341X_ENC_SET_BIT_RATE 0x95 -#define CX2341X_ENC_SET_GOP_PROPERTIES 0x97 -#define CX2341X_ENC_SET_ASPECT_RATIO 0x99 -#define CX2341X_ENC_SET_DNR_FILTER_MODE 0x9b -#define CX2341X_ENC_SET_DNR_FILTER_PROPS 0x9d -#define CX2341X_ENC_SET_CORING_LEVELS 0x9f -#define CX2341X_ENC_SET_SPATIAL_FILTER_TYPE 0xa1 -#define CX2341X_ENC_SET_3_2_PULLDOWN 0xb1 -#define CX2341X_ENC_SET_VBI_LINE 0xb7 -#define CX2341X_ENC_SET_STREAM_TYPE 0xb9 -#define CX2341X_ENC_SET_OUTPUT_PORT 0xbb -#define CX2341X_ENC_SET_AUDIO_PROPERTIES 0xbd -#define CX2341X_ENC_HALT_FW 0xc3 -#define CX2341X_ENC_GET_VERSION 0xc4 -#define CX2341X_ENC_SET_GOP_CLOSURE 0xc5 -#define CX2341X_ENC_GET_SEQ_END 0xc6 -#define CX2341X_ENC_SET_PGM_INDEX_INFO 0xc7 -#define CX2341X_ENC_SET_VBI_CONFIG 0xc8 -#define CX2341X_ENC_SET_DMA_BLOCK_SIZE 0xc9 -#define CX2341X_ENC_GET_PREV_DMA_INFO_MB_10 0xca -#define CX2341X_ENC_GET_PREV_DMA_INFO_MB_9 0xcb -#define CX2341X_ENC_SCHED_DMA_TO_HOST 0xcc -#define CX2341X_ENC_INITIALIZE_INPUT 0xcd -#define CX2341X_ENC_SET_FRAME_DROP_RATE 0xd0 -#define CX2341X_ENC_PAUSE_ENCODER 0xd2 -#define CX2341X_ENC_REFRESH_INPUT 0xd3 -#define CX2341X_ENC_SET_COPYRIGHT 0xd4 -#define CX2341X_ENC_SET_EVENT_NOTIFICATION 0xd5 -#define CX2341X_ENC_SET_NUM_VSYNC_LINES 0xd6 -#define CX2341X_ENC_SET_PLACEHOLDER 0xd7 -#define CX2341X_ENC_MUTE_VIDEO 0xd9 -#define CX2341X_ENC_MUTE_AUDIO 0xda -#define CX2341X_ENC_UNKNOWN 0xdb -#define CX2341X_ENC_MISC 0xdc - -/* OSD API, specific to the cx23415 */ -#define CX2341X_OSD_GET_FRAMEBUFFER 0x41 -#define CX2341X_OSD_GET_PIXEL_FORMAT 0x42 -#define CX2341X_OSD_SET_PIXEL_FORMAT 0x43 -#define CX2341X_OSD_GET_STATE 0x44 -#define CX2341X_OSD_SET_STATE 0x45 -#define CX2341X_OSD_GET_OSD_COORDS 0x46 -#define CX2341X_OSD_SET_OSD_COORDS 0x47 -#define CX2341X_OSD_GET_SCREEN_COORDS 0x48 -#define CX2341X_OSD_SET_SCREEN_COORDS 0x49 -#define CX2341X_OSD_GET_GLOBAL_ALPHA 0x4a -#define CX2341X_OSD_SET_GLOBAL_ALPHA 0x4b -#define CX2341X_OSD_SET_BLEND_COORDS 0x4c -#define CX2341X_OSD_GET_FLICKER_STATE 0x4f -#define CX2341X_OSD_SET_FLICKER_STATE 0x50 -#define CX2341X_OSD_BLT_COPY 0x52 -#define CX2341X_OSD_BLT_FILL 0x53 -#define CX2341X_OSD_BLT_TEXT 0x54 -#define CX2341X_OSD_SET_FRAMEBUFFER_WINDOW 0x56 -#define CX2341X_OSD_SET_CHROMA_KEY 0x60 -#define CX2341X_OSD_GET_ALPHA_CONTENT_INDEX 0x61 -#define CX2341X_OSD_SET_ALPHA_CONTENT_INDEX 0x62 - -#endif /* CX2341X_H */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 553bd2d7b..b85ef5059 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -22,7 +22,7 @@ #include // for linux/firmware.h #include -#include "cx2341x.h" +#include #include "pvrusb2-util.h" #include "pvrusb2-encoder.h" #include "pvrusb2-hdw-internal.h" -- cgit v1.2.3 From 950e8155e83cdf480a41449cf024494b6ec25e69 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 31 Mar 2006 00:57:28 +0200 Subject: Previous change for cx2341X boards broke the remote support From: Hans Verkuil Partially revert previous change to fix IR support. Signed-off-by: Hans Verkuil --- linux/drivers/media/video/ir-kbd-i2c.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/ir-kbd-i2c.c b/linux/drivers/media/video/ir-kbd-i2c.c index ab1c47648..15ce0ff08 100644 --- a/linux/drivers/media/video/ir-kbd-i2c.c +++ b/linux/drivers/media/video/ir-kbd-i2c.c @@ -410,7 +410,6 @@ static int ir_probe(struct i2c_adapter *adap) */ static const int probe_bttv[] = { 0x1a, 0x18, 0x4b, 0x64, 0x30, -1}; - static const int probe_cx2341x[] = { 0x18, 0x7a, -1}; static const int probe_saa7134[] = { 0x7a, 0x47, -1 }; static const int probe_em28XX[] = { 0x30, 0x47, -1 }; const int *probe = NULL; @@ -423,7 +422,7 @@ static int ir_probe(struct i2c_adapter *adap) probe = probe_bttv; break; case I2C_HW_B_CX2341X: - probe = probe_cx2341x; + probe = probe_bttv; break; case I2C_HW_SAA7134: probe = probe_saa7134; -- cgit v1.2.3 From 376fb9365c967e7d2bb2aee61e01685595bf1111 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 30 Mar 2006 22:14:40 -0600 Subject: Make parts of pvrusb2 compile-time options (sysfs and debugifc) From: Mike Isely Arrange things in pvrusb2 so that the sysfs-using component doesn't have to be compiled in. Control this with new CONFIG_VIDEO_PVRUSB2_SYSFS variable. This will allow the driver to still be used even if sysfs has not been compiled into the kernel. Also arrange things similarly in pvrusb2 for the debug interface, which is something that normally should not need to be built but is valuable for bug chasing. This is controlled now with CONFIG_VIDEO_PVRUSB2_DEBUGIFC. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-main.c | 10 ++++++++++ linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c | 14 ++++++++++++++ 2 files changed, 24 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c index c070fc195..40fb6ae41 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c @@ -35,7 +35,9 @@ #include "pvrusb2-context.h" #include "pvrusb2-debug.h" #include "pvrusb2-v4l2.h" +#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS #include "pvrusb2-sysfs.h" +#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ #define DRIVER_AUTHOR "Mike Isely " #define DRIVER_DESC "Hauppauge WinTV-PVR-USB2 MPEG2 Encoder/Tuner" @@ -60,13 +62,17 @@ int pvrusb2_debug = DEFAULT_DEBUG_MASK; module_param_named(debug,pvrusb2_debug,int,S_IRUGO|S_IWUSR); MODULE_PARM_DESC(debug, "Debug trace mask"); +#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS static struct pvr2_sysfs_class *class_ptr = 0; +#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ static void pvr_setup_attach(struct pvr2_context *pvr) { /* Create association with v4l layer */ pvr2_v4l2_create(pvr); +#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS pvr2_sysfs_create(pvr,class_ptr); +#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ } static int pvr_probe(struct usb_interface *intf, @@ -138,7 +144,9 @@ static int __init pvr_init(void) request_module("tda9887"); request_module("wm8775"); +#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS class_ptr = pvr2_sysfs_class_create(); +#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ ret = usb_register(&pvr_driver); @@ -155,7 +163,9 @@ static void __exit pvr_exit(void) pvr2_trace(PVR2_TRACE_INIT,"pvr_exit"); +#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS pvr2_sysfs_class_destroy(class_ptr); +#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ usb_deregister(&pvr_driver); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index c5d44aa6c..053c0b24c 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -26,14 +26,18 @@ #include "pvrusb2-sysfs.h" #include "pvrusb2-hdw.h" #include "pvrusb2-debug.h" +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC #include "pvrusb2-debugifc.h" +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ #define pvr2_sysfs_trace(...) pvr2_trace(PVR2_TRACE_SYSFS,__VA_ARGS__) struct pvr2_sysfs { struct pvr2_channel channel; struct class_device *class_dev; +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC struct pvr2_sysfs_debugifc *debugifc; +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ struct pvr2_sysfs_ctl_item *item_first; struct pvr2_sysfs_ctl_item *item_last; struct sysfs_ops kops; @@ -42,10 +46,12 @@ struct pvr2_sysfs { struct class_device_attribute attr_unit_number; }; +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC struct pvr2_sysfs_debugifc { struct class_device_attribute attr_debugcmd; struct class_device_attribute attr_debuginfo; }; +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ struct pvr2_sysfs_ctl_item { struct class_device_attribute attr_name; @@ -531,6 +537,7 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) sysfs_create_group(&sfp->class_dev->kobj,&cip->grp); } +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC static ssize_t debuginfo_show(struct class_device *,char *); static ssize_t debugcmd_show(struct class_device *,char *); static ssize_t debugcmd_store(struct class_device *,const char *,size_t count); @@ -565,6 +572,7 @@ static void pvr2_sysfs_tear_down_debugifc(struct pvr2_sysfs *sfp) kfree(sfp->debugifc); sfp->debugifc = 0; } +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ static void pvr2_sysfs_add_controls(struct pvr2_sysfs *sfp) @@ -608,7 +616,9 @@ static void pvr2_sysfs_release(struct class_device *class_dev) static void class_dev_destroy(struct pvr2_sysfs *sfp) { if (!sfp->class_dev) return; +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC pvr2_sysfs_tear_down_debugifc(sfp); +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ pvr2_sysfs_tear_down_controls(sfp); class_device_remove_file(sfp->class_dev,&sfp->attr_v4l_minor_number); class_device_remove_file(sfp->class_dev,&sfp->attr_unit_number); @@ -684,7 +694,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, class_device_create_file(sfp->class_dev,&sfp->attr_unit_number); pvr2_sysfs_add_controls(sfp); +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC pvr2_sysfs_add_debugifc(sfp); +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ } @@ -755,6 +767,7 @@ void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *clp) } +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC static ssize_t debuginfo_show(struct class_device *class_dev,char *buf) { struct pvr2_sysfs *sfp; @@ -787,6 +800,7 @@ static ssize_t debugcmd_store(struct class_device *class_dev, if (ret < 0) return ret; return count; } +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ /* -- cgit v1.2.3 From 012c447c3dd586921468215450cb6cce65052a3c Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 30 Mar 2006 22:16:09 -0600 Subject: pvrusb2 driver initialization tweak From: Mike Isely Make permanent the removal of the call to pvr2_reset_ctl_endpoints(). It is known positively now that this step was both unneeded and caused harm to communication with the hardware during driver initialization. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 7b27d96ab..32bdaeba7 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1229,10 +1229,9 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) cptr->ctl_def->default_value); } -#if 0 // Suspect this is doing more harm than good - pvr2_reset_ctl_endpoints(hdw); - if (!pvr2_hdw_dev_ok(hdw)) return; -#endif + // Do not use pvr2_reset_ctl_endpoints() here. It is not + // thread-safe against the normal pvr2_send_request() mechanism. + // (We should make it thread safe). ret = pvr2_hdw_get_eeprom_addr(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; -- cgit v1.2.3 From 401def2d2cdb15034aa721cc524c9d7c70d3ddcd Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 30 Mar 2006 22:51:33 -0600 Subject: Clean up comments in pvrusb2-eeprom.c From: Mike Isely Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c | 72 +--------------------- 1 file changed, 2 insertions(+), 70 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c index 634095efc..78bc968c0 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c @@ -27,57 +27,8 @@ #define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__) -/* - - isely@pobox.com 16-Oct-2005 - There are two method by which we can - theoretically retrieve information from the device's eeprom: - - Method #1: We expect tveeprom to attach to our I2C adapter as a - client, in which case we send it a command to tell us what it - knows about the device. This is the "indirect" method. - - Method #2: We retrieve the eeprom contents ourselves and call into - tveeprom_hauppauge_analog() to parse the data and tell us what - it knows about the device. This is the "direct" method. - - Unfortunately it isn't perfectly clear which method is the best. - They each have pros and cons: - - #1 is simpler & more portable and has an API which is more stable. - - #1 doesn't provide as much information as #2 does. For example, we - can't retrieve the device's serial number with method #1. - - #1 requires that tveeprom.ko autonomously detect the eeprom chip on - its own; we can't help it out here. Worse still, it seems that - the eeprom in some PVR USB2 devices (like mine) can't be detected - correctly (I don't see an ack on a zero length write which is - what the I2C core attempts). - - #2 uses an unstable API. Current the ivtv implementation of #2 uses - a completely different tveeprom struct than the v4l - implementation of #2. This causes a usability nightmare. - - Since I can't decide, both methods are implemented below. Method #2 - (direct) is the default choice, but if you want to try method #1, - then define PVR2_EEPROM_INDIRECT and cross your fingers... - - If you use method #1, please be aware that you won't have a serial - number for the device and thus the sysfs interface may be a little - different. In addition, if tveeprom.ko fails to detect the eeprom - you may have to force it using standard i2c module options (try - force=-1,80). FINALLY (and this may foreclose this option for you - completely), the PVR USB2 eeprom seems to have valid data only in - the upper 128 bytes - the lower 128 bytes causes tveeprom.ko to - abort. In method #2 we only read the upper 128 bytes... - - */ - - -/* Stuff common to direct approach of operation tveeprom */ - /* Read and analyze data in the eeprom. Use tveeprom to figure out @@ -121,7 +72,7 @@ static u8 *pvr2_eeprom_fetch(struct pvr2_hdw *hdw) /* FX2 documentation states that a 16bit-addressed eeprom is expected if the I2C address is an odd number (yeah, this is - strange bit it's what they do) */ + strange but it's what they do) */ mode16 = (addr & 1); eepromSize = (mode16 ? 4096 : 256); trace_eeprom("Examining %d byte eeprom at location 0x%x" @@ -165,15 +116,7 @@ static u8 *pvr2_eeprom_fetch(struct pvr2_hdw *hdw) } -/*VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV*/ -/* BEGIN DIRECT METHOD, V4L ONLY */ - - -/* Directly call eeprom analysis function within tveeprom. This - version directly assumes it is talking to the V4L version of - tveeprom.ko and does not attempt anything ugly to maintain - backwards compatibility. */ - +/* Directly call eeprom analysis function within tveeprom. */ int pvr2_eeprom_analyze(struct pvr2_hdw *hdw) { u8 *eeprom; @@ -211,17 +154,6 @@ int pvr2_eeprom_analyze(struct pvr2_hdw *hdw) return 0; } - - -/* END DIRECT METHOD, V4L ONLY */ -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - - - - - - /* Stuff for Emacs to see, in order to encourage consistent editing style: *** Local Variables: *** -- cgit v1.2.3 From 0bce98602d455b54f9e8f4915f7288e0b4b6bdb8 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Fri, 31 Mar 2006 01:47:58 -0500 Subject: Kconfig: add menu options for pvrusb2 sysfs and debug interface support From: Michael Krufky Signed-off-by: Michael Krufky --- linux/drivers/media/video/pvrusb2/Kconfig | 27 +++++++++++++++++++++++++++ linux/drivers/media/video/pvrusb2/Makefile | 7 +++++-- 2 files changed, 32 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/Kconfig b/linux/drivers/media/video/pvrusb2/Kconfig index 9824ad267..b22ab7f5f 100644 --- a/linux/drivers/media/video/pvrusb2/Kconfig +++ b/linux/drivers/media/video/pvrusb2/Kconfig @@ -12,3 +12,30 @@ config VIDEO_PVRUSB2 To compile this driver as a module, choose M here: the module will be called pvrusb2 + +config VIDEO_PVRUSB2_SYSFS + bool "pvrusb2 sysfs support" + default y + depends on VIDEO_PVRUSB2 && SYSFS && EXPERIMENTAL + ---help--- + This option enables the operation of a sysfs based + interface for query and control of the pvrusb2 driver. + + This is not generally needed for v4l applications, + although certain applications are optimized to take + advantage of this feature. + + If you are in doubt, say Y. + + Note: This feature is experimental and subject to change. + +config VIDEO_PVRUSB2_DEBUGIFC + bool "pvrusb2 debug interface" + depends on VIDEO_PVRUSB2_SYSFS + ---help--- + This option enables the inclusion of a debug interface + in the pvrusb2 driver, hosted through sysfs. + + You do not need to select this option unless you plan + on debugging the driver or performing a manual firmware + extraction. diff --git a/linux/drivers/media/video/pvrusb2/Makefile b/linux/drivers/media/video/pvrusb2/Makefile index 4a37fe723..b0d3064ef 100644 --- a/linux/drivers/media/video/pvrusb2/Makefile +++ b/linux/drivers/media/video/pvrusb2/Makefile @@ -1,11 +1,14 @@ +obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o +obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o + pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ pvrusb2-audio.o pvrusb2-i2c-chips-v4l2.o \ pvrusb2-encoder.o pvrusb2-video-v4l.o \ pvrusb2-eeprom.o pvrusb2-tuner.o pvrusb2-demod.o \ pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \ - pvrusb2-sysfs.o pvrusb2-context.o pvrusb2-io.o \ + pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \ pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \ - pvrusb2-ioread.o pvrusb2-debugifc.o + $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y) obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o -- cgit v1.2.3 From 0932c037375ef26f5d0cfbec3742519e0f0d5cae Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 1 Apr 2006 20:27:52 +0200 Subject: More msp3400 and bttv fixes From: Hans Verkuil - remove obsolete VIDIOC_S_INPUT i2c call in bttv - translate VIDIOCSFREQ to VIDIOC_S_FREQUENCY in i2c call - improve muting during carrier scan in msp3400 - don't start scan unless really needed. - no longer reset chip for msp3400c/d. - remove v4l2 check in tuner-core (radio stops after using the TV) - add missing VIDIOC_INT_ strings in v4l2-common.c Signed-off-by: Hans Verkuil --- linux/drivers/media/video/bt8xx/bttv-driver.c | 13 +++--- linux/drivers/media/video/msp3400-driver.c | 32 +++++++------- linux/drivers/media/video/msp3400-driver.h | 1 + linux/drivers/media/video/msp3400-kthreads.c | 60 ++++++++++----------------- linux/drivers/media/video/tuner-core.c | 6 +++ linux/drivers/media/video/v4l2-common.c | 8 +++- 6 files changed, 62 insertions(+), 58 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/bt8xx/bttv-driver.c b/linux/drivers/media/video/bt8xx/bttv-driver.c index 1e13ee966..ea9f82586 100644 --- a/linux/drivers/media/video/bt8xx/bttv-driver.c +++ b/linux/drivers/media/video/bt8xx/bttv-driver.c @@ -1046,7 +1046,6 @@ i2c_vidiocschan(struct bttv *btv) { v4l2_std_id std = bttv_tvnorms[btv->tvnorm].v4l2_id; - bttv_call_i2c_clients(btv, VIDIOC_S_INPUT, &btv->input); bttv_call_i2c_clients(btv, VIDIOC_S_STD, &std); if (btv->c.type == BTTV_BOARD_VOODOOTV_FM) bttv_tda9880_setnorm(btv,btv->tvnorm); @@ -1629,12 +1628,16 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) } case VIDIOCSFREQ: { - unsigned long *freq = arg; + struct v4l2_frequency freq; + + memset(&freq, 0, sizeof(freq)); + freq.frequency = *(unsigned long *)arg; mutex_lock(&btv->lock); - btv->freq=*freq; - bttv_call_i2c_clients(btv,VIDIOCSFREQ,freq); + freq.type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + btv->freq = *(unsigned long *)arg; + bttv_call_i2c_clients(btv,VIDIOC_S_FREQUENCY,&freq); if (btv->has_matchbox && btv->radio_user) - tea5757_set_freq(btv,*freq); + tea5757_set_freq(btv,*(unsigned long *)arg); mutex_unlock(&btv->lock); return 0; } diff --git a/linux/drivers/media/video/msp3400-driver.c b/linux/drivers/media/video/msp3400-driver.c index 6ef656765..be3a3bb70 100644 --- a/linux/drivers/media/video/msp3400-driver.c +++ b/linux/drivers/media/video/msp3400-driver.c @@ -307,17 +307,19 @@ void msp_set_audio(struct i2c_client *client) struct msp_state *state = i2c_get_clientdata(client); int bal = 0, bass, treble, loudness; int val = 0; + int reallymuted = state->muted | state->scan_in_progress; - if (!state->muted) + if (!reallymuted) val = (state->volume * 0x7f / 65535) << 8; - v4l_dbg(1, msp_debug, client, "mute=%s volume=%d\n", - state->muted ? "on" : "off", state->volume); + v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n", + state->muted ? "on" : "off", state->scan_in_progress ? "yes" : "no", + state->volume); msp_write_dsp(client, 0x0000, val); - msp_write_dsp(client, 0x0007, state->muted ? 0x1 : (val | 0x1)); + msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1)); if (state->has_scart2_out_volume) - msp_write_dsp(client, 0x0040, state->muted ? 0x1 : (val | 0x1)); + msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1)); if (state->has_headphones) msp_write_dsp(client, 0x0006, val); if (!state->has_sound_processing) @@ -723,21 +725,23 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) int sc_in = rt->input & 0x7; int sc1_out = rt->output & 0xf; int sc2_out = (rt->output >> 4) & 0xf; - u16 val; + u16 val, reg; + if (state->routing.input == rt->input && + state->routing.output == rt->output) + break; state->routing = *rt; - if (state->opmode == OPMODE_AUTOSELECT) { - val = msp_read_dem(client, 0x30) & ~0x100; - msp_write_dem(client, 0x30, val | (tuner ? 0x100 : 0)); - } else { - val = msp_read_dem(client, 0xbb) & ~0x100; - msp_write_dem(client, 0xbb, val | (tuner ? 0x100 : 0)); - } msp_set_scart(client, sc_in, 0); msp_set_scart(client, sc1_out, 1); msp_set_scart(client, sc2_out, 2); msp_set_audmode(client); - msp_wake_thread(client); + reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb; + val = msp_read_dem(client, reg); + if (tuner != ((val >> 8) & 1)) { + msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); + /* wake thread when a new tuner input is chosen */ + msp_wake_thread(client); + } break; } diff --git a/linux/drivers/media/video/msp3400-driver.h b/linux/drivers/media/video/msp3400-driver.h index 90c2b1297..3e26d7b4d 100644 --- a/linux/drivers/media/video/msp3400-driver.h +++ b/linux/drivers/media/video/msp3400-driver.h @@ -86,6 +86,7 @@ struct msp_state { int volume, muted; int balance, loudness; int bass, treble; + int scan_in_progress; /* thread */ struct task_struct *kthread; diff --git a/linux/drivers/media/video/msp3400-kthreads.c b/linux/drivers/media/video/msp3400-kthreads.c index 6a4137c81..714047178 100644 --- a/linux/drivers/media/video/msp3400-kthreads.c +++ b/linux/drivers/media/video/msp3400-kthreads.c @@ -389,7 +389,7 @@ static int msp3400c_detect_stereo(struct i2c_client *client) if (val > 32767) val -= 65536; v4l_dbg(2, msp_debug, client, "stereo detect register: %d\n", val); - if (val > 4096) { + if (val > 8192) { rxsubchans = V4L2_TUNER_SUB_STEREO; } else if (val < -4096) { rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; @@ -493,12 +493,14 @@ int msp3400c_thread(void *data) if (state->radio || MSP_MODE_EXTERN == state->mode) { /* no carrier scan, just unmute */ v4l_dbg(1, msp_debug, client, "thread: no carrier scan\n"); + state->scan_in_progress = 0; msp_set_audio(client); continue; } - /* put into sane state (and mute) */ - msp_reset(client); + /* mute audio */ + state->scan_in_progress = 1; + msp_set_audio(client); msp3400c_set_mode(client, MSP_MODE_AM_DETECT); val1 = val2 = 0; @@ -506,10 +508,6 @@ int msp3400c_thread(void *data) state->watch_stereo = 0; state->nicam_on = 0; - /* some time for the tuner to sync */ - if (msp_sleep(state, 200)) - goto restart; - /* carrier detect pass #1 -- main carrier */ cd = msp3400c_carrier_detect_main; count = ARRAY_SIZE(msp3400c_carrier_detect_main); @@ -621,28 +619,26 @@ int msp3400c_thread(void *data) no_second: state->second = msp3400c_carrier_detect_main[max1].cdo; msp3400c_set_mode(client, MSP_MODE_FM_TERRA); - state->rxsubchans = V4L2_TUNER_SUB_MONO; break; } msp3400c_set_carrier(client, state->second, state->main); - /* unmute, restore misc registers */ - msp_set_audio(client); - - msp_write_dsp(client, 0x13, state->acb); + /* unmute */ + state->scan_in_progress = 0; msp3400c_set_audmode(client); + msp_set_audio(client); if (msp_debug) msp3400c_print_mode(client); /* monitor tv audio mode, the first time don't wait so long to get a quick stereo/bilingual result */ - count = 20; + count = 3; while (state->watch_stereo) { - watch_stereo(client); - if (msp_sleep(state, count ? 200 : 5000)) + if (msp_sleep(state, count ? 1000 : 5000)) goto restart; if (count) count--; + watch_stereo(client); } } v4l_dbg(1, msp_debug, client, "thread: exit\n"); @@ -685,16 +681,14 @@ int msp3410d_thread(void *data) if (state->mode == MSP_MODE_EXTERN) { /* no carrier scan needed, just unmute */ v4l_dbg(1, msp_debug, client, "thread: no carrier scan\n"); + state->scan_in_progress = 0; msp_set_audio(client); continue; } - /* put into sane state (and mute) */ - msp_reset(client); - - /* some time for the tuner to sync */ - if (msp_sleep(state,200)) - goto restart; + /* mute audio */ + state->scan_in_progress = 1; + msp_set_audio(client); /* start autodetect. Note: autodetect is not supported for NTSC-M and radio, hence we force the standard in those cases. */ @@ -734,6 +728,7 @@ int msp3410d_thread(void *data) state->main = msp_stdlist[i].main; state->second = msp_stdlist[i].second; state->std = val; + state->rxsubchans = V4L2_TUNER_SUB_MONO; if (msp_amsound && !state->radio && (state->v4l2_std & V4L2_STD_SECAM) && (val != 0x0009)) { @@ -755,20 +750,17 @@ int msp3410d_thread(void *data) else state->mode = MSP_MODE_FM_NICAM1; /* just turn on stereo */ - state->rxsubchans = V4L2_TUNER_SUB_STEREO; state->nicam_on = 1; state->watch_stereo = 1; break; case 0x0009: state->mode = MSP_MODE_AM_NICAM; - state->rxsubchans = V4L2_TUNER_SUB_MONO; state->nicam_on = 1; state->watch_stereo = 1; break; case 0x0020: /* BTSC */ /* The pre-'G' models only have BTSC-mono */ state->mode = MSP_MODE_BTSC; - state->rxsubchans = V4L2_TUNER_SUB_MONO; break; case 0x0040: /* FM radio */ state->mode = MSP_MODE_FM_RADIO; @@ -778,15 +770,12 @@ int msp3410d_thread(void *data) msp3400c_set_mode(client, MSP_MODE_FM_RADIO); msp3400c_set_carrier(client, MSP_CARRIER(10.7), MSP_CARRIER(10.7)); - /* scart routing (this doesn't belong here I think) */ - msp_set_scart(client,SCART_IN2,0); break; case 0x0002: case 0x0003: case 0x0004: case 0x0005: state->mode = MSP_MODE_FM_TERRA; - state->rxsubchans = V4L2_TUNER_SUB_MONO; state->watch_stereo = 1; break; } @@ -800,20 +789,19 @@ int msp3410d_thread(void *data) if (state->has_i2s_conf) msp_write_dem(client, 0x40, state->i2s_mode); - /* unmute, restore misc registers */ - msp_set_audio(client); - - msp_write_dsp(client, 0x13, state->acb); + /* unmute */ msp3400c_set_audmode(client); + state->scan_in_progress = 0; + msp_set_audio(client); /* monitor tv audio mode, the first time don't wait so long to get a quick stereo/bilingual result */ - count = 20; + count = 3; while (state->watch_stereo) { - watch_stereo(client); - if (msp_sleep(state, count ? 200 : 5000)) + if (msp_sleep(state, count ? 1000 : 5000)) goto restart; if (count) count--; + watch_stereo(client); } } v4l_dbg(1, msp_debug, client, "thread: exit\n"); @@ -934,10 +922,6 @@ static void msp34xxg_reset(struct i2c_client *client) msp_reset(client); - /* make sure that input/output is muted (paranoid mode) */ - /* ACB, mute DSP input, mute SCART 1 */ - msp_write_dsp(client, 0x13, 0x0f20); - if (state->has_i2s_conf) msp_write_dem(client, 0x40, state->i2s_mode); diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index 87581654f..15282bbbb 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -615,10 +615,16 @@ static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, static inline int check_v4l2(struct tuner *t) { + /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for + TV, v4l1 for radio), until that is fixed this code is disabled. + Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2) + first. */ +#if 0 if (t->using_v4l2) { tuner_dbg ("ignore v4l1 call\n"); return EINVAL; } +#endif return 0; } diff --git a/linux/drivers/media/video/v4l2-common.c b/linux/drivers/media/video/v4l2-common.c index ae5538212..5f71edf32 100644 --- a/linux/drivers/media/video/v4l2-common.c +++ b/linux/drivers/media/video/v4l2-common.c @@ -337,6 +337,7 @@ static const char *v4l2_int_ioctls[] = { [_IOC_NR(TUNER_SET_STANDBY)] = "TUNER_SET_STANDBY", [_IOC_NR(TDA9887_SET_CONFIG)] = "TDA9887_SET_CONFIG", + [_IOC_NR(VIDIOC_INT_S_TUNER_MODE)] = "VIDIOC_INT_S_TUNER_MODE", [_IOC_NR(VIDIOC_INT_S_REGISTER)] = "VIDIOC_INT_S_REGISTER", [_IOC_NR(VIDIOC_INT_G_REGISTER)] = "VIDIOC_INT_G_REGISTER", [_IOC_NR(VIDIOC_INT_RESET)] = "VIDIOC_INT_RESET", @@ -345,7 +346,12 @@ static const char *v4l2_int_ioctls[] = { [_IOC_NR(VIDIOC_INT_S_VBI_DATA)] = "VIDIOC_INT_S_VBI_DATA", [_IOC_NR(VIDIOC_INT_G_VBI_DATA)] = "VIDIOC_INT_G_VBI_DATA", [_IOC_NR(VIDIOC_INT_G_CHIP_IDENT)] = "VIDIOC_INT_G_CHIP_IDENT", - [_IOC_NR(VIDIOC_INT_I2S_CLOCK_FREQ)] = "VIDIOC_INT_I2S_CLOCK_FREQ" + [_IOC_NR(VIDIOC_INT_I2S_CLOCK_FREQ)] = "VIDIOC_INT_I2S_CLOCK_FREQ", + [_IOC_NR(VIDIOC_INT_S_STANDBY)] = "VIDIOC_INT_S_STANDBY", + [_IOC_NR(VIDIOC_INT_S_AUDIO_ROUTING)] = "VIDIOC_INT_S_AUDIO_ROUTING", + [_IOC_NR(VIDIOC_INT_G_AUDIO_ROUTING)] = "VIDIOC_INT_G_AUDIO_ROUTING", + [_IOC_NR(VIDIOC_INT_S_VIDEO_ROUTING)] = "VIDIOC_INT_S_VIDEO_ROUTING", + [_IOC_NR(VIDIOC_INT_G_VIDEO_ROUTING)] = "VIDIOC_INT_G_VIDEO_ROUTING" }; #define V4L2_INT_IOCTLS ARRAY_SIZE(v4l2_int_ioctls) -- cgit v1.2.3 From 75ba9961e3fc167c48412d75bc01d55fab89cac0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 1 Apr 2006 21:00:18 +0200 Subject: Keep experimental SLICED_VBI defines under an #if 0 From: Hans Verkuil As per request by Mauro the experimental SLICED_VBI defines are back under placed under an #if 0. Signed-off-by: Hans Verkuil --- linux/drivers/media/video/tvp5150.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/tvp5150.c b/linux/drivers/media/video/tvp5150.c index 1a2cc873e..543516347 100644 --- a/linux/drivers/media/video/tvp5150.c +++ b/linux/drivers/media/video/tvp5150.c @@ -531,41 +531,47 @@ struct i2c_vbi_ram_value { static struct i2c_vbi_ram_value vbi_ram_default[] = { + /* FIXME: Current api doesn't handle all VBI types, those not + yet supported are placed under #if 0 */ +#if 0 /* keep */ {0x010, /* Teletext, SECAM, WST System A */ - {0,6,23,1}, + {V4L2_SLICED_TELETEXT_SECAM,6,23,1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26, 0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 } }, +#endif {0x030, /* Teletext, PAL, WST System B */ {V4L2_SLICED_TELETEXT_B,6,22,1}, { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b, 0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 } }, +#if 0 /* keep */ {0x050, /* Teletext, PAL, WST System C */ - {0,6,22,1}, + {V4L2_SLICED_TELETEXT_PAL_C,6,22,1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, 0xa6, 0x98, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } }, {0x070, /* Teletext, NTSC, WST System B */ - {0,10,21,1}, + {V4L2_SLICED_TELETEXT_NTSC_B,10,21,1}, { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x23, 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } }, {0x090, /* Tetetext, NTSC NABTS System C */ - {0,10,21,1}, + {V4L2_SLICED_TELETEXT_NTSC_C,10,21,1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00 } }, {0x0b0, /* Teletext, NTSC-J, NABTS System D */ - {0,10,21,1}, + {V4L2_SLICED_TELETEXT_NTSC_D,10,21,1}, { 0xaa, 0xaa, 0xff, 0xff, 0xa7, 0x2e, 0x20, 0x23, 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } }, {0x0d0, /* Closed Caption, PAL/SECAM */ - {0,22,22,1}, + {V4L2_SLICED_CAPTION_625,22,22,1}, { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, 0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } }, +#endif {0x0f0, /* Closed Caption, NTSC */ {V4L2_SLICED_CAPTION_525,21,21,1}, { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, @@ -576,21 +582,23 @@ static struct i2c_vbi_ram_value vbi_ram_default[] = { 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42, 0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 } }, +#if 0 /* keep */ {0x130, /* Wide Screen Signal, NTSC C */ - {0,20,20,1}, + {V4L2_SLICED_WSS_525,20,20,1}, { 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43, 0x69, 0x7c, 0x08, 0x00, 0x00, 0x00, 0x39, 0x00 } }, {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */ - {0,6,22,0}, + {V4l2_SLICED_VITC_625,6,22,0}, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, 0xa6, 0x85, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } }, {0x170, /* Vertical Interval Timecode (VITC), NTSC */ - {0,10,20,0}, + {V4l2_SLICED_VITC_525,10,20,0}, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, 0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } }, +#endif {0x190, /* Video Program System (VPS), PAL */ {V4L2_SLICED_VPS,16,16,0}, { 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d, -- cgit v1.2.3 From 1d6852ba00b82d0342468dc905f33226d7e9860b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 1 Apr 2006 21:06:55 +0200 Subject: Add back the msp_sleep that waits for the tuner to settle down From: Hans Verkuil I removed a msp_sleep call that is really needed, otherwise the second carrier is not detected correctly. Signed-off-by: Hans Verkuil --- linux/drivers/media/video/msp3400-kthreads.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/msp3400-kthreads.c b/linux/drivers/media/video/msp3400-kthreads.c index 714047178..aaaa26219 100644 --- a/linux/drivers/media/video/msp3400-kthreads.c +++ b/linux/drivers/media/video/msp3400-kthreads.c @@ -508,6 +508,10 @@ int msp3400c_thread(void *data) state->watch_stereo = 0; state->nicam_on = 0; + /* wait for tuner to settle down after a channel change */ + if (msp_sleep(state, 200)) + goto restart; + /* carrier detect pass #1 -- main carrier */ cd = msp3400c_carrier_detect_main; count = ARRAY_SIZE(msp3400c_carrier_detect_main); @@ -699,6 +703,10 @@ int msp3410d_thread(void *data) state->watch_stereo = 0; state->nicam_on = 0; + /* wait for tuner to settle down after a channel change */ + if (msp_sleep(state, 200)) + goto restart; + if (msp_debug) v4l_dbg(2, msp_debug, client, "setting standard: %s (0x%04x)\n", msp_standard_std_name(std), std); -- cgit v1.2.3 From 8929143e56ef440073aef532a80f620b6fafdb65 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 1 Apr 2006 21:40:21 +0200 Subject: Remove obsolete commands from tvp5150.c From: Hans Verkuil - Remove old DECODER_ commands from tvp5150.c, replacing them with newer ones if appropriate. - Small VIDIOC_G_TUNER fixes in msp3400 and tuner. - Fix VIDIOC_S_TUNER support in em28xx. Signed-off-by: Hans Verkuil --- linux/drivers/media/video/em28xx/em28xx-video.c | 31 +----- linux/drivers/media/video/msp3400-driver.c | 2 +- linux/drivers/media/video/tuner-core.c | 2 + linux/drivers/media/video/tvp5150.c | 122 +----------------------- 4 files changed, 11 insertions(+), 146 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 748fd6200..60feda020 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1207,26 +1207,16 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp, case VIDIOC_G_TUNER: { struct v4l2_tuner *t = arg; - int status = 0; if (0 != t->index) return -EINVAL; memset(t, 0, sizeof(*t)); strcpy(t->name, "Tuner"); - t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM; - t->rangehigh = 0xffffffffUL; /* FIXME: set correct range */ -/* t->signal = 0xffff;*/ -/* em28xx_i2c_call_clients(dev,VIDIOC_G_TUNER,t);*/ - /* No way to get signal strength? */ mutex_lock(&dev->lock); - em28xx_i2c_call_clients(dev, DECODER_GET_STATUS, - &status); + /* let clients fill in the remainder of this struct */ + em28xx_i2c_call_clients(dev, cmd, t); mutex_unlock(&dev->lock); - t->signal = - (status & DECODER_STATUS_GOOD) != 0 ? 0xffff : 0; - em28xx_videodbg("VIDIO_G_TUNER: signal=%x, afc=%x\n", t->signal, t->afc); return 0; @@ -1234,26 +1224,13 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp, case VIDIOC_S_TUNER: { struct v4l2_tuner *t = arg; - int status = 0; if (0 != t->index) return -EINVAL; - memset(t, 0, sizeof(*t)); - strcpy(t->name, "Tuner"); - t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM; - t->rangehigh = 0xffffffffUL; /* FIXME: set correct range */ -/* t->signal = 0xffff; */ - /* No way to get signal strength? */ mutex_lock(&dev->lock); - em28xx_i2c_call_clients(dev, DECODER_GET_STATUS, - &status); + /* let clients handle this */ + em28xx_i2c_call_clients(dev, cmd, t); mutex_unlock(&dev->lock); - t->signal = - (status & DECODER_STATUS_GOOD) != 0 ? 0xffff : 0; - - em28xx_videodbg("VIDIO_S_TUNER: signal=%x, afc=%x\n", - t->signal, t->afc); return 0; } case VIDIOC_G_FREQUENCY: diff --git a/linux/drivers/media/video/msp3400-driver.c b/linux/drivers/media/video/msp3400-driver.c index be3a3bb70..79e2bdb11 100644 --- a/linux/drivers/media/video/msp3400-driver.c +++ b/linux/drivers/media/video/msp3400-driver.c @@ -755,7 +755,7 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) msp_detect_stereo(client); vt->audmode = state->audmode; vt->rxsubchans = state->rxsubchans; - vt->capability = V4L2_TUNER_CAP_STEREO | + vt->capability |= V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; break; } diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index 15282bbbb..35ba614c0 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -807,6 +807,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) switch_v4l2(); tuner->type = t->mode; + if (t->mode == V4L2_TUNER_ANALOG_TV) + tuner->capability |= V4L2_TUNER_CAP_NORM; if (t->mode != V4L2_TUNER_RADIO) { tuner->rangelow = tv_range[0] * 16; tuner->rangehigh = tv_range[1] * 16; diff --git a/linux/drivers/media/video/tvp5150.c b/linux/drivers/media/video/tvp5150.c index 543516347..aa7139089 100644 --- a/linux/drivers/media/video/tvp5150.c +++ b/linux/drivers/media/video/tvp5150.c @@ -907,7 +907,6 @@ static int tvp5150_command(struct i2c_client *c, case 0: case VIDIOC_INT_RESET: - case DECODER_INIT: tvp5150_reset(c); break; case VIDIOC_S_STD: @@ -1053,99 +1052,15 @@ static int tvp5150_command(struct i2c_client *c, #endif case VIDIOC_LOG_STATUS: - case DECODER_DUMP: dump_reg(c); break; - case DECODER_GET_CAPABILITIES: + case VIDIOC_G_TUNER: { - struct video_decoder_capability *cap = arg; - - cap->flags = VIDEO_DECODER_PAL | - VIDEO_DECODER_NTSC | - VIDEO_DECODER_SECAM | - VIDEO_DECODER_AUTO | VIDEO_DECODER_CCIR; - cap->inputs = 3; - cap->outputs = 1; - break; - } - case DECODER_GET_STATUS: - { - int *iarg = arg; - int status; - int res=0; - status = tvp5150_read(c, 0x88); - if(status&0x08){ - res |= DECODER_STATUS_COLOR; - } - if(status&0x04 && status&0x02){ - res |= DECODER_STATUS_GOOD; - } - *iarg=res; - break; - } - - case DECODER_SET_GPIO: - break; - - case DECODER_SET_VBI_BYPASS: - break; - - case DECODER_SET_NORM: - { - int *iarg = arg; - - switch (*iarg) { - - case VIDEO_MODE_NTSC: - break; - - case VIDEO_MODE_PAL: - break; - - case VIDEO_MODE_SECAM: - break; - - case VIDEO_MODE_AUTO: - break; - - default: - return -EINVAL; - - } - decoder->norm = *iarg; - break; - } - case DECODER_SET_INPUT: - { - int *iarg = arg; - if (*iarg < 0 || *iarg > 3) { - return -EINVAL; - } - - decoder->input = *iarg; - tvp5150_selmux(c, decoder->input); - - break; - } - case DECODER_SET_OUTPUT: - { - int *iarg = arg; - - /* not much choice of outputs */ - if (*iarg != 0) { - return -EINVAL; - } - break; - } - case DECODER_ENABLE_OUTPUT: - { - int *iarg = arg; - - decoder->enable = (*iarg != 0); - - tvp5150_selmux(c, decoder->input); + struct v4l2_tuner *vt = arg; + int status = tvp5150_read(c, 0x88); + vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0; break; } case VIDIOC_QUERYCTRL: @@ -1191,35 +1106,6 @@ static int tvp5150_command(struct i2c_client *c, return -EINVAL; } - case DECODER_SET_PICTURE: - { - struct video_picture *pic = arg; - if (decoder->bright != pic->brightness) { - /* We want 0 to 255 we get 0-65535 */ - decoder->bright = pic->brightness; - tvp5150_write(c, TVP5150_BRIGHT_CTL, - decoder->bright >> 8); - } - if (decoder->contrast != pic->contrast) { - /* We want 0 to 255 we get 0-65535 */ - decoder->contrast = pic->contrast; - tvp5150_write(c, TVP5150_CONTRAST_CTL, - decoder->contrast >> 8); - } - if (decoder->sat != pic->colour) { - /* We want 0 to 255 we get 0-65535 */ - decoder->sat = pic->colour; - tvp5150_write(c, TVP5150_SATURATION_CTL, - decoder->contrast >> 8); - } - if (decoder->hue != pic->hue) { - /* We want -128 to 127 we get 0-65535 */ - decoder->hue = pic->hue; - tvp5150_write(c, TVP5150_HUE_CTL, - (decoder->hue - 32768) >> 8); - } - break; - } default: return -EINVAL; } -- cgit v1.2.3 From 29fafe9bd09810fa43e5db62eebc23b985f68180 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 1 Apr 2006 16:00:41 -0500 Subject: Kconfig: fix VP-3054 Secondary I2C Bus build configuration menu dependencies From: Michael Krufky This patch fixes a dependency problem that affected the indentation order within the individual frontend selection support menus for cx88-dvb. - created a boolean dependency link for VIDEO_CX88_VP3054, so that it's tristate value will be the same as that of VIDEO_CX88_DVB. - VIDEO_CX88_VP3054 is automatically selected by VIDEO_CX88_DVB_ALL_FRONTENDS, but is otherwise selected by VIDEO_CX88_DVB_VP3054, offered as an option under VIDEO_CX88_DVB_MT352 Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/Kconfig | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/Kconfig b/linux/drivers/media/video/cx88/Kconfig index 2c586f903..630273992 100644 --- a/linux/drivers/media/video/cx88/Kconfig +++ b/linux/drivers/media/video/cx88/Kconfig @@ -1,3 +1,7 @@ +config VIDEO_CX88_VP3054 + tristate + depends on VIDEO_CX88_DVB && DVB_MT352 + config VIDEO_CX88 tristate "Conexant 2388x (bt878 successor) support" depends on VIDEO_DEV && PCI && I2C @@ -73,10 +77,11 @@ config VIDEO_CX88_DVB_MT352 This adds DVB-T support for cards based on the Connexant 2388x chip and the MT352 demodulator. -config VIDEO_CX88_VP3054 - tristate "VP-3054 Secondary I2C Bus Support" - default m - depends on DVB_MT352 +config VIDEO_CX88_DVB_VP3054 + bool "VP-3054 Secondary I2C Bus Support" + default y + depends on VIDEO_CX88_DVB_MT352 + select VIDEO_CX88_VP3054 ---help--- This adds DVB-T support for cards based on the Connexant 2388x chip and the MT352 demodulator, -- cgit v1.2.3 From dbe2e81959f002660345a0b09161b230df7765cf Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 1 Apr 2006 23:03:23 +0200 Subject: Make msp3400 routing defines more consistent From: Hans Verkuil Renamed various msp3400 routing defines to be more consistent and less confusing. Esp. the MSP_DSP_OUT defines were confusing since it is really a DSP input. Signed-off-by: Hans Verkuil --- linux/drivers/media/video/bt8xx/bttv-driver.c | 12 ++++++------ linux/drivers/media/video/em28xx/em28xx-cards.c | 4 ++-- linux/drivers/media/video/em28xx/em28xx-video.c | 2 +- linux/drivers/media/video/msp3400-kthreads.c | 4 ++-- linux/drivers/media/video/pvrusb2/pvrusb2-audio.c | 10 +++++----- 5 files changed, 16 insertions(+), 16 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/bt8xx/bttv-driver.c b/linux/drivers/media/video/bt8xx/bttv-driver.c index ea9f82586..9586f91e3 100644 --- a/linux/drivers/media/video/bt8xx/bttv-driver.c +++ b/linux/drivers/media/video/bt8xx/bttv-driver.c @@ -994,12 +994,12 @@ audio_mux(struct bttv *btv, int input, int mute) For now this is sufficient. */ switch (input) { case TVAUDIO_INPUT_RADIO: - route.input = MSP_INPUT(MSP_IN_SCART_2, MSP_IN_TUNER_1, - MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART); + route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); break; case TVAUDIO_INPUT_EXTERN: - route.input = MSP_INPUT(MSP_IN_SCART_1, MSP_IN_TUNER_1, - MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART); + route.input = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); break; case TVAUDIO_INPUT_INTERN: /* Yes, this is the same input as for RADIO. I doubt @@ -1007,8 +1007,8 @@ audio_mux(struct bttv *btv, int input, int mute) input is the BTTV_BOARD_AVERMEDIA98. I wonder how that was tested. My guess is that the whole INTERN input does not work. */ - route.input = MSP_INPUT(MSP_IN_SCART_2, MSP_IN_TUNER_1, - MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART); + route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); break; case TVAUDIO_INPUT_TUNER: default: diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 3622019e3..7892587d7 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -152,8 +152,8 @@ struct em28xx_board em28xx_boards[] = { },{ .type = EM28XX_VMUX_SVIDEO, .vmux = 2, - .amux = MSP_INPUT(MSP_IN_SCART_1, MSP_IN_TUNER_1, - MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART), + .amux = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART), }}, }, #ifdef CONFIG_XC3028 diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 60feda020..840178958 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -247,7 +247,7 @@ static void video_mux(struct em28xx *dev, int index) if (dev->i2s_speed) em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed); route.input = dev->ctl_ainput; - route.output = MSP_OUTPUT(MSP_OUT_SCART1_DA); + route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); /* Note: this is msp3400 specific */ em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, &route); ainput = EM28XX_AUDIO_SRC_TUNER; diff --git a/linux/drivers/media/video/msp3400-kthreads.c b/linux/drivers/media/video/msp3400-kthreads.c index aaaa26219..d016a3310 100644 --- a/linux/drivers/media/video/msp3400-kthreads.c +++ b/linux/drivers/media/video/msp3400-kthreads.c @@ -888,11 +888,11 @@ static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) break; } - if (in == MSP_DSP_OUT_TUNER) + if (in == MSP_DSP_IN_TUNER) source = (source << 8) | 0x20; /* the msp34x2g puts the MAIN_AVC, MAIN and AUX sources in 12, 13, 14 instead of 11, 12, 13. So we add one for that msp version. */ - else if (in >= MSP_DSP_OUT_MAIN_AVC && state->has_dolby_pro_logic) + else if (in >= MSP_DSP_IN_MAIN_AVC && state->has_dolby_pro_logic) source = ((in + 1) << 8) | matrix; else source = (in << 8) | matrix; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c index 79395d540..031412327 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -74,7 +74,7 @@ static void set_stereo(struct pvr2_msp3400_handler *ctxt) } route.input = MSP_INPUT_DEFAULT; - route.output = MSP_OUTPUT(MSP_OUT_SCART1_DA); + route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); switch (hdw->controls[PVR2_CID_INPUT].value) { case PVR2_CVAL_INPUT_TV: break; @@ -83,14 +83,14 @@ static void set_stereo(struct pvr2_msp3400_handler *ctxt) we're still using the tuner. */ /* HV: actually it is more likely to be the SCART2 input if the ivtv experience is any indication. */ - route.input = MSP_INPUT(MSP_IN_SCART_2, MSP_IN_TUNER_1, - MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART); + route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); break; case PVR2_CVAL_INPUT_SVIDEO: case PVR2_CVAL_INPUT_COMPOSITE: /* SCART 1 input */ - route.input = MSP_INPUT(MSP_IN_SCART_1, MSP_IN_TUNER_1, - MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART); + route.input = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); break; } pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route); -- cgit v1.2.3 From cbea0cdfe46b67fb696b0a045e8ace7dddf4ca49 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 1 Apr 2006 20:36:50 -0600 Subject: Split out PVR USB2 model 24xxx hardware support to a config option From: Mike Isely Since there are lingering stability problems with support of the newer PVR USB2 model 24xxx series hardware, I have isolate those changes with a config option. This commit leaves that option off. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/Kconfig | 18 ++++++++++++++++++ linux/drivers/media/video/pvrusb2/Makefile | 6 +++++- .../drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h | 2 ++ linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 8 ++++++++ .../media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c | 4 ++++ 5 files changed, 37 insertions(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/Kconfig b/linux/drivers/media/video/pvrusb2/Kconfig index b22ab7f5f..629dc4e05 100644 --- a/linux/drivers/media/video/pvrusb2/Kconfig +++ b/linux/drivers/media/video/pvrusb2/Kconfig @@ -13,6 +13,24 @@ config VIDEO_PVRUSB2 To compile this driver as a module, choose M here: the module will be called pvrusb2 +config VIDEO_PVRUSB2_24XXX + bool "Hauppauge WinTV-PVR USB2 support for 24xxx model series" + depends on VIDEO_PVRUSB2 && EXPERIMENTAL + ---help--- + This option enables inclusion of additional logic to operate + newer WinTV-PVR USB2 devices whose model number is of the + form "24xxx" (leading prefix of "24" followed by 3 digits). + To see if you may need this option, examine the white + sticker on the underside of your device. Enabling this + option will not harm support for older devices, however it + is a separate option because of the experimental nature of + this new feature. + + If you are in doubt, say N. + + Note: This feature is _very_ experimental. You have been + warned. + config VIDEO_PVRUSB2_SYSFS bool "pvrusb2 sysfs support" default y diff --git a/linux/drivers/media/video/pvrusb2/Makefile b/linux/drivers/media/video/pvrusb2/Makefile index b0d3064ef..53fccce4f 100644 --- a/linux/drivers/media/video/pvrusb2/Makefile +++ b/linux/drivers/media/video/pvrusb2/Makefile @@ -1,13 +1,17 @@ obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o +obj-pvrusb2-24xxx-$(CONFIG_VIDEO_PVRUSB2_24XXX) := \ + pvrusb2-cx2584x-v4l.o \ + pvrusb2-wm8775.o + pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ pvrusb2-audio.o pvrusb2-i2c-chips-v4l2.o \ pvrusb2-encoder.o pvrusb2-video-v4l.o \ pvrusb2-eeprom.o pvrusb2-tuner.o pvrusb2-demod.o \ pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \ pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \ - pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \ + $(obj-pvrusb2-24xxx-y) \ $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y) obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 189075bbe..9c5f0f74c 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -199,7 +199,9 @@ struct pvr2_decoder_ctrl { /* Known major hardware variants, keyed from device ID */ #define PVR2_HDW_TYPE_29XXX 0 +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX #define PVR2_HDW_TYPE_24XXX 1 +#endif /* This structure contains all state data directly needed to manipulate the hardware (as opposed to complying with a kernel diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 32bdaeba7..f0de1e154 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -38,7 +38,9 @@ struct usb_device_id pvr2_device_table[] = { [PVR2_HDW_TYPE_29XXX] = { USB_DEVICE(0x2040, 0x2900) }, +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX [PVR2_HDW_TYPE_24XXX] = { USB_DEVICE(0x2040, 0x2400) }, +#endif { } }; @@ -46,7 +48,9 @@ MODULE_DEVICE_TABLE(usb, pvr2_device_table); static const char *pvr2_device_names[] = { [PVR2_HDW_TYPE_29XXX] = "WinTV PVR USB2 Model Category 29xxxx", +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX [PVR2_HDW_TYPE_24XXX] = "WinTV PVR USB2 Model Category 24xxxx", +#endif }; static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = 0}; @@ -625,9 +629,11 @@ int pvr2_upload_firmware1(struct pvr2_hdw *hdw) static const char *fw_files_29xxx[] = { "v4l-pvrusb2-29xxx-01.fw", }; +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX static const char *fw_files_24xxx[] = { "v4l-pvrusb2-24xxx-01.fw", }; +#endif static const struct { const char **lst; unsigned int cnt; @@ -636,10 +642,12 @@ int pvr2_upload_firmware1(struct pvr2_hdw *hdw) fw_files_29xxx, sizeof(fw_files_29xxx)/sizeof(fw_files_29xxx[0]), }, +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX [PVR2_HDW_TYPE_24XXX] = { fw_files_24xxx, sizeof(fw_files_24xxx)/sizeof(fw_files_24xxx[0]), }, +#endif }; hdw->fw1_state = FW1_STATE_FAILED; // default result diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c index fc9d76792..89d0da053 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c @@ -28,8 +28,10 @@ #include "pvrusb2-tuner.h" #include "pvrusb2-demod.h" #include "pvrusb2-video-v4l.h" +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX #include "pvrusb2-cx2584x-v4l.h" #include "pvrusb2-wm8775.h" +#endif #define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) @@ -71,6 +73,7 @@ void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) return; } } +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX if (id == I2C_DRIVERID_CX25840) { if (pvr2_i2c_cx2584x_v4l_setup(hdw,cp)) { return; @@ -81,6 +84,7 @@ void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) return; } } +#endif if (id == I2C_DRIVERID_SAA711X) { if (pvr2_i2c_decoder_v4l_setup(hdw,cp)) { return; -- cgit v1.2.3 From 6499c4b8ab1554b94358bc6d69709e1c22d8b606 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 2 Apr 2006 04:14:57 -0300 Subject: Fix some errors on bttv_risc_overlay From: Duncan Sands There are tree mistakes on bttv_risc_overlay. 1) When skip_odd is true, the number of lines for which instructions are written is (height+1)/2, not height/2. 2) This occurs when clipping: the number of instruction bytes written can be as much as 8 + 12*nclips, not 8 + 8*nclips, as currently estimated. 3) Coverity check were wrong with nskips=0, since it means that it can clipped at most one line. Signed-off-by: Duncan Sands Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/bt8xx/bttv-risc.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/bt8xx/bttv-risc.c b/linux/drivers/media/video/bt8xx/bttv-risc.c index 53dfe0f4d..4401d7320 100644 --- a/linux/drivers/media/video/bt8xx/bttv-risc.c +++ b/linux/drivers/media/video/bt8xx/bttv-risc.c @@ -234,7 +234,7 @@ bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc, const struct bttv_format *fmt, struct bttv_overlay *ov, int skip_even, int skip_odd) { - int instructions,rc,line,maxy,start,end,skip,nskips; + int dwords,rc,line,maxy,start,end,skip,nskips; struct btcx_skiplist *skips; u32 *rp,ri,ra; u32 addr; @@ -243,12 +243,12 @@ bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc, if (NULL == (skips = kmalloc(sizeof(*skips) * ov->nclips,GFP_KERNEL))) return -ENOMEM; - /* estimate risc mem: worst case is (clip+1) * lines instructions + /* estimate risc mem: worst case is (1.5*clip+1) * lines instructions + sync + jump (all 2 dwords) */ - instructions = (ov->nclips + 1) * - ((skip_even || skip_odd) ? ov->w.height>>1 : ov->w.height); - instructions += 2; - if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*8)) < 0) { + dwords = (3 * ov->nclips + 2) * + ((skip_even || skip_odd) ? (ov->w.height+1)>>1 : ov->w.height); + dwords += 4; + if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,dwords*4)) < 0) { kfree(skips); return rc; } @@ -277,8 +277,6 @@ bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc, if (line > maxy) btcx_calc_skips(line, ov->w.width, &maxy, skips, &nskips, ov->clips, ov->nclips); - else - nskips = 0; /* write out risc code */ for (start = 0, skip = 0; start < ov->w.width; start = end) { -- cgit v1.2.3 From ccc9a7ea538fa702d897eead24eefc64bd4960d9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 2 Apr 2006 04:51:51 -0300 Subject: Kernel-sync patch From: Mauro Carvalho Chehab Mostly because of documentation renames from mkfrufy. Also, a small fix at cpia2. kernel-sync Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/Kconfig | 14 +++++++------- linux/drivers/media/video/cpia2/cpia2.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index 911da5210..98f7201cd 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -439,8 +439,8 @@ config USB_OV511 depends on USB && VIDEO_DEV ---help--- Say Y here if you want to connect this type of camera to your - computer's USB port. See for more - information and for a list of supported cameras. + computer's USB port. See + for more information and for a list of supported cameras. To compile this driver as a module, choose M here: the module will be called ov511. @@ -450,8 +450,8 @@ config USB_SE401 depends on USB && VIDEO_DEV ---help--- Say Y here if you want to connect this type of camera to your - computer's USB port. See for more - information and for a list of supported cameras. + computer's USB port. See + for more information and for a list of supported cameras. To compile this driver as a module, choose M here: the module will be called se401. @@ -464,8 +464,8 @@ config USB_STV680 ---help--- Say Y here if you want to connect this type of camera to your computer's USB port. This includes the Pencam line of cameras. - See for more information and for - a list of supported cameras. + See for more information + and for a list of supported cameras. To compile this driver as a module, choose M here: the module will be called stv680. @@ -483,7 +483,7 @@ config USB_W9968CF resolutions and framerates, but cannot be included in the official Linux kernel for performance purposes. - See for more informations. + See for more info. To compile this driver as a module, choose M here: the module will be called w9968cf. diff --git a/linux/drivers/media/video/cpia2/cpia2.h b/linux/drivers/media/video/cpia2/cpia2.h index 7480c1827..a53056c82 100644 --- a/linux/drivers/media/video/cpia2/cpia2.h +++ b/linux/drivers/media/video/cpia2/cpia2.h @@ -462,7 +462,7 @@ int cpia2_init_camera(struct camera_data *cam); int cpia2_allocate_buffers(struct camera_data *cam); void cpia2_free_buffers(struct camera_data *cam); long cpia2_read(struct camera_data *cam, - char __user *buf, unsigned long count, int noblock); + char *buf, unsigned long count, int noblock); unsigned int cpia2_poll(struct camera_data *cam, struct file *filp, poll_table *wait); int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma); -- cgit v1.2.3 From 3b4a71877b2fb7be1919cc2e0b1699e6b30e30a7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 2 Apr 2006 13:21:02 +0200 Subject: Improve line-in handling From: Hans Verkuil - improve handling of the EXTERN input: don't start an unnecessary carrier scan - improve the LOG_STATUS output - ensure that a carrier scan is started again when switching back to the tuner. - set correct prescale for L-NICAM Signed-off-by: Hans Verkuil --- linux/drivers/media/video/msp3400-driver.c | 23 +++++++++++----- linux/drivers/media/video/msp3400-kthreads.c | 40 +++++++++++++++++++--------- 2 files changed, 45 insertions(+), 18 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/msp3400-driver.c b/linux/drivers/media/video/msp3400-driver.c index 79e2bdb11..837894adb 100644 --- a/linux/drivers/media/video/msp3400-driver.c +++ b/linux/drivers/media/video/msp3400-driver.c @@ -726,22 +726,31 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) int sc1_out = rt->output & 0xf; int sc2_out = (rt->output >> 4) & 0xf; u16 val, reg; + int i; + int extern_input = 1; if (state->routing.input == rt->input && state->routing.output == rt->output) break; state->routing = *rt; + /* check if the tuner input is used */ + for (i = 0; i < 5; i++) { + if (((rt->input >> (4 + i * 4)) & 0xf) == 0) + extern_input = 0; + } + if (extern_input) + state->mode = MSP_MODE_EXTERN; + else + state->mode = MSP_MODE_AM_DETECT; msp_set_scart(client, sc_in, 0); msp_set_scart(client, sc1_out, 1); msp_set_scart(client, sc2_out, 2); msp_set_audmode(client); reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb; val = msp_read_dem(client, reg); - if (tuner != ((val >> 8) & 1)) { - msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); - /* wake thread when a new tuner input is chosen */ - msp_wake_thread(client); - } + msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); + /* wake thread when a new input is chosen */ + msp_wake_thread(client); break; } @@ -846,7 +855,9 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) case MSP_MODE_EXTERN: p = "External input"; break; default: p = "unknown"; break; } - if (state->opmode == OPMODE_MANUAL) { + if (state->mode == MSP_MODE_EXTERN) { + v4l_info(client, "Mode: %s\n", p); + } else if (state->opmode == OPMODE_MANUAL) { v4l_info(client, "Mode: %s (%s%s)\n", p, (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); diff --git a/linux/drivers/media/video/msp3400-kthreads.c b/linux/drivers/media/video/msp3400-kthreads.c index d016a3310..20702d71a 100644 --- a/linux/drivers/media/video/msp3400-kthreads.c +++ b/linux/drivers/media/video/msp3400-kthreads.c @@ -250,19 +250,21 @@ static void msp3400c_set_audmode(struct i2c_client *client) the hardware does not support SAP. So the rxsubchans combination of STEREO | LANG2 does not occur. */ - /* switch to mono if only mono is available */ - if (state->rxsubchans == V4L2_TUNER_SUB_MONO) - audmode = V4L2_TUNER_MODE_MONO; - /* if bilingual */ - else if (state->rxsubchans & V4L2_TUNER_SUB_LANG2) { - /* and mono or stereo, then fallback to lang1 */ - if (audmode == V4L2_TUNER_MODE_MONO || - audmode == V4L2_TUNER_MODE_STEREO) - audmode = V4L2_TUNER_MODE_LANG1; + if (state->mode != MSP_MODE_EXTERN) { + /* switch to mono if only mono is available */ + if (state->rxsubchans == V4L2_TUNER_SUB_MONO) + audmode = V4L2_TUNER_MODE_MONO; + /* if bilingual */ + else if (state->rxsubchans & V4L2_TUNER_SUB_LANG2) { + /* and mono or stereo, then fallback to lang1 */ + if (audmode == V4L2_TUNER_MODE_MONO || + audmode == V4L2_TUNER_MODE_STEREO) + audmode = V4L2_TUNER_MODE_LANG1; + } + /* if stereo, and audmode is not mono, then switch to stereo */ + else if (audmode != V4L2_TUNER_MODE_MONO) + audmode = V4L2_TUNER_MODE_STEREO; } - /* if stereo, and audmode is not mono, then switch to stereo */ - else if (audmode != V4L2_TUNER_MODE_MONO) - audmode = V4L2_TUNER_MODE_STEREO; /* switch demodulator */ switch (state->mode) { @@ -494,6 +496,7 @@ int msp3400c_thread(void *data) /* no carrier scan, just unmute */ v4l_dbg(1, msp_debug, client, "thread: no carrier scan\n"); state->scan_in_progress = 0; + state->rxsubchans = V4L2_TUNER_SUB_STEREO; msp_set_audio(client); continue; } @@ -986,6 +989,14 @@ int msp34xxg_thread(void *data) #endif break; + if (state->mode == MSP_MODE_EXTERN) { + /* no carrier scan needed, just unmute */ + v4l_dbg(1, msp_debug, client, "thread: no carrier scan\n"); + state->scan_in_progress = 0; + msp_set_audio(client); + continue; + } + /* setup the chip*/ msp34xxg_reset(client); state->std = state->radio ? 0x40 : msp_standard; @@ -1017,6 +1028,11 @@ int msp34xxg_thread(void *data) v4l_dbg(1, msp_debug, client, "detected standard: %s (0x%04x)\n", msp_standard_std_name(state->std), state->std); + if (state->std == 9) { + /* AM NICAM mode */ + msp_write_dsp(client, 0x0e, 0x7c00); + } + /* unmute: dispatch sound to scart output, set scart volume */ msp_set_audio(client); -- cgit v1.2.3 From 33ba11f17dbf2c000c797475db2c6b68b7d33662 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 2 Apr 2006 13:42:32 +0200 Subject: Delimit LOG_STATUS output with standardized lines From: Hans Verkuil Add standard lines before/after the LOG_STATUS output: this allows easy reading and parsing from the kernel log with e.g. ivtvctl (to be added to v4l-dvb at a later date). Signed-off-by: Hans Verkuil --- linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index fe7045e8f..56d15c10d 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -673,7 +673,12 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_LOG_STATUS: { + int nr = pvr2_hdw_get_unit_number(hdw); + + printk(KERN_INFO "pvrusb2: ================= START STATUS CARD #%d =================\n", nr); pvr2_hdw_trigger_module_log(hdw); + printk(KERN_INFO "pvrusb2: ================== END STATUS CARD #%d ==================\n", nr); + ret = 0; break; } -- cgit v1.2.3 From 1022e152b0486dbbc19e49c9bbbdec12a0637418 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 2 Apr 2006 09:07:01 -0300 Subject: [PATCH] mark f_ops const in the inode From: Arjan van de Ven Mark the f_ops members of inodes as const, as well as fix the ripple-through this causes by places that copy this f_ops and then "do stuff" with it. kernel-sync Signed-off-by: Arjan van de Ven Signed-off-by: Alexey Dobriyan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- linux/drivers/media/dvb/dvb-core/dvbdev.c | 2 +- linux/drivers/media/video/videodev.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-core/dvbdev.c b/linux/drivers/media/dvb/dvb-core/dvbdev.c index 123ea8344..85d6a6c65 100644 --- a/linux/drivers/media/dvb/dvb-core/dvbdev.c +++ b/linux/drivers/media/dvb/dvb-core/dvbdev.c @@ -93,7 +93,7 @@ static int dvb_device_open(struct inode *inode, struct file *file) if (dvbdev && dvbdev->fops) { int err = 0; - struct file_operations *old_fops; + const struct file_operations *old_fops; file->private_data = dvbdev; old_fops = file->f_op; diff --git a/linux/drivers/media/video/videodev.c b/linux/drivers/media/video/videodev.c index 8747de34a..e54b8d9f7 100644 --- a/linux/drivers/media/video/videodev.c +++ b/linux/drivers/media/video/videodev.c @@ -111,7 +111,7 @@ static int video_open(struct inode *inode, struct file *file) unsigned int minor = iminor(inode); int err = 0; struct video_device *vfl; - struct file_operations *old_fops; + const struct file_operations *old_fops; if(minor>=VIDEO_NUM_DEVICES) return -ENODEV; -- cgit v1.2.3 From f538287daa555411910d95317990cce75e57225a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 2 Apr 2006 09:35:05 -0300 Subject: cpia2: fix function prototype From: Randy Dunlap Fix address space warning (from sparse): drivers/media/video/cpia2/cpia2_core.c:2355:6: error: symbol 'cpia2_read' redeclared with different type (originally declared at drivers/media/video/cpia2/cpia2.h:458) - incompatible argument 2 (different address spaces) kernel-sync Signed-off-by: Randy Dunlap Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/cpia2/cpia2.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cpia2/cpia2.h b/linux/drivers/media/video/cpia2/cpia2.h index a53056c82..7480c1827 100644 --- a/linux/drivers/media/video/cpia2/cpia2.h +++ b/linux/drivers/media/video/cpia2/cpia2.h @@ -462,7 +462,7 @@ int cpia2_init_camera(struct camera_data *cam); int cpia2_allocate_buffers(struct camera_data *cam); void cpia2_free_buffers(struct camera_data *cam); long cpia2_read(struct camera_data *cam, - char *buf, unsigned long count, int noblock); + char __user *buf, unsigned long count, int noblock); unsigned int cpia2_poll(struct camera_data *cam, struct file *filp, poll_table *wait); int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma); -- cgit v1.2.3 From 7d27e5f3b2bbe8ca82a41dce5203a67d7595ab09 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 2 Apr 2006 17:50:42 +0200 Subject: Add support for VIDIOC_INT_S_CRYSTAL_FREQ internal command. From: Hans Verkuil Some saa7115-based cards use a different crystal frequency and a different audio clock generation. Add a new VIDIOC_INT_S_CRYSTAL_FREQ command to be able to set these values. Also change the default APLL setting to 0. It makes no sense to have the audio clock independent from the video clock, this can lead to audio/video synchronization problems. Setting this to 0 is also consistent with the old saa7114.c source and the way the Hauppauge Windows driver sets it. Signed-off-by: Hans Verkuil --- linux/drivers/media/video/saa7115.c | 51 +++++++++++++++++++++++++++------ linux/drivers/media/video/v4l2-common.c | 9 +++++- 2 files changed, 51 insertions(+), 9 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/saa7115.c b/linux/drivers/media/video/saa7115.c index 1ecfacea5..6c40aa397 100644 --- a/linux/drivers/media/video/saa7115.c +++ b/linux/drivers/media/video/saa7115.c @@ -83,6 +83,10 @@ struct saa7115_state { int sat; enum v4l2_chip_ident ident; u32 audclk_freq; + u32 crystal_freq; + u8 ucgc; + u8 cgcdiv; + u8 apll; }; /* ----------------------------------------------------------------------- */ @@ -388,10 +392,6 @@ static const unsigned char saa7113_init_auto_input[] = { #endif static const unsigned char saa7115_init_misc[] = { - 0x38, 0x03, /* audio stuff */ - 0x39, 0x10, - 0x3a, 0x08, - 0x81, 0x01, /* reg 0x15,0x16 define blanking window */ 0x82, 0x00, 0x83, 0x01, /* I port settings */ @@ -597,6 +597,7 @@ static int saa7115_set_audio_clock_freq(struct i2c_client *client, u32 freq) u32 acni; u32 hz; u64 f; + u8 acc = 0; /* reg 0x3a, audio clock control */ v4l_dbg(1, debug, client, "set audio clock freq: %d\n", freq); @@ -604,18 +605,34 @@ static int saa7115_set_audio_clock_freq(struct i2c_client *client, u32 freq) if (freq < 32000 || freq > 48000) return -EINVAL; + /* The saa7113 has no audio clock */ + if (state->ident == V4L2_IDENT_SAA7113) + return 0; + /* hz is the refresh rate times 100 */ hz = (state->std & V4L2_STD_525_60) ? 5994 : 5000; /* acpf = (256 * freq) / field_frequency == (256 * 100 * freq) / hz */ acpf = (25600 * freq) / hz; /* acni = (256 * freq * 2^23) / crystal_frequency = (freq * 2^(8+23)) / crystal_frequency = - (freq << 31) / 32.11 MHz */ + (freq << 31) / crystal_frequency */ f = freq; f = f << 31; - do_div(f, 32110000); + do_div(f, state->crystal_freq); acni = f; + if (state->ucgc) { + acpf = acpf * state->cgcdiv / 16; + acni = acni * state->cgcdiv / 16; + acc = 0x80; + if (state->cgcdiv == 3) + acc |= 0x40; + } + if (state->apll) + acc |= 0x08; + saa7115_write(client, 0x38, 0x03); + saa7115_write(client, 0x39, 0x10); + saa7115_write(client, 0x3a, acc); saa7115_write(client, 0x30, acpf & 0xff); saa7115_write(client, 0x31, (acpf >> 8) & 0xff); saa7115_write(client, 0x32, (acpf >> 16) & 0x03); @@ -1273,6 +1290,21 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar } break; + case VIDIOC_INT_S_CRYSTAL_FREQ: + { + struct v4l2_crystal_freq *freq = arg; + + if (freq->freq != SAA7115_FREQ_32_11_MHZ && + freq->freq != SAA7115_FREQ_24_576_MHZ) + return -EINVAL; + state->crystal_freq = freq->freq; + state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; + state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; + state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0; + saa7115_set_audio_clock_freq(client, state->audclk_freq); + break; + } + case VIDIOC_INT_DECODE_VBI_LINE: saa7115_decode_vbi_line(client, arg); break; @@ -1422,10 +1454,13 @@ static int saa7115_attach(struct i2c_adapter *adapter, int address, int kind) v4l_dbg(1, debug, client, "writing init values\n"); /* init to 60hz/48khz */ - if (state->ident == V4L2_IDENT_SAA7113) + if (state->ident == V4L2_IDENT_SAA7113) { + state->crystal_freq = SAA7115_FREQ_24_576_MHZ; saa7115_writeregs(client, saa7113_init_auto_input); - else + } else { + state->crystal_freq = SAA7115_FREQ_32_11_MHZ; saa7115_writeregs(client, saa7115_init_auto_input); + } saa7115_writeregs(client, saa7115_init_misc); saa7115_writeregs(client, saa7115_cfg_60hz_fullres_x); saa7115_writeregs(client, saa7115_cfg_60hz_fullres_y); diff --git a/linux/drivers/media/video/v4l2-common.c b/linux/drivers/media/video/v4l2-common.c index 5f71edf32..4b7ef8350 100644 --- a/linux/drivers/media/video/v4l2-common.c +++ b/linux/drivers/media/video/v4l2-common.c @@ -351,7 +351,8 @@ static const char *v4l2_int_ioctls[] = { [_IOC_NR(VIDIOC_INT_S_AUDIO_ROUTING)] = "VIDIOC_INT_S_AUDIO_ROUTING", [_IOC_NR(VIDIOC_INT_G_AUDIO_ROUTING)] = "VIDIOC_INT_G_AUDIO_ROUTING", [_IOC_NR(VIDIOC_INT_S_VIDEO_ROUTING)] = "VIDIOC_INT_S_VIDEO_ROUTING", - [_IOC_NR(VIDIOC_INT_G_VIDEO_ROUTING)] = "VIDIOC_INT_G_VIDEO_ROUTING" + [_IOC_NR(VIDIOC_INT_G_VIDEO_ROUTING)] = "VIDIOC_INT_G_VIDEO_ROUTING", + [_IOC_NR(VIDIOC_INT_S_CRYSTAL_FREQ)] = "VIDIOC_INT_S_CRYSTAL_FREQ" }; #define V4L2_INT_IOCTLS ARRAY_SIZE(v4l2_int_ioctls) @@ -698,6 +699,12 @@ void v4l_printk_ioctl_arg(char *s,unsigned int cmd, void *arg) printk ("%s: input=0x%x, output=0x%x\n", s, p->input, p->output); break; } + case VIDIOC_INT_S_CRYSTAL_FREQ: + { + struct v4l2_crystal_freq *p=arg; + printk ("%s: freq=%u, flags=0x%x\n", s, p->freq, p->flags); + break; + } case VIDIOC_G_SLICED_VBI_CAP: { struct v4l2_sliced_vbi_cap *p=arg; -- cgit v1.2.3 From cb228051a31b5d0f78a3b3e7e8a960d1658afcf5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 2 Apr 2006 18:35:00 +0200 Subject: Fix video input setting of em28xx, use _INT_S_VIDEO_ROUTING in tvp5150 From: Hans Verkuil - Use new routing input defines in em28xx-cards.c - Fix S-Video settings for tvp5150-based cards (input was copied from saa7115 based cards and worked only because S-Video was selected in the default: case) - Replace VIDIOC_S_INPUT by VIDIOC_INT_S_VIDEO_ROUTING in em28xx-video.c - Remove the now obsolete VIDIOC_S_INPUT handler in saa7115.c - Add VIDIOC_INT_G/S_VIDEO_ROUTING in tvp5150.c - Add new media/tvp5150.h with the routing defines. Signed-off-by: Hans Verkuil --- linux/drivers/media/video/em28xx/em28xx-cards.c | 76 +++++++++++++------------ linux/drivers/media/video/em28xx/em28xx-video.c | 18 +++--- linux/drivers/media/video/saa7115.c | 28 --------- linux/drivers/media/video/tvp5150.c | 45 +++++++++------ 4 files changed, 79 insertions(+), 88 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 7892587d7..6b5a8a6b1 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -30,6 +30,8 @@ #include "compat.h" #include #include +#include +#include #include #include #include @@ -47,11 +49,11 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_SAA7113, .input = {{ .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 0, + .vmux = SAA7115_COMPOSITE0, .amux = 1, },{ .type = EM28XX_VMUX_SVIDEO, - .vmux = 9, + .vmux = SAA7115_SVIDEO3, .amux = 1, }}, }, @@ -65,11 +67,11 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_SAA7113, .input = {{ .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 0, + .vmux = SAA7115_COMPOSITE0, .amux = 1, },{ .type = EM28XX_VMUX_SVIDEO, - .vmux = 9, + .vmux = SAA7115_SVIDEO3, .amux = 1, }}, }, @@ -83,11 +85,11 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_SAA7113, .input = {{ .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 0, + .vmux = SAA7115_COMPOSITE0, .amux = 1, },{ .type = EM28XX_VMUX_SVIDEO, - .vmux = 9, + .vmux = SAA7115_SVIDEO3, .amux = 1, }}, }, @@ -101,15 +103,15 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_SAA7113, .input = {{ .type = EM28XX_VMUX_TELEVISION, - .vmux = 2, + .vmux = SAA7115_COMPOSITE2, .amux = 1, },{ .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 0, + .vmux = SAA7115_COMPOSITE0, .amux = 1, },{ .type = EM28XX_VMUX_SVIDEO, - .vmux = 9, + .vmux = SAA7115_SVIDEO3, .amux = 1, }}, }, @@ -123,15 +125,15 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_SAA7113, .input = {{ .type = EM28XX_VMUX_TELEVISION, - .vmux = 2, + .vmux = SAA7115_COMPOSITE2, .amux = 0, },{ .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 0, + .vmux = SAA7115_COMPOSITE0, .amux = 1, },{ .type = EM28XX_VMUX_SVIDEO, - .vmux = 9, + .vmux = SAA7115_SVIDEO3, .amux = 1, }}, }, @@ -147,11 +149,11 @@ struct em28xx_board em28xx_boards[] = { /*FIXME: S-Video not tested */ .input = {{ .type = EM28XX_VMUX_TELEVISION, - .vmux = 0, + .vmux = TVP5150_COMPOSITE0, .amux = MSP_INPUT_DEFAULT, },{ .type = EM28XX_VMUX_SVIDEO, - .vmux = 2, + .vmux = TVP5150_SVIDEO, .amux = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, MSP_DSP_IN_SCART, MSP_DSP_IN_SCART), }}, @@ -167,15 +169,15 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_TVP5150, .input = {{ .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 2, + .vmux = TVP5150_COMPOSITE1, .amux = 1, },{ .type = EM28XX_VMUX_TELEVISION, - .vmux = 0, + .vmux = TVP5150_COMPOSITE0, .amux = 0, },{ .type = EM28XX_VMUX_SVIDEO, - .vmux = 9, + .vmux = TVP5150_SVIDEO, .amux = 1, }}, }, @@ -189,15 +191,15 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_TVP5150, .input = {{ .type = EM28XX_VMUX_TELEVISION, - .vmux = 0, + .vmux = TVP5150_COMPOSITE0, .amux = 0, },{ .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 2, + .vmux = TVP5150_COMPOSITE1, .amux = 1, },{ .type = EM28XX_VMUX_SVIDEO, - .vmux = 9, + .vmux = TVP5150_SVIDEO, .amux = 1, }}, }, @@ -213,15 +215,15 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_TVP5150, .input = {{ .type = EM28XX_VMUX_TELEVISION, - .vmux = 0, + .vmux = TVP5150_COMPOSITE0, .amux = 0, },{ .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 2, + .vmux = TVP5150_COMPOSITE1, .amux = 1, },{ .type = EM28XX_VMUX_SVIDEO, - .vmux = 9, + .vmux = TVP5150_SVIDEO, .amux = 1, }}, }, @@ -236,15 +238,15 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_SAA7114, .input = {{ .type = EM28XX_VMUX_TELEVISION, - .vmux = 4, + .vmux = SAA7115_COMPOSITE4, .amux = 0, },{ .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 0, + .vmux = SAA7115_COMPOSITE0, .amux = 1, },{ .type = EM28XX_VMUX_SVIDEO, - .vmux = 9, + .vmux = SAA7115_SVIDEO3, .amux = 1, }}, }, @@ -259,15 +261,15 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_SAA7113, .input = {{ .type = EM28XX_VMUX_TELEVISION, - .vmux = 2, + .vmux = SAA7115_COMPOSITE2, .amux = 0, },{ .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 0, + .vmux = SAA7115_COMPOSITE0, .amux = 1, },{ .type = EM28XX_VMUX_SVIDEO, - .vmux = 9, + .vmux = SAA7115_SVIDEO3, .amux = 1, }}, }, @@ -282,15 +284,15 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_SAA7113, .input = {{ .type = EM28XX_VMUX_TELEVISION, - .vmux = 2, + .vmux = SAA7115_COMPOSITE2, .amux = 0, },{ .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 0, + .vmux = SAA7115_COMPOSITE0, .amux = 1, },{ .type = EM28XX_VMUX_SVIDEO, - .vmux = 9, + .vmux = SAA7115_SVIDEO3, .amux = 1, }}, }, @@ -305,15 +307,15 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_SAA7113, .input = {{ .type = EM28XX_VMUX_TELEVISION, - .vmux = 2, + .vmux = SAA7115_COMPOSITE2, .amux = 0, },{ .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 0, + .vmux = SAA7115_COMPOSITE0, .amux = 1, },{ .type = EM28XX_VMUX_SVIDEO, - .vmux = 9, + .vmux = SAA7115_SVIDEO3, .amux = 1, }}, }, @@ -325,11 +327,11 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_SAA7113, .input = {{ .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 0, + .vmux = SAA7115_COMPOSITE0, .amux = 1, },{ .type = EM28XX_VMUX_SVIDEO, - .vmux = 9, + .vmux = SAA7115_SVIDEO3, .amux = 1, }}, }, diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 840178958..836e7c7e5 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -192,8 +192,12 @@ static int em28xx_config(struct em28xx *dev) static void em28xx_config_i2c(struct em28xx *dev) { struct v4l2_frequency f; + struct v4l2_routing route; + + route.input = INPUT(dev->ctl_input)->vmux; + route.output = 0; em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, NULL); - em28xx_i2c_call_clients(dev, VIDIOC_S_INPUT, &dev->ctl_input); + em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL); /* configure tuner */ @@ -231,19 +235,19 @@ static void em28xx_empty_framequeues(struct em28xx *dev) static void video_mux(struct em28xx *dev, int index) { - int input, ainput; + int ainput; + struct v4l2_routing route; - input = INPUT(index)->vmux; + route.input = INPUT(index)->vmux; + route.output = 0; dev->ctl_input = index; dev->ctl_ainput = INPUT(index)->amux; - em28xx_i2c_call_clients(dev, VIDIOC_S_INPUT, &input); + em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); - em28xx_videodbg("Setting input index=%d, vmux=%d, amux=%d\n",index,input,dev->ctl_ainput); + em28xx_videodbg("Setting input index=%d, vmux=%d, amux=%d\n",index,route.input,dev->ctl_ainput); if (dev->has_msp34xx) { - struct v4l2_routing route; - if (dev->i2s_speed) em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed); route.input = dev->ctl_ainput; diff --git a/linux/drivers/media/video/saa7115.c b/linux/drivers/media/video/saa7115.c index 6c40aa397..ea7c7c65b 100644 --- a/linux/drivers/media/video/saa7115.c +++ b/linux/drivers/media/video/saa7115.c @@ -1251,34 +1251,6 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar break; } - case VIDIOC_G_INPUT: - *(int *)arg = state->input; - break; - - case VIDIOC_S_INPUT: - v4l_dbg(1, debug, client, "decoder set input %d\n", *iarg); - /* inputs from 0-9 are available */ - if (*iarg < 0 || *iarg > 9) { - return -EINVAL; - } - - if (state->input == *iarg) - break; - v4l_dbg(1, debug, client, "now setting %s input\n", - *iarg >= 6 ? "S-Video" : "Composite"); - state->input = *iarg; - - /* select mode */ - saa7115_write(client, 0x02, - (saa7115_read(client, 0x02) & 0xf0) | - state->input); - - /* bypass chrominance trap for modes 6..9 */ - saa7115_write(client, 0x09, - (saa7115_read(client, 0x09) & 0x7f) | - (state->input < 6 ? 0x0 : 0x80)); - break; - case VIDIOC_STREAMON: case VIDIOC_STREAMOFF: v4l_dbg(1, debug, client, "%s output\n", diff --git a/linux/drivers/media/video/tvp5150.c b/linux/drivers/media/video/tvp5150.c index aa7139089..7cafcd52b 100644 --- a/linux/drivers/media/video/tvp5150.c +++ b/linux/drivers/media/video/tvp5150.c @@ -11,6 +11,7 @@ #include #include #include +#include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) #include #include @@ -116,7 +117,7 @@ struct tvp5150 { struct i2c_client *client; v4l2_std_id norm; /* Current set standard */ - int input; + struct v4l2_routing route; int enable; int bright; int contrast; @@ -314,29 +315,26 @@ static void dump_reg(struct i2c_client *c) /**************************************************************************** Basic functions ****************************************************************************/ -enum tvp5150_input { - TVP5150_ANALOG_CH0 = 0, - TVP5150_SVIDEO = 1, - TVP5150_ANALOG_CH1 = 2, - TVP5150_BLACK_SCREEN = 8 -}; -static inline void tvp5150_selmux(struct i2c_client *c, - enum tvp5150_input input) +static inline void tvp5150_selmux(struct i2c_client *c) { int opmode=0; - struct tvp5150 *decoder = i2c_get_clientdata(c); + int input = 0; - if (!decoder->enable) - input |= TVP5150_BLACK_SCREEN; + if ((decoder->route.output & TVP5150_BLACK_SCREEN) || !decoder->enable) + input = 8; switch (input) { - case TVP5150_ANALOG_CH0: - case TVP5150_ANALOG_CH1: + case TVP5150_COMPOSITE1: + input |= 2; + /* fall through */ + case TVP5150_COMPOSITE0: opmode=0x30; /* TV Mode */ break; + case TVP5150_SVIDEO: default: + input |= 1; opmode=0; /* Auto Mode */ break; } @@ -839,7 +837,7 @@ static inline void tvp5150_reset(struct i2c_client *c) tvp5150_vdp_init(c, vbi_ram_default); /* Selects decoder input */ - tvp5150_selmux(c, decoder->input); + tvp5150_selmux(c); /* Initializes TVP5150 to stream enabled values */ tvp5150_write_inittab(c, tvp5150_init_enable); @@ -909,6 +907,21 @@ static int tvp5150_command(struct i2c_client *c, case VIDIOC_INT_RESET: tvp5150_reset(c); break; + case VIDIOC_INT_G_VIDEO_ROUTING: + { + struct v4l2_routing *route = arg; + + *route = decoder->route; + break; + } + case VIDIOC_INT_S_VIDEO_ROUTING: + { + struct v4l2_routing *route = arg; + + decoder->route = *route; + tvp5150_selmux(c); + break; + } case VIDIOC_S_STD: if (decoder->norm == *(v4l2_std_id *)arg) break; @@ -1167,7 +1180,7 @@ static int tvp5150_detect_client(struct i2c_adapter *adapter, rv = i2c_attach_client(c); core->norm = V4L2_STD_ALL; /* Default is autodetect */ - core->input = 2; + core->route.input = TVP5150_COMPOSITE1; core->enable = 1; core->bright = 32768; core->contrast = 32768; -- cgit v1.2.3 From 8f854188187cc4a15f5f3f775fd4aa0b1eff2457 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 2 Apr 2006 18:11:10 -0400 Subject: removed trailing newlines From: Michael Krufky Signed-off-by: Michael Krufky --- linux/drivers/media/common/ir-functions.c | 1 - linux/drivers/media/common/ir-keymaps.c | 1 - 2 files changed, 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/common/ir-functions.c b/linux/drivers/media/common/ir-functions.c index 8b7dd6e3e..3b988b012 100644 --- a/linux/drivers/media/common/ir-functions.c +++ b/linux/drivers/media/common/ir-functions.c @@ -278,4 +278,3 @@ EXPORT_SYMBOL_GPL(ir_decode_pulsedistance); * c-basic-offset: 8 * End: */ - diff --git a/linux/drivers/media/common/ir-keymaps.c b/linux/drivers/media/common/ir-keymaps.c index 03c4a7c50..1cdd5b0df 100644 --- a/linux/drivers/media/common/ir-keymaps.c +++ b/linux/drivers/media/common/ir-keymaps.c @@ -1476,4 +1476,3 @@ IR_KEYTAB_TYPE ir_codes_hauppauge_new[IR_KEYTAB_SIZE] = { }; EXPORT_SYMBOL_GPL(ir_codes_hauppauge_new); - -- cgit v1.2.3 From b1afc839e4484e8b37d6a818f7135d9fb3f476dd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 2 Apr 2006 22:40:33 -0300 Subject: Clean up some cruft in or51132 frontend From: Trent Piepho There is some old code in the or51133 firmware loading function that has no effect. Left a comment to in case it helps someone trying to reverse engineer the chip. kernel-sync with V4L/DVB (3708a) Signed-off-by: Trent Piepho Signed-off-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/frontends/or51132.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/or51132.c b/linux/drivers/media/dvb/frontends/or51132.c index 80e0f2812..dca1de555 100644 --- a/linux/drivers/media/dvb/frontends/or51132.c +++ b/linux/drivers/media/dvb/frontends/or51132.c @@ -106,9 +106,8 @@ static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware { struct or51132_state* state = fe->demodulator_priv; static u8 run_buf[] = {0x7F,0x01}; - static u8 get_ver_buf[] = {0x04,0x00,0x30,0x00,0x00}; - u8 rec_buf[14]; - u8 cmd_buf[14]; + u8 rec_buf[8]; + u8 cmd_buf[3]; u32 firmwareAsize, firmwareBsize; int i,ret; @@ -157,7 +156,6 @@ static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware cmd_buf[0] = 0x10; cmd_buf[1] = 0x10; cmd_buf[2] = 0x00; - cmd_buf[3] = 0x00; msleep(20); /* 20ms */ if ((ret = i2c_writebytes(state,state->config->demod_address, cmd_buf,3))) { @@ -167,8 +165,6 @@ static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware cmd_buf[0] = 0x04; cmd_buf[1] = 0x17; - cmd_buf[2] = 0x00; - cmd_buf[3] = 0x00; msleep(20); /* 20ms */ if ((ret = i2c_writebytes(state,state->config->demod_address, cmd_buf,2))) { @@ -178,8 +174,6 @@ static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware cmd_buf[0] = 0x00; cmd_buf[1] = 0x00; - cmd_buf[2] = 0x00; - cmd_buf[3] = 0x00; msleep(20); /* 20ms */ if ((ret = i2c_writebytes(state,state->config->demod_address, cmd_buf,2))) { @@ -189,7 +183,11 @@ static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware for(i=0;i<4;i++) { msleep(20); /* 20ms */ - get_ver_buf[4] = i+1; + /* One apon a time, this command might have had something + to do with getting the firmware version, but it's + not used anymore: + {0x04,0x00,0x30,0x00,i+1} */ + /* Read 8 bytes, two bytes at a time */ if ((ret = i2c_readbytes(state,state->config->demod_address, &rec_buf[i*2],2))) { printk(KERN_WARNING @@ -208,7 +206,6 @@ static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware cmd_buf[0] = 0x10; cmd_buf[1] = 0x00; cmd_buf[2] = 0x00; - cmd_buf[3] = 0x00; msleep(20); /* 20ms */ if ((ret = i2c_writebytes(state,state->config->demod_address, cmd_buf,3))) { -- cgit v1.2.3 From 30b707f350a19c509cef7bc77307e7103ff0905c Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 3 Apr 2006 00:29:09 -0400 Subject: fix spelling error / typo in comments From: Michael Krufky Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/frontends/or51132.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/or51132.c b/linux/drivers/media/dvb/frontends/or51132.c index dca1de555..a2a19aeb3 100644 --- a/linux/drivers/media/dvb/frontends/or51132.c +++ b/linux/drivers/media/dvb/frontends/or51132.c @@ -183,7 +183,7 @@ static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware for(i=0;i<4;i++) { msleep(20); /* 20ms */ - /* One apon a time, this command might have had something + /* Once upon a time, this command might have had something to do with getting the firmware version, but it's not used anymore: {0x04,0x00,0x30,0x00,i+1} */ -- cgit v1.2.3 From e375e2853810cf2cc8f734369c90b7c007d72a3f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 3 Apr 2006 07:53:40 -0300 Subject: Change all emails to the currently used one. From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/bt8xx/bttv-cards.c | 2 +- linux/drivers/media/video/cx88/cx88-alsa.c | 4 ++-- linux/drivers/media/video/em28xx/em28xx-cards.c | 2 +- linux/drivers/media/video/em28xx/em28xx-core.c | 2 +- linux/drivers/media/video/em28xx/em28xx-i2c.c | 2 +- linux/drivers/media/video/em28xx/em28xx-input.c | 2 +- linux/drivers/media/video/em28xx/em28xx-video.c | 4 ++-- linux/drivers/media/video/em28xx/em28xx.h | 2 +- linux/drivers/media/video/tea5761.c | 2 +- linux/drivers/media/video/tea5767.c | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/bt8xx/bttv-cards.c b/linux/drivers/media/video/bt8xx/bttv-cards.c index 51e47b7cc..f3e3c624d 100644 --- a/linux/drivers/media/video/bt8xx/bttv-cards.c +++ b/linux/drivers/media/video/bt8xx/bttv-cards.c @@ -2786,7 +2786,7 @@ struct tvcard bttv_tvcards[] = { }, /* ---- card 0x88---------------------------------- */ [BTTV_BOARD_ACORP_Y878F] = { - /* Mauro Carvalho Chehab */ + /* Mauro Carvalho Chehab */ .name = "Acorp Y878F", .video_inputs = 3, .audio_inputs = 1, diff --git a/linux/drivers/media/video/cx88/cx88-alsa.c b/linux/drivers/media/video/cx88/cx88-alsa.c index 80aef2421..df82e8251 100644 --- a/linux/drivers/media/video/cx88/cx88-alsa.c +++ b/linux/drivers/media/video/cx88/cx88-alsa.c @@ -5,7 +5,7 @@ * PCI function #1 of the cx2388x. * * (c) 2005,2006 Ricardo Cerqueira - * (c) 2005 Mauro Carvalho Chehab + * (c) 2005 Mauro Carvalho Chehab * Based on a dummy cx88 module by Gerd Knorr * Based on dummy.c by Jaroslav Kysela * @@ -137,7 +137,7 @@ MODULE_PARM_DESC(index, "Index value for cx88x capture interface(s)."); MODULE_DESCRIPTION("ALSA driver module for cx2388x based TV cards"); MODULE_AUTHOR("Ricardo Cerqueira"); -MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_AUTHOR("Mauro Carvalho Chehab "); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{Conexant,23881}," "{{Conexant,23882}," diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 6b5a8a6b1..84b764b7d 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -3,7 +3,7 @@ Copyright (C) 2005 Ludovico Cavedon Markus Rechberger - Mauro Carvalho Chehab + Mauro Carvalho Chehab Sascha Sommer This program is free software; you can redistribute it and/or modify diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index febf1188e..91422b642 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -3,7 +3,7 @@ Copyright (C) 2005 Ludovico Cavedon Markus Rechberger - Mauro Carvalho Chehab + Mauro Carvalho Chehab Sascha Sommer This program is free software; you can redistribute it and/or modify diff --git a/linux/drivers/media/video/em28xx/em28xx-i2c.c b/linux/drivers/media/video/em28xx/em28xx-i2c.c index b531f2839..c077c39c2 100644 --- a/linux/drivers/media/video/em28xx/em28xx-i2c.c +++ b/linux/drivers/media/video/em28xx/em28xx-i2c.c @@ -3,7 +3,7 @@ Copyright (C) 2005 Ludovico Cavedon Markus Rechberger - Mauro Carvalho Chehab + Mauro Carvalho Chehab Sascha Sommer This program is free software; you can redistribute it and/or modify diff --git a/linux/drivers/media/video/em28xx/em28xx-input.c b/linux/drivers/media/video/em28xx/em28xx-input.c index a8d3cd4b0..38fe35da8 100644 --- a/linux/drivers/media/video/em28xx/em28xx-input.c +++ b/linux/drivers/media/video/em28xx/em28xx-input.c @@ -3,7 +3,7 @@ Copyright (C) 2005 Ludovico Cavedon Markus Rechberger - Mauro Carvalho Chehab + Mauro Carvalho Chehab Sascha Sommer This program is free software; you can redistribute it and/or modify diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 836e7c7e5..1093a68f0 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -3,7 +3,7 @@ Copyright (C) 2005 Ludovico Cavedon Markus Rechberger - Mauro Carvalho Chehab + Mauro Carvalho Chehab Sascha Sommer Some parts based on SN9C10x PC Camera Controllers GPL driver made @@ -49,7 +49,7 @@ #define DRIVER_AUTHOR "Ludovico Cavedon , " \ "Markus Rechberger , " \ - "Mauro Carvalho Chehab , " \ + "Mauro Carvalho Chehab , " \ "Sascha Sommer " #define DRIVER_NAME "em28xx" diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index c560ab329..97744214b 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -3,7 +3,7 @@ Copyright (C) 2005 Markus Rechberger Ludovico Cavedon - Mauro Carvalho Chehab + Mauro Carvalho Chehab Based on the em2800 driver from Sascha Sommer diff --git a/linux/drivers/media/video/tea5761.c b/linux/drivers/media/video/tea5761.c index 785cd105d..a0f075ed7 100644 --- a/linux/drivers/media/video/tea5761.c +++ b/linux/drivers/media/video/tea5761.c @@ -4,7 +4,7 @@ * * $Id: tea5761.c,v 1.5 2006/01/11 21:01:01 hverkuil Exp $ * - * Copyright (c) 2005 Mauro Carvalho Chehab (mchehab@brturbo.com.br) + * Copyright (c) 2005 Mauro Carvalho Chehab (mchehab@infradead.org) * This code is placed under the terms of the GNU General Public License * */ diff --git a/linux/drivers/media/video/tea5767.c b/linux/drivers/media/video/tea5767.c index e8bf32ec1..43fe70fec 100644 --- a/linux/drivers/media/video/tea5767.c +++ b/linux/drivers/media/video/tea5767.c @@ -4,7 +4,7 @@ * * $Id: tea5767.c,v 1.36 2006/01/15 17:04:52 hverkuil Exp $ * - * Copyright (c) 2005 Mauro Carvalho Chehab (mchehab@brturbo.com.br) + * Copyright (c) 2005 Mauro Carvalho Chehab (mchehab@infradead.org) * This code is placed under the terms of the GNU General Public License * * tea5767 autodetection thanks to Torsten Seeboth and Atsushi Nakagawa -- cgit v1.2.3 From cc7536722d24a3deec8ac51616e26f5dc267f112 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 3 Apr 2006 23:49:27 -0400 Subject: fix warning: assignment discards qualifiers from pointer target type From: Michael Krufky The following warning in all kernels < 2.6.17 is caused by changeset a875912f0aada25949e3201bcd97e177040fa782 : http://linuxtv.org/hg/v4l-dvb?cmd=changeset;node=a875912f0aad videodev.c: In function 'video_open': videodev.c:131: warning: assignment discards qualifiers from pointer target type videodev.c:136: warning: assignment discards qualifiers from pointer target type dvbdev.c: In function 'dvb_device_open': dvbdev.c:105: warning: assignment discards qualifiers from pointer target type This changeset restores backwards-compatability. Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/dvb-core/dvbdev.c | 4 ++++ linux/drivers/media/video/videodev.c | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-core/dvbdev.c b/linux/drivers/media/dvb/dvb-core/dvbdev.c index 85d6a6c65..9b824c31b 100644 --- a/linux/drivers/media/dvb/dvb-core/dvbdev.c +++ b/linux/drivers/media/dvb/dvb-core/dvbdev.c @@ -93,7 +93,11 @@ static int dvb_device_open(struct inode *inode, struct file *file) if (dvbdev && dvbdev->fops) { int err = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) const struct file_operations *old_fops; +#else + struct file_operations *old_fops; +#endif file->private_data = dvbdev; old_fops = file->f_op; diff --git a/linux/drivers/media/video/videodev.c b/linux/drivers/media/video/videodev.c index e54b8d9f7..c6d0825c8 100644 --- a/linux/drivers/media/video/videodev.c +++ b/linux/drivers/media/video/videodev.c @@ -111,7 +111,11 @@ static int video_open(struct inode *inode, struct file *file) unsigned int minor = iminor(inode); int err = 0; struct video_device *vfl; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) const struct file_operations *old_fops; +#else + struct file_operations *old_fops; +#endif if(minor>=VIDEO_NUM_DEVICES) return -ENODEV; -- cgit v1.2.3 From 9fea63f4c39cf81e86921ea4a0c440029f6d49b9 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 4 Apr 2006 00:56:30 -0400 Subject: Fix spelling From: Trent Piepho It's "Terrestrial" Signed-off-by: Trent Piepho --- linux/drivers/media/dvb/frontends/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/Kconfig b/linux/drivers/media/dvb/frontends/Kconfig index 37d5e0af1..6d90ff3f7 100644 --- a/linux/drivers/media/dvb/frontends/Kconfig +++ b/linux/drivers/media/dvb/frontends/Kconfig @@ -157,7 +157,7 @@ config DVB_STV0297 help A DVB-C tuner module. Say Y when you want to support this frontend. -comment "ATSC (North American/Korean Terresterial DTV) frontends" +comment "ATSC (North American/Korean Terrestrial/Cable DTV) frontends" depends on DVB_CORE config DVB_NXT200X -- cgit v1.2.3 From 5bf059bca09b4e3a492a700b31cab1577c77d2ce Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 4 Apr 2006 13:30:29 +0100 Subject: Avoid unnecessary firmware re-loads in or51132 frontend From: Trent Piepho As QAM_64, QAM_256, and QAM_AUTO all use the same firmware, switching between these modulations doesn't require a firmware re-load. This also fixes a mishandled error condition, in which the firmware file is loaded into the kernel, the clock mode is changed, but then the firmware upload to the device fails. The modulation change is aborted, but the clock mode would still be changed. Signed-off-by: Trent Piepho Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/or51132.c | 81 +++++++++++++++++------------ 1 file changed, 49 insertions(+), 32 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/or51132.c b/linux/drivers/media/dvb/frontends/or51132.c index a2a19aeb3..56d1109b9 100644 --- a/linux/drivers/media/dvb/frontends/or51132.c +++ b/linux/drivers/media/dvb/frontends/or51132.c @@ -310,6 +310,25 @@ static int or51132_setmode(struct dvb_frontend* fe) return 0; } +/* Some modulations use the same firmware. This classifies modulations + by the firmware they use. */ +#define MOD_FWCLASS_UNKNOWN 0 +#define MOD_FWCLASS_VSB 1 +#define MOD_FWCLASS_QAM 2 +static int modulation_fw_class(fe_modulation_t modulation) +{ + switch(modulation) { + case VSB_8: + return MOD_FWCLASS_VSB; + case QAM_AUTO: + case QAM_64: + case QAM_256: + return MOD_FWCLASS_QAM; + default: + return MOD_FWCLASS_UNKNOWN; + } +} + static int or51132_set_parameters(struct dvb_frontend* fe, struct dvb_frontend_parameters *param) { @@ -317,45 +336,40 @@ static int or51132_set_parameters(struct dvb_frontend* fe, u8 buf[4]; struct or51132_state* state = fe->demodulator_priv; const struct firmware *fw; - - /* Change only if we are actually changing the modulation */ - if (state->current_modulation != param->u.vsb.modulation) { - switch(param->u.vsb.modulation) { - case VSB_8: + const char *fwname; + int clock_mode; + + /* Upload new firmware only if we need a different one */ + if (modulation_fw_class(state->current_modulation) != + modulation_fw_class(param->u.vsb.modulation)) { + switch(modulation_fw_class(param->u.vsb.modulation)) { + case MOD_FWCLASS_VSB: dprintk("set_parameters VSB MODE\n"); - printk("or51132: Waiting for firmware upload(%s)...\n", - OR51132_VSB_FIRMWARE); - ret = request_firmware(&fw, OR51132_VSB_FIRMWARE, - &state->i2c->dev); - if (ret){ - printk(KERN_WARNING "or51132: No firmware up" - "loaded(timeout or file not found?)\n"); - return ret; - } + fwname = OR51132_VSB_FIRMWARE; + /* Set non-punctured clock for VSB */ - state->config->set_ts_params(fe, 0); + clock_mode = 0; break; - case QAM_AUTO: - case QAM_64: - case QAM_256: + case MOD_FWCLASS_QAM: dprintk("set_parameters QAM MODE\n"); - printk("or51132: Waiting for firmware upload(%s)...\n", - OR51132_QAM_FIRMWARE); - ret = request_firmware(&fw, OR51132_QAM_FIRMWARE, - &state->i2c->dev); - if (ret){ - printk(KERN_WARNING "or51132: No firmware up" - "loaded(timeout or file not found?)\n"); - return ret; - } + fwname = OR51132_QAM_FIRMWARE; + /* Set punctured clock for QAM */ - state->config->set_ts_params(fe, 1); + clock_mode = 1; break; default: - printk("or51132:Modulation type(%d) UNSUPPORTED\n", + printk("or51132: Modulation type(%d) UNSUPPORTED\n", param->u.vsb.modulation); return -1; - }; + } + printk("or51132: Waiting for firmware upload(%s)...\n", + fwname); + ret = request_firmware(&fw, fwname, &state->i2c->dev); + if (ret) { + printk(KERN_WARNING "or51132: No firmware up" + "loaded(timeout or file not found?)\n"); + return ret; + } ret = or51132_load_firmware(fe, fw); release_firmware(fw); if (ret) { @@ -364,7 +378,10 @@ static int or51132_set_parameters(struct dvb_frontend* fe, return ret; } printk("or51132: Firmware upload complete.\n"); - + state->config->set_ts_params(fe, clock_mode); + } + /* Change only if we are actually changing the modulation */ + if (state->current_modulation != param->u.vsb.modulation) { state->current_modulation = param->u.vsb.modulation; or51132_setmode(fe); } @@ -373,7 +390,7 @@ static int or51132_set_parameters(struct dvb_frontend* fe, param->frequency, 0); dprintk("set_parameters tuner bytes: 0x%02x 0x%02x " "0x%02x 0x%02x\n",buf[0],buf[1],buf[2],buf[3]); - if (i2c_writebytes(state, state->config->pll_address ,buf, 4)) + if (i2c_writebytes(state, state->config->pll_address, buf, 4)) printk(KERN_WARNING "or51132: set_parameters error " "writing to tuner\n"); -- cgit v1.2.3 From d8eae1def78bd2a6dfa017145311a249c7aa3bc6 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 4 Apr 2006 13:30:33 +0100 Subject: Remove a wee bit of cruft From: Trent Piepho A few lines that do nothing in the or51132 frontend, removed. Signed-off-by: Trent Piepho Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/or51132.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/or51132.c b/linux/drivers/media/dvb/frontends/or51132.c index 56d1109b9..619856b07 100644 --- a/linux/drivers/media/dvb/frontends/or51132.c +++ b/linux/drivers/media/dvb/frontends/or51132.c @@ -240,7 +240,7 @@ static int or51132_sleep(struct dvb_frontend* fe) static int or51132_setmode(struct dvb_frontend* fe) { struct or51132_state* state = fe->demodulator_priv; - unsigned char cmd_buf[4]; + unsigned char cmd_buf[3]; dprintk("setmode %d\n",(int)state->current_modulation); /* set operation mode in Receiver 1 register; */ @@ -260,7 +260,6 @@ static int or51132_setmode(struct dvb_frontend* fe) default: printk("setmode:Modulation set to unsupported value\n"); }; - cmd_buf[3] = 0x00; if (i2c_writebytes(state,state->config->demod_address, cmd_buf,3)) { printk(KERN_WARNING "or51132: set_mode error 1\n"); @@ -298,7 +297,6 @@ static int or51132_setmode(struct dvb_frontend* fe) default: printk("setmode: Modulation set to unsupported value\n"); }; - cmd_buf[3] = 0x00; msleep(20); /* 20ms */ if (i2c_writebytes(state,state->config->demod_address, cmd_buf,3)) { -- cgit v1.2.3 From 90f7453adee2bcad39c6630b760358eb799f238d Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 4 Apr 2006 13:41:47 +0100 Subject: Fix mutex in dvb_register_device to work. From: Andrew de Quincey This mutex is meant to stop two devices getting the same ID. dvbdev_get_free_id() scans the list of already allocated devices to find a free id. Unfortunately, since the mutex is unlocked before the card is added to the above list, it is still possible for two of them to get the same id. Solution: move the mutex after the list_add. Its debatable whether this mutex lock is actually needed, but I'm unwilling to just remove it in case something does depend on it. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/dvb-core/dvbdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-core/dvbdev.c b/linux/drivers/media/dvb/dvb-core/dvbdev.c index 9b824c31b..041ed7fd1 100644 --- a/linux/drivers/media/dvb/dvb-core/dvbdev.c +++ b/linux/drivers/media/dvb/dvb-core/dvbdev.c @@ -230,8 +230,6 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, return -ENOMEM; } - mutex_unlock(&dvbdev_register_lock); - memcpy(dvbdev, template, sizeof(struct dvb_device)); dvbdev->type = type; dvbdev->id = id; @@ -242,6 +240,8 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, list_add_tail (&dvbdev->list_head, &adap->device_list); + mutex_unlock(&dvbdev_register_lock); + devfs_mk_cdev(MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)), S_IFCHR | S_IRUSR | S_IWUSR, "dvb/adapter%d/%s%d", adap->num, dnames[type], id); -- cgit v1.2.3 From cef164ebf464c7b7072b4c17c1056d95a8071581 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 4 Apr 2006 11:55:30 -0400 Subject: kbuild: fix unneeded rebuilds in drivers/media/video after moving source tree From: Carl-Daniel Hailfinger Date: Mon, 3 Apr 2006 22:35:36 +0000 (+0200) Subject: kbuild: fix unneeded rebuilds in drivers/media/video after moving source tree X-Git-Url: http://kernel.org/git/?p=linux/kernel/git/sam/kbuild-bugfix.git;a=commitdiff;h=8036dc6bdca0faa981be01377728678a6f6f3fde kbuild: fix unneeded rebuilds in drivers/media/video after moving source tree This fixes some uneeded rebuilds under drivers/media/video after moving the source tree. The makefiles used $(src) and $(srctree) for include paths, which is unnecessary. Changed to use relative paths. Compile tested, produces byte-identical code to the previous makefiles. Signed-off-by: Carl-Daniel Hailfinger Signed-off-by: Sam Ravnborg Signed-off-by: Michael Krufky --- linux/drivers/media/video/Makefile | 2 +- linux/drivers/media/video/cx25840/Makefile | 2 +- linux/drivers/media/video/cx88/Makefile | 6 +++--- linux/drivers/media/video/em28xx/Makefile | 2 +- linux/drivers/media/video/saa7134/Makefile | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index 6bb050173..bfd5bce8e 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -85,4 +85,4 @@ obj-$(CONFIG_USB_IBMCAM) += usbvideo/ obj-$(CONFIG_USB_KONICAWC) += usbvideo/ obj-$(CONFIG_USB_VICAM) += usbvideo/ -EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff --git a/linux/drivers/media/video/cx25840/Makefile b/linux/drivers/media/video/cx25840/Makefile index 32a896c23..6e8665be8 100644 --- a/linux/drivers/media/video/cx25840/Makefile +++ b/linux/drivers/media/video/cx25840/Makefile @@ -3,4 +3,4 @@ cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \ obj-$(CONFIG_VIDEO_CX25840) += cx25840.o -EXTRA_CFLAGS += -I$(src)/.. +EXTRA_CFLAGS += -Idrivers/media/video diff --git a/linux/drivers/media/video/cx88/Makefile b/linux/drivers/media/video/cx88/Makefile index 6482b9aa6..0dcd09b9b 100644 --- a/linux/drivers/media/video/cx88/Makefile +++ b/linux/drivers/media/video/cx88/Makefile @@ -8,9 +8,9 @@ obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o obj-$(CONFIG_VIDEO_CX88_ALSA) += cx88-alsa.o obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o -EXTRA_CFLAGS += -I$(src)/.. -EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/dvb-core -EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/frontends +EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends extra-cflags-$(CONFIG_VIDEO_BUF_DVB) += -DHAVE_VIDEO_BUF_DVB=1 extra-cflags-$(CONFIG_DVB_CX22702) += -DHAVE_CX22702=1 diff --git a/linux/drivers/media/video/em28xx/Makefile b/linux/drivers/media/video/em28xx/Makefile index da457a05b..826d0e340 100644 --- a/linux/drivers/media/video/em28xx/Makefile +++ b/linux/drivers/media/video/em28xx/Makefile @@ -3,4 +3,4 @@ em28xx-objs := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \ obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o -EXTRA_CFLAGS += -I$(src)/.. +EXTRA_CFLAGS += -Idrivers/media/video diff --git a/linux/drivers/media/video/saa7134/Makefile b/linux/drivers/media/video/saa7134/Makefile index 1ba998424..be7b9ee69 100644 --- a/linux/drivers/media/video/saa7134/Makefile +++ b/linux/drivers/media/video/saa7134/Makefile @@ -11,9 +11,9 @@ obj-$(CONFIG_VIDEO_SAA7134_OSS) += saa7134-oss.o obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o -EXTRA_CFLAGS += -I$(src)/.. -EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/dvb-core -EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/frontends +EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends extra-cflags-$(CONFIG_VIDEO_BUF_DVB) += -DHAVE_VIDEO_BUF_DVB=1 extra-cflags-$(CONFIG_DVB_MT352) += -DHAVE_MT352=1 -- cgit v1.2.3 From 36a1860e0d188401c2fd2697bab9024ba5ecd5a0 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 4 Apr 2006 12:16:30 -0400 Subject: kbuild: use relative path to -I From: Sam Ravnborg Date: Tue, 4 Apr 2006 14:56:10 +0000 (+0200) Subject: kbuild: use relative path to -I X-Git-Url: http://kernel.org/git/?p=linux/kernel/git/sam/kbuild-bugfix.git;a=commitdiff;h=b46da0567d3baa6783106e7463801292cdc79ddd kbuild: use relative path to -I Using a relative path has the advantage that when the kernel source tree is moved the relevant .o files will not be rebuild just because the path to the kernel src has changed. This also got rid of a user of TOPDIR - which has been deprecated for a long time now. Signed-off-by: Sam Ravnborg Signed-off-by: Michael Krufky --- linux/drivers/media/video/bt8xx/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/bt8xx/Makefile b/linux/drivers/media/video/bt8xx/Makefile index 94350f21c..db641a36b 100644 --- a/linux/drivers/media/video/bt8xx/Makefile +++ b/linux/drivers/media/video/bt8xx/Makefile @@ -9,4 +9,4 @@ bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \ obj-$(CONFIG_VIDEO_BT848) += bttv.o EXTRA_CFLAGS += -I$(src)/.. -EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -- cgit v1.2.3 From b2711be0f47322a63ecba6e505413b4e31883c96 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 4 Apr 2006 21:23:04 -0400 Subject: kbuild: drivers/media/video/bt8xx: remove $(src) from include path From: Michael Krufky - replaced '$(src)/..' with 'drivers/media/video' Signed-off-by: Michael Krufky --- linux/drivers/media/video/bt8xx/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/bt8xx/Makefile b/linux/drivers/media/video/bt8xx/Makefile index db641a36b..a096a0341 100644 --- a/linux/drivers/media/video/bt8xx/Makefile +++ b/linux/drivers/media/video/bt8xx/Makefile @@ -8,5 +8,5 @@ bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \ obj-$(CONFIG_VIDEO_BT848) += bttv.o -EXTRA_CFLAGS += -I$(src)/.. +EXTRA_CFLAGS += -Idrivers/media/video EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -- cgit v1.2.3 From 6d5c9c64cf281711f099edb4b42cae8c9db6b7d9 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Wed, 5 Apr 2006 18:09:45 +0100 Subject: Fix TT budget-ci 1.1 CI slots From: Andrew de Quincey It turns out the firmware on the TT budget-ci 1.1 slots doesn't generate interrupts. This patch adds support for this using polling mode on these slots. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/ttpci/budget-ci.c | 105 ++++++++++++++++++++++++------ 1 file changed, 85 insertions(+), 20 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/ttpci/budget-ci.c b/linux/drivers/media/dvb/ttpci/budget-ci.c index 5f91036f5..e64a609cf 100644 --- a/linux/drivers/media/dvb/ttpci/budget-ci.c +++ b/linux/drivers/media/dvb/ttpci/budget-ci.c @@ -71,6 +71,7 @@ struct budget_ci { struct tasklet_struct msp430_irq_tasklet; struct tasklet_struct ciintf_irq_tasklet; int slot_status; + int ci_irq; struct dvb_ca_en50221 ca; char ir_dev_name[50]; u8 tuner_pll_address; /* used for philips_tdm1316l configs */ @@ -276,8 +277,10 @@ static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) if (slot != 0) return -EINVAL; - // trigger on RISING edge during reset so we know when READY is re-asserted - saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); + if (budget_ci->ci_irq) { + // trigger on RISING edge during reset so we know when READY is re-asserted + saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); + } budget_ci->slot_status = SLOTSTATUS_RESET; ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); msleep(1); @@ -370,11 +373,50 @@ static void ciintf_interrupt(unsigned long data) } } +static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + unsigned int flags; + + // ensure we don't get spurious IRQs during initialisation + if (!budget_ci->budget.ci_present) + return -EINVAL; + + // read the CAM status + flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); + if (flags & CICONTROL_CAMDETECT) { + // mark it as present if it wasn't before + if (budget_ci->slot_status & SLOTSTATUS_NONE) { + budget_ci->slot_status = SLOTSTATUS_PRESENT; + } + + // during a RESET, we check if we can read from IO memory to see when CAM is ready + if (budget_ci->slot_status & SLOTSTATUS_RESET) { + if (ciintf_read_attribute_mem(ca, slot, 0) == 0x1d) { + budget_ci->slot_status = SLOTSTATUS_READY; + } + } + } else { + budget_ci->slot_status = SLOTSTATUS_NONE; + } + + if (budget_ci->slot_status != SLOTSTATUS_NONE) { + if (budget_ci->slot_status & SLOTSTATUS_READY) { + return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; + } + return DVB_CA_EN50221_POLL_CAM_PRESENT; + } + + return 0; +} + static int ciintf_init(struct budget_ci *budget_ci) { struct saa7146_dev *saa = budget_ci->budget.dev; int flags; int result; + int ci_version; + int ca_flags; memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221)); @@ -382,16 +424,29 @@ static int ciintf_init(struct budget_ci *budget_ci) saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800); // test if it is there - if ((ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0) & 0xa0) != 0xa0) { + ci_version = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0); + if ((ci_version & 0xa0) != 0xa0) { result = -ENODEV; goto error; } + // determine whether a CAM is present or not flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); budget_ci->slot_status = SLOTSTATUS_NONE; if (flags & CICONTROL_CAMDETECT) budget_ci->slot_status = SLOTSTATUS_PRESENT; + // version 0xa2 of the CI firmware doesn't generate interrupts + if (ci_version == 0xa2) { + ca_flags = 0; + budget_ci->ci_irq = 0; + } else { + ca_flags = DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE | + DVB_CA_EN50221_FLAG_IRQ_FR | + DVB_CA_EN50221_FLAG_IRQ_DA; + budget_ci->ci_irq = 1; + } + // register CI interface budget_ci->ca.owner = THIS_MODULE; budget_ci->ca.read_attribute_mem = ciintf_read_attribute_mem; @@ -401,23 +456,27 @@ static int ciintf_init(struct budget_ci *budget_ci) budget_ci->ca.slot_reset = ciintf_slot_reset; budget_ci->ca.slot_shutdown = ciintf_slot_shutdown; budget_ci->ca.slot_ts_enable = ciintf_slot_ts_enable; + budget_ci->ca.poll_slot_status = ciintf_poll_slot_status; budget_ci->ca.data = budget_ci; if ((result = dvb_ca_en50221_init(&budget_ci->budget.dvb_adapter, &budget_ci->ca, - DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE | - DVB_CA_EN50221_FLAG_IRQ_FR | - DVB_CA_EN50221_FLAG_IRQ_DA, 1)) != 0) { + ca_flags, 1)) != 0) { printk("budget_ci: CI interface detected, but initialisation failed.\n"); goto error; } + // Setup CI slot IRQ - tasklet_init(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt, (unsigned long) budget_ci); - if (budget_ci->slot_status != SLOTSTATUS_NONE) { - saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); - } else { - saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); + if (budget_ci->ci_irq) { + tasklet_init(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt, (unsigned long) budget_ci); + if (budget_ci->slot_status != SLOTSTATUS_NONE) { + saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); + } else { + saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); + } + saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_03); } - saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_03); + + // enable interface ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, CICONTROL_RESET, 1, 0); @@ -426,10 +485,12 @@ static int ciintf_init(struct budget_ci *budget_ci) budget_ci->budget.ci_present = 1; // forge a fake CI IRQ so the CAM state is setup correctly - flags = DVB_CA_EN50221_CAMCHANGE_REMOVED; - if (budget_ci->slot_status != SLOTSTATUS_NONE) - flags = DVB_CA_EN50221_CAMCHANGE_INSERTED; - dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags); + if (budget_ci->ci_irq) { + flags = DVB_CA_EN50221_CAMCHANGE_REMOVED; + if (budget_ci->slot_status != SLOTSTATUS_NONE) + flags = DVB_CA_EN50221_CAMCHANGE_INSERTED; + dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags); + } return 0; @@ -443,9 +504,13 @@ static void ciintf_deinit(struct budget_ci *budget_ci) struct saa7146_dev *saa = budget_ci->budget.dev; // disable CI interrupts - saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_03); - saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); - tasklet_kill(&budget_ci->ciintf_irq_tasklet); + if (budget_ci->ci_irq) { + saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_03); + saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); + tasklet_kill(&budget_ci->ciintf_irq_tasklet); + } + + // reset interface ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); msleep(1); ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, @@ -473,7 +538,7 @@ static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr) if (*isr & MASK_10) ttpci_budget_irq10_handler(dev, isr); - if ((*isr & MASK_03) && (budget_ci->budget.ci_present)) + if ((*isr & MASK_03) && (budget_ci->budget.ci_present) && (budget_ci->ci_irq)) tasklet_schedule(&budget_ci->ciintf_irq_tasklet); } -- cgit v1.2.3 From 7c0ef076eb4257ba9803b2312a5e734101900d4b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 5 Apr 2006 16:36:51 -0300 Subject: Remove DMX_GET_EVENT and associated data structures From: Andreas Oberritter The ioctl DMX_GET_EVENT has never been implemented. I guess no software is using it because of its lack of implementation. Future software won't use it, too, because this API doesn't make much sense the way it is: Frontend events have their own different API. Scrambling events can't be generated in a useful way by the hardware I know of. Signed-off-by: Andreas Oberritter Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-core/dmxdev.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-core/dmxdev.c b/linux/drivers/media/dvb/dvb-core/dmxdev.c index 04578df3f..988499dfd 100644 --- a/linux/drivers/media/dvb/dvb-core/dmxdev.c +++ b/linux/drivers/media/dvb/dvb-core/dmxdev.c @@ -872,9 +872,6 @@ static int dvb_demux_do_ioctl(struct inode *inode, struct file *file, mutex_unlock(&dmxdevfilter->mutex); break; - case DMX_GET_EVENT: - break; - case DMX_GET_PES_PIDS: if (!dmxdev->demux->get_pes_pids) { ret = -EINVAL; -- cgit v1.2.3 From ec5a185a486ece7e0e6e48e49339808b05120e17 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Wed, 5 Apr 2006 22:19:26 -0400 Subject: kbuild: drivers/media/video/pvrusb2: remove $(src) from include path From: Michael Krufky The pvrusb2 driver does not need 'drivers/media/video' in its include path. This patch removes '$(src)/..' from the Makefile Signed-off-by: Michael Krufky --- linux/drivers/media/video/pvrusb2/Makefile | 2 -- 1 file changed, 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/Makefile b/linux/drivers/media/video/pvrusb2/Makefile index 53fccce4f..ce78cd00f 100644 --- a/linux/drivers/media/video/pvrusb2/Makefile +++ b/linux/drivers/media/video/pvrusb2/Makefile @@ -15,5 +15,3 @@ pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y) obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o - -EXTRA_CFLAGS += -I$(src)/.. -- cgit v1.2.3 From 76a47ac5aead7ba38156a30c8817303794ede373 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Wed, 5 Apr 2006 23:00:11 -0400 Subject: kbuild: VIDEO_PVRUSB2_24XXX must select VIDEO_CX25840 and VIDEO_WM8775 From: Michael Krufky Signed-off-by: Michael Krufky --- linux/drivers/media/video/pvrusb2/Kconfig | 2 ++ 1 file changed, 2 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/Kconfig b/linux/drivers/media/video/pvrusb2/Kconfig index 629dc4e05..89031a2f5 100644 --- a/linux/drivers/media/video/pvrusb2/Kconfig +++ b/linux/drivers/media/video/pvrusb2/Kconfig @@ -16,6 +16,8 @@ config VIDEO_PVRUSB2 config VIDEO_PVRUSB2_24XXX bool "Hauppauge WinTV-PVR USB2 support for 24xxx model series" depends on VIDEO_PVRUSB2 && EXPERIMENTAL + select VIDEO_CX25840 + select VIDEO_WM8775 ---help--- This option enables inclusion of additional logic to operate newer WinTV-PVR USB2 devices whose model number is of the -- cgit v1.2.3 From 90b3eeab5b4a9004ebe62de315b0be80835332c5 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Thu, 6 Apr 2006 10:03:09 +0100 Subject: Add support for FE_GET_FRONTEND to or51132 frontend From: Trent Piepho Adds the FE_GET_FRONTEND ioctl to the or51132 frontend. Current modulation is read from the hardware, while frequency is taken from the driver's state. Signed-off-by: Trent Piepho --- linux/drivers/media/dvb/frontends/or51132.c | 39 +++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/or51132.c b/linux/drivers/media/dvb/frontends/or51132.c index 619856b07..c25ec7344 100644 --- a/linux/drivers/media/dvb/frontends/or51132.c +++ b/linux/drivers/media/dvb/frontends/or51132.c @@ -400,6 +400,44 @@ static int or51132_set_parameters(struct dvb_frontend* fe, return 0; } +static int or51132_get_parameters(struct dvb_frontend* fe, + struct dvb_frontend_parameters *param) +{ + struct or51132_state* state = fe->demodulator_priv; + u8 buf[2]; + + /* Receiver Status */ + buf[0]=0x04; + buf[1]=0x00; + msleep(30); /* 30ms */ + if (i2c_writebytes(state,state->config->demod_address,buf,2)) { + printk(KERN_WARNING "or51132: get_parameters write error\n"); + return -EREMOTEIO; + } + msleep(30); /* 30ms */ + if (i2c_readbytes(state,state->config->demod_address,buf,2)) { + printk(KERN_WARNING "or51132: get_parameters read error\n"); + return -EREMOTEIO; + } + switch(buf[0]) { + case 0x06: param->u.vsb.modulation = VSB_8; break; + case 0x43: param->u.vsb.modulation = QAM_64; break; + case 0x45: param->u.vsb.modulation = QAM_256; break; + default: + printk(KERN_WARNING "or51132: unknown status 0x%02x\n", + buf[0]); + return -EREMOTEIO; + } + + /* FIXME: Read frequency from frontend, take AFC into account */ + param->frequency = state->current_frequency; + + /* FIXME: How to read inversion setting? Receiver 6 register? */ + param->inversion = INVERSION_AUTO; + + return 0; +} + static int or51132_read_status(struct dvb_frontend* fe, fe_status_t* status) { struct or51132_state* state = fe->demodulator_priv; @@ -618,6 +656,7 @@ static struct dvb_frontend_ops or51132_ops = { .sleep = or51132_sleep, .set_frontend = or51132_set_parameters, + .get_frontend = or51132_get_parameters, .get_tune_settings = or51132_get_tune_settings, .read_status = or51132_read_status, -- cgit v1.2.3 From 7a1e768ccdb212dd6997320abc369c21b6522a2d Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Thu, 6 Apr 2006 13:42:46 +0100 Subject: Fix oops in budget-av with CI From: Andrew de Quincey Now that the CI code reinitialises the frontend, need to move the CI initialisation to after the frontend init in order to ensure the frontend is always in a good state. Fixes an oops caused by the frontend being NULL as well. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/ttpci/budget-av.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/ttpci/budget-av.c b/linux/drivers/media/dvb/ttpci/budget-av.c index 8efe3ce5f..8a7cd7d50 100644 --- a/linux/drivers/media/dvb/ttpci/budget-av.c +++ b/linux/drivers/media/dvb/ttpci/budget-av.c @@ -1190,8 +1190,6 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio SAA7146_HPS_SYNC_PORT_A); saa7113_setinput(budget_av, 0); - } else { - ciintf_init(budget_av); } /* fixme: find some sane values here... */ @@ -1211,6 +1209,10 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio budget_av->budget.dvb_adapter.priv = budget_av; frontend_init(budget_av); + if (!budget_av->has_saa7113) { + ciintf_init(budget_av); + } + return 0; } -- cgit v1.2.3 From e917e4731925164934af07fb1970c494ba27c579 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Thu, 6 Apr 2006 14:37:56 +0100 Subject: budget-core doesn't check if register adapter fails From: Andrew de Quincey If you have more than DVB_MAX_ADAPTERS in your machine, this causes an oops. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/ttpci/budget-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/ttpci/budget-core.c b/linux/drivers/media/dvb/ttpci/budget-core.c index ea2066d46..d8af690fa 100644 --- a/linux/drivers/media/dvb/ttpci/budget-core.c +++ b/linux/drivers/media/dvb/ttpci/budget-core.c @@ -400,7 +400,9 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, budget->dev->name, budget->buffer_width, budget->buffer_height); printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size); - dvb_register_adapter(&budget->dvb_adapter, budget->card->name, owner); + if ((ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name, owner)) < 0) { + return ret; + } /* set dd1 stream a & b */ saa7146_write(dev, DD1_STREAM_B, 0x00000000); -- cgit v1.2.3 From c2f7c5def32f35793a62672ab9cbb40e61e371f5 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Thu, 6 Apr 2006 18:32:23 +0100 Subject: Set tone/voltage again if the frontend was reinitialised --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 875033330..62063b00d 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -106,6 +106,8 @@ struct dvb_frontend_private { unsigned long tune_mode_flags; unsigned int delay; unsigned int reinitialise; + int tone; + int voltage; /* swzigzag values */ unsigned int state; @@ -543,6 +545,12 @@ static int dvb_frontend_thread(void *data) if (fepriv->reinitialise) { dvb_frontend_init(fe); + if (fepriv->tone != -1) { + fe->ops->set_tone(fe, fepriv->tone); + } + if (fepriv->voltage != -1) { + fe->ops->set_voltage(fe, fepriv->voltage); + } fepriv->reinitialise = 0; } @@ -794,6 +802,7 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, case FE_SET_TONE: if (fe->ops->set_tone) { err = fe->ops->set_tone(fe, (fe_sec_tone_mode_t) parg); + fepriv->tone = (fe_sec_tone_mode_t) parg; fepriv->state = FESTATE_DISEQC; fepriv->status = 0; } @@ -802,6 +811,7 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, case FE_SET_VOLTAGE: if (fe->ops->set_voltage) { err = fe->ops->set_voltage(fe, (fe_sec_voltage_t) parg); + fepriv->voltage = (fe_sec_voltage_t) parg; fepriv->state = FESTATE_DISEQC; fepriv->status = 0; } @@ -1001,6 +1011,8 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) /* normal tune mode when opened R/W */ fepriv->tune_mode_flags &= ~FE_TUNE_MODE_ONESHOT; + fepriv->tone = -1; + fepriv->voltage = -1; } return ret; -- cgit v1.2.3 From b4171f226d40012c3e7510850dc43bc503adf0c7 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Thu, 6 Apr 2006 21:05:46 +0100 Subject: Fix some more potential oopses From: Andrew de Quincey Spotted a couple more places where it fails to check if dvb_register_adapter() fails. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/cinergyT2/cinergyT2.c | 5 ++++- linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/cinergyT2/cinergyT2.c b/linux/drivers/media/dvb/cinergyT2/cinergyT2.c index 57ab7b0f3..994d12194 100644 --- a/linux/drivers/media/dvb/cinergyT2/cinergyT2.c +++ b/linux/drivers/media/dvb/cinergyT2/cinergyT2.c @@ -919,7 +919,10 @@ static int cinergyt2_probe (struct usb_interface *intf, return -ENOMEM; } - dvb_register_adapter(&cinergyt2->adapter, DRIVER_NAME, THIS_MODULE); + if ((err = dvb_register_adapter(&cinergyt2->adapter, DRIVER_NAME, THIS_MODULE)) < 0) { + kfree(cinergyt2); + return err; + } cinergyt2->demux.priv = cinergyt2; cinergyt2->demux.filternum = 256; diff --git a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index 69a550871..9484f26f7 100644 --- a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -1515,7 +1515,11 @@ static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *i mutex_unlock(&ttusb->semi2c); - dvb_register_adapter(&ttusb->adapter, "Technotrend/Hauppauge Nova-USB", THIS_MODULE); + if ((result = dvb_register_adapter(&ttusb->adapter, "Technotrend/Hauppauge Nova-USB", THIS_MODULE)) < 0) { + ttusb_free_iso_urbs(ttusb); + kfree(ttusb); + return result; + } ttusb->adapter.priv = ttusb; /* i2c */ -- cgit v1.2.3 From 9e876a907f566b5717449fc835cd3e0c3f8d0324 Mon Sep 17 00:00:00 2001 From: Rusty Scott Date: Fri, 7 Apr 2006 01:21:31 -0400 Subject: Add support for pcHDTV HD5500 ATSC/QAM From: Rusty Scott Added card definitions for the pcHDTV HD5500 ATSC/QAM card Signed-off-by: Rusty Scott Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/cx88-cards.c | 26 ++++++++++++++++++++++++++ linux/drivers/media/video/cx88/cx88-dvb.c | 24 ++++++++++++++++++++++++ linux/drivers/media/video/cx88/cx88-mpeg.c | 1 + linux/drivers/media/video/cx88/cx88.h | 1 + 4 files changed, 52 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index 73b7ed14f..9ab7f9d23 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -603,6 +603,28 @@ struct cx88_board cx88_boards[] = { }, .dvb = 1, }, + [CX88_BOARD_PCHDTV_HD5500] = { + .name = "pcHDTV HD5500 HDTV", + .tuner_type = TUNER_LG_TDVS_H062F, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x87fd, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x87f9, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x87f9, + }}, + .dvb = 1, + }, [CX88_BOARD_HAUPPAUGE_ROSLYN] = { // entry added by Kaustubh D. Bhalerao // GPIO values obtained from regspy, courtesy Sean Covel @@ -1324,6 +1346,10 @@ struct cx88_subid cx88_subids[] = { .subvendor = 0x18ac, .subdevice = 0xdb44, .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID, + },{ + .subvendor = 0x7063, + .subdevice = 0x5500, + .card = CX88_BOARD_PCHDTV_HD5500, }, }; const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids); diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index 2fdabe2f2..38564eaca 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -457,6 +457,14 @@ static struct lgdt330x_config fusionhdtv_5_gold = { .pll_set = lgdt330x_pll_set, .set_ts_params = lgdt330x_set_ts_param, }; + +static struct lgdt330x_config pchdtv_hd5500 = { + .demod_address = 0x59, + .demod_chip = LGDT3303, + .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ + .pll_set = lgdt330x_pll_set, + .set_ts_params = lgdt330x_set_ts_param, +}; #endif #ifdef HAVE_NXT200X @@ -663,6 +671,22 @@ static int dvb_register(struct cx8802_dev *dev) &dev->core->i2c_adap); } break; + case CX88_BOARD_PCHDTV_HD5500: + dev->ts_gen_cntrl = 0x08; + { + /* Do a hardware reset of chip before using it. */ + struct cx88_core *core = dev->core; + + cx_clear(MO_GP0_IO, 1); + mdelay(100); + cx_set(MO_GP0_IO, 1); + mdelay(200); + dev->core->pll_addr = 0x61; + dev->core->pll_desc = &dvb_pll_tdvs_tua6034; + dev->dvb.frontend = lgdt330x_attach(&pchdtv_hd5500, + &dev->core->i2c_adap); + } + break; #endif #ifdef HAVE_NXT200X case CX88_BOARD_ATI_HDTVWONDER: diff --git a/linux/drivers/media/video/cx88/cx88-mpeg.c b/linux/drivers/media/video/cx88/cx88-mpeg.c index e0f9db668..487c6d16c 100644 --- a/linux/drivers/media/video/cx88/cx88-mpeg.c +++ b/linux/drivers/media/video/cx88/cx88-mpeg.c @@ -87,6 +87,7 @@ static int cx8802_start_dma(struct cx8802_dev *dev, case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q: case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T: case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD: + case CX88_BOARD_PCHDTV_HD5500: cx_write(TS_SOP_STAT, 1<<13); break; case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h index a8a0ccd4e..2f8f4fd12 100644 --- a/linux/drivers/media/video/cx88/cx88.h +++ b/linux/drivers/media/video/cx88/cx88.h @@ -198,6 +198,7 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL 44 #define CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT 45 #define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID 46 +#define CX88_BOARD_PCHDTV_HD5500 47 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, -- cgit v1.2.3 From 044018d8e1d0693185d570e8a1b8e18ec86df6ee Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Fri, 7 Apr 2006 02:24:54 -0400 Subject: cx88 cleanup: move CX88_BOARD_PCHDTV_HD5500 to the end of the cards array From: Michael Krufky - new cards should always be added to the end of the list. Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/cx88-cards.c | 45 ++++++++++++++--------------- 1 file changed, 22 insertions(+), 23 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index 9ab7f9d23..9d9c58028 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -603,28 +603,6 @@ struct cx88_board cx88_boards[] = { }, .dvb = 1, }, - [CX88_BOARD_PCHDTV_HD5500] = { - .name = "pcHDTV HD5500 HDTV", - .tuner_type = TUNER_LG_TDVS_H062F, - .radio_type = UNSET, - .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, - .tda9887_conf = TDA9887_PRESENT, - .input = {{ - .type = CX88_VMUX_TELEVISION, - .vmux = 0, - .gpio0 = 0x87fd, - },{ - .type = CX88_VMUX_COMPOSITE1, - .vmux = 1, - .gpio0 = 0x87f9, - },{ - .type = CX88_VMUX_SVIDEO, - .vmux = 2, - .gpio0 = 0x87f9, - }}, - .dvb = 1, - }, [CX88_BOARD_HAUPPAUGE_ROSLYN] = { // entry added by Kaustubh D. Bhalerao // GPIO values obtained from regspy, courtesy Sean Covel @@ -1128,7 +1106,28 @@ struct cx88_board cx88_boards[] = { }}, .dvb = 1, }, - + [CX88_BOARD_PCHDTV_HD5500] = { + .name = "pcHDTV HD5500 HDTV", + .tuner_type = TUNER_LG_TDVS_H062F, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x87fd, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x87f9, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x87f9, + }}, + .dvb = 1, + }, }; const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards); -- cgit v1.2.3 From c6d961cb656bbd8d45f75563328123f0c2935157 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 7 Apr 2006 10:04:56 -0300 Subject: Saa7134: Fix oops with disable_ir=1 From: Sergey Vlasov When disable_ir=1 parameter is used, or when saa7134_input_init1() fails for any other reason, dev->remote will remain NULL, and the driver will oops in saa7134_hwinit2(). Therefore dev->remote must be checked before dereferencing. Signed-off-by: Sergey Vlasov Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/saa7134/saa7134-core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/saa7134/saa7134-core.c b/linux/drivers/media/video/saa7134/saa7134-core.c index 6d2aedd31..1be908a46 100644 --- a/linux/drivers/media/video/saa7134/saa7134-core.c +++ b/linux/drivers/media/video/saa7134/saa7134-core.c @@ -616,6 +616,8 @@ static irqreturn_t saa7134_irq(int irq, void *dev_id, struct pt_regs *regs) if (report & SAA7134_IRQ_REPORT_GPIO16) { switch (dev->has_remote) { case SAA7134_REMOTE_GPIO: + if (!dev->remote) + break; if (dev->remote->mask_keydown & 0x10000) { saa7134_input_irq(dev); } @@ -632,6 +634,8 @@ static irqreturn_t saa7134_irq(int irq, void *dev_id, struct pt_regs *regs) if (report & SAA7134_IRQ_REPORT_GPIO18) { switch (dev->has_remote) { case SAA7134_REMOTE_GPIO: + if (!dev->remote) + break; if ((dev->remote->mask_keydown & 0x40000) || (dev->remote->mask_keyup & 0x40000)) { saa7134_input_irq(dev); @@ -744,7 +748,7 @@ static int saa7134_hwinit2(struct saa7134_dev *dev) SAA7134_IRQ2_INTE_PE | SAA7134_IRQ2_INTE_AR; - if (dev->has_remote == SAA7134_REMOTE_GPIO) { + if (dev->has_remote == SAA7134_REMOTE_GPIO && dev->remote) { if (dev->remote->mask_keydown & 0x10000) irq2_mask |= SAA7134_IRQ2_INTE_GPIO16; else if (dev->remote->mask_keydown & 0x40000) -- cgit v1.2.3 From f8d3d508803c4ce572d52d5a5aa24163f60275ab Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 7 Apr 2006 18:50:09 -0300 Subject: Fix a bug at pluto2 Makefile From: Mauro Carvalho Chehab When pluto2 were selected, all other module dependencies were just discarded. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/pluto2/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/pluto2/Makefile b/linux/drivers/media/dvb/pluto2/Makefile index 86ca84b2b..ce6a9aaf9 100644 --- a/linux/drivers/media/dvb/pluto2/Makefile +++ b/linux/drivers/media/dvb/pluto2/Makefile @@ -1,3 +1,3 @@ -obj-$(CONFIG_DVB_PLUTO2) = pluto2.o +obj-$(CONFIG_DVB_PLUTO2) += pluto2.o EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ -- cgit v1.2.3 From 672521a35cb79a8a1ae4de74f446ed1ae73a31b2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 7 Apr 2006 23:28:55 -0300 Subject: Added almost all other V4L missing drivers to the tree From: Mauro Carvalho Chehab Now, drivers/media tree is complete. Only missing zoran36120 since this won't work and it is a good candidate to be removed from kernel soon. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/radio/Kconfig | 354 ++ linux/drivers/media/radio/Makefile | 22 + linux/drivers/media/radio/miropcm20-radio.c | 266 ++ linux/drivers/media/radio/miropcm20-rds-core.c | 211 + linux/drivers/media/radio/miropcm20-rds-core.h | 19 + linux/drivers/media/radio/miropcm20-rds.c | 134 + linux/drivers/media/radio/radio-aimslab.c | 370 ++ linux/drivers/media/radio/radio-aztech.c | 317 ++ linux/drivers/media/radio/radio-cadet.c | 622 +++ linux/drivers/media/radio/radio-gemtek-pci.c | 417 ++ linux/drivers/media/radio/radio-gemtek.c | 306 ++ linux/drivers/media/radio/radio-maestro.c | 383 ++ linux/drivers/media/radio/radio-maxiradio.c | 352 ++ linux/drivers/media/radio/radio-rtrack2.c | 268 ++ linux/drivers/media/radio/radio-sf16fmi.c | 330 ++ linux/drivers/media/radio/radio-sf16fmr2.c | 436 ++ linux/drivers/media/radio/radio-terratec.c | 343 ++ linux/drivers/media/radio/radio-trust.c | 322 ++ linux/drivers/media/radio/radio-typhoon.c | 385 ++ linux/drivers/media/radio/radio-zoltrix.c | 387 ++ linux/drivers/media/video/adv7170.c | 519 +++ linux/drivers/media/video/adv7175.c | 537 +++ linux/drivers/media/video/arv.c | 913 ++++ linux/drivers/media/video/bt819.c | 643 +++ linux/drivers/media/video/bt856.c | 426 ++ linux/drivers/media/video/bw-qcam.c | 1029 ++++ linux/drivers/media/video/bw-qcam.h | 68 + linux/drivers/media/video/c-qcam.c | 858 ++++ linux/drivers/media/video/cpia.c | 4073 ++++++++++++++++ linux/drivers/media/video/cpia.h | 432 ++ linux/drivers/media/video/cpia_pp.c | 881 ++++ linux/drivers/media/video/cpia_usb.c | 649 +++ linux/drivers/media/video/cs8420.h | 50 + linux/drivers/media/video/dabfirmware.h | 1408 ++++++ linux/drivers/media/video/dabusb.c | 874 ++++ linux/drivers/media/video/dabusb.h | 85 + linux/drivers/media/video/dsbr100.c | 430 ++ linux/drivers/media/video/ibmmpeg2.h | 94 + linux/drivers/media/video/indycam.c | 471 ++ linux/drivers/media/video/indycam.h | 108 + linux/drivers/media/video/meye.c | 2044 ++++++++ linux/drivers/media/video/meye.h | 320 ++ linux/drivers/media/video/ov511.c | 5932 ++++++++++++++++++++++++ linux/drivers/media/video/ov511.h | 569 +++ linux/drivers/media/video/planb.c | 2304 +++++++++ linux/drivers/media/video/planb.h | 231 + linux/drivers/media/video/pms.c | 1064 +++++ linux/drivers/media/video/saa5246a.c | 837 ++++ linux/drivers/media/video/saa5246a.h | 364 ++ linux/drivers/media/video/saa5249.c | 720 +++ linux/drivers/media/video/saa7110.c | 604 +++ linux/drivers/media/video/saa7111.c | 614 +++ linux/drivers/media/video/saa7114.c | 1221 +++++ linux/drivers/media/video/saa7121.h | 132 + linux/drivers/media/video/saa7146.h | 114 + linux/drivers/media/video/saa7146reg.h | 283 ++ linux/drivers/media/video/saa7185.c | 508 ++ linux/drivers/media/video/saa7191.c | 808 ++++ linux/drivers/media/video/saa7191.h | 255 + linux/drivers/media/video/saa7196.h | 117 + linux/drivers/media/video/se401.c | 1435 ++++++ linux/drivers/media/video/se401.h | 235 + linux/drivers/media/video/stradis.c | 2213 +++++++++ linux/drivers/media/video/stv680.c | 1509 ++++++ linux/drivers/media/video/stv680.h | 227 + linux/drivers/media/video/tuner-3036.c | 214 + linux/drivers/media/video/videocodec.c | 481 ++ linux/drivers/media/video/videocodec.h | 359 ++ linux/drivers/media/video/vino.c | 4680 +++++++++++++++++++ linux/drivers/media/video/vino.h | 138 + linux/drivers/media/video/vpx3220.c | 749 +++ linux/drivers/media/video/w9966.c | 986 ++++ linux/drivers/media/video/w9968cf.c | 3691 +++++++++++++++ linux/drivers/media/video/w9968cf.h | 331 ++ linux/drivers/media/video/w9968cf_decoder.h | 86 + linux/drivers/media/video/w9968cf_vpp.h | 40 + linux/drivers/media/video/zoran.h | 515 ++ linux/drivers/media/video/zoran_card.c | 1578 +++++++ linux/drivers/media/video/zoran_card.h | 45 + linux/drivers/media/video/zoran_device.c | 1786 +++++++ linux/drivers/media/video/zoran_device.h | 91 + linux/drivers/media/video/zoran_driver.c | 4700 +++++++++++++++++++ linux/drivers/media/video/zoran_procfs.c | 234 + linux/drivers/media/video/zoran_procfs.h | 36 + linux/drivers/media/video/zr36016.c | 531 +++ linux/drivers/media/video/zr36016.h | 111 + linux/drivers/media/video/zr36050.c | 905 ++++ linux/drivers/media/video/zr36050.h | 184 + linux/drivers/media/video/zr36057.h | 168 + linux/drivers/media/video/zr36060.c | 1014 ++++ linux/drivers/media/video/zr36060.h | 220 + 91 files changed, 69325 insertions(+) create mode 100644 linux/drivers/media/radio/Kconfig create mode 100644 linux/drivers/media/radio/Makefile create mode 100644 linux/drivers/media/radio/miropcm20-radio.c create mode 100644 linux/drivers/media/radio/miropcm20-rds-core.c create mode 100644 linux/drivers/media/radio/miropcm20-rds-core.h create mode 100644 linux/drivers/media/radio/miropcm20-rds.c create mode 100644 linux/drivers/media/radio/radio-aimslab.c create mode 100644 linux/drivers/media/radio/radio-aztech.c create mode 100644 linux/drivers/media/radio/radio-cadet.c create mode 100644 linux/drivers/media/radio/radio-gemtek-pci.c create mode 100644 linux/drivers/media/radio/radio-gemtek.c create mode 100644 linux/drivers/media/radio/radio-maestro.c create mode 100644 linux/drivers/media/radio/radio-maxiradio.c create mode 100644 linux/drivers/media/radio/radio-rtrack2.c create mode 100644 linux/drivers/media/radio/radio-sf16fmi.c create mode 100644 linux/drivers/media/radio/radio-sf16fmr2.c create mode 100644 linux/drivers/media/radio/radio-terratec.c create mode 100644 linux/drivers/media/radio/radio-trust.c create mode 100644 linux/drivers/media/radio/radio-typhoon.c create mode 100644 linux/drivers/media/radio/radio-zoltrix.c create mode 100644 linux/drivers/media/video/adv7170.c create mode 100644 linux/drivers/media/video/adv7175.c create mode 100644 linux/drivers/media/video/arv.c create mode 100644 linux/drivers/media/video/bt819.c create mode 100644 linux/drivers/media/video/bt856.c create mode 100644 linux/drivers/media/video/bw-qcam.c create mode 100644 linux/drivers/media/video/bw-qcam.h create mode 100644 linux/drivers/media/video/c-qcam.c create mode 100644 linux/drivers/media/video/cpia.c create mode 100644 linux/drivers/media/video/cpia.h create mode 100644 linux/drivers/media/video/cpia_pp.c create mode 100644 linux/drivers/media/video/cpia_usb.c create mode 100644 linux/drivers/media/video/cs8420.h create mode 100644 linux/drivers/media/video/dabfirmware.h create mode 100644 linux/drivers/media/video/dabusb.c create mode 100644 linux/drivers/media/video/dabusb.h create mode 100644 linux/drivers/media/video/dsbr100.c create mode 100644 linux/drivers/media/video/ibmmpeg2.h create mode 100644 linux/drivers/media/video/indycam.c create mode 100644 linux/drivers/media/video/indycam.h create mode 100644 linux/drivers/media/video/meye.c create mode 100644 linux/drivers/media/video/meye.h create mode 100644 linux/drivers/media/video/ov511.c create mode 100644 linux/drivers/media/video/ov511.h create mode 100644 linux/drivers/media/video/planb.c create mode 100644 linux/drivers/media/video/planb.h create mode 100644 linux/drivers/media/video/pms.c create mode 100644 linux/drivers/media/video/saa5246a.c create mode 100644 linux/drivers/media/video/saa5246a.h create mode 100644 linux/drivers/media/video/saa5249.c create mode 100644 linux/drivers/media/video/saa7110.c create mode 100644 linux/drivers/media/video/saa7111.c create mode 100644 linux/drivers/media/video/saa7114.c create mode 100644 linux/drivers/media/video/saa7121.h create mode 100644 linux/drivers/media/video/saa7146.h create mode 100644 linux/drivers/media/video/saa7146reg.h create mode 100644 linux/drivers/media/video/saa7185.c create mode 100644 linux/drivers/media/video/saa7191.c create mode 100644 linux/drivers/media/video/saa7191.h create mode 100644 linux/drivers/media/video/saa7196.h create mode 100644 linux/drivers/media/video/se401.c create mode 100644 linux/drivers/media/video/se401.h create mode 100644 linux/drivers/media/video/stradis.c create mode 100644 linux/drivers/media/video/stv680.c create mode 100644 linux/drivers/media/video/stv680.h create mode 100644 linux/drivers/media/video/tuner-3036.c create mode 100644 linux/drivers/media/video/videocodec.c create mode 100644 linux/drivers/media/video/videocodec.h create mode 100644 linux/drivers/media/video/vino.c create mode 100644 linux/drivers/media/video/vino.h create mode 100644 linux/drivers/media/video/vpx3220.c create mode 100644 linux/drivers/media/video/w9966.c create mode 100644 linux/drivers/media/video/w9968cf.c create mode 100644 linux/drivers/media/video/w9968cf.h create mode 100644 linux/drivers/media/video/w9968cf_decoder.h create mode 100644 linux/drivers/media/video/w9968cf_vpp.h create mode 100644 linux/drivers/media/video/zoran.h create mode 100644 linux/drivers/media/video/zoran_card.c create mode 100644 linux/drivers/media/video/zoran_card.h create mode 100644 linux/drivers/media/video/zoran_device.c create mode 100644 linux/drivers/media/video/zoran_device.h create mode 100644 linux/drivers/media/video/zoran_driver.c create mode 100644 linux/drivers/media/video/zoran_procfs.c create mode 100644 linux/drivers/media/video/zoran_procfs.h create mode 100644 linux/drivers/media/video/zr36016.c create mode 100644 linux/drivers/media/video/zr36016.h create mode 100644 linux/drivers/media/video/zr36050.c create mode 100644 linux/drivers/media/video/zr36050.h create mode 100644 linux/drivers/media/video/zr36057.h create mode 100644 linux/drivers/media/video/zr36060.c create mode 100644 linux/drivers/media/video/zr36060.h (limited to 'linux/drivers') diff --git a/linux/drivers/media/radio/Kconfig b/linux/drivers/media/radio/Kconfig new file mode 100644 index 000000000..d318be383 --- /dev/null +++ b/linux/drivers/media/radio/Kconfig @@ -0,0 +1,354 @@ +# +# Multimedia Video device configuration +# + +menu "Radio Adapters" + depends on VIDEO_DEV!=n + +config RADIO_CADET + tristate "ADS Cadet AM/FM Tuner" + depends on ISA && VIDEO_DEV + ---help--- + Choose Y here if you have one of these AM/FM radio cards, and then + fill in the port address below. + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux API. Information on + this API and pointers to "v4l" programs may be found at + . + + Further documentation on this driver can be found on the WWW at + . + + To compile this driver as a module, choose M here: the + module will be called radio-cadet. + +config RADIO_RTRACK + tristate "AIMSlab RadioTrack (aka RadioReveal) support" + depends on ISA && VIDEO_DEV + ---help--- + Choose Y here if you have one of these FM radio cards, and then fill + in the port address below. + + Note that newer AIMSlab RadioTrack cards have a different chipset + and are not supported by this driver. For these cards, use the + RadioTrack II driver below. + + If you have a GemTeks combined (PnP) sound- and radio card you must + use this driver as a module and setup the card with isapnptools. + You must also pass the module a suitable io parameter, 0x248 has + been reported to be used by these cards. + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux API. Information on + this API and pointers to "v4l" programs may be found at + . More information is + contained in the file + . + + To compile this driver as a module, choose M here: the + module will be called radio-aimslab. + +config RADIO_RTRACK_PORT + hex "RadioTrack i/o port (0x20f or 0x30f)" + depends on RADIO_RTRACK=y + default "20f" + help + Enter either 0x30f or 0x20f here. The card default is 0x30f, if you + haven't changed the jumper setting on the card. + +config RADIO_RTRACK2 + tristate "AIMSlab RadioTrack II support" + depends on ISA && VIDEO_DEV + ---help--- + Choose Y here if you have this FM radio card, and then fill in the + port address below. + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux API. Information on + this API and pointers to "v4l" programs may be found at + . + + To compile this driver as a module, choose M here: the + module will be called radio-rtrack2. + +config RADIO_RTRACK2_PORT + hex "RadioTrack II i/o port (0x20c or 0x30c)" + depends on RADIO_RTRACK2=y + default "30c" + help + Enter either 0x30c or 0x20c here. The card default is 0x30c, if you + haven't changed the jumper setting on the card. + +config RADIO_AZTECH + tristate "Aztech/Packard Bell Radio" + depends on ISA && VIDEO_DEV + ---help--- + Choose Y here if you have one of these FM radio cards, and then fill + in the port address below. + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux API. Information on + this API and pointers to "v4l" programs may be found at + . + + To compile this driver as a module, choose M here: the + module will be called radio-aztech. + +config RADIO_AZTECH_PORT + hex "Aztech/Packard Bell I/O port (0x350 or 0x358)" + depends on RADIO_AZTECH=y + default "350" + help + Enter either 0x350 or 0x358 here. The card default is 0x350, if you + haven't changed the setting of jumper JP3 on the card. Removing the + jumper sets the card to 0x358. + +config RADIO_GEMTEK + tristate "GemTek Radio Card support" + depends on ISA && VIDEO_DEV + ---help--- + Choose Y here if you have this FM radio card, and then fill in the + port address below. + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux API. Information on + this API and pointers to "v4l" programs may be found at + . + + To compile this driver as a module, choose M here: the + module will be called radio-gemtek. + +config RADIO_GEMTEK_PORT + hex "GemTek i/o port (0x20c, 0x30c, 0x24c or 0x34c)" + depends on RADIO_GEMTEK=y + default "34c" + help + Enter either 0x20c, 0x30c, 0x24c or 0x34c here. The card default is + 0x34c, if you haven't changed the jumper setting on the card. On + Sound Vision 16 Gold PnP with FM Radio (ESS1869+FM Gemtek), the I/O + port is 0x28c. + +config RADIO_GEMTEK_PCI + tristate "GemTek PCI Radio Card support" + depends on VIDEO_DEV && PCI + ---help--- + Choose Y here if you have this PCI FM radio card. + + In order to control your radio card, you will need to use programs + that are compatible with the Video for Linux API. Information on + this API and pointers to "v4l" programs may be found at + . + + To compile this driver as a module, choose M here: the + module will be called radio-gemtek-pci. + +config RADIO_MAXIRADIO + tristate "Guillemot MAXI Radio FM 2000 radio" + depends on VIDEO_DEV && PCI + ---help--- + Choose Y here if you have this radio card. This card may also be + found as Gemtek PCI FM. + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux API. Information on + this API and pointers to "v4l" programs may be found at + . + + To compile this driver as a module, choose M here: the + module will be called radio-maxiradio. + +config RADIO_MAESTRO + tristate "Maestro on board radio" + depends on VIDEO_DEV + ---help--- + Say Y here to directly support the on-board radio tuner on the + Maestro 2 or 2E sound card. + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux API. Information on + this API and pointers to "v4l" programs may be found at + . + + To compile this driver as a module, choose M here: the + module will be called radio-maestro. + +config RADIO_MIROPCM20 + tristate "miroSOUND PCM20 radio" + depends on ISA && VIDEO_DEV && SOUND_ACI_MIXER + ---help--- + Choose Y here if you have this FM radio card. You also need to say Y + to "ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20 radio)" (in "Sound") + for this to work. + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux API. Information on + this API and pointers to "v4l" programs may be found at + . + + To compile this driver as a module, choose M here: the + module will be called miropcm20. + +config RADIO_MIROPCM20_RDS + tristate "miroSOUND PCM20 radio RDS user interface (EXPERIMENTAL)" + depends on RADIO_MIROPCM20 && EXPERIMENTAL + ---help--- + Choose Y here if you want to see RDS/RBDS information like + RadioText, Programme Service name, Clock Time and date, Programme + TYpe and Traffic Announcement/Programme identification. You also + need to say Y to "miroSOUND PCM20 radio" and devfs! + + It's not possible to read the raw RDS packets from the device, so + the driver cant provide an V4L interface for this. But the + availability of RDS is reported over V4L by the basic driver + already. Here RDS can be read from files in /dev/v4l/rds. + + To compile this driver as a module, choose M here: the + module will be called miropcm20-rds. + +config RADIO_SF16FMI + tristate "SF16FMI Radio" + depends on ISA && VIDEO_DEV + ---help--- + Choose Y here if you have one of these FM radio cards. If you + compile the driver into the kernel and your card is not PnP one, you + have to add "sf16fm=" to the kernel command line (I/O address is + 0x284 or 0x384). + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux API. Information on + this API and pointers to "v4l" programs may be found at + . + + To compile this driver as a module, choose M here: the + module will be called radio-sf16fmi. + +config RADIO_SF16FMR2 + tristate "SF16FMR2 Radio" + depends on ISA && VIDEO_DEV + ---help--- + Choose Y here if you have one of these FM radio cards. + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux API. Information on + this API and pointers to "v4l" programs may be found on the WWW at + . + + To compile this driver as a module, choose M here: the + module will be called radio-sf16fmr2. + +config RADIO_TERRATEC + tristate "TerraTec ActiveRadio ISA Standalone" + depends on ISA && VIDEO_DEV + ---help--- + Choose Y here if you have this FM radio card, and then fill in the + port address below. (TODO) + + Note: This driver is in its early stages. Right now volume and + frequency control and muting works at least for me, but + unfortunately I have not found anybody who wants to use this card + with Linux. So if it is this what YOU are trying to do right now, + PLEASE DROP ME A NOTE!! Rolf Offermanns . + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux API. Information on + this API and pointers to "v4l" programs may be found at + . + + To compile this driver as a module, choose M here: the + module will be called radio-terratec. + +config RADIO_TERRATEC_PORT + hex "Terratec i/o port (normally 0x590)" + depends on RADIO_TERRATEC=y + default "590" + help + Fill in the I/O port of your TerraTec FM radio card. If unsure, go + with the default. + +config RADIO_TRUST + tristate "Trust FM radio card" + depends on ISA && VIDEO_DEV + help + This is a driver for the Trust FM radio cards. Say Y if you have + such a card and want to use it under Linux. + + To compile this driver as a module, choose M here: the + module will be called radio-trust. + +config RADIO_TRUST_PORT + hex "Trust i/o port (usually 0x350 or 0x358)" + depends on RADIO_TRUST=y + default "350" + help + Enter the I/O port of your Trust FM radio card. If unsure, try the + values "0x350" or "0x358". + +config RADIO_TYPHOON + tristate "Typhoon Radio (a.k.a. EcoRadio)" + depends on ISA && VIDEO_DEV + ---help--- + Choose Y here if you have one of these FM radio cards, and then fill + in the port address and the frequency used for muting below. + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux API. Information on + this API and pointers to "v4l" programs may be found at + . + + To compile this driver as a module, choose M here: the + module will be called radio-typhoon. + +config RADIO_TYPHOON_PROC_FS + bool "Support for /proc/radio-typhoon" + depends on PROC_FS && RADIO_TYPHOON + help + Say Y here if you want the typhoon radio card driver to write + status information (frequency, volume, muted, mute frequency, + base address) to /proc/radio-typhoon. The file can be viewed with + your favorite pager (i.e. use "more /proc/radio-typhoon" or "less + /proc/radio-typhoon" or simply "cat /proc/radio-typhoon"). + +config RADIO_TYPHOON_PORT + hex "Typhoon I/O port (0x316 or 0x336)" + depends on RADIO_TYPHOON=y + default "316" + help + Enter the I/O port of your Typhoon or EcoRadio radio card. + +config RADIO_TYPHOON_MUTEFREQ + int "Typhoon frequency set when muting the device (kHz)" + depends on RADIO_TYPHOON=y + default "87500" + help + Enter the frequency used for muting the radio. The device is never + completely silent. If the volume is just turned down, you can still + hear silent voices and music. For that reason, the frequency of the + radio device is set to the frequency you can enter here whenever + the device is muted. There should be no local radio station at that + frequency. + +config RADIO_ZOLTRIX + tristate "Zoltrix Radio" + depends on ISA && VIDEO_DEV + ---help--- + Choose Y here if you have one of these FM radio cards, and then fill + in the port address below. + + In order to control your radio card, you will need to use programs + that are compatible with the Video For Linux API. Information on + this API and pointers to "v4l" programs may be found at + . + + To compile this driver as a module, choose M here: the + module will be called radio-zoltrix. + +config RADIO_ZOLTRIX_PORT + hex "ZOLTRIX I/O port (0x20c or 0x30c)" + depends on RADIO_ZOLTRIX=y + default "20c" + help + Enter the I/O port of your Zoltrix radio card. + +endmenu + diff --git a/linux/drivers/media/radio/Makefile b/linux/drivers/media/radio/Makefile new file mode 100644 index 000000000..8b351945d --- /dev/null +++ b/linux/drivers/media/radio/Makefile @@ -0,0 +1,22 @@ +# +# Makefile for the kernel character device drivers. +# + +miropcm20-objs := miropcm20-rds-core.o miropcm20-radio.o + +obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o +obj-$(CONFIG_RADIO_RTRACK2) += radio-rtrack2.o +obj-$(CONFIG_RADIO_SF16FMI) += radio-sf16fmi.o +obj-$(CONFIG_RADIO_SF16FMR2) += radio-sf16fmr2.o +obj-$(CONFIG_RADIO_CADET) += radio-cadet.o +obj-$(CONFIG_RADIO_TYPHOON) += radio-typhoon.o +obj-$(CONFIG_RADIO_TERRATEC) += radio-terratec.o +obj-$(CONFIG_RADIO_MAXIRADIO) += radio-maxiradio.o +obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o +obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o +obj-$(CONFIG_RADIO_MIROPCM20) += miropcm20.o +obj-$(CONFIG_RADIO_MIROPCM20_RDS) += miropcm20-rds.o +obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o +obj-$(CONFIG_RADIO_GEMTEK_PCI) += radio-gemtek-pci.o +obj-$(CONFIG_RADIO_TRUST) += radio-trust.o +obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o diff --git a/linux/drivers/media/radio/miropcm20-radio.c b/linux/drivers/media/radio/miropcm20-radio.c new file mode 100644 index 000000000..c305ecbc0 --- /dev/null +++ b/linux/drivers/media/radio/miropcm20-radio.c @@ -0,0 +1,266 @@ +/* Miro PCM20 radio driver for Linux radio support + * (c) 1998 Ruurd Reitsma + * Thanks to Norberto Pellici for the ACI device interface specification + * The API part is based on the radiotrack driver by M. Kirkwood + * This driver relies on the aci mixer (drivers/sound/aci.c) + * Look there for further info... + */ + +/* Revision history: + * + * 1998 Ruurd Reitsma + * 2000-09-05 Robert Siemer + * removed unfinished volume control (maybe adding it later again) + * use OSS-mixer; added stereo control + */ + +/* What ever you think about the ACI, version 0x07 is not very well! + * I can't get frequency, 'tuner status', 'tuner flags' or mute/mono + * conditions... Robert + */ + +#include +#include +#include "compat.h" +#include +#include "../../../sound/oss/aci.h" +#include "miropcm20-rds-core.h" + +static int radio_nr = -1; +module_param(radio_nr, int, 0); + +struct pcm20_device { + unsigned long freq; + int muted; + int stereo; +}; + + +static int pcm20_mute(struct pcm20_device *dev, unsigned char mute) +{ + dev->muted = mute; + return aci_write_cmd(ACI_SET_TUNERMUTE, mute); +} + +static int pcm20_stereo(struct pcm20_device *dev, unsigned char stereo) +{ + dev->stereo = stereo; + return aci_write_cmd(ACI_SET_TUNERMONO, !stereo); +} + +static int pcm20_setfreq(struct pcm20_device *dev, unsigned long freq) +{ + unsigned char freql; + unsigned char freqh; + + dev->freq=freq; + + freq /= 160; + if (!(aci_version==0x07 || aci_version>=0xb0)) + freq /= 10; /* I don't know exactly which version + * needs this hack */ + freql = freq & 0xff; + freqh = freq >> 8; + + aci_rds_cmd(RDS_RESET, NULL, 0); + pcm20_stereo(dev, 1); + + return aci_rw_cmd(ACI_WRITE_TUNE, freql, freqh); +} + +static int pcm20_getflags(struct pcm20_device *dev, __u32 *flags, __u16 *signal) +{ + /* okay, check for signal, stereo and rds here... */ + int i; + unsigned char buf; + + if ((i=aci_rw_cmd(ACI_READ_TUNERSTATION, -1, -1))<0) + return i; + pr_debug("check_sig: 0x%x\n", i); + if (i & 0x80) { + /* no signal from tuner */ + *flags=0; + *signal=0; + return 0; + } else + *signal=0xffff; + + if ((i=aci_rw_cmd(ACI_READ_TUNERSTEREO, -1, -1))<0) + return i; + if (i & 0x40) { + *flags=0; + } else { + /* stereo */ + *flags=VIDEO_TUNER_STEREO_ON; + /* I can't see stereo, when forced to mono */ + dev->stereo=1; + } + + if ((i=aci_rds_cmd(RDS_STATUS, &buf, 1))<0) + return i; + if (buf & 1) + /* RDS available */ + *flags|=VIDEO_TUNER_RDS_ON; + else + return 0; + + if ((i=aci_rds_cmd(RDS_RXVALUE, &buf, 1))<0) + return i; + pr_debug("rds-signal: %d\n", buf); + if (buf > 15) { + printk("miropcm20-radio: RX strengths unexpected high...\n"); + buf=15; + } + /* refine signal */ + if ((*signal=SCALE(15, 0xffff, buf))==0) + *signal = 1; + + return 0; +} + +static int pcm20_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct pcm20_device *pcm20 = dev->priv; + int i; + + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability *v = arg; + memset(v,0,sizeof(*v)); + v->type=VID_TYPE_TUNER; + strcpy(v->name, "Miro PCM20"); + v->channels=1; + v->audios=1; + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + if(v->tuner) /* Only 1 tuner */ + return -EINVAL; + v->rangelow=87*16000; + v->rangehigh=108*16000; + pcm20_getflags(pcm20, &v->flags, &v->signal); + v->flags|=VIDEO_TUNER_LOW; + v->mode=VIDEO_MODE_AUTO; + strcpy(v->name, "FM"); + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *v = arg; + if(v->tuner!=0) + return -EINVAL; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: + { + unsigned long *freq = arg; + *freq = pcm20->freq; + return 0; + } + case VIDIOCSFREQ: + { + unsigned long *freq = arg; + pcm20->freq = *freq; + i=pcm20_setfreq(pcm20, pcm20->freq); + pr_debug("First view (setfreq): 0x%x\n", i); + return i; + } + case VIDIOCGAUDIO: + { + struct video_audio *v = arg; + memset(v,0, sizeof(*v)); + v->flags=VIDEO_AUDIO_MUTABLE; + if (pcm20->muted) + v->flags|=VIDEO_AUDIO_MUTE; + v->mode=VIDEO_SOUND_STEREO; + if (pcm20->stereo) + v->mode|=VIDEO_SOUND_MONO; + /* v->step=2048; */ + strcpy(v->name, "Radio"); + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio *v = arg; + if(v->audio) + return -EINVAL; + + pcm20_mute(pcm20, !!(v->flags&VIDEO_AUDIO_MUTE)); + if(v->flags&VIDEO_SOUND_MONO) + pcm20_stereo(pcm20, 0); + if(v->flags&VIDEO_SOUND_STEREO) + pcm20_stereo(pcm20, 1); + + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int pcm20_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, pcm20_do_ioctl); +} + +static struct pcm20_device pcm20_unit = { + .freq = 87*16000, + .muted = 1, +}; + +static struct file_operations pcm20_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = pcm20_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device pcm20_radio = { + .owner = THIS_MODULE, + .name = "Miro PCM 20 radio", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_RTRACK, + .fops = &pcm20_fops, + .priv = &pcm20_unit +}; + +static int __init pcm20_init(void) +{ + if(video_register_device(&pcm20_radio, VFL_TYPE_RADIO, radio_nr)==-1) + goto video_register_device; + + if(attach_aci_rds()<0) + goto attach_aci_rds; + + printk(KERN_INFO "Miro PCM20 radio card driver.\n"); + + return 0; + + attach_aci_rds: + video_unregister_device(&pcm20_radio); + video_register_device: + return -EINVAL; +} + +MODULE_AUTHOR("Ruurd Reitsma"); +MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card."); +MODULE_LICENSE("GPL"); + +static void __exit pcm20_cleanup(void) +{ + unload_aci_rds(); + video_unregister_device(&pcm20_radio); +} + +module_init(pcm20_init); +module_exit(pcm20_cleanup); diff --git a/linux/drivers/media/radio/miropcm20-rds-core.c b/linux/drivers/media/radio/miropcm20-rds-core.c new file mode 100644 index 000000000..b602c73e2 --- /dev/null +++ b/linux/drivers/media/radio/miropcm20-rds-core.c @@ -0,0 +1,211 @@ +/* + * Many thanks to Fred Seidel , the + * designer of the RDS decoder hardware. With his help + * I was able to code this driver. + * Thanks also to Norberto Pellicci, Dominic Mounteney + * and www.teleauskunft.de + * for good hints on finding Fred. It was somewhat hard + * to locate him here in Germany... [: + * + * Revision history: + * + * 2000-08-09 Robert Siemer + * RDS support for MiroSound PCM20 radio + */ + +#include +#include +#include +#include +#include +#include + +#include +#include "../../../sound/oss/aci.h" +#include "miropcm20-rds-core.h" + +#define DEBUG 0 + +static struct mutex aci_rds_mutex; + +#define RDS_DATASHIFT 2 /* Bit 2 */ +#define RDS_DATAMASK (1 << RDS_DATASHIFT) +#define RDS_BUSYMASK 0x10 /* Bit 4 */ +#define RDS_CLOCKMASK 0x08 /* Bit 3 */ + +#define RDS_DATA(x) (((x) >> RDS_DATASHIFT) & 1) + + +#if DEBUG +static void print_matrix(char array[], unsigned int length) +{ + int i, j; + + for (i=0; i=0; j--) { + printk("%d", (array[i] >> j) & 0x1); + } + if (i%8 == 0) + printk(" byte-border\n"); + else + printk("\n"); + } +} +#endif /* DEBUG */ + +static int byte2trans(unsigned char byte, unsigned char sendbuffer[], int size) +{ + int i; + + if (size != 8) + return -1; + for (i = 7; i >= 0; i--) + sendbuffer[7-i] = (byte & (1 << i)) ? RDS_DATAMASK : 0; + sendbuffer[0] |= RDS_CLOCKMASK; + + return 0; +} + +static int rds_waitread(void) +{ + unsigned char byte; + int i=2000; + + do { + byte=inb(RDS_REGISTER); + i--; + } + while ((byte & RDS_BUSYMASK) && i); + + if (i) { + #if DEBUG + printk(KERN_DEBUG "rds_waitread()"); + print_matrix(&byte, 1); + #endif + return (byte); + } else { + printk(KERN_WARNING "aci-rds: rds_waitread() timeout...\n"); + return -1; + } +} + +/* don't use any ..._nowait() function if you are not sure what you do... */ + +static inline void rds_rawwrite_nowait(unsigned char byte) +{ + #if DEBUG + printk(KERN_DEBUG "rds_rawwrite()"); + print_matrix(&byte, 1); + #endif + outb(byte, RDS_REGISTER); +} + +static int rds_rawwrite(unsigned char byte) +{ + if (rds_waitread() >= 0) { + rds_rawwrite_nowait(byte); + return 0; + } else + return -1; +} + +static int rds_write(unsigned char cmd) +{ + unsigned char sendbuffer[8]; + int i; + + if (byte2trans(cmd, sendbuffer, 8) != 0){ + return -1; + } else { + for (i=0; i<8; i++) { + rds_rawwrite(sendbuffer[i]); + } + } + return 0; +} + +static int rds_readcycle_nowait(void) +{ + rds_rawwrite_nowait(0); + return rds_waitread(); +} + +static int rds_readcycle(void) +{ + if (rds_rawwrite(0) < 0) + return -1; + return rds_waitread(); +} + +static int rds_read(unsigned char databuffer[], int datasize) +{ + #define READSIZE (8*datasize) + + int i,j; + + if (datasize < 1) /* nothing to read */ + return 0; + + /* to be able to use rds_readcycle_nowait() + I have to waitread() here */ + if (rds_waitread() < 0) + return -1; + + memset(databuffer, 0, datasize); + + for (i=0; i< READSIZE; i++) + if((j=rds_readcycle_nowait()) < 0) { + return -1; + } else { + databuffer[i/8]|=(RDS_DATA(j) << (7-(i%8))); + } + + return 0; +} + +static int rds_ack(void) +{ + int i=rds_readcycle(); + + if (i < 0) + return -1; + if (i & RDS_DATAMASK) { + return 0; /* ACK */ + } else { + printk(KERN_DEBUG "aci-rds: NACK\n"); + return 1; /* NACK */ + } +} + +int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize) +{ + int ret; + + if (mutex_lock_interruptible(&aci_rds_mutex)) + return -EINTR; + + rds_write(cmd); + + /* RDS_RESET doesn't need further processing */ + if (cmd!=RDS_RESET && (rds_ack() || rds_read(databuffer, datasize))) + ret = -1; + else + ret = 0; + + mutex_unlock(&aci_rds_mutex); + + return ret; +} +EXPORT_SYMBOL(aci_rds_cmd); + +int __init attach_aci_rds(void) +{ + mutex_init(&aci_rds_mutex); + return 0; +} + +void __exit unload_aci_rds(void) +{ +} +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/radio/miropcm20-rds-core.h b/linux/drivers/media/radio/miropcm20-rds-core.h new file mode 100644 index 000000000..aeb5761f0 --- /dev/null +++ b/linux/drivers/media/radio/miropcm20-rds-core.h @@ -0,0 +1,19 @@ +#ifndef _MIROPCM20_RDS_CORE_H_ +#define _MIROPCM20_RDS_CORE_H_ + +extern int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize); + +#define RDS_STATUS 0x01 +#define RDS_STATIONNAME 0x02 +#define RDS_TEXT 0x03 +#define RDS_ALTFREQ 0x04 +#define RDS_TIMEDATE 0x05 +#define RDS_PI_CODE 0x06 +#define RDS_PTYTATP 0x07 +#define RDS_RESET 0x08 +#define RDS_RXVALUE 0x09 + +extern void __exit unload_aci_rds(void); +extern int __init attach_aci_rds(void); + +#endif /* _MIROPCM20_RDS_CORE_H_ */ diff --git a/linux/drivers/media/radio/miropcm20-rds.c b/linux/drivers/media/radio/miropcm20-rds.c new file mode 100644 index 000000000..e09214082 --- /dev/null +++ b/linux/drivers/media/radio/miropcm20-rds.c @@ -0,0 +1,134 @@ +/* MiroSOUND PCM20 radio rds interface driver + * (c) 2001 Robert Siemer + * Thanks to Fred Seidel. See miropcm20-rds-core.c for further information. + */ + +/* Revision history: + * + * 2001-04-18 Robert Siemer + * separate file for user interface driver + */ + +#include +#include +#include +#include +#include +#include /* current, TASK_*, schedule_timeout() */ +#include +#include +#include "miropcm20-rds-core.h" + +static char * text_buffer; +static int rds_users = 0; + + +static int rds_f_open(struct inode *in, struct file *fi) +{ + if (rds_users) + return -EBUSY; + + rds_users++; + if ((text_buffer=kmalloc(66, GFP_KERNEL)) == 0) { + rds_users--; + printk(KERN_NOTICE "aci-rds: Out of memory by open()...\n"); + return -ENOMEM; + } + + return 0; +} + +static int rds_f_release(struct inode *in, struct file *fi) +{ + kfree(text_buffer); + + rds_users--; + return 0; +} + +static void print_matrix(char *ch, char out[]) +{ + int j; + + for (j=7; j>=0; j--) { + out[7-j] = ((*ch >> j) & 0x1) + '0'; + } +} + +static ssize_t rds_f_read(struct file *file, char __user *buffer, size_t length, loff_t *offset) +{ +// i = sprintf(text_buffer, "length: %d, offset: %d\n", length, *offset); + + char c; + char bits[8]; + + msleep(2000); + aci_rds_cmd(RDS_STATUS, &c, 1); + print_matrix(&c, bits); + if (copy_to_user(buffer, bits, 8)) + return -EFAULT; + +/* if ((c >> 3) & 1) { + aci_rds_cmd(RDS_STATIONNAME, text_buffer+1, 8); + text_buffer[0] = ' ' ; + text_buffer[9] = '\n'; + return copy_to_user(buffer+8, text_buffer, 10) ? -EFAULT: 18; + } +*/ +/* if ((c >> 6) & 1) { + aci_rds_cmd(RDS_PTYTATP, &c, 1); + if ( c & 1) + sprintf(text_buffer, " M"); + else + sprintf(text_buffer, " S"); + if ((c >> 1) & 1) + sprintf(text_buffer+2, " TA"); + else + sprintf(text_buffer+2, " --"); + if ((c >> 7) & 1) + sprintf(text_buffer+5, " TP"); + else + sprintf(text_buffer+5, " --"); + sprintf(text_buffer+8, " %2d\n", (c >> 2) & 0x1f); + return copy_to_user(buffer+8, text_buffer, 12) ? -EFAULT: 20; + } +*/ + + if ((c >> 4) & 1) { + aci_rds_cmd(RDS_TEXT, text_buffer, 65); + text_buffer[0] = ' ' ; + text_buffer[65] = '\n'; + return copy_to_user(buffer+8, text_buffer,66) ? -EFAULT : 66+8; + } else { + put_user('\n', buffer+8); + return 9; + } +} + +static struct file_operations rds_fops = { + .owner = THIS_MODULE, + .read = rds_f_read, + .open = rds_f_open, + .release = rds_f_release +}; + +static struct miscdevice rds_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "radiotext", + .devfs_name = "v4l/rds/radiotext", + .fops = &rds_fops, +}; + +static int __init miropcm20_rds_init(void) +{ + return misc_register(&rds_miscdev); +} + +static void __exit miropcm20_rds_cleanup(void) +{ + misc_deregister(&rds_miscdev); +} + +module_init(miropcm20_rds_init); +module_exit(miropcm20_rds_cleanup); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/radio/radio-aimslab.c b/linux/drivers/media/radio/radio-aimslab.c new file mode 100644 index 000000000..0fa385ef8 --- /dev/null +++ b/linux/drivers/media/radio/radio-aimslab.c @@ -0,0 +1,370 @@ +/* radiotrack (radioreveal) driver for Linux radio support + * (c) 1997 M. Kirkwood + * Converted to new API by Alan Cox + * Various bugfixes and enhancements by Russell Kroll + * + * History: + * 1999-02-24 Russell Kroll + * Fine tuning/VIDEO_TUNER_LOW + * Frequency range expanded to start at 87 MHz + * + * TODO: Allow for more than one of these foolish entities :-) + * + * Notes on the hardware (reverse engineered from other peoples' + * reverse engineering of AIMS' code :-) + * + * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); + * + * The signal strength query is unsurprisingly inaccurate. And it seems + * to indicate that (on my card, at least) the frequency setting isn't + * too great. (I have to tune up .025MHz from what the freq should be + * to get a report that the thing is tuned.) + * + * Volume control is (ugh) analogue: + * out(port, start_increasing_volume); + * wait(a_wee_while); + * out(port, stop_changing_the_volume); + * + */ + +#include /* Modules */ +#include /* Initdata */ +#include /* request_region */ +#include /* udelay */ +#include /* outb, outb_p */ +#include /* copy to/from user */ +#include "compat.h" +#include /* kernel radio structs */ +#include /* CONFIG_RADIO_RTRACK_PORT */ +#include /* Lock for the I/O */ + +#ifndef CONFIG_RADIO_RTRACK_PORT +#define CONFIG_RADIO_RTRACK_PORT -1 +#endif + +static int io = CONFIG_RADIO_RTRACK_PORT; +static int radio_nr = -1; +static struct mutex lock; + +struct rt_device +{ + int port; + int curvol; + unsigned long curfreq; + int muted; +}; + + +/* local things */ + +static void sleep_delay(long n) +{ + /* Sleep nicely for 'n' uS */ + int d=n/(1000000/HZ); + if(!d) + udelay(n); + else + msleep(jiffies_to_msecs(d)); +} + +static void rt_decvol(void) +{ + outb(0x58, io); /* volume down + sigstr + on */ + sleep_delay(100000); + outb(0xd8, io); /* volume steady + sigstr + on */ +} + +static void rt_incvol(void) +{ + outb(0x98, io); /* volume up + sigstr + on */ + sleep_delay(100000); + outb(0xd8, io); /* volume steady + sigstr + on */ +} + +static void rt_mute(struct rt_device *dev) +{ + dev->muted = 1; + mutex_lock(&lock); + outb(0xd0, io); /* volume steady, off */ + mutex_unlock(&lock); +} + +static int rt_setvol(struct rt_device *dev, int vol) +{ + int i; + + mutex_lock(&lock); + + if(vol == dev->curvol) { /* requested volume = current */ + if (dev->muted) { /* user is unmuting the card */ + dev->muted = 0; + outb (0xd8, io); /* enable card */ + } + mutex_unlock(&lock); + return 0; + } + + if(vol == 0) { /* volume = 0 means mute the card */ + outb(0x48, io); /* volume down but still "on" */ + sleep_delay(2000000); /* make sure it's totally down */ + outb(0xd0, io); /* volume steady, off */ + dev->curvol = 0; /* track the volume state! */ + mutex_unlock(&lock); + return 0; + } + + dev->muted = 0; + if(vol > dev->curvol) + for(i = dev->curvol; i < vol; i++) + rt_incvol(); + else + for(i = dev->curvol; i > vol; i--) + rt_decvol(); + + dev->curvol = vol; + mutex_unlock(&lock); + return 0; +} + +/* the 128+64 on these outb's is to keep the volume stable while tuning + * without them, the volume _will_ creep up with each frequency change + * and bit 4 (+16) is to keep the signal strength meter enabled + */ + +static void send_0_byte(int port, struct rt_device *dev) +{ + if ((dev->curvol == 0) || (dev->muted)) { + outb_p(128+64+16+ 1, port); /* wr-enable + data low */ + outb_p(128+64+16+2+1, port); /* clock */ + } + else { + outb_p(128+64+16+8+ 1, port); /* on + wr-enable + data low */ + outb_p(128+64+16+8+2+1, port); /* clock */ + } + sleep_delay(1000); +} + +static void send_1_byte(int port, struct rt_device *dev) +{ + if ((dev->curvol == 0) || (dev->muted)) { + outb_p(128+64+16+4 +1, port); /* wr-enable+data high */ + outb_p(128+64+16+4+2+1, port); /* clock */ + } + else { + outb_p(128+64+16+8+4 +1, port); /* on+wr-enable+data high */ + outb_p(128+64+16+8+4+2+1, port); /* clock */ + } + + sleep_delay(1000); +} + +static int rt_setfreq(struct rt_device *dev, unsigned long freq) +{ + int i; + + /* adapted from radio-aztech.c */ + + /* now uses VIDEO_TUNER_LOW for fine tuning */ + + freq += 171200; /* Add 10.7 MHz IF */ + freq /= 800; /* Convert to 50 kHz units */ + + mutex_lock(&lock); /* Stop other ops interfering */ + + send_0_byte (io, dev); /* 0: LSB of frequency */ + + for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ + if (freq & (1 << i)) + send_1_byte (io, dev); + else + send_0_byte (io, dev); + + send_0_byte (io, dev); /* 14: test bit - always 0 */ + send_0_byte (io, dev); /* 15: test bit - always 0 */ + + send_0_byte (io, dev); /* 16: band data 0 - always 0 */ + send_0_byte (io, dev); /* 17: band data 1 - always 0 */ + send_0_byte (io, dev); /* 18: band data 2 - always 0 */ + send_0_byte (io, dev); /* 19: time base - always 0 */ + + send_0_byte (io, dev); /* 20: spacing (0 = 25 kHz) */ + send_1_byte (io, dev); /* 21: spacing (1 = 25 kHz) */ + send_0_byte (io, dev); /* 22: spacing (0 = 25 kHz) */ + send_1_byte (io, dev); /* 23: AM/FM (FM = 1, always) */ + + if ((dev->curvol == 0) || (dev->muted)) + outb (0xd0, io); /* volume steady + sigstr */ + else + outb (0xd8, io); /* volume steady + sigstr + on */ + + mutex_unlock(&lock); + + return 0; +} + +static int rt_getsigstr(struct rt_device *dev) +{ + if (inb(io) & 2) /* bit set = no signal present */ + return 0; + return 1; /* signal present */ +} + +static int rt_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct rt_device *rt=dev->priv; + + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability *v = arg; + memset(v,0,sizeof(*v)); + v->type=VID_TYPE_TUNER; + v->channels=1; + v->audios=1; + strcpy(v->name, "RadioTrack"); + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + if(v->tuner) /* Only 1 tuner */ + return -EINVAL; + v->rangelow=(87*16000); + v->rangehigh=(108*16000); + v->flags=VIDEO_TUNER_LOW; + v->mode=VIDEO_MODE_AUTO; + strcpy(v->name, "FM"); + v->signal=0xFFFF*rt_getsigstr(rt); + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *v = arg; + if(v->tuner!=0) + return -EINVAL; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: + { + unsigned long *freq = arg; + *freq = rt->curfreq; + return 0; + } + case VIDIOCSFREQ: + { + unsigned long *freq = arg; + rt->curfreq = *freq; + rt_setfreq(rt, rt->curfreq); + return 0; + } + case VIDIOCGAUDIO: + { + struct video_audio *v = arg; + memset(v,0, sizeof(*v)); + v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME; + v->volume=rt->curvol * 6554; + v->step=6554; + strcpy(v->name, "Radio"); + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio *v = arg; + if(v->audio) + return -EINVAL; + if(v->flags&VIDEO_AUDIO_MUTE) + rt_mute(rt); + else + rt_setvol(rt,v->volume/6554); + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int rt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, rt_do_ioctl); +} + +static struct rt_device rtrack_unit; + +static struct file_operations rtrack_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = rt_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device rtrack_radio= +{ + .owner = THIS_MODULE, + .name = "RadioTrack radio", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_RTRACK, + .fops = &rtrack_fops, +}; + +static int __init rtrack_init(void) +{ + if(io==-1) + { + printk(KERN_ERR "You must set an I/O address with io=0x???\n"); + return -EINVAL; + } + + if (!request_region(io, 2, "rtrack")) + { + printk(KERN_ERR "rtrack: port 0x%x already in use\n", io); + return -EBUSY; + } + + rtrack_radio.priv=&rtrack_unit; + + if(video_register_device(&rtrack_radio, VFL_TYPE_RADIO, radio_nr)==-1) + { + release_region(io, 2); + return -EINVAL; + } + printk(KERN_INFO "AIMSlab RadioTrack/RadioReveal card driver.\n"); + + /* Set up the I/O locking */ + + mutex_init(&lock); + + /* mute card - prevents noisy bootups */ + + /* this ensures that the volume is all the way down */ + outb(0x48, io); /* volume down but still "on" */ + sleep_delay(2000000); /* make sure it's totally down */ + outb(0xc0, io); /* steady volume, mute card */ + rtrack_unit.curvol = 0; + + return 0; +} + +MODULE_AUTHOR("M.Kirkwood"); +MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card."); +MODULE_LICENSE("GPL"); + +module_param(io, int, 0); +MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)"); +module_param(radio_nr, int, 0); + +static void __exit cleanup_rtrack_module(void) +{ + video_unregister_device(&rtrack_radio); + release_region(io,2); +} + +module_init(rtrack_init); +module_exit(cleanup_rtrack_module); + diff --git a/linux/drivers/media/radio/radio-aztech.c b/linux/drivers/media/radio/radio-aztech.c new file mode 100644 index 000000000..97f88c907 --- /dev/null +++ b/linux/drivers/media/radio/radio-aztech.c @@ -0,0 +1,317 @@ +/* radio-aztech.c - Aztech radio card driver for Linux 2.2 + * + * Adapted to support the Video for Linux API by + * Russell Kroll . Based on original tuner code by: + * + * Quay Ly + * Donald Song + * Jason Lewis (jlewis@twilight.vtc.vsc.edu) + * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) + * William McGrath (wmcgrath@twilight.vtc.vsc.edu) + * + * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/ + * along with more information on the card itself. + * + * History: + * 1999-02-24 Russell Kroll + * Fine tuning/VIDEO_TUNER_LOW + * Range expanded to 87-108 MHz (from 87.9-107.8) + * + * Notable changes from the original source: + * - includes stripped down to the essentials + * - for loops used as delays replaced with udelay() + * - #defines removed, changed to static values + * - tuning structure changed - no more character arrays, other changes +*/ + +#include /* Modules */ +#include /* Initdata */ +#include /* request_region */ +#include /* udelay */ +#include /* outb, outb_p */ +#include /* copy to/from user */ +#include "compat.h" +#include /* kernel radio structs */ +#include /* CONFIG_RADIO_AZTECH_PORT */ + +/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */ + +#ifndef CONFIG_RADIO_AZTECH_PORT +#define CONFIG_RADIO_AZTECH_PORT -1 +#endif + +static int io = CONFIG_RADIO_AZTECH_PORT; +static int radio_nr = -1; +static int radio_wait_time = 1000; +static struct mutex lock; + +struct az_device +{ + int curvol; + unsigned long curfreq; + int stereo; +}; + +static int volconvert(int level) +{ + level>>=14; /* Map 16bits down to 2 bit */ + level&=3; + + /* convert to card-friendly values */ + switch (level) + { + case 0: + return 0; + case 1: + return 1; + case 2: + return 4; + case 3: + return 5; + } + return 0; /* Quieten gcc */ +} + +static void send_0_byte (struct az_device *dev) +{ + udelay(radio_wait_time); + outb_p(2+volconvert(dev->curvol), io); + outb_p(64+2+volconvert(dev->curvol), io); +} + +static void send_1_byte (struct az_device *dev) +{ + udelay (radio_wait_time); + outb_p(128+2+volconvert(dev->curvol), io); + outb_p(128+64+2+volconvert(dev->curvol), io); +} + +static int az_setvol(struct az_device *dev, int vol) +{ + mutex_lock(&lock); + outb (volconvert(vol), io); + mutex_unlock(&lock); + return 0; +} + +/* thanks to Michael Dwyer for giving me a dose of clues in + * the signal strength department.. + * + * This card has a stereo bit - bit 0 set = mono, not set = stereo + * It also has a "signal" bit - bit 1 set = bad signal, not set = good + * + */ + +static int az_getsigstr(struct az_device *dev) +{ + if (inb(io) & 2) /* bit set = no signal present */ + return 0; + return 1; /* signal present */ +} + +static int az_getstereo(struct az_device *dev) +{ + if (inb(io) & 1) /* bit set = mono */ + return 0; + return 1; /* stereo */ +} + +static int az_setfreq(struct az_device *dev, unsigned long frequency) +{ + int i; + + frequency += 171200; /* Add 10.7 MHz IF */ + frequency /= 800; /* Convert to 50 kHz units */ + + mutex_lock(&lock); + + send_0_byte (dev); /* 0: LSB of frequency */ + + for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ + if (frequency & (1 << i)) + send_1_byte (dev); + else + send_0_byte (dev); + + send_0_byte (dev); /* 14: test bit - always 0 */ + send_0_byte (dev); /* 15: test bit - always 0 */ + send_0_byte (dev); /* 16: band data 0 - always 0 */ + if (dev->stereo) /* 17: stereo (1 to enable) */ + send_1_byte (dev); + else + send_0_byte (dev); + + send_1_byte (dev); /* 18: band data 1 - unknown */ + send_0_byte (dev); /* 19: time base - always 0 */ + send_0_byte (dev); /* 20: spacing (0 = 25 kHz) */ + send_1_byte (dev); /* 21: spacing (1 = 25 kHz) */ + send_0_byte (dev); /* 22: spacing (0 = 25 kHz) */ + send_1_byte (dev); /* 23: AM/FM (FM = 1, always) */ + + /* latch frequency */ + + udelay (radio_wait_time); + outb_p(128+64+volconvert(dev->curvol), io); + + mutex_unlock(&lock); + + return 0; +} + +static int az_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct az_device *az = dev->priv; + + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability *v = arg; + memset(v,0,sizeof(*v)); + v->type=VID_TYPE_TUNER; + v->channels=1; + v->audios=1; + strcpy(v->name, "Aztech Radio"); + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + if(v->tuner) /* Only 1 tuner */ + return -EINVAL; + v->rangelow=(87*16000); + v->rangehigh=(108*16000); + v->flags=VIDEO_TUNER_LOW; + v->mode=VIDEO_MODE_AUTO; + v->signal=0xFFFF*az_getsigstr(az); + if(az_getstereo(az)) + v->flags|=VIDEO_TUNER_STEREO_ON; + strcpy(v->name, "FM"); + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *v = arg; + if(v->tuner!=0) + return -EINVAL; + return 0; + } + case VIDIOCGFREQ: + { + unsigned long *freq = arg; + *freq = az->curfreq; + return 0; + } + case VIDIOCSFREQ: + { + unsigned long *freq = arg; + az->curfreq = *freq; + az_setfreq(az, az->curfreq); + return 0; + } + case VIDIOCGAUDIO: + { + struct video_audio *v = arg; + memset(v,0, sizeof(*v)); + v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME; + if(az->stereo) + v->mode=VIDEO_SOUND_STEREO; + else + v->mode=VIDEO_SOUND_MONO; + v->volume=az->curvol; + v->step=16384; + strcpy(v->name, "Radio"); + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio *v = arg; + if(v->audio) + return -EINVAL; + az->curvol=v->volume; + + az->stereo=(v->mode&VIDEO_SOUND_STEREO)?1:0; + if(v->flags&VIDEO_AUDIO_MUTE) + az_setvol(az,0); + else + az_setvol(az,az->curvol); + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int az_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, az_do_ioctl); +} + +static struct az_device aztech_unit; + +static struct file_operations aztech_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = az_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device aztech_radio= +{ + .owner = THIS_MODULE, + .name = "Aztech radio", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_AZTECH, + .fops = &aztech_fops, +}; + +static int __init aztech_init(void) +{ + if(io==-1) + { + printk(KERN_ERR "You must set an I/O address with io=0x???\n"); + return -EINVAL; + } + + if (!request_region(io, 2, "aztech")) + { + printk(KERN_ERR "aztech: port 0x%x already in use\n", io); + return -EBUSY; + } + + mutex_init(&lock); + aztech_radio.priv=&aztech_unit; + + if(video_register_device(&aztech_radio, VFL_TYPE_RADIO, radio_nr)==-1) + { + release_region(io,2); + return -EINVAL; + } + + printk(KERN_INFO "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n"); + /* mute card - prevents noisy bootups */ + outb (0, io); + return 0; +} + +MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); +MODULE_DESCRIPTION("A driver for the Aztech radio card."); +MODULE_LICENSE("GPL"); + +module_param(io, int, 0); +module_param(radio_nr, int, 0); +MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)"); + +static void __exit aztech_cleanup(void) +{ + video_unregister_device(&aztech_radio); + release_region(io,2); +} + +module_init(aztech_init); +module_exit(aztech_cleanup); diff --git a/linux/drivers/media/radio/radio-cadet.c b/linux/drivers/media/radio/radio-cadet.c new file mode 100644 index 000000000..559be5d00 --- /dev/null +++ b/linux/drivers/media/radio/radio-cadet.c @@ -0,0 +1,622 @@ +/* radio-cadet.c - A video4linux driver for the ADS Cadet AM/FM Radio Card + * + * by Fred Gleason + * Version 0.3.3 + * + * (Loosely) based on code for the Aztech radio card by + * + * Russell Kroll (rkroll@exploits.org) + * Quay Ly + * Donald Song + * Jason Lewis (jlewis@twilight.vtc.vsc.edu) + * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) + * William McGrath (wmcgrath@twilight.vtc.vsc.edu) + * + * History: + * 2000-04-29 Russell Kroll + * Added ISAPnP detection for Linux 2.3/2.4 + * + * 2001-01-10 Russell Kroll + * Removed dead CONFIG_RADIO_CADET_PORT code + * PnP detection on load is now default (no args necessary) + * + * 2002-01-17 Adam Belay + * Updated to latest pnp code + * + * 2003-01-31 Alan Cox + * Cleaned up locking, delay code, general odds and ends + */ + +#include /* Modules */ +#include /* Initdata */ +#include /* request_region */ +#include /* udelay */ +#include /* outb, outb_p */ +#include /* copy to/from user */ +#include "compat.h" +#include /* kernel radio structs */ +#include +#include + +#define RDS_BUFFER 256 + +static int io=-1; /* default to isapnp activation */ +static int radio_nr = -1; +static int users=0; +static int curtuner=0; +static int tunestat=0; +static int sigstrength=0; +static wait_queue_head_t read_queue; +static struct timer_list readtimer; +static __u8 rdsin=0,rdsout=0,rdsstat=0; +static unsigned char rdsbuf[RDS_BUFFER]; +static spinlock_t cadet_io_lock; + +static int cadet_probe(void); + +/* + * Signal Strength Threshold Values + * The V4L API spec does not define any particular unit for the signal + * strength value. These values are in microvolts of RF at the tuner's input. + */ +static __u16 sigtable[2][4]={{5,10,30,150},{28,40,63,1000}}; + +static int cadet_getrds(void) +{ + int rdsstat=0; + + spin_lock(&cadet_io_lock); + outb(3,io); /* Select Decoder Control/Status */ + outb(inb(io+1)&0x7f,io+1); /* Reset RDS detection */ + spin_unlock(&cadet_io_lock); + + msleep(100); + + spin_lock(&cadet_io_lock); + outb(3,io); /* Select Decoder Control/Status */ + if((inb(io+1)&0x80)!=0) { + rdsstat|=VIDEO_TUNER_RDS_ON; + } + if((inb(io+1)&0x10)!=0) { + rdsstat|=VIDEO_TUNER_MBS_ON; + } + spin_unlock(&cadet_io_lock); + return rdsstat; +} + +static int cadet_getstereo(void) +{ + int ret = 0; + if(curtuner != 0) /* Only FM has stereo capability! */ + return 0; + + spin_lock(&cadet_io_lock); + outb(7,io); /* Select tuner control */ + if( (inb(io+1) & 0x40) == 0) + ret = 1; + spin_unlock(&cadet_io_lock); + return ret; +} + +static unsigned cadet_gettune(void) +{ + int curvol,i; + unsigned fifo=0; + + /* + * Prepare for read + */ + + spin_lock(&cadet_io_lock); + + outb(7,io); /* Select tuner control */ + curvol=inb(io+1); /* Save current volume/mute setting */ + outb(0x00,io+1); /* Ensure WRITE-ENABLE is LOW */ + tunestat=0xffff; + + /* + * Read the shift register + */ + for(i=0;i<25;i++) { + fifo=(fifo<<1)|((inb(io+1)>>7)&0x01); + if(i<24) { + outb(0x01,io+1); + tunestat&=inb(io+1); + outb(0x00,io+1); + } + } + + /* + * Restore volume/mute setting + */ + outb(curvol,io+1); + spin_unlock(&cadet_io_lock); + + return fifo; +} + +static unsigned cadet_getfreq(void) +{ + int i; + unsigned freq=0,test,fifo=0; + + /* + * Read current tuning + */ + fifo=cadet_gettune(); + + /* + * Convert to actual frequency + */ + if(curtuner==0) { /* FM */ + test=12500; + for(i=0;i<14;i++) { + if((fifo&0x01)!=0) { + freq+=test; + } + test=test<<1; + fifo=fifo>>1; + } + freq-=10700000; /* IF frequency is 10.7 MHz */ + freq=(freq*16)/1000000; /* Make it 1/16 MHz */ + } + if(curtuner==1) { /* AM */ + freq=((fifo&0x7fff)-2010)*16; + } + + return freq; +} + +static void cadet_settune(unsigned fifo) +{ + int i; + unsigned test; + + spin_lock(&cadet_io_lock); + + outb(7,io); /* Select tuner control */ + /* + * Write the shift register + */ + test=0; + test=(fifo>>23)&0x02; /* Align data for SDO */ + test|=0x1c; /* SDM=1, SWE=1, SEN=1, SCK=0 */ + outb(7,io); /* Select tuner control */ + outb(test,io+1); /* Initialize for write */ + for(i=0;i<25;i++) { + test|=0x01; /* Toggle SCK High */ + outb(test,io+1); + test&=0xfe; /* Toggle SCK Low */ + outb(test,io+1); + fifo=fifo<<1; /* Prepare the next bit */ + test=0x1c|((fifo>>23)&0x02); + outb(test,io+1); + } + spin_unlock(&cadet_io_lock); +} + +static void cadet_setfreq(unsigned freq) +{ + unsigned fifo; + int i,j,test; + int curvol; + + /* + * Formulate a fifo command + */ + fifo=0; + if(curtuner==0) { /* FM */ + test=102400; + freq=(freq*1000)/16; /* Make it kHz */ + freq+=10700; /* IF is 10700 kHz */ + for(i=0;i<14;i++) { + fifo=fifo<<1; + if(freq>=test) { + fifo|=0x01; + freq-=test; + } + test=test>>1; + } + } + if(curtuner==1) { /* AM */ + fifo=(freq/16)+2010; /* Make it kHz */ + fifo|=0x100000; /* Select AM Band */ + } + + /* + * Save current volume/mute setting + */ + + spin_lock(&cadet_io_lock); + outb(7,io); /* Select tuner control */ + curvol=inb(io+1); + spin_unlock(&cadet_io_lock); + + /* + * Tune the card + */ + for(j=3;j>-1;j--) { + cadet_settune(fifo|(j<<16)); + + spin_lock(&cadet_io_lock); + outb(7,io); /* Select tuner control */ + outb(curvol,io+1); + spin_unlock(&cadet_io_lock); + + msleep(100); + + cadet_gettune(); + if((tunestat & 0x40) == 0) { /* Tuned */ + sigstrength=sigtable[curtuner][j]; + return; + } + } + sigstrength=0; +} + + +static int cadet_getvol(void) +{ + int ret = 0; + + spin_lock(&cadet_io_lock); + + outb(7,io); /* Select tuner control */ + if((inb(io + 1) & 0x20) != 0) + ret = 0xffff; + + spin_unlock(&cadet_io_lock); + return ret; +} + + +static void cadet_setvol(int vol) +{ + spin_lock(&cadet_io_lock); + outb(7,io); /* Select tuner control */ + if(vol>0) + outb(0x20,io+1); + else + outb(0x00,io+1); + spin_unlock(&cadet_io_lock); +} + +static void cadet_handler(unsigned long data) +{ + /* + * Service the RDS fifo + */ + + if(spin_trylock(&cadet_io_lock)) + { + outb(0x3,io); /* Select RDS Decoder Control */ + if((inb(io+1)&0x20)!=0) { + printk(KERN_CRIT "cadet: RDS fifo overflow\n"); + } + outb(0x80,io); /* Select RDS fifo */ + while((inb(io)&0x80)!=0) { + rdsbuf[rdsin]=inb(io+1); + if(rdsin==rdsout) + printk(KERN_WARNING "cadet: RDS buffer overflow\n"); + else + rdsin++; + } + spin_unlock(&cadet_io_lock); + } + + /* + * Service pending read + */ + if( rdsin!=rdsout) + wake_up_interruptible(&read_queue); + + /* + * Clean up and exit + */ + init_timer(&readtimer); + readtimer.function=cadet_handler; + readtimer.data=(unsigned long)0; + readtimer.expires=jiffies+(HZ/20); + add_timer(&readtimer); +} + + + +static ssize_t cadet_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + int i=0; + unsigned char readbuf[RDS_BUFFER]; + + if(rdsstat==0) { + spin_lock(&cadet_io_lock); + rdsstat=1; + outb(0x80,io); /* Select RDS fifo */ + spin_unlock(&cadet_io_lock); + init_timer(&readtimer); + readtimer.function=cadet_handler; + readtimer.data=(unsigned long)0; + readtimer.expires=jiffies+(HZ/20); + add_timer(&readtimer); + } + if(rdsin==rdsout) { + if (file->f_flags & O_NONBLOCK) + return -EWOULDBLOCK; + interruptible_sleep_on(&read_queue); + } + while( itype=VID_TYPE_TUNER; + v->channels=2; + v->audios=1; + strcpy(v->name, "ADS Cadet"); + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + if((v->tuner<0)||(v->tuner>1)) { + return -EINVAL; + } + switch(v->tuner) { + case 0: + strcpy(v->name,"FM"); + v->rangelow=1400; /* 87.5 MHz */ + v->rangehigh=1728; /* 108.0 MHz */ + v->flags=0; + v->mode=0; + v->mode|=VIDEO_MODE_AUTO; + v->signal=sigstrength; + if(cadet_getstereo()==1) { + v->flags|=VIDEO_TUNER_STEREO_ON; + } + v->flags|=cadet_getrds(); + break; + case 1: + strcpy(v->name,"AM"); + v->rangelow=8320; /* 520 kHz */ + v->rangehigh=26400; /* 1650 kHz */ + v->flags=0; + v->flags|=VIDEO_TUNER_LOW; + v->mode=0; + v->mode|=VIDEO_MODE_AUTO; + v->signal=sigstrength; + break; + } + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *v = arg; + if((v->tuner<0)||(v->tuner>1)) { + return -EINVAL; + } + curtuner=v->tuner; + return 0; + } + case VIDIOCGFREQ: + { + unsigned long *freq = arg; + *freq = cadet_getfreq(); + return 0; + } + case VIDIOCSFREQ: + { + unsigned long *freq = arg; + if((curtuner==0)&&((*freq<1400)||(*freq>1728))) { + return -EINVAL; + } + if((curtuner==1)&&((*freq<8320)||(*freq>26400))) { + return -EINVAL; + } + cadet_setfreq(*freq); + return 0; + } + case VIDIOCGAUDIO: + { + struct video_audio *v = arg; + memset(v,0, sizeof(*v)); + v->flags=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME; + if(cadet_getstereo()==0) { + v->mode=VIDEO_SOUND_MONO; + } else { + v->mode=VIDEO_SOUND_STEREO; + } + v->volume=cadet_getvol(); + v->step=0xffff; + strcpy(v->name, "Radio"); + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio *v = arg; + if(v->audio) + return -EINVAL; + cadet_setvol(v->volume); + if(v->flags&VIDEO_AUDIO_MUTE) + cadet_setvol(0); + else + cadet_setvol(0xffff); + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int cadet_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, cadet_do_ioctl); +} + +static int cadet_open(struct inode *inode, struct file *file) +{ + if(users) + return -EBUSY; + users++; + init_waitqueue_head(&read_queue); + return 0; +} + +static int cadet_release(struct inode *inode, struct file *file) +{ + del_timer_sync(&readtimer); + rdsstat=0; + users--; + return 0; +} + + +static struct file_operations cadet_fops = { + .owner = THIS_MODULE, + .open = cadet_open, + .release = cadet_release, + .read = cadet_read, + .ioctl = cadet_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device cadet_radio= +{ + .owner = THIS_MODULE, + .name = "Cadet radio", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_CADET, + .fops = &cadet_fops, +}; + +static struct pnp_device_id cadet_pnp_devices[] = { + /* ADS Cadet AM/FM Radio Card */ + {.id = "MSM0c24", .driver_data = 0}, + {.id = ""} +}; + +MODULE_DEVICE_TABLE(pnp, cadet_pnp_devices); + +static int cadet_pnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev_id) +{ + if (!dev) + return -ENODEV; + /* only support one device */ + if (io > 0) + return -EBUSY; + + if (!pnp_port_valid(dev, 0)) { + return -ENODEV; + } + + io = pnp_port_start(dev, 0); + + printk ("radio-cadet: PnP reports device at %#x\n", io); + + return io; +} + +static struct pnp_driver cadet_pnp_driver = { + .name = "radio-cadet", + .id_table = cadet_pnp_devices, + .probe = cadet_pnp_probe, + .remove = NULL, +}; + +static int cadet_probe(void) +{ + static int iovals[8]={0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e}; + int i; + + for(i=0;i<8;i++) { + io=iovals[i]; + if (request_region(io, 2, "cadet-probe")) { + cadet_setfreq(1410); + if(cadet_getfreq()==1410) { + release_region(io, 2); + return io; + } + release_region(io, 2); + } + } + return -1; +} + +/* + * io should only be set if the user has used something like + * isapnp (the userspace program) to initialize this card for us + */ + +static int __init cadet_init(void) +{ + spin_lock_init(&cadet_io_lock); + + /* + * If a probe was requested then probe ISAPnP first (safest) + */ + if (io < 0) + pnp_register_driver(&cadet_pnp_driver); + /* + * If that fails then probe unsafely if probe is requested + */ + if(io < 0) + io = cadet_probe (); + + /* + * Else we bail out + */ + + if(io < 0) { +#ifdef MODULE + printk(KERN_ERR "You must set an I/O address with io=0x???\n"); +#endif + goto fail; + } + if (!request_region(io,2,"cadet")) + goto fail; + if(video_register_device(&cadet_radio,VFL_TYPE_RADIO,radio_nr)==-1) { + release_region(io,2); + goto fail; + } + printk(KERN_INFO "ADS Cadet Radio Card at 0x%x\n",io); + return 0; +fail: + pnp_unregister_driver(&cadet_pnp_driver); + return -1; +} + + + +MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); +MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card."); +MODULE_LICENSE("GPL"); + +module_param(io, int, 0); +MODULE_PARM_DESC(io, "I/O address of Cadet card (0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e)"); +module_param(radio_nr, int, 0); + +static void __exit cadet_cleanup_module(void) +{ + video_unregister_device(&cadet_radio); + release_region(io,2); + pnp_unregister_driver(&cadet_pnp_driver); +} + +module_init(cadet_init); +module_exit(cadet_cleanup_module); + diff --git a/linux/drivers/media/radio/radio-gemtek-pci.c b/linux/drivers/media/radio/radio-gemtek-pci.c new file mode 100644 index 000000000..c8777e953 --- /dev/null +++ b/linux/drivers/media/radio/radio-gemtek-pci.c @@ -0,0 +1,417 @@ +/* + *************************************************************************** + * + * radio-gemtek-pci.c - Gemtek PCI Radio driver + * (C) 2001 Vladimir Shebordaev + * + *************************************************************************** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + *************************************************************************** + * + * Gemtek Corp still silently refuses to release any specifications + * of their multimedia devices, so the protocol still has to be + * reverse engineered. + * + * The v4l code was inspired by Jonas Munsin's Gemtek serial line + * radio device driver. + * + * Please, let me know if this piece of code was useful :) + * + * TODO: multiple device support and portability were not tested + * + *************************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include "compat.h" +#include +#include + +#include +#include + +#ifndef PCI_VENDOR_ID_GEMTEK +#define PCI_VENDOR_ID_GEMTEK 0x5046 +#endif + +#ifndef PCI_DEVICE_ID_GEMTEK_PR103 +#define PCI_DEVICE_ID_GEMTEK_PR103 0x1001 +#endif + +#ifndef GEMTEK_PCI_RANGE_LOW +#define GEMTEK_PCI_RANGE_LOW (87*16000) +#endif + +#ifndef GEMTEK_PCI_RANGE_HIGH +#define GEMTEK_PCI_RANGE_HIGH (108*16000) +#endif + +#ifndef TRUE +#define TRUE (1) +#endif + +#ifndef FALSE +#define FALSE (0) +#endif + +struct gemtek_pci_card { + struct video_device *videodev; + + u32 iobase; + u32 length; + u8 chiprev; + u16 model; + + u32 current_frequency; + u8 mute; +}; + +static const char rcsid[] = "$Id: radio-gemtek-pci.c,v 1.1 2001/07/23 08:08:16 ted Exp ted $"; + +static int nr_radio = -1; + +static inline u8 gemtek_pci_out( u16 value, u32 port ) +{ + outw( value, port ); + + return (u8)value; +} + +#define _b0( v ) *((u8 *)&v) +static void __gemtek_pci_cmd( u16 value, u32 port, u8 *last_byte, int keep ) +{ + register u8 byte = *last_byte; + + if ( !value ) { + if ( !keep ) + value = (u16)port; + byte &= 0xfd; + } else + byte |= 2; + + _b0( value ) = byte; + outw( value, port ); + byte |= 1; + _b0( value ) = byte; + outw( value, port ); + byte &= 0xfe; + _b0( value ) = byte; + outw( value, port ); + + *last_byte = byte; +} + +static inline void gemtek_pci_nil( u32 port, u8 *last_byte ) +{ + __gemtek_pci_cmd( 0x00, port, last_byte, FALSE ); +} + +static inline void gemtek_pci_cmd( u16 cmd, u32 port, u8 *last_byte ) +{ + __gemtek_pci_cmd( cmd, port, last_byte, TRUE ); +} + +static void gemtek_pci_setfrequency( struct gemtek_pci_card *card, unsigned long frequency ) +{ + register int i; + register u32 value = frequency / 200 + 856; + register u16 mask = 0x8000; + u8 last_byte; + u32 port = card->iobase; + + last_byte = gemtek_pci_out( 0x06, port ); + + i = 0; + do { + gemtek_pci_nil( port, &last_byte ); + i++; + } while ( i < 9 ); + + i = 0; + do { + gemtek_pci_cmd( value & mask, port, &last_byte ); + mask >>= 1; + i++; + } while ( i < 16 ); + + outw( 0x10, port ); +} + + +static inline void gemtek_pci_mute( struct gemtek_pci_card *card ) +{ + outb( 0x1f, card->iobase ); + card->mute = TRUE; +} + +static inline void gemtek_pci_unmute( struct gemtek_pci_card *card ) +{ + if ( card->mute ) { + gemtek_pci_setfrequency( card, card->current_frequency ); + card->mute = FALSE; + } +} + +static inline unsigned int gemtek_pci_getsignal( struct gemtek_pci_card *card ) +{ + return ( inb( card->iobase ) & 0x08 ) ? 0 : 1; +} + +static int gemtek_pci_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct gemtek_pci_card *card = dev->priv; + + switch ( cmd ) { + case VIDIOCGCAP: + { + struct video_capability *c = arg; + + memset(c,0,sizeof(*c)); + c->type = VID_TYPE_TUNER; + c->channels = 1; + c->audios = 1; + strcpy( c->name, "Gemtek PCI Radio" ); + return 0; + } + + case VIDIOCGTUNER: + { + struct video_tuner *t = arg; + + if ( t->tuner ) + return -EINVAL; + + t->rangelow = GEMTEK_PCI_RANGE_LOW; + t->rangehigh = GEMTEK_PCI_RANGE_HIGH; + t->flags = VIDEO_TUNER_LOW; + t->mode = VIDEO_MODE_AUTO; + t->signal = 0xFFFF * gemtek_pci_getsignal( card ); + strcpy( t->name, "FM" ); + return 0; + } + + case VIDIOCSTUNER: + { + struct video_tuner *t = arg; + if ( t->tuner ) + return -EINVAL; + return 0; + } + + case VIDIOCGFREQ: + { + unsigned long *freq = arg; + *freq = card->current_frequency; + return 0; + } + case VIDIOCSFREQ: + { + unsigned long *freq = arg; + + if ( (*freq < GEMTEK_PCI_RANGE_LOW) || + (*freq > GEMTEK_PCI_RANGE_HIGH) ) + return -EINVAL; + + gemtek_pci_setfrequency( card, *freq ); + card->current_frequency = *freq; + card->mute = FALSE; + + return 0; + } + + case VIDIOCGAUDIO: + { + struct video_audio *a = arg; + + memset( a, 0, sizeof( *a ) ); + a->flags |= VIDEO_AUDIO_MUTABLE; + a->volume = 1; + a->step = 65535; + strcpy( a->name, "Radio" ); + return 0; + } + + case VIDIOCSAUDIO: + { + struct video_audio *a = arg; + + if ( a->audio ) + return -EINVAL; + + if ( a->flags & VIDEO_AUDIO_MUTE ) + gemtek_pci_mute( card ); + else + gemtek_pci_unmute( card ); + return 0; + } + + default: + return -ENOIOCTLCMD; + } +} + +static int gemtek_pci_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, gemtek_pci_do_ioctl); +} + +enum { + GEMTEK_PR103 +}; + +static char *card_names[] __devinitdata = { + "GEMTEK_PR103" +}; + +static struct pci_device_id gemtek_pci_id[] = +{ + { PCI_VENDOR_ID_GEMTEK, PCI_DEVICE_ID_GEMTEK_PR103, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, GEMTEK_PR103 }, + { 0 } +}; + +MODULE_DEVICE_TABLE( pci, gemtek_pci_id ); + +static int mx = 1; + +static struct file_operations gemtek_pci_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = gemtek_pci_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device vdev_template = { + .owner = THIS_MODULE, + .name = "Gemtek PCI Radio", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_GEMTEK, + .fops = &gemtek_pci_fops, +}; + +static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci_device_id *pci_id ) +{ + struct gemtek_pci_card *card; + struct video_device *devradio; + + if ( (card = kzalloc( sizeof( struct gemtek_pci_card ), GFP_KERNEL )) == NULL ) { + printk( KERN_ERR "gemtek_pci: out of memory\n" ); + return -ENOMEM; + } + + if ( pci_enable_device( pci_dev ) ) + goto err_pci; + + card->iobase = pci_resource_start( pci_dev, 0 ); + card->length = pci_resource_len( pci_dev, 0 ); + + if ( request_region( card->iobase, card->length, card_names[pci_id->driver_data] ) == NULL ) { + printk( KERN_ERR "gemtek_pci: i/o port already in use\n" ); + goto err_pci; + } + + pci_read_config_byte( pci_dev, PCI_REVISION_ID, &card->chiprev ); + pci_read_config_word( pci_dev, PCI_SUBSYSTEM_ID, &card->model ); + + pci_set_drvdata( pci_dev, card ); + + if ( (devradio = kmalloc( sizeof( struct video_device ), GFP_KERNEL )) == NULL ) { + printk( KERN_ERR "gemtek_pci: out of memory\n" ); + goto err_video; + } + *devradio = vdev_template; + + if ( video_register_device( devradio, VFL_TYPE_RADIO , nr_radio) == -1 ) { + kfree( devradio ); + goto err_video; + } + + card->videodev = devradio; + devradio->priv = card; + gemtek_pci_mute( card ); + + printk( KERN_INFO "Gemtek PCI Radio (rev. %d) found at 0x%04x-0x%04x.\n", + card->chiprev, card->iobase, card->iobase + card->length - 1 ); + + return 0; + +err_video: + release_region( card->iobase, card->length ); + +err_pci: + kfree( card ); + return -ENODEV; +} + +static void __devexit gemtek_pci_remove( struct pci_dev *pci_dev ) +{ + struct gemtek_pci_card *card = pci_get_drvdata( pci_dev ); + + video_unregister_device( card->videodev ); + kfree( card->videodev ); + + release_region( card->iobase, card->length ); + + if ( mx ) + gemtek_pci_mute( card ); + + kfree( card ); + + pci_set_drvdata( pci_dev, NULL ); +} + +static struct pci_driver gemtek_pci_driver = +{ + .name = "gemtek_pci", + .id_table = gemtek_pci_id, + .probe = gemtek_pci_probe, + .remove = __devexit_p(gemtek_pci_remove), +}; + +static int __init gemtek_pci_init_module( void ) +{ + return pci_register_driver( &gemtek_pci_driver ); +} + +static void __exit gemtek_pci_cleanup_module( void ) +{ + return pci_unregister_driver( &gemtek_pci_driver ); +} + +MODULE_AUTHOR( "Vladimir Shebordaev " ); +MODULE_DESCRIPTION( "The video4linux driver for the Gemtek PCI Radio Card" ); +MODULE_LICENSE("GPL"); + +module_param(mx, bool, 0); +MODULE_PARM_DESC( mx, "single digit: 1 - turn off the turner upon module exit (default), 0 - do not" ); +module_param(nr_radio, int, 0); +MODULE_PARM_DESC( nr_radio, "video4linux device number to use"); + +module_init( gemtek_pci_init_module ); +module_exit( gemtek_pci_cleanup_module ); + diff --git a/linux/drivers/media/radio/radio-gemtek.c b/linux/drivers/media/radio/radio-gemtek.c new file mode 100644 index 000000000..199f2df7a --- /dev/null +++ b/linux/drivers/media/radio/radio-gemtek.c @@ -0,0 +1,306 @@ +/* GemTek radio card driver for Linux (C) 1998 Jonas Munsin + * + * GemTek hasn't released any specs on the card, so the protocol had to + * be reverse engineered with dosemu. + * + * Besides the protocol changes, this is mostly a copy of: + * + * RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff + * + * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood + * Converted to new API by Alan Cox + * Various bugfixes and enhancements by Russell Kroll + * + * TODO: Allow for more than one of these foolish entities :-) + * + */ + +#include /* Modules */ +#include /* Initdata */ +#include /* request_region */ +#include /* udelay */ +#include /* outb, outb_p */ +#include /* copy to/from user */ +#include "compat.h" +#include /* kernel radio structs */ +#include /* CONFIG_RADIO_GEMTEK_PORT */ +#include + +#ifndef CONFIG_RADIO_GEMTEK_PORT +#define CONFIG_RADIO_GEMTEK_PORT -1 +#endif + +static int io = CONFIG_RADIO_GEMTEK_PORT; +static int radio_nr = -1; +static spinlock_t lock; + +struct gemtek_device +{ + int port; + unsigned long curfreq; + int muted; +}; + + +/* local things */ + +/* the correct way to mute the gemtek may be to write the last written + * frequency || 0x10, but just writing 0x10 once seems to do it as well + */ +static void gemtek_mute(struct gemtek_device *dev) +{ + if(dev->muted) + return; + spin_lock(&lock); + outb(0x10, io); + spin_unlock(&lock); + dev->muted = 1; +} + +static void gemtek_unmute(struct gemtek_device *dev) +{ + if(dev->muted == 0) + return; + spin_lock(&lock); + outb(0x20, io); + spin_unlock(&lock); + dev->muted = 0; +} + +static void zero(void) +{ + outb_p(0x04, io); + udelay(5); + outb_p(0x05, io); + udelay(5); +} + +static void one(void) +{ + outb_p(0x06, io); + udelay(5); + outb_p(0x07, io); + udelay(5); +} + +static int gemtek_setfreq(struct gemtek_device *dev, unsigned long freq) +{ + int i; + +/* freq = 78.25*((float)freq/16000.0 + 10.52); */ + + freq /= 16; + freq += 10520; + freq *= 7825; + freq /= 100000; + + spin_lock(&lock); + + /* 2 start bits */ + outb_p(0x03, io); + udelay(5); + outb_p(0x07, io); + udelay(5); + + /* 28 frequency bits (lsb first) */ + for (i = 0; i < 14; i++) + if (freq & (1 << i)) + one(); + else + zero(); + /* 36 unknown bits */ + for (i = 0; i < 11; i++) + zero(); + one(); + for (i = 0; i < 4; i++) + zero(); + one(); + zero(); + + /* 2 end bits */ + outb_p(0x03, io); + udelay(5); + outb_p(0x07, io); + udelay(5); + + spin_unlock(&lock); + + return 0; +} + +static int gemtek_getsigstr(struct gemtek_device *dev) +{ + spin_lock(&lock); + inb(io); + udelay(5); + spin_unlock(&lock); + if (inb(io) & 8) /* bit set = no signal present */ + return 0; + return 1; /* signal present */ +} + +static int gemtek_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct gemtek_device *rt=dev->priv; + + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability *v = arg; + memset(v,0,sizeof(*v)); + v->type=VID_TYPE_TUNER; + v->channels=1; + v->audios=1; + strcpy(v->name, "GemTek"); + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + if(v->tuner) /* Only 1 tuner */ + return -EINVAL; + v->rangelow=87*16000; + v->rangehigh=108*16000; + v->flags=VIDEO_TUNER_LOW; + v->mode=VIDEO_MODE_AUTO; + v->signal=0xFFFF*gemtek_getsigstr(rt); + strcpy(v->name, "FM"); + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *v = arg; + if(v->tuner!=0) + return -EINVAL; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: + { + unsigned long *freq = arg; + *freq = rt->curfreq; + return 0; + } + case VIDIOCSFREQ: + { + unsigned long *freq = arg; + rt->curfreq = *freq; + /* needs to be called twice in order for getsigstr to work */ + gemtek_setfreq(rt, rt->curfreq); + gemtek_setfreq(rt, rt->curfreq); + return 0; + } + case VIDIOCGAUDIO: + { + struct video_audio *v = arg; + memset(v,0, sizeof(*v)); + v->flags|=VIDEO_AUDIO_MUTABLE; + v->volume=1; + v->step=65535; + strcpy(v->name, "Radio"); + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio *v = arg; + if(v->audio) + return -EINVAL; + + if(v->flags&VIDEO_AUDIO_MUTE) + gemtek_mute(rt); + else + gemtek_unmute(rt); + + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int gemtek_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, gemtek_do_ioctl); +} + +static struct gemtek_device gemtek_unit; + +static struct file_operations gemtek_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = gemtek_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device gemtek_radio= +{ + .owner = THIS_MODULE, + .name = "GemTek radio", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_GEMTEK, + .fops = &gemtek_fops, +}; + +static int __init gemtek_init(void) +{ + if(io==-1) + { + printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c (io=0x020c or io=0x248 for the combined sound/radiocard)\n"); + return -EINVAL; + } + + if (!request_region(io, 4, "gemtek")) + { + printk(KERN_ERR "gemtek: port 0x%x already in use\n", io); + return -EBUSY; + } + + gemtek_radio.priv=&gemtek_unit; + + if(video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr)==-1) + { + release_region(io, 4); + return -EINVAL; + } + printk(KERN_INFO "GemTek Radio Card driver.\n"); + + spin_lock_init(&lock); + + /* this is _maybe_ unnecessary */ + outb(0x01, io); + + /* mute card - prevents noisy bootups */ + gemtek_unit.muted = 0; + gemtek_mute(&gemtek_unit); + + return 0; +} + +MODULE_AUTHOR("Jonas Munsin"); +MODULE_DESCRIPTION("A driver for the GemTek Radio Card"); +MODULE_LICENSE("GPL"); + +module_param(io, int, 0); +MODULE_PARM_DESC(io, "I/O address of the GemTek card (0x20c, 0x30c, 0x24c or 0x34c (0x20c or 0x248 have been reported to work for the combined sound/radiocard))."); +module_param(radio_nr, int, 0); + +static void __exit gemtek_cleanup(void) +{ + video_unregister_device(&gemtek_radio); + release_region(io,4); +} + +module_init(gemtek_init); +module_exit(gemtek_cleanup); + +/* + Local variables: + compile-command: "gcc -c -DMODVERSIONS -D__KERNEL__ -DMODULE -O6 -Wall -Wstrict-prototypes -I /home/blp/tmp/linux-2.1.111-rtrack/include radio-rtrack2.c" + End: +*/ diff --git a/linux/drivers/media/radio/radio-maestro.c b/linux/drivers/media/radio/radio-maestro.c new file mode 100644 index 000000000..613a84142 --- /dev/null +++ b/linux/drivers/media/radio/radio-maestro.c @@ -0,0 +1,383 @@ +/* Maestro PCI sound card radio driver for Linux support + * (c) 2000 A. Tlalka, atlka@pg.gda.pl + * Notes on the hardware + * + * + Frequency control is done digitally + * + No volume control - only mute/unmute - you have to use Aux line volume + * control on Maestro card to set the volume + * + Radio status (tuned/not_tuned and stereo/mono) is valid some time after + * frequency setting (>100ms) and only when the radio is unmuted. + * version 0.02 + * + io port is automatically detected - only the first radio is used + * version 0.03 + * + thread access locking additions + * version 0.04 + * + code improvements + * + VIDEO_TUNER_LOW is permanent + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "compat.h" +#include + + +#define DRIVER_VERSION "0.05" + +#define GPIO_DATA 0x60 /* port offset from ESS_IO_BASE */ + +#define IO_MASK 4 /* mask register offset from GPIO_DATA + bits 1=unmask write to given bit */ +#define IO_DIR 8 /* direction register offset from GPIO_DATA + bits 0/1=read/write direction */ + +#define GPIO6 0x0040 /* mask bits for GPIO lines */ +#define GPIO7 0x0080 +#define GPIO8 0x0100 +#define GPIO9 0x0200 + +#define STR_DATA GPIO6 /* radio TEA5757 pins and GPIO bits */ +#define STR_CLK GPIO7 +#define STR_WREN GPIO8 +#define STR_MOST GPIO9 + +#define FREQ_LO 50*16000 +#define FREQ_HI 150*16000 + +#define FREQ_IF 171200 /* 10.7*16000 */ +#define FREQ_STEP 200 /* 12.5*16 */ + +#define FREQ2BITS(x) ((((unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1))\ + /(FREQ_STEP<<2))<<2) /* (x==fmhz*16*1000) -> bits */ + +#define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF) + +static int radio_nr = -1; +module_param(radio_nr, int, 0); + +static int radio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static int maestro_probe(struct pci_dev *pdev, const struct pci_device_id *ent); +static void maestro_remove(struct pci_dev *pdev); + +static struct pci_device_id maestro_r_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ESS1968), + .class = PCI_CLASS_MULTIMEDIA_AUDIO << 8, + .class_mask = 0xffff00 }, + { PCI_DEVICE(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ESS1978), + .class = PCI_CLASS_MULTIMEDIA_AUDIO << 8, + .class_mask = 0xffff00 }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, maestro_r_pci_tbl); + +static struct pci_driver maestro_r_driver = { + .name = "maestro_radio", + .id_table = maestro_r_pci_tbl, + .probe = maestro_probe, + .remove = __devexit_p(maestro_remove), +}; + +static struct file_operations maestro_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = radio_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device maestro_radio = { + .name = "Maestro radio", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_SF16MI, + .fops = &maestro_fops, +}; + +struct radio_device { + u16 io, /* base of Maestro card radio io (GPIO_DATA)*/ + muted, /* VIDEO_AUDIO_MUTE */ + stereo, /* VIDEO_TUNER_STEREO_ON */ + tuned; /* signal strength (0 or 0xffff) */ + struct mutex lock; +}; + +static u32 radio_bits_get(struct radio_device *dev) +{ + register u16 io=dev->io, l, rdata; + register u32 data=0; + u16 omask; + + omask = inw(io + IO_MASK); + outw(~(STR_CLK | STR_WREN), io + IO_MASK); + outw(0, io); + udelay(16); + + for (l=24;l--;) { + outw(STR_CLK, io); /* HI state */ + udelay(2); + if(!l) + dev->tuned = inw(io) & STR_MOST ? 0 : 0xffff; + outw(0, io); /* LO state */ + udelay(2); + data <<= 1; /* shift data */ + rdata = inw(io); + if(!l) + dev->stereo = rdata & STR_MOST ? + 0 : VIDEO_TUNER_STEREO_ON; + else + if(rdata & STR_DATA) + data++; + udelay(2); + } + + if(dev->muted) + outw(STR_WREN, io); + + udelay(4); + outw(omask, io + IO_MASK); + + return data & 0x3ffe; +} + +static void radio_bits_set(struct radio_device *dev, u32 data) +{ + register u16 io=dev->io, l, bits; + u16 omask, odir; + + omask = inw(io + IO_MASK); + odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN); + outw(odir | STR_DATA, io + IO_DIR); + outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK); + udelay(16); + for (l=25;l;l--) { + bits = ((data >> 18) & STR_DATA) | STR_WREN ; + data <<= 1; /* shift data */ + outw(bits, io); /* start strobe */ + udelay(2); + outw(bits | STR_CLK, io); /* HI level */ + udelay(2); + outw(bits, io); /* LO level */ + udelay(4); + } + + if(!dev->muted) + outw(0, io); + + udelay(4); + outw(omask, io + IO_MASK); + outw(odir, io + IO_DIR); + msleep(125); +} + +static inline int radio_function(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct radio_device *card = video_get_drvdata(dev); + + switch (cmd) { + case VIDIOCGCAP: { + struct video_capability *v = arg; + memset(v, 0, sizeof(*v)); + strcpy(v->name, "Maestro radio"); + v->type = VID_TYPE_TUNER; + v->channels = v->audios = 1; + return 0; + } case VIDIOCGTUNER: { + struct video_tuner *v = arg; + if (v->tuner) + return -EINVAL; + (void)radio_bits_get(card); + v->flags = VIDEO_TUNER_LOW | card->stereo; + v->signal = card->tuned; + strcpy(v->name, "FM"); + v->rangelow = FREQ_LO; + v->rangehigh = FREQ_HI; + v->mode = VIDEO_MODE_AUTO; + return 0; + } case VIDIOCSTUNER: { + struct video_tuner *v = arg; + if (v->tuner != 0) + return -EINVAL; + return 0; + } case VIDIOCGFREQ: { + unsigned long *freq = arg; + *freq = BITS2FREQ(radio_bits_get(card)); + return 0; + } case VIDIOCSFREQ: { + unsigned long *freq = arg; + if (*freq < FREQ_LO || *freq > FREQ_HI) + return -EINVAL; + radio_bits_set(card, FREQ2BITS(*freq)); + return 0; + } case VIDIOCGAUDIO: { + struct video_audio *v = arg; + memset(v, 0, sizeof(*v)); + strcpy(v->name, "Radio"); + v->flags = VIDEO_AUDIO_MUTABLE | card->muted; + v->mode = VIDEO_SOUND_STEREO; + return 0; + } case VIDIOCSAUDIO: { + struct video_audio *v = arg; + if (v->audio) + return -EINVAL; + { + register u16 io = card->io; + register u16 omask = inw(io + IO_MASK); + outw(~STR_WREN, io + IO_MASK); + outw((card->muted = v->flags & VIDEO_AUDIO_MUTE) ? + STR_WREN : 0, io); + udelay(4); + outw(omask, io + IO_MASK); + msleep(125); + return 0; + } + } case VIDIOCGUNIT: { + struct video_unit *v = arg; + v->video = VIDEO_NO_UNIT; + v->vbi = VIDEO_NO_UNIT; + v->radio = dev->minor; + v->audio = 0; + v->teletext = VIDEO_NO_UNIT; + return 0; + } default: + return -ENOIOCTLCMD; + } +} + +static int radio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct video_device *dev = video_devdata(file); + struct radio_device *card = video_get_drvdata(dev); + int ret; + + mutex_lock(&card->lock); + ret = video_usercopy(inode, file, cmd, arg, radio_function); + mutex_unlock(&card->lock); + + return ret; +} + +static u16 __devinit radio_power_on(struct radio_device *dev) +{ + register u16 io = dev->io; + register u32 ofreq; + u16 omask, odir; + + omask = inw(io + IO_MASK); + odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN); + outw(odir & ~STR_WREN, io + IO_DIR); + dev->muted = inw(io) & STR_WREN ? 0 : VIDEO_AUDIO_MUTE; + outw(odir, io + IO_DIR); + outw(~(STR_WREN | STR_CLK), io + IO_MASK); + outw(dev->muted ? 0 : STR_WREN, io); + udelay(16); + outw(omask, io + IO_MASK); + ofreq = radio_bits_get(dev); + + if ((ofreq < FREQ2BITS(FREQ_LO)) || (ofreq > FREQ2BITS(FREQ_HI))) + ofreq = FREQ2BITS(FREQ_LO); + radio_bits_set(dev, ofreq); + + return (ofreq == radio_bits_get(dev)); +} + +static int __devinit maestro_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct radio_device *radio_unit; + struct video_device *maestro_radio_inst; + int retval; + + retval = pci_enable_device(pdev); + if (retval) { + dev_err(&pdev->dev, "enabling pci device failed!\n"); + goto err; + } + + retval = -ENOMEM; + + radio_unit = kzalloc(sizeof(*radio_unit), GFP_KERNEL); + if (radio_unit == NULL) { + dev_err(&pdev->dev, "not enough memory\n"); + goto err; + } + + radio_unit->io = pci_resource_start(pdev, 0) + GPIO_DATA; + mutex_init(&radio_unit->lock); + + maestro_radio_inst = video_device_alloc(); + if (maestro_radio_inst == NULL) { + dev_err(&pdev->dev, "not enough memory\n"); + goto errfr; + } + + memcpy(maestro_radio_inst, &maestro_radio, sizeof(maestro_radio)); + video_set_drvdata(maestro_radio_inst, radio_unit); + pci_set_drvdata(pdev, maestro_radio_inst); + + retval = video_register_device(maestro_radio_inst, VFL_TYPE_RADIO, + radio_nr); + if (retval) { + printk(KERN_ERR "can't register video device!\n"); + goto errfr1; + } + + if (!radio_power_on(radio_unit)) { + retval = -EIO; + goto errunr; + } + + dev_info(&pdev->dev, "version " DRIVER_VERSION " time " __TIME__ " " + __DATE__ "\n"); + dev_info(&pdev->dev, "radio chip initialized\n"); + + return 0; +errunr: + video_unregister_device(maestro_radio_inst); +errfr1: + kfree(maestro_radio_inst); +errfr: + kfree(radio_unit); +err: + return retval; + +} + +static void __devexit maestro_remove(struct pci_dev *pdev) +{ + struct video_device *vdev = pci_get_drvdata(pdev); + + video_unregister_device(vdev); +} + +static int __init maestro_radio_init(void) +{ + int retval = pci_register_driver(&maestro_r_driver); + + if (retval) + printk(KERN_ERR "error during registration pci driver\n"); + + return retval; +} + +static void __exit maestro_radio_exit(void) +{ + pci_unregister_driver(&maestro_r_driver); +} + +module_init(maestro_radio_init); +module_exit(maestro_radio_exit); + +MODULE_AUTHOR("Adam Tlalka, atlka@pg.gda.pl"); +MODULE_DESCRIPTION("Radio driver for the Maestro PCI sound card radio."); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/radio/radio-maxiradio.c b/linux/drivers/media/radio/radio-maxiradio.c new file mode 100644 index 000000000..da6b6148e --- /dev/null +++ b/linux/drivers/media/radio/radio-maxiradio.c @@ -0,0 +1,352 @@ +/* + * Guillemot Maxi Radio FM 2000 PCI radio card driver for Linux + * (C) 2001 Dimitromanolakis Apostolos + * + * Based in the radio Maestro PCI driver. Actually it uses the same chip + * for radio but different pci controller. + * + * I didn't have any specs I reversed engineered the protocol from + * the windows driver (radio.dll). + * + * The card uses the TEA5757 chip that includes a search function but it + * is useless as I haven't found any way to read back the frequency. If + * anybody does please mail me. + * + * For the pdf file see: + * http://www.semiconductors.philips.com/pip/TEA5757H/V1 + * + * + * CHANGES: + * 0.75b + * - better pci interface thanks to Francois Romieu + * + * 0.75 + * - tiding up + * - removed support for multiple devices as it didn't work anyway + * + * BUGS: + * - card unmutes if you change frequency + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "compat.h" +#include + +/* version 0.75 Sun Feb 4 22:51:27 EET 2001 */ +#define DRIVER_VERSION "0.75" + +#ifndef PCI_VENDOR_ID_GUILLEMOT +#define PCI_VENDOR_ID_GUILLEMOT 0x5046 +#endif + +#ifndef PCI_DEVICE_ID_GUILLEMOT +#define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001 +#endif + + +/* TEA5757 pin mappings */ +static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16 ; + +static int radio_nr = -1; +module_param(radio_nr, int, 0); + + +#define FREQ_LO 50*16000 +#define FREQ_HI 150*16000 + +#define FREQ_IF 171200 /* 10.7*16000 */ +#define FREQ_STEP 200 /* 12.5*16 */ + +#define FREQ2BITS(x) ((( (unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1))\ + /(FREQ_STEP<<2))<<2) /* (x==fmhz*16*1000) -> bits */ + +#define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF) + + +static int radio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +static struct file_operations maxiradio_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = radio_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; +static struct video_device maxiradio_radio = +{ + .owner = THIS_MODULE, + .name = "Maxi Radio FM2000 radio", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_SF16MI, + .fops = &maxiradio_fops, +}; + +static struct radio_device +{ + __u16 io, /* base of radio io */ + muted, /* VIDEO_AUDIO_MUTE */ + stereo, /* VIDEO_TUNER_STEREO_ON */ + tuned; /* signal strength (0 or 0xffff) */ + + unsigned long freq; + + struct mutex lock; +} radio_unit = {0, 0, 0, 0, }; + + +static void outbit(unsigned long bit, __u16 io) +{ + if(bit != 0) + { + outb( power|wren|data ,io); udelay(4); + outb( power|wren|data|clk ,io); udelay(4); + outb( power|wren|data ,io); udelay(4); + } + else + { + outb( power|wren ,io); udelay(4); + outb( power|wren|clk ,io); udelay(4); + outb( power|wren ,io); udelay(4); + } +} + +static void turn_power(__u16 io, int p) +{ + if(p != 0) outb(power, io); else outb(0,io); +} + + +static void set_freq(__u16 io, __u32 data) +{ + unsigned long int si; + int bl; + + /* TEA5757 shift register bits (see pdf) */ + + outbit(0,io); // 24 search + outbit(1,io); // 23 search up/down + + outbit(0,io); // 22 stereo/mono + + outbit(0,io); // 21 band + outbit(0,io); // 20 band (only 00=FM works I think) + + outbit(0,io); // 19 port ? + outbit(0,io); // 18 port ? + + outbit(0,io); // 17 search level + outbit(0,io); // 16 search level + + si = 0x8000; + for(bl = 1; bl <= 16 ; bl++) { outbit(data & si,io); si >>=1; } + + outb(power,io); +} + +static int get_stereo(__u16 io) +{ + outb(power,io); udelay(4); + return !(inb(io) & mo_st); +} + +static int get_tune(__u16 io) +{ + outb(power+clk,io); udelay(4); + return !(inb(io) & mo_st); +} + + +static inline int radio_function(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct radio_device *card=dev->priv; + + switch(cmd) { + case VIDIOCGCAP: { + struct video_capability *v = arg; + + memset(v,0,sizeof(*v)); + strcpy(v->name, "Maxi Radio FM2000 radio"); + v->type=VID_TYPE_TUNER; + v->channels=v->audios=1; + return 0; + } + case VIDIOCGTUNER: { + struct video_tuner *v = arg; + + if(v->tuner) + return -EINVAL; + + card->stereo = 0xffff * get_stereo(card->io); + card->tuned = 0xffff * get_tune(card->io); + + v->flags = VIDEO_TUNER_LOW | card->stereo; + v->signal = card->tuned; + + strcpy(v->name, "FM"); + + v->rangelow = FREQ_LO; + v->rangehigh = FREQ_HI; + v->mode = VIDEO_MODE_AUTO; + + return 0; + } + case VIDIOCSTUNER: { + struct video_tuner *v = arg; + if(v->tuner!=0) + return -EINVAL; + return 0; + } + case VIDIOCGFREQ: { + unsigned long *freq = arg; + + *freq = card->freq; + return 0; + } + case VIDIOCSFREQ: { + unsigned long *freq = arg; + + if (*freq < FREQ_LO || *freq > FREQ_HI) + return -EINVAL; + card->freq = *freq; + set_freq(card->io, FREQ2BITS(card->freq)); + msleep(125); + return 0; + } + case VIDIOCGAUDIO: { + struct video_audio *v = arg; + memset(v,0,sizeof(*v)); + strcpy(v->name, "Radio"); + v->flags=VIDEO_AUDIO_MUTABLE | card->muted; + v->mode=VIDEO_SOUND_STEREO; + return 0; + } + + case VIDIOCSAUDIO: { + struct video_audio *v = arg; + + if(v->audio) + return -EINVAL; + card->muted = v->flags & VIDEO_AUDIO_MUTE; + if(card->muted) + turn_power(card->io, 0); + else + set_freq(card->io, FREQ2BITS(card->freq)); + return 0; + } + case VIDIOCGUNIT: { + struct video_unit *v = arg; + + v->video=VIDEO_NO_UNIT; + v->vbi=VIDEO_NO_UNIT; + v->radio=dev->minor; + v->audio=0; + v->teletext=VIDEO_NO_UNIT; + return 0; + } + default: return -ENOIOCTLCMD; + } +} + +static int radio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct video_device *dev = video_devdata(file); + struct radio_device *card=dev->priv; + int ret; + + mutex_lock(&card->lock); + ret = video_usercopy(inode, file, cmd, arg, radio_function); + mutex_unlock(&card->lock); + return ret; +} + +MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net"); +MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio."); +MODULE_LICENSE("GPL"); + + +static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + if(!request_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0), "Maxi Radio FM 2000")) { + printk(KERN_ERR "radio-maxiradio: can't reserve I/O ports\n"); + goto err_out; + } + + if (pci_enable_device(pdev)) + goto err_out_free_region; + + radio_unit.io = pci_resource_start(pdev, 0); + mutex_init(&radio_unit.lock); + maxiradio_radio.priv = &radio_unit; + + if(video_register_device(&maxiradio_radio, VFL_TYPE_RADIO, radio_nr)==-1) { + printk("radio-maxiradio: can't register device!"); + goto err_out_free_region; + } + + printk(KERN_INFO "radio-maxiradio: version " + DRIVER_VERSION + " time " + __TIME__ " " + __DATE__ + "\n"); + + printk(KERN_INFO "radio-maxiradio: found Guillemot MAXI Radio device (io = 0x%x)\n", + radio_unit.io); + return 0; + +err_out_free_region: + release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); +err_out: + return -ENODEV; +} + +static void __devexit maxiradio_remove_one(struct pci_dev *pdev) +{ + video_unregister_device(&maxiradio_radio); + release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); +} + +static struct pci_device_id maxiradio_pci_tbl[] = { + { PCI_VENDOR_ID_GUILLEMOT, PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO, + PCI_ANY_ID, PCI_ANY_ID, }, + { 0,} +}; + +MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl); + +static struct pci_driver maxiradio_driver = { + .name = "radio-maxiradio", + .id_table = maxiradio_pci_tbl, + .probe = maxiradio_init_one, + .remove = __devexit_p(maxiradio_remove_one), +}; + +static int __init maxiradio_radio_init(void) +{ + return pci_register_driver(&maxiradio_driver); +} + +static void __exit maxiradio_radio_exit(void) +{ + pci_unregister_driver(&maxiradio_driver); +} + +module_init(maxiradio_radio_init); +module_exit(maxiradio_radio_exit); diff --git a/linux/drivers/media/radio/radio-rtrack2.c b/linux/drivers/media/radio/radio-rtrack2.c new file mode 100644 index 000000000..defdc83e4 --- /dev/null +++ b/linux/drivers/media/radio/radio-rtrack2.c @@ -0,0 +1,268 @@ +/* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff + * + * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood + * Converted to new API by Alan Cox + * Various bugfixes and enhancements by Russell Kroll + * + * TODO: Allow for more than one of these foolish entities :-) + * + */ + +#include /* Modules */ +#include /* Initdata */ +#include /* request_region */ +#include /* udelay */ +#include /* outb, outb_p */ +#include /* copy to/from user */ +#include "compat.h" +#include /* kernel radio structs */ +#include /* CONFIG_RADIO_RTRACK2_PORT */ +#include + +#ifndef CONFIG_RADIO_RTRACK2_PORT +#define CONFIG_RADIO_RTRACK2_PORT -1 +#endif + +static int io = CONFIG_RADIO_RTRACK2_PORT; +static int radio_nr = -1; +static spinlock_t lock; + +struct rt_device +{ + int port; + unsigned long curfreq; + int muted; +}; + + +/* local things */ + +static void rt_mute(struct rt_device *dev) +{ + if(dev->muted) + return; + spin_lock(&lock); + outb(1, io); + spin_unlock(&lock); + dev->muted = 1; +} + +static void rt_unmute(struct rt_device *dev) +{ + if(dev->muted == 0) + return; + spin_lock(&lock); + outb(0, io); + spin_unlock(&lock); + dev->muted = 0; +} + +static void zero(void) +{ + outb_p(1, io); + outb_p(3, io); + outb_p(1, io); +} + +static void one(void) +{ + outb_p(5, io); + outb_p(7, io); + outb_p(5, io); +} + +static int rt_setfreq(struct rt_device *dev, unsigned long freq) +{ + int i; + + freq = freq / 200 + 856; + + spin_lock(&lock); + + outb_p(0xc8, io); + outb_p(0xc9, io); + outb_p(0xc9, io); + + for (i = 0; i < 10; i++) + zero (); + + for (i = 14; i >= 0; i--) + if (freq & (1 << i)) + one (); + else + zero (); + + outb_p(0xc8, io); + if (!dev->muted) + outb_p(0, io); + + spin_unlock(&lock); + return 0; +} + +static int rt_getsigstr(struct rt_device *dev) +{ + if (inb(io) & 2) /* bit set = no signal present */ + return 0; + return 1; /* signal present */ +} + +static int rt_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct rt_device *rt=dev->priv; + + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability *v = arg; + memset(v,0,sizeof(*v)); + v->type=VID_TYPE_TUNER; + v->channels=1; + v->audios=1; + strcpy(v->name, "RadioTrack II"); + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + if(v->tuner) /* Only 1 tuner */ + return -EINVAL; + v->rangelow=88*16000; + v->rangehigh=108*16000; + v->flags=VIDEO_TUNER_LOW; + v->mode=VIDEO_MODE_AUTO; + v->signal=0xFFFF*rt_getsigstr(rt); + strcpy(v->name, "FM"); + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *v = arg; + if(v->tuner!=0) + return -EINVAL; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: + { + unsigned long *freq = arg; + *freq = rt->curfreq; + return 0; + } + case VIDIOCSFREQ: + { + unsigned long *freq = arg; + rt->curfreq = *freq; + rt_setfreq(rt, rt->curfreq); + return 0; + } + case VIDIOCGAUDIO: + { + struct video_audio *v = arg; + memset(v,0, sizeof(*v)); + v->flags|=VIDEO_AUDIO_MUTABLE; + v->volume=1; + v->step=65535; + strcpy(v->name, "Radio"); + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio *v = arg; + if(v->audio) + return -EINVAL; + + if(v->flags&VIDEO_AUDIO_MUTE) + rt_mute(rt); + else + rt_unmute(rt); + + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int rt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, rt_do_ioctl); +} + +static struct rt_device rtrack2_unit; + +static struct file_operations rtrack2_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = rt_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device rtrack2_radio= +{ + .owner = THIS_MODULE, + .name = "RadioTrack II radio", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_RTRACK2, + .fops = &rtrack2_fops, +}; + +static int __init rtrack2_init(void) +{ + if(io==-1) + { + printk(KERN_ERR "You must set an I/O address with io=0x20c or io=0x30c\n"); + return -EINVAL; + } + if (!request_region(io, 4, "rtrack2")) + { + printk(KERN_ERR "rtrack2: port 0x%x already in use\n", io); + return -EBUSY; + } + + rtrack2_radio.priv=&rtrack2_unit; + + spin_lock_init(&lock); + if(video_register_device(&rtrack2_radio, VFL_TYPE_RADIO, radio_nr)==-1) + { + release_region(io, 4); + return -EINVAL; + } + + printk(KERN_INFO "AIMSlab Radiotrack II card driver.\n"); + + /* mute card - prevents noisy bootups */ + outb(1, io); + rtrack2_unit.muted = 1; + + return 0; +} + +MODULE_AUTHOR("Ben Pfaff"); +MODULE_DESCRIPTION("A driver for the RadioTrack II radio card."); +MODULE_LICENSE("GPL"); + +module_param(io, int, 0); +MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20c or 0x30c)"); +module_param(radio_nr, int, 0); + +static void __exit rtrack2_cleanup_module(void) +{ + video_unregister_device(&rtrack2_radio); + release_region(io,4); +} + +module_init(rtrack2_init); +module_exit(rtrack2_cleanup_module); + +/* + Local variables: + compile-command: "mmake" + End: +*/ diff --git a/linux/drivers/media/radio/radio-sf16fmi.c b/linux/drivers/media/radio/radio-sf16fmi.c new file mode 100644 index 000000000..c1d3b5c33 --- /dev/null +++ b/linux/drivers/media/radio/radio-sf16fmi.c @@ -0,0 +1,330 @@ +/* SF16FMI radio driver for Linux radio support + * heavily based on rtrack driver... + * (c) 1997 M. Kirkwood + * (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz + * + * Fitted to new interface by Alan Cox + * Made working and cleaned up functions + * Support for ISAPnP by Ladislav Michl + * + * Notes on the hardware + * + * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); + * No volume control - only mute/unmute - you have to use line volume + * control on SB-part of SF16FMI + * + */ + +#include /* __setup */ +#include /* Modules */ +#include /* Initdata */ +#include /* request_region */ +#include /* udelay */ +#include "compat.h" +#include /* kernel radio structs */ +#include +#include /* outb, outb_p */ +#include /* copy to/from user */ +#include + +struct fmi_device +{ + int port; + int curvol; /* 1 or 0 */ + unsigned long curfreq; /* freq in kHz */ + __u32 flags; +}; + +static int io = -1; +static int radio_nr = -1; +static struct pnp_dev *dev = NULL; +static struct mutex lock; + +/* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */ +/* It is only useful to give freq in intervall of 800 (=0.05Mhz), + * other bits will be truncated, e.g 92.7400016 -> 92.7, but + * 92.7400017 -> 92.75 + */ +#define RSF16_ENCODE(x) ((x)/800+214) +#define RSF16_MINFREQ 87*16000 +#define RSF16_MAXFREQ 108*16000 + +static void outbits(int bits, unsigned int data, int port) +{ + while(bits--) { + if(data & 1) { + outb(5, port); + udelay(6); + outb(7, port); + udelay(6); + } else { + outb(1, port); + udelay(6); + outb(3, port); + udelay(6); + } + data>>=1; + } +} + +static inline void fmi_mute(int port) +{ + mutex_lock(&lock); + outb(0x00, port); + mutex_unlock(&lock); +} + +static inline void fmi_unmute(int port) +{ + mutex_lock(&lock); + outb(0x08, port); + mutex_unlock(&lock); +} + +static inline int fmi_setfreq(struct fmi_device *dev) +{ + int myport = dev->port; + unsigned long freq = dev->curfreq; + + mutex_lock(&lock); + + outbits(16, RSF16_ENCODE(freq), myport); + outbits(8, 0xC0, myport); + msleep(143); /* was schedule_timeout(HZ/7) */ + mutex_unlock(&lock); + if (dev->curvol) fmi_unmute(myport); + return 0; +} + +static inline int fmi_getsigstr(struct fmi_device *dev) +{ + int val; + int res; + int myport = dev->port; + + + mutex_lock(&lock); + val = dev->curvol ? 0x08 : 0x00; /* unmute/mute */ + outb(val, myport); + outb(val | 0x10, myport); + msleep(143); /* was schedule_timeout(HZ/7) */ + res = (int)inb(myport+1); + outb(val, myport); + + mutex_unlock(&lock); + return (res & 2) ? 0 : 0xFFFF; +} + +static int fmi_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct fmi_device *fmi=dev->priv; + + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability *v = arg; + memset(v,0,sizeof(*v)); + strcpy(v->name, "SF16-FMx radio"); + v->type=VID_TYPE_TUNER; + v->channels=1; + v->audios=1; + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + int mult; + + if(v->tuner) /* Only 1 tuner */ + return -EINVAL; + strcpy(v->name, "FM"); + mult = (fmi->flags & VIDEO_TUNER_LOW) ? 1 : 1000; + v->rangelow = RSF16_MINFREQ/mult; + v->rangehigh = RSF16_MAXFREQ/mult; + v->flags=fmi->flags; + v->mode=VIDEO_MODE_AUTO; + v->signal = fmi_getsigstr(fmi); + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *v = arg; + if(v->tuner!=0) + return -EINVAL; + fmi->flags = v->flags & VIDEO_TUNER_LOW; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: + { + unsigned long *freq = arg; + *freq = fmi->curfreq; + if (!(fmi->flags & VIDEO_TUNER_LOW)) + *freq /= 1000; + return 0; + } + case VIDIOCSFREQ: + { + unsigned long *freq = arg; + if (!(fmi->flags & VIDEO_TUNER_LOW)) + *freq *= 1000; + if (*freq < RSF16_MINFREQ || *freq > RSF16_MAXFREQ ) + return -EINVAL; + /*rounding in steps of 800 to match th freq + that will be used */ + fmi->curfreq = (*freq/800)*800; + fmi_setfreq(fmi); + return 0; + } + case VIDIOCGAUDIO: + { + struct video_audio *v = arg; + memset(v,0,sizeof(*v)); + v->flags=( (!fmi->curvol)*VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE); + strcpy(v->name, "Radio"); + v->mode=VIDEO_SOUND_STEREO; + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio *v = arg; + if(v->audio) + return -EINVAL; + fmi->curvol= v->flags&VIDEO_AUDIO_MUTE ? 0 : 1; + fmi->curvol ? + fmi_unmute(fmi->port) : fmi_mute(fmi->port); + return 0; + } + case VIDIOCGUNIT: + { + struct video_unit *v = arg; + v->video=VIDEO_NO_UNIT; + v->vbi=VIDEO_NO_UNIT; + v->radio=dev->minor; + v->audio=0; /* How do we find out this??? */ + v->teletext=VIDEO_NO_UNIT; + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int fmi_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, fmi_do_ioctl); +} + +static struct fmi_device fmi_unit; + +static struct file_operations fmi_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = fmi_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device fmi_radio= +{ + .owner = THIS_MODULE, + .name = "SF16FMx radio", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_SF16MI, + .fops = &fmi_fops, +}; + +/* ladis: this is my card. does any other types exist? */ +static struct isapnp_device_id id_table[] __devinitdata = { + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad10), 0}, + { ISAPNP_CARD_END, }, +}; + +MODULE_DEVICE_TABLE(isapnp, id_table); + +static int isapnp_fmi_probe(void) +{ + int i = 0; + + while (id_table[i].card_vendor != 0 && dev == NULL) { + dev = pnp_find_dev(NULL, id_table[i].vendor, + id_table[i].function, NULL); + i++; + } + + if (!dev) + return -ENODEV; + if (pnp_device_attach(dev) < 0) + return -EAGAIN; + if (pnp_activate_dev(dev) < 0) { + printk ("radio-sf16fmi: PnP configure failed (out of resources?)\n"); + pnp_device_detach(dev); + return -ENOMEM; + } + if (!pnp_port_valid(dev, 0)) { + pnp_device_detach(dev); + return -ENODEV; + } + + i = pnp_port_start(dev, 0); + printk ("radio-sf16fmi: PnP reports card at %#x\n", i); + + return i; +} + +static int __init fmi_init(void) +{ + if (io < 0) + io = isapnp_fmi_probe(); + if (io < 0) { + printk(KERN_ERR "radio-sf16fmi: No PnP card found.\n"); + return io; + } + if (!request_region(io, 2, "radio-sf16fmi")) { + printk(KERN_ERR "radio-sf16fmi: port 0x%x already in use\n", io); + return -EBUSY; + } + + fmi_unit.port = io; + fmi_unit.curvol = 0; + fmi_unit.curfreq = 0; + fmi_unit.flags = VIDEO_TUNER_LOW; + fmi_radio.priv = &fmi_unit; + + mutex_init(&lock); + + if (video_register_device(&fmi_radio, VFL_TYPE_RADIO, radio_nr) == -1) { + release_region(io, 2); + return -EINVAL; + } + + printk(KERN_INFO "SF16FMx radio card driver at 0x%x\n", io); + /* mute card - prevents noisy bootups */ + fmi_mute(io); + return 0; +} + +MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood"); +MODULE_DESCRIPTION("A driver for the SF16MI radio."); +MODULE_LICENSE("GPL"); + +module_param(io, int, 0); +MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)"); +module_param(radio_nr, int, 0); + +static void __exit fmi_cleanup_module(void) +{ + video_unregister_device(&fmi_radio); + release_region(io, 2); + if (dev) + pnp_device_detach(dev); +} + +module_init(fmi_init); +module_exit(fmi_cleanup_module); diff --git a/linux/drivers/media/radio/radio-sf16fmr2.c b/linux/drivers/media/radio/radio-sf16fmr2.c new file mode 100644 index 000000000..d90d87840 --- /dev/null +++ b/linux/drivers/media/radio/radio-sf16fmr2.c @@ -0,0 +1,436 @@ +/* SF16FMR2 radio driver for Linux radio support + * heavily based on fmi driver... + * (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com + * + * Notes on the hardware + * + * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); + * No volume control - only mute/unmute - you have to use line volume + * + * For read stereo/mono you must wait 0.1 sec after set frequency and + * card unmuted so I set frequency on unmute + * Signal handling seem to work only on autoscanning (not implemented) + */ + +#include /* Modules */ +#include /* Initdata */ +#include /* request_region */ +#include /* udelay */ +#include /* outb, outb_p */ +#include /* copy to/from user */ +#include "compat.h" +#include /* kernel radio structs */ +#include + +static struct mutex lock; + +#undef DEBUG +//#define DEBUG 1 + +#ifdef DEBUG +# define debug_print(s) printk s +#else +# define debug_print(s) +#endif + +/* this should be static vars for module size */ +struct fmr2_device +{ + int port; + int curvol; /* 0-65535, if not volume 0 or 65535 */ + int mute; + int stereo; /* card is producing stereo audio */ + unsigned long curfreq; /* freq in kHz */ + int card_type; + __u32 flags; +}; + +static int io = 0x384; +static int radio_nr = -1; + +/* hw precision is 12.5 kHz + * It is only useful to give freq in intervall of 200 (=0.0125Mhz), + * other bits will be truncated + */ +#define RSF16_ENCODE(x) ((x)/200+856) +#define RSF16_MINFREQ 87*16000 +#define RSF16_MAXFREQ 108*16000 + +static inline void wait(int n,int port) +{ + for (;n;--n) inb(port); +} + +static void outbits(int bits, unsigned int data, int nWait, int port) +{ + int bit; + for(;--bits>=0;) { + bit = (data>>bits) & 1; + outb(bit,port); + wait(nWait,port); + outb(bit|2,port); + wait(nWait,port); + outb(bit,port); + wait(nWait,port); + } +} + +static inline void fmr2_mute(int port) +{ + outb(0x00, port); + wait(4,port); +} + +static inline void fmr2_unmute(int port) +{ + outb(0x04, port); + wait(4,port); +} + +static inline int fmr2_stereo_mode(int port) +{ + int n = inb(port); + outb(6,port); + inb(port); + n = ((n>>3)&1)^1; + debug_print((KERN_DEBUG "stereo: %d\n", n)); + return n; +} + +static int fmr2_product_info(struct fmr2_device *dev) +{ + int n = inb(dev->port); + n &= 0xC1; + if (n == 0) + { + /* this should support volume set */ + dev->card_type = 12; + return 0; + } + /* not volume (mine is 11) */ + dev->card_type = (n==128)?11:0; + return n; +} + +static inline int fmr2_getsigstr(struct fmr2_device *dev) +{ + /* !!! work only if scanning freq */ + int port = dev->port, res = 0xffff; + outb(5,port); + wait(4,port); + if (!(inb(port)&1)) res = 0; + debug_print((KERN_DEBUG "signal: %d\n", res)); + return res; +} + +/* set frequency and unmute card */ +static int fmr2_setfreq(struct fmr2_device *dev) +{ + int port = dev->port; + unsigned long freq = dev->curfreq; + + fmr2_mute(port); + + /* 0x42 for mono output + * 0x102 forward scanning + * 0x182 scansione avanti + */ + outbits(9,0x2,3,port); + outbits(16,RSF16_ENCODE(freq),2,port); + + fmr2_unmute(port); + + /* wait 0.11 sec */ + msleep(110); + + /* NOTE if mute this stop radio + you must set freq on unmute */ + dev->stereo = fmr2_stereo_mode(port); + return 0; +} + +/* !!! not tested, in my card this does't work !!! */ +static int fmr2_setvolume(struct fmr2_device *dev) +{ + int i,a,n, port = dev->port; + + if (dev->card_type != 11) return 1; + + switch( (dev->curvol+(1<<11)) >> 12 ) + { + case 0: case 1: n = 0x21; break; + case 2: n = 0x84; break; + case 3: n = 0x90; break; + case 4: n = 0x104; break; + case 5: n = 0x110; break; + case 6: n = 0x204; break; + case 7: n = 0x210; break; + case 8: n = 0x402; break; + case 9: n = 0x404; break; + default: + case 10: n = 0x408; break; + case 11: n = 0x410; break; + case 12: n = 0x801; break; + case 13: n = 0x802; break; + case 14: n = 0x804; break; + case 15: n = 0x808; break; + case 16: n = 0x810; break; + } + for(i=12;--i>=0;) + { + a = ((n >> i) & 1) << 6; /* if (a=0) a= 0; else a= 0x40; */ + outb(a|4, port); + wait(4,port); + outb(a|0x24, port); + wait(4,port); + outb(a|4, port); + wait(4,port); + } + for(i=6;--i>=0;) + { + a = ((0x18 >> i) & 1) << 6; + outb(a|4, port); + wait(4,port); + outb(a|0x24, port); + wait(4,port); + outb(a|4, port); + wait(4,port); + } + wait(4,port); + outb(0x14, port); + + return 0; +} + +static int fmr2_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct fmr2_device *fmr2 = dev->priv; + debug_print((KERN_DEBUG "freq %ld flags %d vol %d mute %d " + "stereo %d type %d\n", + fmr2->curfreq, fmr2->flags, fmr2->curvol, fmr2->mute, + fmr2->stereo, fmr2->card_type)); + + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability *v = arg; + memset(v,0,sizeof(*v)); + strcpy(v->name, "SF16-FMR2 radio"); + v->type=VID_TYPE_TUNER; + v->channels=1; + v->audios=1; + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + int mult; + + if(v->tuner) /* Only 1 tuner */ + return -EINVAL; + strcpy(v->name, "FM"); + mult = (fmr2->flags & VIDEO_TUNER_LOW) ? 1 : 1000; + v->rangelow = RSF16_MINFREQ/mult; + v->rangehigh = RSF16_MAXFREQ/mult; + v->flags = fmr2->flags | VIDEO_AUDIO_MUTABLE; + if (fmr2->mute) + v->flags |= VIDEO_AUDIO_MUTE; + v->mode=VIDEO_MODE_AUTO; + mutex_lock(&lock); + v->signal = fmr2_getsigstr(fmr2); + mutex_unlock(&lock); + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *v = arg; + if (v->tuner!=0) + return -EINVAL; + fmr2->flags = v->flags & VIDEO_TUNER_LOW; + return 0; + } + case VIDIOCGFREQ: + { + unsigned long *freq = arg; + *freq = fmr2->curfreq; + if (!(fmr2->flags & VIDEO_TUNER_LOW)) + *freq /= 1000; + return 0; + } + case VIDIOCSFREQ: + { + unsigned long *freq = arg; + if (!(fmr2->flags & VIDEO_TUNER_LOW)) + *freq *= 1000; + if ( *freq < RSF16_MINFREQ || *freq > RSF16_MAXFREQ ) + return -EINVAL; + /* rounding in steps of 200 to match th freq + * that will be used + */ + fmr2->curfreq = (*freq/200)*200; + + /* set card freq (if not muted) */ + if (fmr2->curvol && !fmr2->mute) + { + mutex_lock(&lock); + fmr2_setfreq(fmr2); + mutex_unlock(&lock); + } + return 0; + } + case VIDIOCGAUDIO: + { + struct video_audio *v = arg; + memset(v,0,sizeof(*v)); + /* !!! do not return VIDEO_AUDIO_MUTE */ + v->flags = VIDEO_AUDIO_MUTABLE; + strcpy(v->name, "Radio"); + /* get current stereo mode */ + v->mode = fmr2->stereo ? VIDEO_SOUND_STEREO: VIDEO_SOUND_MONO; + /* volume supported ? */ + if (fmr2->card_type == 11) + { + v->flags |= VIDEO_AUDIO_VOLUME; + v->step = 1 << 12; + v->volume = fmr2->curvol; + } + debug_print((KERN_DEBUG "Get flags %d vol %d\n", v->flags, v->volume)); + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio *v = arg; + if(v->audio) + return -EINVAL; + debug_print((KERN_DEBUG "Set flags %d vol %d\n", v->flags, v->volume)); + /* set volume */ + if (v->flags & VIDEO_AUDIO_VOLUME) + fmr2->curvol = v->volume; /* !!! set with precision */ + if (fmr2->card_type != 11) fmr2->curvol = 65535; + fmr2->mute = 0; + if (v->flags & VIDEO_AUDIO_MUTE) + fmr2->mute = 1; +#ifdef DEBUG + if (fmr2->curvol && !fmr2->mute) + printk(KERN_DEBUG "unmute\n"); + else + printk(KERN_DEBUG "mute\n"); +#endif + mutex_lock(&lock); + if (fmr2->curvol && !fmr2->mute) + { + fmr2_setvolume(fmr2); + fmr2_setfreq(fmr2); + } + else fmr2_mute(fmr2->port); + mutex_unlock(&lock); + return 0; + } + case VIDIOCGUNIT: + { + struct video_unit *v = arg; + v->video=VIDEO_NO_UNIT; + v->vbi=VIDEO_NO_UNIT; + v->radio=dev->minor; + v->audio=0; /* How do we find out this??? */ + v->teletext=VIDEO_NO_UNIT; + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int fmr2_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) + { + return video_usercopy(inode, file, cmd, arg, fmr2_do_ioctl); +} + +static struct fmr2_device fmr2_unit; + +static struct file_operations fmr2_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = fmr2_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device fmr2_radio= +{ + .owner = THIS_MODULE, + .name = "SF16FMR2 radio", + . type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_SF16FMR2, + .fops = &fmr2_fops, +}; + +static int __init fmr2_init(void) +{ + fmr2_unit.port = io; + fmr2_unit.curvol = 0; + fmr2_unit.mute = 0; + fmr2_unit.curfreq = 0; + fmr2_unit.stereo = 1; + fmr2_unit.flags = VIDEO_TUNER_LOW; + fmr2_unit.card_type = 0; + fmr2_radio.priv = &fmr2_unit; + + mutex_init(&lock); + + if (request_region(io, 2, "sf16fmr2")) + { + printk(KERN_ERR "fmr2: port 0x%x already in use\n", io); + return -EBUSY; + } + + if(video_register_device(&fmr2_radio, VFL_TYPE_RADIO, radio_nr)==-1) + { + release_region(io, 2); + return -EINVAL; + } + + printk(KERN_INFO "SF16FMR2 radio card driver at 0x%x.\n", io); + debug_print((KERN_DEBUG "Mute %d Low %d\n",VIDEO_AUDIO_MUTE,VIDEO_TUNER_LOW)); + /* mute card - prevents noisy bootups */ + mutex_lock(&lock); + fmr2_mute(io); + fmr2_product_info(&fmr2_unit); + mutex_unlock(&lock); + debug_print((KERN_DEBUG "card_type %d\n", fmr2_unit.card_type)); + return 0; +} + +MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com"); +MODULE_DESCRIPTION("A driver for the SF16FMR2 radio."); +MODULE_LICENSE("GPL"); + +module_param(io, int, 0); +MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)"); +module_param(radio_nr, int, 0); + +static void __exit fmr2_cleanup_module(void) +{ + video_unregister_device(&fmr2_radio); + release_region(io,2); +} + +module_init(fmr2_init); +module_exit(fmr2_cleanup_module); + +#ifndef MODULE + +static int __init fmr2_setup_io(char *str) +{ + get_option(&str, &io); + return 1; +} + +__setup("sf16fmr2=", fmr2_setup_io); + +#endif diff --git a/linux/drivers/media/radio/radio-terratec.c b/linux/drivers/media/radio/radio-terratec.c new file mode 100644 index 000000000..ee8cc71a0 --- /dev/null +++ b/linux/drivers/media/radio/radio-terratec.c @@ -0,0 +1,343 @@ +/* Terratec ActiveRadio ISA Standalone card driver for Linux radio support + * (c) 1999 R. Offermanns (rolf@offermanns.de) + * based on the aimslab radio driver from M. Kirkwood + * many thanks to Michael Becker and Friedhelm Birth (from TerraTec) + * + * + * History: + * 1999-05-21 First preview release + * + * Notes on the hardware: + * There are two "main" chips on the card: + * - Philips OM5610 (http://www-us.semiconductors.philips.com/acrobat/datasheets/OM5610_2.pdf) + * - Philips SAA6588 (http://www-us.semiconductors.philips.com/acrobat/datasheets/SAA6588_1.pdf) + * (you can get the datasheet at the above links) + * + * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); + * Volume Control is done digitally + * + * there is a I2C controlled RDS decoder (SAA6588) onboard, which i would like to support someday + * (as soon i have understand how to get started :) + * If you can help me out with that, please contact me!! + * + * + */ + +#include /* Modules */ +#include /* Initdata */ +#include /* request_region */ +#include /* udelay */ +#include /* outb, outb_p */ +#include /* copy to/from user */ +#include "compat.h" +#include /* kernel radio structs */ +#include /* CONFIG_RADIO_TERRATEC_PORT */ +#include + +#ifndef CONFIG_RADIO_TERRATEC_PORT +#define CONFIG_RADIO_TERRATEC_PORT 0x590 +#endif + +/**************** this ones are for the terratec *******************/ +#define BASEPORT 0x590 +#define VOLPORT 0x591 +#define WRT_DIS 0x00 +#define CLK_OFF 0x00 +#define IIC_DATA 0x01 +#define IIC_CLK 0x02 +#define DATA 0x04 +#define CLK_ON 0x08 +#define WRT_EN 0x10 +/*******************************************************************/ + +static int io = CONFIG_RADIO_TERRATEC_PORT; +static int radio_nr = -1; +static spinlock_t lock; + +struct tt_device +{ + int port; + int curvol; + unsigned long curfreq; + int muted; +}; + + +/* local things */ + +static void cardWriteVol(int volume) +{ + int i; + volume = volume+(volume * 32); // change both channels + spin_lock(&lock); + for (i=0;i<8;i++) + { + if (volume & (0x80>>i)) + outb(0x80, VOLPORT); + else outb(0x00, VOLPORT); + } + spin_unlock(&lock); +} + + + +static void tt_mute(struct tt_device *dev) +{ + dev->muted = 1; + cardWriteVol(0); +} + +static int tt_setvol(struct tt_device *dev, int vol) +{ + +// printk(KERN_ERR "setvol called, vol = %d\n", vol); + + if(vol == dev->curvol) { /* requested volume = current */ + if (dev->muted) { /* user is unmuting the card */ + dev->muted = 0; + cardWriteVol(vol); /* enable card */ + } + + return 0; + } + + if(vol == 0) { /* volume = 0 means mute the card */ + cardWriteVol(0); /* "turn off card" by setting vol to 0 */ + dev->curvol = vol; /* track the volume state! */ + return 0; + } + + dev->muted = 0; + + cardWriteVol(vol); + + dev->curvol = vol; + + return 0; + +} + + +/* this is the worst part in this driver */ +/* many more or less strange things are going on here, but hey, it works :) */ + +static int tt_setfreq(struct tt_device *dev, unsigned long freq1) +{ + int freq; + int i; + int p; + int temp; + long rest; + + unsigned char buffer[25]; /* we have to bit shift 25 registers */ + freq = freq1/160; /* convert the freq. to a nice to handle value */ + for(i=24;i>-1;i--) + buffer[i]=0; + + rest = freq*10+10700; /* i once had understood what is going on here */ + /* maybe some wise guy (friedhelm?) can comment this stuff */ + i=13; + p=10; + temp=102400; + while (rest!=0) + { + if (rest%temp == rest) + buffer[i] = 0; + else + { + buffer[i] = 1; + rest = rest-temp; + } + i--; + p--; + temp = temp/2; + } + + spin_lock(&lock); + + for (i=24;i>-1;i--) /* bit shift the values to the radiocard */ + { + if (buffer[i]==1) + { + outb(WRT_EN|DATA, BASEPORT); + outb(WRT_EN|DATA|CLK_ON , BASEPORT); + outb(WRT_EN|DATA, BASEPORT); + } + else + { + outb(WRT_EN|0x00, BASEPORT); + outb(WRT_EN|0x00|CLK_ON , BASEPORT); + } + } + outb(0x00, BASEPORT); + + spin_unlock(&lock); + + return 0; +} + +static int tt_getsigstr(struct tt_device *dev) /* TODO */ +{ + if (inb(io) & 2) /* bit set = no signal present */ + return 0; + return 1; /* signal present */ +} + + +/* implement the video4linux api */ + +static int tt_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct tt_device *tt=dev->priv; + + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability *v = arg; + memset(v,0,sizeof(*v)); + v->type=VID_TYPE_TUNER; + v->channels=1; + v->audios=1; + strcpy(v->name, "ActiveRadio"); + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + if(v->tuner) /* Only 1 tuner */ + return -EINVAL; + v->rangelow=(87*16000); + v->rangehigh=(108*16000); + v->flags=VIDEO_TUNER_LOW; + v->mode=VIDEO_MODE_AUTO; + strcpy(v->name, "FM"); + v->signal=0xFFFF*tt_getsigstr(tt); + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *v = arg; + if(v->tuner!=0) + return -EINVAL; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: + { + unsigned long *freq = arg; + *freq = tt->curfreq; + return 0; + } + case VIDIOCSFREQ: + { + unsigned long *freq = arg; + tt->curfreq = *freq; + tt_setfreq(tt, tt->curfreq); + return 0; + } + case VIDIOCGAUDIO: + { + struct video_audio *v = arg; + memset(v,0, sizeof(*v)); + v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME; + v->volume=tt->curvol * 6554; + v->step=6554; + strcpy(v->name, "Radio"); + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio *v = arg; + if(v->audio) + return -EINVAL; + if(v->flags&VIDEO_AUDIO_MUTE) + tt_mute(tt); + else + tt_setvol(tt,v->volume/6554); + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int tt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, tt_do_ioctl); +} + +static struct tt_device terratec_unit; + +static struct file_operations terratec_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = tt_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device terratec_radio= +{ + .owner = THIS_MODULE, + .name = "TerraTec ActiveRadio", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_TERRATEC, + .fops = &terratec_fops, +}; + +static int __init terratec_init(void) +{ + if(io==-1) + { + printk(KERN_ERR "You must set an I/O address with io=0x???\n"); + return -EINVAL; + } + if (!request_region(io, 2, "terratec")) + { + printk(KERN_ERR "TerraTec: port 0x%x already in use\n", io); + return -EBUSY; + } + + terratec_radio.priv=&terratec_unit; + + spin_lock_init(&lock); + + if(video_register_device(&terratec_radio, VFL_TYPE_RADIO, radio_nr)==-1) + { + release_region(io,2); + return -EINVAL; + } + + printk(KERN_INFO "TERRATEC ActivRadio Standalone card driver.\n"); + + /* mute card - prevents noisy bootups */ + + /* this ensures that the volume is all the way down */ + cardWriteVol(0); + terratec_unit.curvol = 0; + + return 0; +} + +MODULE_AUTHOR("R.OFFERMANNS & others"); +MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card."); +MODULE_LICENSE("GPL"); +module_param(io, int, 0); +MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)"); +module_param(radio_nr, int, 0); + +static void __exit terratec_cleanup_module(void) +{ + video_unregister_device(&terratec_radio); + release_region(io,2); + printk(KERN_INFO "TERRATEC ActivRadio Standalone card driver unloaded.\n"); +} + +module_init(terratec_init); +module_exit(terratec_cleanup_module); + diff --git a/linux/drivers/media/radio/radio-trust.c b/linux/drivers/media/radio/radio-trust.c new file mode 100644 index 000000000..448725ca9 --- /dev/null +++ b/linux/drivers/media/radio/radio-trust.c @@ -0,0 +1,322 @@ +/* radio-trust.c - Trust FM Radio card driver for Linux 2.2 + * by Eric Lammerts + * + * Based on radio-aztech.c. Original notes: + * + * Adapted to support the Video for Linux API by + * Russell Kroll . Based on original tuner code by: + * + * Quay Ly + * Donald Song + * Jason Lewis (jlewis@twilight.vtc.vsc.edu) + * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) + * William McGrath (wmcgrath@twilight.vtc.vsc.edu) + * + * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/ + */ + +#include +#include +#include +#include +#include +#include +#include "compat.h" +#include +#include /* CONFIG_RADIO_TRUST_PORT */ + +/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */ + +#ifndef CONFIG_RADIO_TRUST_PORT +#define CONFIG_RADIO_TRUST_PORT -1 +#endif + +static int io = CONFIG_RADIO_TRUST_PORT; +static int radio_nr = -1; +static int ioval = 0xf; +static __u16 curvol; +static __u16 curbass; +static __u16 curtreble; +static unsigned long curfreq; +static int curstereo; +static int curmute; + +/* i2c addresses */ +#define TDA7318_ADDR 0x88 +#define TSA6060T_ADDR 0xc4 + +#define TR_DELAY do { inb(io); inb(io); inb(io); } while(0) +#define TR_SET_SCL outb(ioval |= 2, io) +#define TR_CLR_SCL outb(ioval &= 0xfd, io) +#define TR_SET_SDA outb(ioval |= 1, io) +#define TR_CLR_SDA outb(ioval &= 0xfe, io) + +static void write_i2c(int n, ...) +{ + unsigned char val, mask; + va_list args; + + va_start(args, n); + + /* start condition */ + TR_SET_SDA; + TR_SET_SCL; + TR_DELAY; + TR_CLR_SDA; + TR_CLR_SCL; + TR_DELAY; + + for(; n; n--) { + val = va_arg(args, unsigned); + for(mask = 0x80; mask; mask >>= 1) { + if(val & mask) + TR_SET_SDA; + else + TR_CLR_SDA; + TR_SET_SCL; + TR_DELAY; + TR_CLR_SCL; + TR_DELAY; + } + /* acknowledge bit */ + TR_SET_SDA; + TR_SET_SCL; + TR_DELAY; + TR_CLR_SCL; + TR_DELAY; + } + + /* stop condition */ + TR_CLR_SDA; + TR_DELAY; + TR_SET_SCL; + TR_DELAY; + TR_SET_SDA; + TR_DELAY; + + va_end(args); +} + +static void tr_setvol(__u16 vol) +{ + curvol = vol / 2048; + write_i2c(2, TDA7318_ADDR, curvol ^ 0x1f); +} + +static int basstreble2chip[15] = { + 0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8 +}; + +static void tr_setbass(__u16 bass) +{ + curbass = bass / 4370; + write_i2c(2, TDA7318_ADDR, 0x60 | basstreble2chip[curbass]); +} + +static void tr_settreble(__u16 treble) +{ + curtreble = treble / 4370; + write_i2c(2, TDA7318_ADDR, 0x70 | basstreble2chip[curtreble]); +} + +static void tr_setstereo(int stereo) +{ + curstereo = !!stereo; + ioval = (ioval & 0xfb) | (!curstereo << 2); + outb(ioval, io); +} + +static void tr_setmute(int mute) +{ + curmute = !!mute; + ioval = (ioval & 0xf7) | (curmute << 3); + outb(ioval, io); +} + +static int tr_getsigstr(void) +{ + int i, v; + + for(i = 0, v = 0; i < 100; i++) v |= inb(io); + return (v & 1)? 0 : 0xffff; +} + +static int tr_getstereo(void) +{ + /* don't know how to determine it, just return the setting */ + return curstereo; +} + +static void tr_setfreq(unsigned long f) +{ + f /= 160; /* Convert to 10 kHz units */ + f += 1070; /* Add 10.7 MHz IF */ + + write_i2c(5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0); +} + +static int tr_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability *v = arg; + + memset(v,0,sizeof(*v)); + v->type=VID_TYPE_TUNER; + v->channels=1; + v->audios=1; + strcpy(v->name, "Trust FM Radio"); + + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + + if(v->tuner) /* Only 1 tuner */ + return -EINVAL; + + v->rangelow = 87500 * 16; + v->rangehigh = 108000 * 16; + v->flags = VIDEO_TUNER_LOW; + v->mode = VIDEO_MODE_AUTO; + + v->signal = tr_getsigstr(); + if(tr_getstereo()) + v->flags |= VIDEO_TUNER_STEREO_ON; + + strcpy(v->name, "FM"); + + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *v = arg; + if(v->tuner != 0) + return -EINVAL; + return 0; + } + case VIDIOCGFREQ: + { + unsigned long *freq = arg; + *freq = curfreq; + return 0; + } + case VIDIOCSFREQ: + { + unsigned long *freq = arg; + tr_setfreq(*freq); + return 0; + } + case VIDIOCGAUDIO: + { + struct video_audio *v = arg; + + memset(v,0, sizeof(*v)); + v->flags = VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME | + VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE; + v->mode = curstereo? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO; + v->volume = curvol * 2048; + v->step = 2048; + v->bass = curbass * 4370; + v->treble = curtreble * 4370; + + strcpy(v->name, "Trust FM Radio"); + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio *v = arg; + + if(v->audio) + return -EINVAL; + tr_setvol(v->volume); + tr_setbass(v->bass); + tr_settreble(v->treble); + tr_setstereo(v->mode & VIDEO_SOUND_STEREO); + tr_setmute(v->flags & VIDEO_AUDIO_MUTE); + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int tr_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, tr_do_ioctl); +} + +static struct file_operations trust_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = tr_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device trust_radio= +{ + .owner = THIS_MODULE, + .name = "Trust FM Radio", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_TRUST, + .fops = &trust_fops, +}; + +static int __init trust_init(void) +{ + if(io == -1) { + printk(KERN_ERR "You must set an I/O address with io=0x???\n"); + return -EINVAL; + } + if(!request_region(io, 2, "Trust FM Radio")) { + printk(KERN_ERR "trust: port 0x%x already in use\n", io); + return -EBUSY; + } + if(video_register_device(&trust_radio, VFL_TYPE_RADIO, radio_nr)==-1) + { + release_region(io, 2); + return -EINVAL; + } + + printk(KERN_INFO "Trust FM Radio card driver v1.0.\n"); + + write_i2c(2, TDA7318_ADDR, 0x80); /* speaker att. LF = 0 dB */ + write_i2c(2, TDA7318_ADDR, 0xa0); /* speaker att. RF = 0 dB */ + write_i2c(2, TDA7318_ADDR, 0xc0); /* speaker att. LR = 0 dB */ + write_i2c(2, TDA7318_ADDR, 0xe0); /* speaker att. RR = 0 dB */ + write_i2c(2, TDA7318_ADDR, 0x40); /* stereo 1 input, gain = 18.75 dB */ + + tr_setvol(0x8000); + tr_setbass(0x8000); + tr_settreble(0x8000); + tr_setstereo(1); + + /* mute card - prevents noisy bootups */ + tr_setmute(1); + + return 0; +} + +MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); +MODULE_DESCRIPTION("A driver for the Trust FM Radio card."); +MODULE_LICENSE("GPL"); + +module_param(io, int, 0); +MODULE_PARM_DESC(io, "I/O address of the Trust FM Radio card (0x350 or 0x358)"); +module_param(radio_nr, int, 0); + +static void __exit cleanup_trust_module(void) +{ + video_unregister_device(&trust_radio); + release_region(io, 2); +} + +module_init(trust_init); +module_exit(cleanup_trust_module); diff --git a/linux/drivers/media/radio/radio-typhoon.c b/linux/drivers/media/radio/radio-typhoon.c new file mode 100644 index 000000000..8e87d0b42 --- /dev/null +++ b/linux/drivers/media/radio/radio-typhoon.c @@ -0,0 +1,385 @@ +/* Typhoon Radio Card driver for radio support + * (c) 1999 Dr. Henrik Seidel + * + * Card manufacturer: + * http://194.18.155.92/idc/prod2.idc?nr=50753&lang=e + * + * Notes on the hardware + * + * This card has two output sockets, one for speakers and one for line. + * The speaker output has volume control, but only in four discrete + * steps. The line output has neither volume control nor mute. + * + * The card has auto-stereo according to its manual, although it all + * sounds mono to me (even with the Win/DOS drivers). Maybe it's my + * antenna - I really don't know for sure. + * + * Frequency control is done digitally. + * + * Volume control is done digitally, but there are only four different + * possible values. So you should better always turn the volume up and + * use line control. I got the best results by connecting line output + * to the sound card microphone input. For such a configuration the + * volume control has no effect, since volume control only influences + * the speaker output. + * + * There is no explicit mute/unmute. So I set the radio frequency to a + * value where I do expect just noise and turn the speaker volume down. + * The frequency change is necessary since the card never seems to be + * completely silent. + */ + +#include /* Modules */ +#include /* Initdata */ +#include /* request_region */ +#include /* radio card status report */ +#include /* outb, outb_p */ +#include /* copy to/from user */ +#include "compat.h" +#include /* kernel radio structs */ +#include /* CONFIG_RADIO_TYPHOON_* */ + +#define BANNER "Typhoon Radio Card driver v0.1\n" + +#ifndef CONFIG_RADIO_TYPHOON_PORT +#define CONFIG_RADIO_TYPHOON_PORT -1 +#endif + +#ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ +#define CONFIG_RADIO_TYPHOON_MUTEFREQ 0 +#endif + +#ifndef CONFIG_PROC_FS +#undef CONFIG_RADIO_TYPHOON_PROC_FS +#endif + +struct typhoon_device { + int users; + int iobase; + int curvol; + int muted; + unsigned long curfreq; + unsigned long mutefreq; + struct mutex lock; +}; + +static void typhoon_setvol_generic(struct typhoon_device *dev, int vol); +static int typhoon_setfreq_generic(struct typhoon_device *dev, + unsigned long frequency); +static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency); +static void typhoon_mute(struct typhoon_device *dev); +static void typhoon_unmute(struct typhoon_device *dev); +static int typhoon_setvol(struct typhoon_device *dev, int vol); +static int typhoon_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +#ifdef CONFIG_RADIO_TYPHOON_PROC_FS +static int typhoon_get_info(char *buf, char **start, off_t offset, int len); +#endif + +static void typhoon_setvol_generic(struct typhoon_device *dev, int vol) +{ + mutex_lock(&dev->lock); + vol >>= 14; /* Map 16 bit to 2 bit */ + vol &= 3; + outb_p(vol / 2, dev->iobase); /* Set the volume, high bit. */ + outb_p(vol % 2, dev->iobase + 2); /* Set the volume, low bit. */ + mutex_unlock(&dev->lock); +} + +static int typhoon_setfreq_generic(struct typhoon_device *dev, + unsigned long frequency) +{ + unsigned long outval; + unsigned long x; + + /* + * The frequency transfer curve is not linear. The best fit I could + * get is + * + * outval = -155 + exp((f + 15.55) * 0.057)) + * + * where frequency f is in MHz. Since we don't have exp in the kernel, + * I approximate this function by a third order polynomial. + * + */ + + mutex_lock(&dev->lock); + x = frequency / 160; + outval = (x * x + 2500) / 5000; + outval = (outval * x + 5000) / 10000; + outval -= (10 * x * x + 10433) / 20866; + outval += 4 * x - 11505; + + outb_p((outval >> 8) & 0x01, dev->iobase + 4); + outb_p(outval >> 9, dev->iobase + 6); + outb_p(outval & 0xff, dev->iobase + 8); + mutex_unlock(&dev->lock); + + return 0; +} + +static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency) +{ + typhoon_setfreq_generic(dev, frequency); + dev->curfreq = frequency; + return 0; +} + +static void typhoon_mute(struct typhoon_device *dev) +{ + if (dev->muted == 1) + return; + typhoon_setvol_generic(dev, 0); + typhoon_setfreq_generic(dev, dev->mutefreq); + dev->muted = 1; +} + +static void typhoon_unmute(struct typhoon_device *dev) +{ + if (dev->muted == 0) + return; + typhoon_setfreq_generic(dev, dev->curfreq); + typhoon_setvol_generic(dev, dev->curvol); + dev->muted = 0; +} + +static int typhoon_setvol(struct typhoon_device *dev, int vol) +{ + if (dev->muted && vol != 0) { /* user is unmuting the card */ + dev->curvol = vol; + typhoon_unmute(dev); + return 0; + } + if (vol == dev->curvol) /* requested volume == current */ + return 0; + + if (vol == 0) { /* volume == 0 means mute the card */ + typhoon_mute(dev); + dev->curvol = vol; + return 0; + } + typhoon_setvol_generic(dev, vol); + dev->curvol = vol; + return 0; +} + + +static int typhoon_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct typhoon_device *typhoon = dev->priv; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability *v = arg; + memset(v,0,sizeof(*v)); + v->type = VID_TYPE_TUNER; + v->channels = 1; + v->audios = 1; + strcpy(v->name, "Typhoon Radio"); + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + if (v->tuner) /* Only 1 tuner */ + return -EINVAL; + v->rangelow = 875 * 1600; + v->rangehigh = 1080 * 1600; + v->flags = VIDEO_TUNER_LOW; + v->mode = VIDEO_MODE_AUTO; + v->signal = 0xFFFF; /* We can't get the signal strength */ + strcpy(v->name, "FM"); + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *v = arg; + if (v->tuner != 0) + return -EINVAL; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: + { + unsigned long *freq = arg; + *freq = typhoon->curfreq; + return 0; + } + case VIDIOCSFREQ: + { + unsigned long *freq = arg; + typhoon->curfreq = *freq; + typhoon_setfreq(typhoon, typhoon->curfreq); + return 0; + } + case VIDIOCGAUDIO: + { + struct video_audio *v = arg; + memset(v, 0, sizeof(*v)); + v->flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME; + v->mode |= VIDEO_SOUND_MONO; + v->volume = typhoon->curvol; + v->step = 1 << 14; + strcpy(v->name, "Typhoon Radio"); + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio *v = arg; + if (v->audio) + return -EINVAL; + if (v->flags & VIDEO_AUDIO_MUTE) + typhoon_mute(typhoon); + else + typhoon_unmute(typhoon); + if (v->flags & VIDEO_AUDIO_VOLUME) + typhoon_setvol(typhoon, v->volume); + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int typhoon_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, typhoon_do_ioctl); +} + +static struct typhoon_device typhoon_unit = +{ + .iobase = CONFIG_RADIO_TYPHOON_PORT, + .curfreq = CONFIG_RADIO_TYPHOON_MUTEFREQ, + .mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ, +}; + +static struct file_operations typhoon_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = typhoon_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device typhoon_radio = +{ + .owner = THIS_MODULE, + .name = "Typhoon Radio", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_TYPHOON, + .fops = &typhoon_fops, +}; + +#ifdef CONFIG_RADIO_TYPHOON_PROC_FS + +static int typhoon_get_info(char *buf, char **start, off_t offset, int len) +{ + char *out = buf; + + #ifdef MODULE + #define MODULEPROCSTRING "Driver loaded as a module" + #else + #define MODULEPROCSTRING "Driver compiled into kernel" + #endif + + /* output must be kept under PAGE_SIZE */ + out += sprintf(out, BANNER); + out += sprintf(out, "Load type: " MODULEPROCSTRING "\n\n"); + out += sprintf(out, "frequency = %lu kHz\n", + typhoon_unit.curfreq >> 4); + out += sprintf(out, "volume = %d\n", typhoon_unit.curvol); + out += sprintf(out, "mute = %s\n", typhoon_unit.muted ? + "on" : "off"); + out += sprintf(out, "iobase = 0x%x\n", typhoon_unit.iobase); + out += sprintf(out, "mute frequency = %lu kHz\n", + typhoon_unit.mutefreq >> 4); + return out - buf; +} + +#endif /* CONFIG_RADIO_TYPHOON_PROC_FS */ + +MODULE_AUTHOR("Dr. Henrik Seidel"); +MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio)."); +MODULE_LICENSE("GPL"); + +static int io = -1; +static int radio_nr = -1; + +module_param(io, int, 0); +MODULE_PARM_DESC(io, "I/O address of the Typhoon card (0x316 or 0x336)"); +module_param(radio_nr, int, 0); + +#ifdef MODULE +static unsigned long mutefreq = 0; +module_param(mutefreq, ulong, 0); +MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)"); +#endif + +static int __init typhoon_init(void) +{ +#ifdef MODULE + if (io == -1) { + printk(KERN_ERR "radio-typhoon: You must set an I/O address with io=0x316 or io=0x336\n"); + return -EINVAL; + } + typhoon_unit.iobase = io; + + if (mutefreq < 87000 || mutefreq > 108500) { + printk(KERN_ERR "radio-typhoon: You must set a frequency (in kHz) used when muting the card,\n"); + printk(KERN_ERR "radio-typhoon: e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108500)\n"); + return -EINVAL; + } + typhoon_unit.mutefreq = mutefreq; +#endif /* MODULE */ + + printk(KERN_INFO BANNER); + mutex_init(&typhoon_unit.lock); + io = typhoon_unit.iobase; + if (!request_region(io, 8, "typhoon")) { + printk(KERN_ERR "radio-typhoon: port 0x%x already in use\n", + typhoon_unit.iobase); + return -EBUSY; + } + + typhoon_radio.priv = &typhoon_unit; + if (video_register_device(&typhoon_radio, VFL_TYPE_RADIO, radio_nr) == -1) + { + release_region(io, 8); + return -EINVAL; + } + printk(KERN_INFO "radio-typhoon: port 0x%x.\n", typhoon_unit.iobase); + printk(KERN_INFO "radio-typhoon: mute frequency is %lu kHz.\n", + typhoon_unit.mutefreq); + typhoon_unit.mutefreq <<= 4; + + /* mute card - prevents noisy bootups */ + typhoon_mute(&typhoon_unit); + +#ifdef CONFIG_RADIO_TYPHOON_PROC_FS + if (!create_proc_info_entry("driver/radio-typhoon", 0, NULL, + typhoon_get_info)) + printk(KERN_ERR "radio-typhoon: registering /proc/driver/radio-typhoon failed\n"); +#endif + + return 0; +} + +static void __exit typhoon_cleanup_module(void) +{ + +#ifdef CONFIG_RADIO_TYPHOON_PROC_FS + remove_proc_entry("driver/radio-typhoon", NULL); +#endif + + video_unregister_device(&typhoon_radio); + release_region(io, 8); +} + +module_init(typhoon_init); +module_exit(typhoon_cleanup_module); + diff --git a/linux/drivers/media/radio/radio-zoltrix.c b/linux/drivers/media/radio/radio-zoltrix.c new file mode 100644 index 000000000..7c7760e26 --- /dev/null +++ b/linux/drivers/media/radio/radio-zoltrix.c @@ -0,0 +1,387 @@ +/* zoltrix radio plus driver for Linux radio support + * (c) 1998 C. van Schaik + * + * BUGS + * Due to the inconsistency in reading from the signal flags + * it is difficult to get an accurate tuned signal. + * + * It seems that the card is not linear to 0 volume. It cuts off + * at a low volume, and it is not possible (at least I have not found) + * to get fine volume control over the low volume range. + * + * Some code derived from code by Romolo Manfredini + * romolo@bicnet.it + * + * 1999-05-06 - (C. van Schaik) + * - Make signal strength and stereo scans + * kinder to cpu while in delay + * 1999-01-05 - (C. van Schaik) + * - Changed tuning to 1/160Mhz accuracy + * - Added stereo support + * (card defaults to stereo) + * (can explicitly force mono on the card) + * (can detect if station is in stereo) + * - Added unmute function + * - Reworked ioctl functions + * 2002-07-15 - Fix Stereo typo + */ + +#include /* Modules */ +#include /* Initdata */ +#include /* request_region */ +#include /* udelay, msleep */ +#include /* outb, outb_p */ +#include /* copy to/from user */ +#include "compat.h" +#include /* kernel radio structs */ +#include /* CONFIG_RADIO_ZOLTRIX_PORT */ + +#ifndef CONFIG_RADIO_ZOLTRIX_PORT +#define CONFIG_RADIO_ZOLTRIX_PORT -1 +#endif + +static int io = CONFIG_RADIO_ZOLTRIX_PORT; +static int radio_nr = -1; + +struct zol_device { + int port; + int curvol; + unsigned long curfreq; + int muted; + unsigned int stereo; + struct mutex lock; +}; + +static int zol_setvol(struct zol_device *dev, int vol) +{ + dev->curvol = vol; + if (dev->muted) + return 0; + + mutex_lock(&dev->lock); + if (vol == 0) { + outb(0, io); + outb(0, io); + inb(io + 3); /* Zoltrix needs to be read to confirm */ + mutex_unlock(&dev->lock); + return 0; + } + + outb(dev->curvol-1, io); + msleep(10); + inb(io + 2); + mutex_unlock(&dev->lock); + return 0; +} + +static void zol_mute(struct zol_device *dev) +{ + dev->muted = 1; + mutex_lock(&dev->lock); + outb(0, io); + outb(0, io); + inb(io + 3); /* Zoltrix needs to be read to confirm */ + mutex_unlock(&dev->lock); +} + +static void zol_unmute(struct zol_device *dev) +{ + dev->muted = 0; + zol_setvol(dev, dev->curvol); +} + +static int zol_setfreq(struct zol_device *dev, unsigned long freq) +{ + /* tunes the radio to the desired frequency */ + unsigned long long bitmask, f, m; + unsigned int stereo = dev->stereo; + int i; + + if (freq == 0) + return 1; + m = (freq / 160 - 8800) * 2; + f = (unsigned long long) m + 0x4d1c; + + bitmask = 0xc480402c10080000ull; + i = 45; + + mutex_lock(&dev->lock); + + outb(0, io); + outb(0, io); + inb(io + 3); /* Zoltrix needs to be read to confirm */ + + outb(0x40, io); + outb(0xc0, io); + + bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ ( stereo << 31)); + while (i--) { + if ((bitmask & 0x8000000000000000ull) != 0) { + outb(0x80, io); + udelay(50); + outb(0x00, io); + udelay(50); + outb(0x80, io); + udelay(50); + } else { + outb(0xc0, io); + udelay(50); + outb(0x40, io); + udelay(50); + outb(0xc0, io); + udelay(50); + } + bitmask *= 2; + } + /* termination sequence */ + outb(0x80, io); + outb(0xc0, io); + outb(0x40, io); + udelay(1000); + inb(io+2); + + udelay(1000); + + if (dev->muted) + { + outb(0, io); + outb(0, io); + inb(io + 3); + udelay(1000); + } + + mutex_unlock(&dev->lock); + + if(!dev->muted) + { + zol_setvol(dev, dev->curvol); + } + return 0; +} + +/* Get signal strength */ + +static int zol_getsigstr(struct zol_device *dev) +{ + int a, b; + + mutex_lock(&dev->lock); + outb(0x00, io); /* This stuff I found to do nothing */ + outb(dev->curvol, io); + msleep(20); + + a = inb(io); + msleep(10); + b = inb(io); + + mutex_unlock(&dev->lock); + + if (a != b) + return (0); + + if ((a == 0xcf) || (a == 0xdf) /* I found this out by playing */ + || (a == 0xef)) /* with a binary scanner on the card io */ + return (1); + return (0); +} + +static int zol_is_stereo (struct zol_device *dev) +{ + int x1, x2; + + mutex_lock(&dev->lock); + + outb(0x00, io); + outb(dev->curvol, io); + msleep(20); + + x1 = inb(io); + msleep(10); + x2 = inb(io); + + mutex_unlock(&dev->lock); + + if ((x1 == x2) && (x1 == 0xcf)) + return 1; + return 0; +} + +static int zol_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct zol_device *zol = dev->priv; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability *v = arg; + + memset(v,0,sizeof(*v)); + v->type = VID_TYPE_TUNER; + v->channels = 1 + zol->stereo; + v->audios = 1; + strcpy(v->name, "Zoltrix Radio"); + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + if (v->tuner) + return -EINVAL; + strcpy(v->name, "FM"); + v->rangelow = (int) (88.0 * 16000); + v->rangehigh = (int) (108.0 * 16000); + v->flags = zol_is_stereo(zol) + ? VIDEO_TUNER_STEREO_ON : 0; + v->flags |= VIDEO_TUNER_LOW; + v->mode = VIDEO_MODE_AUTO; + v->signal = 0xFFFF * zol_getsigstr(zol); + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *v = arg; + if (v->tuner != 0) + return -EINVAL; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: + { + unsigned long *freq = arg; + *freq = zol->curfreq; + return 0; + } + case VIDIOCSFREQ: + { + unsigned long *freq = arg; + zol->curfreq = *freq; + zol_setfreq(zol, zol->curfreq); + return 0; + } + case VIDIOCGAUDIO: + { + struct video_audio *v = arg; + memset(v, 0, sizeof(*v)); + v->flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME; + v->mode |= zol_is_stereo(zol) + ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO; + v->volume = zol->curvol * 4096; + v->step = 4096; + strcpy(v->name, "Zoltrix Radio"); + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio *v = arg; + if (v->audio) + return -EINVAL; + + if (v->flags & VIDEO_AUDIO_MUTE) + zol_mute(zol); + else { + zol_unmute(zol); + zol_setvol(zol, v->volume / 4096); + } + + if (v->mode & VIDEO_SOUND_STEREO) { + zol->stereo = 1; + zol_setfreq(zol, zol->curfreq); + } + if (v->mode & VIDEO_SOUND_MONO) { + zol->stereo = 0; + zol_setfreq(zol, zol->curfreq); + } + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int zol_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, zol_do_ioctl); +} + +static struct zol_device zoltrix_unit; + +static struct file_operations zoltrix_fops = +{ + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = zol_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device zoltrix_radio = +{ + .owner = THIS_MODULE, + .name = "Zoltrix Radio Plus", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_ZOLTRIX, + .fops = &zoltrix_fops, +}; + +static int __init zoltrix_init(void) +{ + if (io == -1) { + printk(KERN_ERR "You must set an I/O address with io=0x???\n"); + return -EINVAL; + } + if ((io != 0x20c) && (io != 0x30c)) { + printk(KERN_ERR "zoltrix: invalid port, try 0x20c or 0x30c\n"); + return -ENXIO; + } + + zoltrix_radio.priv = &zoltrix_unit; + if (!request_region(io, 2, "zoltrix")) { + printk(KERN_ERR "zoltrix: port 0x%x already in use\n", io); + return -EBUSY; + } + + if (video_register_device(&zoltrix_radio, VFL_TYPE_RADIO, radio_nr) == -1) + { + release_region(io, 2); + return -EINVAL; + } + printk(KERN_INFO "Zoltrix Radio Plus card driver.\n"); + + mutex_init(&zoltrix_unit.lock); + + /* mute card - prevents noisy bootups */ + + /* this ensures that the volume is all the way down */ + + outb(0, io); + outb(0, io); + msleep(20); + inb(io + 3); + + zoltrix_unit.curvol = 0; + zoltrix_unit.stereo = 1; + + return 0; +} + +MODULE_AUTHOR("C.van Schaik"); +MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus."); +MODULE_LICENSE("GPL"); + +module_param(io, int, 0); +MODULE_PARM_DESC(io, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)"); +module_param(radio_nr, int, 0); + +static void __exit zoltrix_cleanup_module(void) +{ + video_unregister_device(&zoltrix_radio); + release_region(io, 2); +} + +module_init(zoltrix_init); +module_exit(zoltrix_cleanup_module); + diff --git a/linux/drivers/media/video/adv7170.c b/linux/drivers/media/video/adv7170.c new file mode 100644 index 000000000..48fe2344b --- /dev/null +++ b/linux/drivers/media/video/adv7170.c @@ -0,0 +1,519 @@ +/* + * adv7170 - adv7170, adv7171 video encoder driver version 0.0.1 + * + * Copyright (C) 2002 Maxim Yevtyushkin + * + * Based on adv7176 driver by: + * + * Copyright (C) 1998 Dave Perks + * Copyright (C) 1999 Wolfgang Scherr + * Copyright (C) 2000 Serguei Miridonov + * - some corrections for Pinnacle Systems Inc. DC10plus card. + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (1/1/2003) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compat.h" +#include +#include + +MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver"); +MODULE_AUTHOR("Maxim Yevtyushkin"); +MODULE_LICENSE("GPL"); + +#include + +#define I2C_NAME(x) (x)->name + +#include + +static int debug = 0; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define dprintk(num, format, args...) \ + do { \ + if (debug >= num) \ + printk(format, ##args); \ + } while (0) + +/* ----------------------------------------------------------------------- */ + +struct adv7170 { + unsigned char reg[128]; + + int norm; + int input; + int enable; + int bright; + int contrast; + int hue; + int sat; +}; + +#define I2C_ADV7170 0xd4 +#define I2C_ADV7171 0x54 + +static char adv7170_name[] = "adv7170"; +static char adv7171_name[] = "adv7171"; + +static char *inputs[] = { "pass_through", "play_back" }; +static char *norms[] = { "PAL", "NTSC" }; + +/* ----------------------------------------------------------------------- */ + +static inline int +adv7170_write (struct i2c_client *client, + u8 reg, + u8 value) +{ + struct adv7170 *encoder = i2c_get_clientdata(client); + + encoder->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int +adv7170_read (struct i2c_client *client, + u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int +adv7170_write_block (struct i2c_client *client, + const u8 *data, + unsigned int len) +{ + int ret = -1; + u8 reg; + + /* the adv7170 has an autoincrement function, use it if + * the adapter understands raw I2C */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + /* do raw I2C, not smbus compatible */ + struct adv7170 *encoder = i2c_get_clientdata(client); + u8 block_data[32]; + int block_len; + + while (len >= 2) { + block_len = 0; + block_data[block_len++] = reg = data[0]; + do { + block_data[block_len++] = + encoder->reg[reg++] = data[1]; + len -= 2; + data += 2; + } while (len >= 2 && data[0] == reg && + block_len < 32); + if ((ret = i2c_master_send(client, block_data, + block_len)) < 0) + break; + } + } else { + /* do some slow I2C emulation kind of thing */ + while (len >= 2) { + reg = *data++; + if ((ret = adv7170_write(client, reg, + *data++)) < 0) + break; + len -= 2; + } + } + + return ret; +} + +/* ----------------------------------------------------------------------- */ +// Output filter: S-Video Composite + +#define MR050 0x11 //0x09 +#define MR060 0x14 //0x0c + +//--------------------------------------------------------------------------- + +#define TR0MODE 0x4c +#define TR0RST 0x80 + +#define TR1CAPT 0x00 +#define TR1PLAY 0x00 + + +static const unsigned char init_NTSC[] = { + 0x00, 0x10, // MR0 + 0x01, 0x20, // MR1 + 0x02, 0x0e, // MR2 RTC control: bits 2 and 1 + 0x03, 0x80, // MR3 + 0x04, 0x30, // MR4 + 0x05, 0x00, // Reserved + 0x06, 0x00, // Reserved + 0x07, TR0MODE, // TM0 + 0x08, TR1CAPT, // TM1 + 0x09, 0x16, // Fsc0 + 0x0a, 0x7c, // Fsc1 + 0x0b, 0xf0, // Fsc2 + 0x0c, 0x21, // Fsc3 + 0x0d, 0x00, // Subcarrier Phase + 0x0e, 0x00, // Closed Capt. Ext 0 + 0x0f, 0x00, // Closed Capt. Ext 1 + 0x10, 0x00, // Closed Capt. 0 + 0x11, 0x00, // Closed Capt. 1 + 0x12, 0x00, // Pedestal Ctl 0 + 0x13, 0x00, // Pedestal Ctl 1 + 0x14, 0x00, // Pedestal Ctl 2 + 0x15, 0x00, // Pedestal Ctl 3 + 0x16, 0x00, // CGMS_WSS_0 + 0x17, 0x00, // CGMS_WSS_1 + 0x18, 0x00, // CGMS_WSS_2 + 0x19, 0x00, // Teletext Ctl +}; + +static const unsigned char init_PAL[] = { + 0x00, 0x71, // MR0 + 0x01, 0x20, // MR1 + 0x02, 0x0e, // MR2 RTC control: bits 2 and 1 + 0x03, 0x80, // MR3 + 0x04, 0x30, // MR4 + 0x05, 0x00, // Reserved + 0x06, 0x00, // Reserved + 0x07, TR0MODE, // TM0 + 0x08, TR1CAPT, // TM1 + 0x09, 0xcb, // Fsc0 + 0x0a, 0x8a, // Fsc1 + 0x0b, 0x09, // Fsc2 + 0x0c, 0x2a, // Fsc3 + 0x0d, 0x00, // Subcarrier Phase + 0x0e, 0x00, // Closed Capt. Ext 0 + 0x0f, 0x00, // Closed Capt. Ext 1 + 0x10, 0x00, // Closed Capt. 0 + 0x11, 0x00, // Closed Capt. 1 + 0x12, 0x00, // Pedestal Ctl 0 + 0x13, 0x00, // Pedestal Ctl 1 + 0x14, 0x00, // Pedestal Ctl 2 + 0x15, 0x00, // Pedestal Ctl 3 + 0x16, 0x00, // CGMS_WSS_0 + 0x17, 0x00, // CGMS_WSS_1 + 0x18, 0x00, // CGMS_WSS_2 + 0x19, 0x00, // Teletext Ctl +}; + + +static int +adv7170_command (struct i2c_client *client, + unsigned int cmd, + void * arg) +{ + struct adv7170 *encoder = i2c_get_clientdata(client); + + switch (cmd) { + + case 0: +#if 0 + /* This is just for testing!!! */ + adv7170_write_block(client, init_common, + sizeof(init_common)); + adv7170_write(client, 0x07, TR0MODE | TR0RST); + adv7170_write(client, 0x07, TR0MODE); +#endif + break; + + case ENCODER_GET_CAPABILITIES: + { + struct video_encoder_capability *cap = arg; + + cap->flags = VIDEO_ENCODER_PAL | + VIDEO_ENCODER_NTSC; + cap->inputs = 2; + cap->outputs = 1; + } + break; + + case ENCODER_SET_NORM: + { + int iarg = *(int *) arg; + + dprintk(1, KERN_DEBUG "%s_command: set norm %d", + I2C_NAME(client), iarg); + + switch (iarg) { + + case VIDEO_MODE_NTSC: + adv7170_write_block(client, init_NTSC, + sizeof(init_NTSC)); + if (encoder->input == 0) + adv7170_write(client, 0x02, 0x0e); // Enable genlock + adv7170_write(client, 0x07, TR0MODE | TR0RST); + adv7170_write(client, 0x07, TR0MODE); + break; + + case VIDEO_MODE_PAL: + adv7170_write_block(client, init_PAL, + sizeof(init_PAL)); + if (encoder->input == 0) + adv7170_write(client, 0x02, 0x0e); // Enable genlock + adv7170_write(client, 0x07, TR0MODE | TR0RST); + adv7170_write(client, 0x07, TR0MODE); + break; + + default: + dprintk(1, KERN_ERR "%s: illegal norm: %d\n", + I2C_NAME(client), iarg); + return -EINVAL; + + } + dprintk(1, KERN_DEBUG "%s: switched to %s\n", I2C_NAME(client), + norms[iarg]); + encoder->norm = iarg; + } + break; + + case ENCODER_SET_INPUT: + { + int iarg = *(int *) arg; + + /* RJ: *iarg = 0: input is from decoder + *iarg = 1: input is from ZR36060 + *iarg = 2: color bar */ + + dprintk(1, KERN_DEBUG "%s_command: set input from %s\n", + I2C_NAME(client), + iarg == 0 ? "decoder" : "ZR36060"); + + switch (iarg) { + + case 0: + adv7170_write(client, 0x01, 0x20); + adv7170_write(client, 0x08, TR1CAPT); /* TR1 */ + adv7170_write(client, 0x02, 0x0e); // Enable genlock + adv7170_write(client, 0x07, TR0MODE | TR0RST); + adv7170_write(client, 0x07, TR0MODE); + //udelay(10); + break; + + case 1: + adv7170_write(client, 0x01, 0x00); + adv7170_write(client, 0x08, TR1PLAY); /* TR1 */ + adv7170_write(client, 0x02, 0x08); + adv7170_write(client, 0x07, TR0MODE | TR0RST); + adv7170_write(client, 0x07, TR0MODE); + //udelay(10); + break; + + default: + dprintk(1, KERN_ERR "%s: illegal input: %d\n", + I2C_NAME(client), iarg); + return -EINVAL; + + } + dprintk(1, KERN_DEBUG "%s: switched to %s\n", I2C_NAME(client), + inputs[iarg]); + encoder->input = iarg; + } + break; + + case ENCODER_SET_OUTPUT: + { + int *iarg = arg; + + /* not much choice of outputs */ + if (*iarg != 0) { + return -EINVAL; + } + } + break; + + case ENCODER_ENABLE_OUTPUT: + { + int *iarg = arg; + + encoder->enable = !!*iarg; + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ +static unsigned short normal_i2c[] = + { I2C_ADV7170 >> 1, (I2C_ADV7170 >> 1) + 1, + I2C_ADV7171 >> 1, (I2C_ADV7171 >> 1) + 1, + I2C_CLIENT_END +}; + +static unsigned short ignore = I2C_CLIENT_END; + +static struct i2c_client_address_data addr_data = { + .normal_i2c = normal_i2c, + .probe = &ignore, + .ignore = &ignore, +}; + +static struct i2c_driver i2c_driver_adv7170; + +static int +adv7170_detect_client (struct i2c_adapter *adapter, + int address, + int kind) +{ + int i; + struct i2c_client *client; + struct adv7170 *encoder; + char *dname; + + dprintk(1, + KERN_INFO + "adv7170.c: detecting adv7170 client on address 0x%x\n", + address << 1); + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == 0) + return -ENOMEM; + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver_adv7170; + if ((client->addr == I2C_ADV7170 >> 1) || + (client->addr == (I2C_ADV7170 >> 1) + 1)) { + dname = adv7170_name; + } else if ((client->addr == I2C_ADV7171 >> 1) || + (client->addr == (I2C_ADV7171 >> 1) + 1)) { + dname = adv7171_name; + } else { + /* We should never get here!!! */ + kfree(client); + return 0; + } + strlcpy(I2C_NAME(client), dname, sizeof(I2C_NAME(client))); + + encoder = kzalloc(sizeof(struct adv7170), GFP_KERNEL); + if (encoder == NULL) { + kfree(client); + return -ENOMEM; + } + encoder->norm = VIDEO_MODE_NTSC; + encoder->input = 0; + encoder->enable = 1; + i2c_set_clientdata(client, encoder); + + i = i2c_attach_client(client); + if (i) { + kfree(client); + kfree(encoder); + return i; + } + + i = adv7170_write_block(client, init_NTSC, sizeof(init_NTSC)); + if (i >= 0) { + i = adv7170_write(client, 0x07, TR0MODE | TR0RST); + i = adv7170_write(client, 0x07, TR0MODE); + i = adv7170_read(client, 0x12); + dprintk(1, KERN_INFO "%s_attach: rev. %d at 0x%02x\n", + I2C_NAME(client), i & 1, client->addr << 1); + } + if (i < 0) { + dprintk(1, KERN_ERR "%s_attach: init error 0x%x\n", + I2C_NAME(client), i); + } + + return 0; +} + +static int +adv7170_attach_adapter (struct i2c_adapter *adapter) +{ + dprintk(1, + KERN_INFO + "adv7170.c: starting probe for adapter %s (0x%x)\n", + I2C_NAME(adapter), adapter->id); + return i2c_probe(adapter, &addr_data, &adv7170_detect_client); +} + +static int +adv7170_detach_client (struct i2c_client *client) +{ + struct adv7170 *encoder = i2c_get_clientdata(client); + int err; + + err = i2c_detach_client(client); + if (err) { + return err; + } + + kfree(encoder); + kfree(client); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver i2c_driver_adv7170 = { + .driver = { + .name = "adv7170", /* name */ + }, + + .id = I2C_DRIVERID_ADV7170, + + .attach_adapter = adv7170_attach_adapter, + .detach_client = adv7170_detach_client, + .command = adv7170_command, +}; + +static int __init +adv7170_init (void) +{ + return i2c_add_driver(&i2c_driver_adv7170); +} + +static void __exit +adv7170_exit (void) +{ + i2c_del_driver(&i2c_driver_adv7170); +} + +module_init(adv7170_init); +module_exit(adv7170_exit); diff --git a/linux/drivers/media/video/adv7175.c b/linux/drivers/media/video/adv7175.c new file mode 100644 index 000000000..052b54f83 --- /dev/null +++ b/linux/drivers/media/video/adv7175.c @@ -0,0 +1,537 @@ +/* + * adv7175 - adv7175a video encoder driver version 0.0.3 + * + * Copyright (C) 1998 Dave Perks + * Copyright (C) 1999 Wolfgang Scherr + * Copyright (C) 2000 Serguei Miridonov + * - some corrections for Pinnacle Systems Inc. DC10plus card. + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (9/9/2002) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compat.h" +#include +#include + +MODULE_DESCRIPTION("Analog Devices ADV7175 video encoder driver"); +MODULE_AUTHOR("Dave Perks"); +MODULE_LICENSE("GPL"); + +#include + +#define I2C_NAME(s) (s)->name + +#include + +static int debug = 0; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define dprintk(num, format, args...) \ + do { \ + if (debug >= num) \ + printk(format, ##args); \ + } while (0) + +/* ----------------------------------------------------------------------- */ + +struct adv7175 { + int norm; + int input; + int enable; + int bright; + int contrast; + int hue; + int sat; +}; + +#define I2C_ADV7175 0xd4 +#define I2C_ADV7176 0x54 + +static char adv7175_name[] = "adv7175"; +static char adv7176_name[] = "adv7176"; + +static char *inputs[] = { "pass_through", "play_back", "color_bar" }; +static char *norms[] = { "PAL", "NTSC", "SECAM->PAL (may not work!)" }; + +/* ----------------------------------------------------------------------- */ + +static inline int +adv7175_write (struct i2c_client *client, + u8 reg, + u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int +adv7175_read (struct i2c_client *client, + u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int +adv7175_write_block (struct i2c_client *client, + const u8 *data, + unsigned int len) +{ + int ret = -1; + u8 reg; + + /* the adv7175 has an autoincrement function, use it if + * the adapter understands raw I2C */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + /* do raw I2C, not smbus compatible */ + u8 block_data[32]; + int block_len; + + while (len >= 2) { + block_len = 0; + block_data[block_len++] = reg = data[0]; + do { + block_data[block_len++] = data[1]; + reg++; + len -= 2; + data += 2; + } while (len >= 2 && data[0] == reg && + block_len < 32); + if ((ret = i2c_master_send(client, block_data, + block_len)) < 0) + break; + } + } else { + /* do some slow I2C emulation kind of thing */ + while (len >= 2) { + reg = *data++; + if ((ret = adv7175_write(client, reg, + *data++)) < 0) + break; + len -= 2; + } + } + + return ret; +} + +static void +set_subcarrier_freq (struct i2c_client *client, + int pass_through) +{ + /* for some reason pass_through NTSC needs + * a different sub-carrier freq to remain stable. */ + if(pass_through) + adv7175_write(client, 0x02, 0x00); + else + adv7175_write(client, 0x02, 0x55); + + adv7175_write(client, 0x03, 0x55); + adv7175_write(client, 0x04, 0x55); + adv7175_write(client, 0x05, 0x25); +} + +/* ----------------------------------------------------------------------- */ +// Output filter: S-Video Composite + +#define MR050 0x11 //0x09 +#define MR060 0x14 //0x0c + +//--------------------------------------------------------------------------- + +#define TR0MODE 0x46 +#define TR0RST 0x80 + +#define TR1CAPT 0x80 +#define TR1PLAY 0x00 + +static const unsigned char init_common[] = { + + 0x00, MR050, /* MR0, PAL enabled */ + 0x01, 0x00, /* MR1 */ + 0x02, 0x0c, /* subc. freq. */ + 0x03, 0x8c, /* subc. freq. */ + 0x04, 0x79, /* subc. freq. */ + 0x05, 0x26, /* subc. freq. */ + 0x06, 0x40, /* subc. phase */ + + 0x07, TR0MODE, /* TR0, 16bit */ + 0x08, 0x21, /* */ + 0x09, 0x00, /* */ + 0x0a, 0x00, /* */ + 0x0b, 0x00, /* */ + 0x0c, TR1CAPT, /* TR1 */ + 0x0d, 0x4f, /* MR2 */ + 0x0e, 0x00, /* */ + 0x0f, 0x00, /* */ + 0x10, 0x00, /* */ + 0x11, 0x00, /* */ +}; + +static const unsigned char init_pal[] = { + 0x00, MR050, /* MR0, PAL enabled */ + 0x01, 0x00, /* MR1 */ + 0x02, 0x0c, /* subc. freq. */ + 0x03, 0x8c, /* subc. freq. */ + 0x04, 0x79, /* subc. freq. */ + 0x05, 0x26, /* subc. freq. */ + 0x06, 0x40, /* subc. phase */ +}; + +static const unsigned char init_ntsc[] = { + 0x00, MR060, /* MR0, NTSC enabled */ + 0x01, 0x00, /* MR1 */ + 0x02, 0x55, /* subc. freq. */ + 0x03, 0x55, /* subc. freq. */ + 0x04, 0x55, /* subc. freq. */ + 0x05, 0x25, /* subc. freq. */ + 0x06, 0x1a, /* subc. phase */ +}; + +static int +adv7175_command (struct i2c_client *client, + unsigned int cmd, + void *arg) +{ + struct adv7175 *encoder = i2c_get_clientdata(client); + + switch (cmd) { + + case 0: + /* This is just for testing!!! */ + adv7175_write_block(client, init_common, + sizeof(init_common)); + adv7175_write(client, 0x07, TR0MODE | TR0RST); + adv7175_write(client, 0x07, TR0MODE); + break; + + case ENCODER_GET_CAPABILITIES: + { + struct video_encoder_capability *cap = arg; + + cap->flags = VIDEO_ENCODER_PAL | + VIDEO_ENCODER_NTSC | + VIDEO_ENCODER_SECAM; /* well, hacky */ + cap->inputs = 2; + cap->outputs = 1; + } + break; + + case ENCODER_SET_NORM: + { + int iarg = *(int *) arg; + + switch (iarg) { + + case VIDEO_MODE_NTSC: + adv7175_write_block(client, init_ntsc, + sizeof(init_ntsc)); + if (encoder->input == 0) + adv7175_write(client, 0x0d, 0x4f); // Enable genlock + adv7175_write(client, 0x07, TR0MODE | TR0RST); + adv7175_write(client, 0x07, TR0MODE); + break; + + case VIDEO_MODE_PAL: + adv7175_write_block(client, init_pal, + sizeof(init_pal)); + if (encoder->input == 0) + adv7175_write(client, 0x0d, 0x4f); // Enable genlock + adv7175_write(client, 0x07, TR0MODE | TR0RST); + adv7175_write(client, 0x07, TR0MODE); + break; + + case VIDEO_MODE_SECAM: // WARNING! ADV7176 does not support SECAM. + /* This is an attempt to convert + * SECAM->PAL (typically it does not work + * due to genlock: when decoder is in SECAM + * and encoder in in PAL the subcarrier can + * not be syncronized with horizontal + * quency) */ + adv7175_write_block(client, init_pal, + sizeof(init_pal)); + if (encoder->input == 0) + adv7175_write(client, 0x0d, 0x49); // Disable genlock + adv7175_write(client, 0x07, TR0MODE | TR0RST); + adv7175_write(client, 0x07, TR0MODE); + break; + default: + dprintk(1, KERN_ERR "%s: illegal norm: %d\n", + I2C_NAME(client), iarg); + return -EINVAL; + + } + dprintk(1, KERN_INFO "%s: switched to %s\n", I2C_NAME(client), + norms[iarg]); + encoder->norm = iarg; + } + break; + + case ENCODER_SET_INPUT: + { + int iarg = *(int *) arg; + + /* RJ: *iarg = 0: input is from SAA7110 + *iarg = 1: input is from ZR36060 + *iarg = 2: color bar */ + + switch (iarg) { + + case 0: + adv7175_write(client, 0x01, 0x00); + + if (encoder->norm == VIDEO_MODE_NTSC) + set_subcarrier_freq(client, 1); + + adv7175_write(client, 0x0c, TR1CAPT); /* TR1 */ + if (encoder->norm == VIDEO_MODE_SECAM) + adv7175_write(client, 0x0d, 0x49); // Disable genlock + else + adv7175_write(client, 0x0d, 0x4f); // Enable genlock + adv7175_write(client, 0x07, TR0MODE | TR0RST); + adv7175_write(client, 0x07, TR0MODE); + //udelay(10); + break; + + case 1: + adv7175_write(client, 0x01, 0x00); + + if (encoder->norm == VIDEO_MODE_NTSC) + set_subcarrier_freq(client, 0); + + adv7175_write(client, 0x0c, TR1PLAY); /* TR1 */ + adv7175_write(client, 0x0d, 0x49); + adv7175_write(client, 0x07, TR0MODE | TR0RST); + adv7175_write(client, 0x07, TR0MODE); + //udelay(10); + break; + + case 2: + adv7175_write(client, 0x01, 0x80); + + if (encoder->norm == VIDEO_MODE_NTSC) + set_subcarrier_freq(client, 0); + + adv7175_write(client, 0x0d, 0x49); + adv7175_write(client, 0x07, TR0MODE | TR0RST); + adv7175_write(client, 0x07, TR0MODE); + //udelay(10); + break; + + default: + dprintk(1, KERN_ERR "%s: illegal input: %d\n", + I2C_NAME(client), iarg); + return -EINVAL; + + } + dprintk(1, KERN_INFO "%s: switched to %s\n", I2C_NAME(client), + inputs[iarg]); + encoder->input = iarg; + } + break; + + case ENCODER_SET_OUTPUT: + { + int *iarg = arg; + + /* not much choice of outputs */ + if (*iarg != 0) { + return -EINVAL; + } + } + break; + + case ENCODER_ENABLE_OUTPUT: + { + int *iarg = arg; + + encoder->enable = !!*iarg; + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ +static unsigned short normal_i2c[] = + { I2C_ADV7175 >> 1, (I2C_ADV7175 >> 1) + 1, + I2C_ADV7176 >> 1, (I2C_ADV7176 >> 1) + 1, + I2C_CLIENT_END +}; + +static unsigned short ignore = I2C_CLIENT_END; + +static struct i2c_client_address_data addr_data = { + .normal_i2c = normal_i2c, + .probe = &ignore, + .ignore = &ignore, +}; + +static struct i2c_driver i2c_driver_adv7175; + +static int +adv7175_detect_client (struct i2c_adapter *adapter, + int address, + int kind) +{ + int i; + struct i2c_client *client; + struct adv7175 *encoder; + char *dname; + + dprintk(1, + KERN_INFO + "adv7175.c: detecting adv7175 client on address 0x%x\n", + address << 1); + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == 0) + return -ENOMEM; + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver_adv7175; + if ((client->addr == I2C_ADV7175 >> 1) || + (client->addr == (I2C_ADV7175 >> 1) + 1)) { + dname = adv7175_name; + } else if ((client->addr == I2C_ADV7176 >> 1) || + (client->addr == (I2C_ADV7176 >> 1) + 1)) { + dname = adv7176_name; + } else { + /* We should never get here!!! */ + kfree(client); + return 0; + } + strlcpy(I2C_NAME(client), dname, sizeof(I2C_NAME(client))); + + encoder = kzalloc(sizeof(struct adv7175), GFP_KERNEL); + if (encoder == NULL) { + kfree(client); + return -ENOMEM; + } + encoder->norm = VIDEO_MODE_PAL; + encoder->input = 0; + encoder->enable = 1; + i2c_set_clientdata(client, encoder); + + i = i2c_attach_client(client); + if (i) { + kfree(client); + kfree(encoder); + return i; + } + + i = adv7175_write_block(client, init_common, sizeof(init_common)); + if (i >= 0) { + i = adv7175_write(client, 0x07, TR0MODE | TR0RST); + i = adv7175_write(client, 0x07, TR0MODE); + i = adv7175_read(client, 0x12); + dprintk(1, KERN_INFO "%s_attach: rev. %d at 0x%x\n", + I2C_NAME(client), i & 1, client->addr << 1); + } + if (i < 0) { + dprintk(1, KERN_ERR "%s_attach: init error 0x%x\n", + I2C_NAME(client), i); + } + + return 0; +} + +static int +adv7175_attach_adapter (struct i2c_adapter *adapter) +{ + dprintk(1, + KERN_INFO + "adv7175.c: starting probe for adapter %s (0x%x)\n", + I2C_NAME(adapter), adapter->id); + return i2c_probe(adapter, &addr_data, &adv7175_detect_client); +} + +static int +adv7175_detach_client (struct i2c_client *client) +{ + struct adv7175 *encoder = i2c_get_clientdata(client); + int err; + + err = i2c_detach_client(client); + if (err) { + return err; + } + + kfree(encoder); + kfree(client); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver i2c_driver_adv7175 = { + .driver = { + .name = "adv7175", /* name */ + }, + + .id = I2C_DRIVERID_ADV7175, + + .attach_adapter = adv7175_attach_adapter, + .detach_client = adv7175_detach_client, + .command = adv7175_command, +}; + +static int __init +adv7175_init (void) +{ + return i2c_add_driver(&i2c_driver_adv7175); +} + +static void __exit +adv7175_exit (void) +{ + i2c_del_driver(&i2c_driver_adv7175); +} + +module_init(adv7175_init); +module_exit(adv7175_exit); diff --git a/linux/drivers/media/video/arv.c b/linux/drivers/media/video/arv.c new file mode 100644 index 000000000..aec57f485 --- /dev/null +++ b/linux/drivers/media/video/arv.c @@ -0,0 +1,913 @@ +/* + * Colour AR M64278(VGA) driver for Video4Linux + * + * Copyright (C) 2003 Takeo Takahashi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Some code is taken from AR driver sample program for M3T-M32700UT. + * + * AR driver sample (M32R SDK): + * Copyright (c) 2003 RENESAS TECHNOROGY CORPORATION + * AND RENESAS SOLUTIONS CORPORATION + * All Rights Reserved. + * + * 2003-09-01: Support w3cam by Takeo Takahashi + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "compat.h" +#include +#include + +#include +#include +#include +#include +#include + +#if 0 +#define DEBUG(n, args...) printk(args) +#define CHECK_LOST 1 +#else +#define DEBUG(n, args...) +#define CHECK_LOST 0 +#endif + +/* + * USE_INT is always 0, interrupt mode is not available + * on linux due to lack of speed + */ +#define USE_INT 0 /* Don't modify */ + +#define VERSION "0.03" + +#define ar_inl(addr) inl((unsigned long)(addr)) +#define ar_outl(val, addr) outl((unsigned long)(val),(unsigned long)(addr)) + +extern struct cpuinfo_m32r boot_cpu_data; + +/* + * CCD pixel size + * Note that M32700UT does not support CIF mode, but QVGA is + * supported by M32700UT hardware using VGA mode of AR LSI. + * + * Supported: VGA (Normal mode, Interlace mode) + * QVGA (Always Interlace mode of VGA) + * + */ +#define AR_WIDTH_VGA 640 +#define AR_HEIGHT_VGA 480 +#define AR_WIDTH_QVGA 320 +#define AR_HEIGHT_QVGA 240 +#define MIN_AR_WIDTH AR_WIDTH_QVGA +#define MIN_AR_HEIGHT AR_HEIGHT_QVGA +#define MAX_AR_WIDTH AR_WIDTH_VGA +#define MAX_AR_HEIGHT AR_HEIGHT_VGA + +/* bits & bytes per pixel */ +#define AR_BITS_PER_PIXEL 16 +#define AR_BYTES_PER_PIXEL (AR_BITS_PER_PIXEL/8) + +/* line buffer size */ +#define AR_LINE_BYTES_VGA (AR_WIDTH_VGA * AR_BYTES_PER_PIXEL) +#define AR_LINE_BYTES_QVGA (AR_WIDTH_QVGA * AR_BYTES_PER_PIXEL) +#define MAX_AR_LINE_BYTES AR_LINE_BYTES_VGA + +/* frame size & type */ +#define AR_FRAME_BYTES_VGA \ + (AR_WIDTH_VGA * AR_HEIGHT_VGA * AR_BYTES_PER_PIXEL) +#define AR_FRAME_BYTES_QVGA \ + (AR_WIDTH_QVGA * AR_HEIGHT_QVGA * AR_BYTES_PER_PIXEL) +#define MAX_AR_FRAME_BYTES \ + (MAX_AR_WIDTH * MAX_AR_HEIGHT * AR_BYTES_PER_PIXEL) + +#define AR_MAX_FRAME 15 + +/* capture size */ +#define AR_SIZE_VGA 0 +#define AR_SIZE_QVGA 1 + +/* capture mode */ +#define AR_MODE_INTERLACE 0 +#define AR_MODE_NORMAL 1 + +struct ar_device { + struct video_device *vdev; + unsigned int start_capture; /* duaring capture in INT. mode. */ +#if USE_INT + unsigned char *line_buff; /* DMA line buffer */ +#endif + unsigned char *frame[MAX_AR_HEIGHT]; /* frame data */ + short size; /* capture size */ + short mode; /* capture mode */ + int width, height; + int frame_bytes, line_bytes; + wait_queue_head_t wait; + struct mutex lock; +}; + +static int video_nr = -1; /* video device number (first free) */ +static unsigned char yuv[MAX_AR_FRAME_BYTES]; + +/* module parameters */ +/* default frequency */ +#define DEFAULT_FREQ 50 /* 50 or 75 (MHz) is available as BCLK */ +static int freq = DEFAULT_FREQ; /* BCLK: available 50 or 70 (MHz) */ +static int vga = 0; /* default mode(0:QVGA mode, other:VGA mode) */ +static int vga_interlace = 0; /* 0 is normal mode for, else interlace mode */ +module_param(freq, int, 0); +module_param(vga, int, 0); +module_param(vga_interlace, int, 0); + +static int ar_initialize(struct video_device *dev); + +static inline void wait_for_vsync(void) +{ + while (ar_inl(ARVCR0) & ARVCR0_VDS) /* wait for VSYNC */ + cpu_relax(); + while (!(ar_inl(ARVCR0) & ARVCR0_VDS)) /* wait for VSYNC */ + cpu_relax(); +} + +static inline void wait_acknowledge(void) +{ + int i; + + for (i = 0; i < 1000; i++) + cpu_relax(); + while (ar_inl(PLDI2CSTS) & PLDI2CSTS_NOACK) + cpu_relax(); +} + +/******************************************************************* + * I2C functions + *******************************************************************/ +void iic(int n, unsigned long addr, unsigned long data1, unsigned long data2, + unsigned long data3) +{ + int i; + + /* Slave Address */ + ar_outl(addr, PLDI2CDATA); + wait_for_vsync(); + + /* Start */ + ar_outl(1, PLDI2CCND); + wait_acknowledge(); + + /* Transfer data 1 */ + ar_outl(data1, PLDI2CDATA); + wait_for_vsync(); + ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN); + wait_acknowledge(); + + /* Transfer data 2 */ + ar_outl(data2, PLDI2CDATA); + wait_for_vsync(); + ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN); + wait_acknowledge(); + + if (n == 3) { + /* Transfer data 3 */ + ar_outl(data3, PLDI2CDATA); + wait_for_vsync(); + ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN); + wait_acknowledge(); + } + + /* Stop */ + for (i = 0; i < 100; i++) + cpu_relax(); + ar_outl(2, PLDI2CCND); + ar_outl(2, PLDI2CCND); + + while (ar_inl(PLDI2CSTS) & PLDI2CSTS_BB) + cpu_relax(); +} + + +void init_iic(void) +{ + DEBUG(1, "init_iic:\n"); + + /* + * ICU Setting (iic) + */ + /* I2C Setting */ + ar_outl(0x0, PLDI2CCR); /* I2CCR Disable */ + ar_outl(0x0300, PLDI2CMOD); /* I2CMOD ACK/8b-data/7b-addr/auto */ + ar_outl(0x1, PLDI2CACK); /* I2CACK ACK */ + + /* I2C CLK */ + /* 50MH-100k */ + if (freq == 75) { + ar_outl(369, PLDI2CFREQ); /* BCLK = 75MHz */ + } else if (freq == 50) { + ar_outl(244, PLDI2CFREQ); /* BCLK = 50MHz */ + } else { + ar_outl(244, PLDI2CFREQ); /* default: BCLK = 50MHz */ + } + ar_outl(0x1, PLDI2CCR); /* I2CCR Enable */ +} + +/************************************************************************** + * + * Video4Linux Interface functions + * + **************************************************************************/ + +static inline void disable_dma(void) +{ + ar_outl(0x8000, M32R_DMAEN_PORTL); /* disable DMA0 */ +} + +static inline void enable_dma(void) +{ + ar_outl(0x8080, M32R_DMAEN_PORTL); /* enable DMA0 */ +} + +static inline void clear_dma_status(void) +{ + ar_outl(0x8000, M32R_DMAEDET_PORTL); /* clear status */ +} + +static inline void wait_for_vertical_sync(int exp_line) +{ +#if CHECK_LOST + int tmout = 10000; /* FIXME */ + int l; + + /* + * check HCOUNT because we cannot check vertical sync. + */ + for (; tmout >= 0; tmout--) { + l = ar_inl(ARVHCOUNT); + if (l == exp_line) + break; + } + if (tmout < 0) + printk("arv: lost %d -> %d\n", exp_line, l); +#else + while (ar_inl(ARVHCOUNT) != exp_line) + cpu_relax(); +#endif +} + +static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + struct video_device *v = video_devdata(file); + struct ar_device *ar = v->priv; + long ret = ar->frame_bytes; /* return read bytes */ + unsigned long arvcr1 = 0; + unsigned long flags; + unsigned char *p; + int h, w; + unsigned char *py, *pu, *pv; +#if ! USE_INT + int l; +#endif + + DEBUG(1, "ar_read()\n"); + + if (ar->size == AR_SIZE_QVGA) + arvcr1 |= ARVCR1_QVGA; + if (ar->mode == AR_MODE_NORMAL) + arvcr1 |= ARVCR1_NORMAL; + + mutex_lock(&ar->lock); + +#if USE_INT + local_irq_save(flags); + disable_dma(); + ar_outl(0xa1871300, M32R_DMA0CR0_PORTL); + ar_outl(0x01000000, M32R_DMA0CR1_PORTL); + + /* set AR FIFO address as source(BSEL5) */ + ar_outl(ARDATA32, M32R_DMA0CSA_PORTL); + ar_outl(ARDATA32, M32R_DMA0RSA_PORTL); + ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); /* destination addr. */ + ar_outl(ar->line_buff, M32R_DMA0RDA_PORTL); /* reload address */ + ar_outl(ar->line_bytes, M32R_DMA0CBCUT_PORTL); /* byte count (bytes) */ + ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL); /* reload count (bytes) */ + + /* + * Okey , kicks AR LSI to invoke an interrupt + */ + ar->start_capture = 0; + ar_outl(arvcr1 | ARVCR1_HIEN, ARVCR1); + local_irq_restore(flags); + /* .... AR interrupts .... */ + interruptible_sleep_on(&ar->wait); + if (signal_pending(current)) { + printk("arv: interrupted while get frame data.\n"); + ret = -EINTR; + goto out_up; + } +#else /* ! USE_INT */ + /* polling */ + ar_outl(arvcr1, ARVCR1); + disable_dma(); + ar_outl(0x8000, M32R_DMAEDET_PORTL); + ar_outl(0xa0861300, M32R_DMA0CR0_PORTL); + ar_outl(0x01000000, M32R_DMA0CR1_PORTL); + ar_outl(ARDATA32, M32R_DMA0CSA_PORTL); + ar_outl(ARDATA32, M32R_DMA0RSA_PORTL); + ar_outl(ar->line_bytes, M32R_DMA0CBCUT_PORTL); + ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL); + + local_irq_save(flags); + while (ar_inl(ARVHCOUNT) != 0) /* wait for 0 */ + cpu_relax(); + if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) { + for (h = 0; h < ar->height; h++) { + wait_for_vertical_sync(h); + if (h < (AR_HEIGHT_VGA/2)) + l = h << 1; + else + l = (((h - (AR_HEIGHT_VGA/2)) << 1) + 1); + ar_outl(virt_to_phys(ar->frame[l]), M32R_DMA0CDA_PORTL); + enable_dma(); + while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000)) + cpu_relax(); + disable_dma(); + clear_dma_status(); + ar_outl(0xa0861300, M32R_DMA0CR0_PORTL); + } + } else { + for (h = 0; h < ar->height; h++) { + wait_for_vertical_sync(h); + ar_outl(virt_to_phys(ar->frame[h]), M32R_DMA0CDA_PORTL); + enable_dma(); + while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000)) + cpu_relax(); + disable_dma(); + clear_dma_status(); + ar_outl(0xa0861300, M32R_DMA0CR0_PORTL); + } + } + local_irq_restore(flags); +#endif /* ! USE_INT */ + + /* + * convert YUV422 to YUV422P + * +--------------------+ + * | Y0,Y1,... | + * | ..............Yn | + * +--------------------+ + * | U0,U1,........Un | + * +--------------------+ + * | V0,V1,........Vn | + * +--------------------+ + */ + py = yuv; + pu = py + (ar->frame_bytes / 2); + pv = pu + (ar->frame_bytes / 4); + for (h = 0; h < ar->height; h++) { + p = ar->frame[h]; + for (w = 0; w < ar->line_bytes; w += 4) { + *py++ = *p++; + *pu++ = *p++; + *py++ = *p++; + *pv++ = *p++; + } + } + if (copy_to_user(buf, yuv, ar->frame_bytes)) { + printk("arv: failed while copy_to_user yuv.\n"); + ret = -EFAULT; + goto out_up; + } + DEBUG(1, "ret = %d\n", ret); +out_up: + mutex_unlock(&ar->lock); + return ret; +} + +static int ar_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct ar_device *ar = dev->priv; + + DEBUG(1, "ar_ioctl()\n"); + switch(cmd) { + case VIDIOCGCAP: + { + struct video_capability *b = arg; + DEBUG(1, "VIDIOCGCAP:\n"); + strcpy(b->name, ar->vdev->name); + b->type = VID_TYPE_CAPTURE; + b->channels = 0; + b->audios = 0; + b->maxwidth = MAX_AR_WIDTH; + b->maxheight = MAX_AR_HEIGHT; + b->minwidth = MIN_AR_WIDTH; + b->minheight = MIN_AR_HEIGHT; + return 0; + } + case VIDIOCGCHAN: + DEBUG(1, "VIDIOCGCHAN:\n"); + return 0; + case VIDIOCSCHAN: + DEBUG(1, "VIDIOCSCHAN:\n"); + return 0; + case VIDIOCGTUNER: + DEBUG(1, "VIDIOCGTUNER:\n"); + return 0; + case VIDIOCSTUNER: + DEBUG(1, "VIDIOCSTUNER:\n"); + return 0; + case VIDIOCGPICT: + DEBUG(1, "VIDIOCGPICT:\n"); + return 0; + case VIDIOCSPICT: + DEBUG(1, "VIDIOCSPICT:\n"); + return 0; + case VIDIOCCAPTURE: + DEBUG(1, "VIDIOCCAPTURE:\n"); + return -EINVAL; + case VIDIOCGWIN: + { + struct video_window *w = arg; + DEBUG(1, "VIDIOCGWIN:\n"); + memset(w, 0, sizeof(w)); + w->width = ar->width; + w->height = ar->height; + return 0; + } + case VIDIOCSWIN: + { + struct video_window *w = arg; + DEBUG(1, "VIDIOCSWIN:\n"); + if ((w->width != AR_WIDTH_VGA || w->height != AR_HEIGHT_VGA) && + (w->width != AR_WIDTH_QVGA || w->height != AR_HEIGHT_QVGA)) + return -EINVAL; + + mutex_lock(&ar->lock); + ar->width = w->width; + ar->height = w->height; + if (ar->width == AR_WIDTH_VGA) { + ar->size = AR_SIZE_VGA; + ar->frame_bytes = AR_FRAME_BYTES_VGA; + ar->line_bytes = AR_LINE_BYTES_VGA; + if (vga_interlace) + ar->mode = AR_MODE_INTERLACE; + else + ar->mode = AR_MODE_NORMAL; + } else { + ar->size = AR_SIZE_QVGA; + ar->frame_bytes = AR_FRAME_BYTES_QVGA; + ar->line_bytes = AR_LINE_BYTES_QVGA; + ar->mode = AR_MODE_INTERLACE; + } + mutex_unlock(&ar->lock); + return 0; + } + case VIDIOCGFBUF: + DEBUG(1, "VIDIOCGFBUF:\n"); + return -EINVAL; + case VIDIOCSFBUF: + DEBUG(1, "VIDIOCSFBUF:\n"); + return -EINVAL; + case VIDIOCKEY: + DEBUG(1, "VIDIOCKEY:\n"); + return 0; + case VIDIOCGFREQ: + DEBUG(1, "VIDIOCGFREQ:\n"); + return -EINVAL; + case VIDIOCSFREQ: + DEBUG(1, "VIDIOCSFREQ:\n"); + return -EINVAL; + case VIDIOCGAUDIO: + DEBUG(1, "VIDIOCGAUDIO:\n"); + return -EINVAL; + case VIDIOCSAUDIO: + DEBUG(1, "VIDIOCSAUDIO:\n"); + return -EINVAL; + case VIDIOCSYNC: + DEBUG(1, "VIDIOCSYNC:\n"); + return -EINVAL; + case VIDIOCMCAPTURE: + DEBUG(1, "VIDIOCMCAPTURE:\n"); + return -EINVAL; + case VIDIOCGMBUF: + DEBUG(1, "VIDIOCGMBUF:\n"); + return -EINVAL; + case VIDIOCGUNIT: + DEBUG(1, "VIDIOCGUNIT:\n"); + return -EINVAL; + case VIDIOCGCAPTURE: + DEBUG(1, "VIDIOCGCAPTURE:\n"); + return -EINVAL; + case VIDIOCSCAPTURE: + DEBUG(1, "VIDIOCSCAPTURE:\n"); + return -EINVAL; + case VIDIOCSPLAYMODE: + DEBUG(1, "VIDIOCSPLAYMODE:\n"); + return -EINVAL; + case VIDIOCSWRITEMODE: + DEBUG(1, "VIDIOCSWRITEMODE:\n"); + return -EINVAL; + case VIDIOCGPLAYINFO: + DEBUG(1, "VIDIOCGPLAYINFO:\n"); + return -EINVAL; + case VIDIOCSMICROCODE: + DEBUG(1, "VIDIOCSMICROCODE:\n"); + return -EINVAL; + case VIDIOCGVBIFMT: + DEBUG(1, "VIDIOCGVBIFMT:\n"); + return -EINVAL; + case VIDIOCSVBIFMT: + DEBUG(1, "VIDIOCSVBIFMT:\n"); + return -EINVAL; + default: + DEBUG(1, "Unknown ioctl(0x%08x)\n", cmd); + return -ENOIOCTLCMD; + } + return 0; +} + +static int ar_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, ar_do_ioctl); +} + +#if USE_INT +/* + * Interrupt handler + */ +static void ar_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + struct ar_device *ar = dev; + unsigned int line_count; + unsigned int line_number; + unsigned int arvcr1; + + line_count = ar_inl(ARVHCOUNT); /* line number */ + if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) { + /* operations for interlace mode */ + if ( line_count < (AR_HEIGHT_VGA/2) ) /* even line */ + line_number = (line_count << 1); + else /* odd line */ + line_number = + (((line_count - (AR_HEIGHT_VGA/2)) << 1) + 1); + } else { + line_number = line_count; + } + + if (line_number == 0) { + /* + * It is an interrupt for line 0. + * we have to start capture. + */ + disable_dma(); +#if 0 + ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); /* needless? */ +#endif + memcpy(ar->frame[0], ar->line_buff, ar->line_bytes); +#if 0 + ar_outl(0xa1861300, M32R_DMA0CR0_PORTL); +#endif + enable_dma(); + ar->start_capture = 1; /* during capture */ + return; + } + + if (ar->start_capture == 1 && line_number <= (ar->height - 1)) { + disable_dma(); + memcpy(ar->frame[line_number], ar->line_buff, ar->line_bytes); + + /* + * if captured all line of a frame, disable AR interrupt + * and wake a process up. + */ + if (line_number == (ar->height - 1)) { /* end of line */ + + ar->start_capture = 0; + + /* disable AR interrupt request */ + arvcr1 = ar_inl(ARVCR1); + arvcr1 &= ~ARVCR1_HIEN; /* clear int. flag */ + ar_outl(arvcr1, ARVCR1); /* disable */ + wake_up_interruptible(&ar->wait); + } else { +#if 0 + ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); + ar_outl(0xa1861300, M32R_DMA0CR0_PORTL); +#endif + enable_dma(); + } + } +} +#endif + +/* + * ar_initialize() + * ar_initialize() is called by video_register_device() and + * initializes AR LSI and peripherals. + * + * -1 is returned in all failures. + * 0 is returned in success. + * + */ +static int ar_initialize(struct video_device *dev) +{ + struct ar_device *ar = dev->priv; + unsigned long cr = 0; + int i,found=0; + + DEBUG(1, "ar_initialize:\n"); + + /* + * initialize AR LSI + */ + ar_outl(0, ARVCR0); /* assert reset of AR LSI */ + for (i = 0; i < 0x18; i++) /* wait for over 10 cycles @ 27MHz */ + cpu_relax(); + ar_outl(ARVCR0_RST, ARVCR0); /* negate reset of AR LSI (enable) */ + for (i = 0; i < 0x40d; i++) /* wait for over 420 cycles @ 27MHz */ + cpu_relax(); + + /* AR uses INT3 of CPU as interrupt pin. */ + ar_outl(ARINTSEL_INT3, ARINTSEL); + + if (ar->size == AR_SIZE_QVGA) + cr |= ARVCR1_QVGA; + if (ar->mode == AR_MODE_NORMAL) + cr |= ARVCR1_NORMAL; + ar_outl(cr, ARVCR1); + + /* + * Initialize IIC so that CPU can communicate with AR LSI, + * and send boot commands to AR LSI. + */ + init_iic(); + + for (i = 0; i < 0x100000; i++) { /* > 0xa1d10, 56ms */ + if ((ar_inl(ARVCR0) & ARVCR0_VDS)) { /* VSYNC */ + found = 1; + break; + } + } + + if (found == 0) + return -ENODEV; + + printk("arv: Initializing "); + + iic(2,0x78,0x11,0x01,0x00); /* start */ + iic(3,0x78,0x12,0x00,0x06); + iic(3,0x78,0x12,0x12,0x30); + iic(3,0x78,0x12,0x15,0x58); + iic(3,0x78,0x12,0x17,0x30); + printk("."); + iic(3,0x78,0x12,0x1a,0x97); + iic(3,0x78,0x12,0x1b,0xff); + iic(3,0x78,0x12,0x1c,0xff); + iic(3,0x78,0x12,0x26,0x10); + iic(3,0x78,0x12,0x27,0x00); + printk("."); + iic(2,0x78,0x34,0x02,0x00); + iic(2,0x78,0x7a,0x10,0x00); + iic(2,0x78,0x80,0x39,0x00); + iic(2,0x78,0x81,0xe6,0x00); + iic(2,0x78,0x8d,0x00,0x00); + printk("."); + iic(2,0x78,0x8e,0x0c,0x00); + iic(2,0x78,0x8f,0x00,0x00); +#if 0 + iic(2,0x78,0x90,0x00,0x00); /* AWB on=1 off=0 */ +#endif + iic(2,0x78,0x93,0x01,0x00); + iic(2,0x78,0x94,0xcd,0x00); + iic(2,0x78,0x95,0x00,0x00); + printk("."); + iic(2,0x78,0x96,0xa0,0x00); + iic(2,0x78,0x97,0x00,0x00); + iic(2,0x78,0x98,0x60,0x00); + iic(2,0x78,0x99,0x01,0x00); + iic(2,0x78,0x9a,0x19,0x00); + printk("."); + iic(2,0x78,0x9b,0x02,0x00); + iic(2,0x78,0x9c,0xe8,0x00); + iic(2,0x78,0x9d,0x02,0x00); + iic(2,0x78,0x9e,0x2e,0x00); + iic(2,0x78,0xb8,0x78,0x00); + iic(2,0x78,0xba,0x05,0x00); +#if 0 + iic(2,0x78,0x83,0x8c,0x00); /* brightness */ +#endif + printk("."); + + /* color correction */ + iic(3,0x78,0x49,0x00,0x95); /* a */ + iic(3,0x78,0x49,0x01,0x96); /* b */ + iic(3,0x78,0x49,0x03,0x85); /* c */ + iic(3,0x78,0x49,0x04,0x97); /* d */ + iic(3,0x78,0x49,0x02,0x7e); /* e(Lo) */ + iic(3,0x78,0x49,0x05,0xa4); /* f(Lo) */ + iic(3,0x78,0x49,0x06,0x04); /* e(Hi) */ + iic(3,0x78,0x49,0x07,0x04); /* e(Hi) */ + iic(2,0x78,0x48,0x01,0x00); /* on=1 off=0 */ + + printk("."); + iic(2,0x78,0x11,0x00,0x00); /* end */ + printk(" done\n"); + return 0; +} + + +void ar_release(struct video_device *vfd) +{ + struct ar_device *ar = vfd->priv; + mutex_lock(&ar->lock); + video_device_release(vfd); +} + +/**************************************************************************** + * + * Video4Linux Module functions + * + ****************************************************************************/ +static struct file_operations ar_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .read = ar_read, + .ioctl = ar_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device ar_template = { + .owner = THIS_MODULE, + .name = "Colour AR VGA", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_ARV, + .fops = &ar_fops, + .release = ar_release, + .minor = -1, +}; + +#define ALIGN4(x) ((((int)(x)) & 0x3) == 0) +static struct ar_device ardev; + +static int __init ar_init(void) +{ + struct ar_device *ar; + int ret; + int i; + + DEBUG(1, "ar_init:\n"); + ret = -EIO; + printk(KERN_INFO "arv: Colour AR VGA driver %s\n", VERSION); + + ar = &ardev; + memset(ar, 0, sizeof(struct ar_device)); + +#if USE_INT + /* allocate a DMA buffer for 1 line. */ + ar->line_buff = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL | GFP_DMA); + if (ar->line_buff == NULL || ! ALIGN4(ar->line_buff)) { + printk("arv: buffer allocation failed for DMA.\n"); + ret = -ENOMEM; + goto out_end; + } +#endif + /* allocate buffers for a frame */ + for (i = 0; i < MAX_AR_HEIGHT; i++) { + ar->frame[i] = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL); + if (ar->frame[i] == NULL || ! ALIGN4(ar->frame[i])) { + printk("arv: buffer allocation failed for frame.\n"); + ret = -ENOMEM; + goto out_line_buff; + } + } + + ar->vdev = video_device_alloc(); + if (!ar->vdev) { + printk(KERN_ERR "arv: video_device_alloc() failed\n"); + return -ENOMEM; + } + memcpy(ar->vdev, &ar_template, sizeof(ar_template)); + ar->vdev->priv = ar; + + if (vga) { + ar->width = AR_WIDTH_VGA; + ar->height = AR_HEIGHT_VGA; + ar->size = AR_SIZE_VGA; + ar->frame_bytes = AR_FRAME_BYTES_VGA; + ar->line_bytes = AR_LINE_BYTES_VGA; + if (vga_interlace) + ar->mode = AR_MODE_INTERLACE; + else + ar->mode = AR_MODE_NORMAL; + } else { + ar->width = AR_WIDTH_QVGA; + ar->height = AR_HEIGHT_QVGA; + ar->size = AR_SIZE_QVGA; + ar->frame_bytes = AR_FRAME_BYTES_QVGA; + ar->line_bytes = AR_LINE_BYTES_QVGA; + ar->mode = AR_MODE_INTERLACE; + } + mutex_init(&ar->lock); + init_waitqueue_head(&ar->wait); + +#if USE_INT + if (request_irq(M32R_IRQ_INT3, ar_interrupt, 0, "arv", ar)) { + printk("arv: request_irq(%d) failed.\n", M32R_IRQ_INT3); + ret = -EIO; + goto out_irq; + } +#endif + + if (ar_initialize(ar->vdev) != 0) { + printk("arv: M64278 not found.\n"); + ret = -ENODEV; + goto out_dev; + } + + /* + * ok, we can initialize h/w according to parameters, + * so register video device as a frame grabber type. + * device is named "video[0-64]". + * video_register_device() initializes h/w using ar_initialize(). + */ + if (video_register_device(ar->vdev, VFL_TYPE_GRABBER, video_nr) != 0) { + /* return -1, -ENFILE(full) or others */ + printk("arv: register video (Colour AR) failed.\n"); + ret = -ENODEV; + goto out_dev; + } + + printk("video%d: Found M64278 VGA (IRQ %d, Freq %dMHz).\n", + ar->vdev->minor, M32R_IRQ_INT3, freq); + + return 0; + +out_dev: +#if USE_INT + free_irq(M32R_IRQ_INT3, ar); + +out_irq: +#endif + for (i = 0; i < MAX_AR_HEIGHT; i++) + kfree(ar->frame[i]); + +out_line_buff: +#if USE_INT + kfree(ar->line_buff); + +out_end: +#endif + return ret; +} + + +static int __init ar_init_module(void) +{ + freq = (boot_cpu_data.bus_clock / 1000000); + printk("arv: Bus clock %d\n", freq); + if (freq != 50 && freq != 75) + freq = DEFAULT_FREQ; + return ar_init(); +} + +static void __exit ar_cleanup_module(void) +{ + struct ar_device *ar; + int i; + + ar = &ardev; + video_unregister_device(ar->vdev); +#if USE_INT + free_irq(M32R_IRQ_INT3, ar); +#endif + for (i = 0; i < MAX_AR_HEIGHT; i++) + kfree(ar->frame[i]); +#if USE_INT + kfree(ar->line_buff); +#endif +} + +module_init(ar_init_module); +module_exit(ar_cleanup_module); + +MODULE_AUTHOR("Takeo Takahashi "); +MODULE_DESCRIPTION("Colour AR M64278(VGA) for Video4Linux"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/bt819.c b/linux/drivers/media/video/bt819.c new file mode 100644 index 000000000..570d5997c --- /dev/null +++ b/linux/drivers/media/video/bt819.c @@ -0,0 +1,643 @@ +/* + * bt819 - BT819A VideoStream Decoder (Rockwell Part) + * + * Copyright (C) 1999 Mike Bernson + * Copyright (C) 1998 Dave Perks + * + * Modifications for LML33/DC10plus unified driver + * Copyright (C) 2000 Serguei Miridonov + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (9/9/2002) + * + * This code was modify/ported from the saa7111 driver written + * by Dave Perks. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compat.h" +#include +#include + +MODULE_DESCRIPTION("Brooktree-819 video decoder driver"); +MODULE_AUTHOR("Mike Bernson & Dave Perks"); +MODULE_LICENSE("GPL"); + +#include + +#define I2C_NAME(s) (s)->name + +#include + +static int debug = 0; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define dprintk(num, format, args...) \ + do { \ + if (debug >= num) \ + printk(format, ##args); \ + } while (0) + +/* ----------------------------------------------------------------------- */ + +struct bt819 { + unsigned char reg[32]; + + int initialized; + int norm; + int input; + int enable; + int bright; + int contrast; + int hue; + int sat; +}; + +struct timing { + int hactive; + int hdelay; + int vactive; + int vdelay; + int hscale; + int vscale; +}; + +/* for values, see the bt819 datasheet */ +static struct timing timing_data[] = { + {864 - 24, 20, 625 - 2, 1, 0x0504, 0x0000}, + {858 - 24, 20, 525 - 2, 1, 0x00f8, 0x0000}, +}; + +#define I2C_BT819 0x8a + +/* ----------------------------------------------------------------------- */ + +static inline int +bt819_write (struct i2c_client *client, + u8 reg, + u8 value) +{ + struct bt819 *decoder = i2c_get_clientdata(client); + + decoder->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int +bt819_setbit (struct i2c_client *client, + u8 reg, + u8 bit, + u8 value) +{ + struct bt819 *decoder = i2c_get_clientdata(client); + + return bt819_write(client, reg, + (decoder-> + reg[reg] & ~(1 << bit)) | + (value ? (1 << bit) : 0)); +} + +static int +bt819_write_block (struct i2c_client *client, + const u8 *data, + unsigned int len) +{ + int ret = -1; + u8 reg; + + /* the bt819 has an autoincrement function, use it if + * the adapter understands raw I2C */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + /* do raw I2C, not smbus compatible */ + struct bt819 *decoder = i2c_get_clientdata(client); + u8 block_data[32]; + int block_len; + + while (len >= 2) { + block_len = 0; + block_data[block_len++] = reg = data[0]; + do { + block_data[block_len++] = + decoder->reg[reg++] = data[1]; + len -= 2; + data += 2; + } while (len >= 2 && data[0] == reg && + block_len < 32); + if ((ret = i2c_master_send(client, block_data, + block_len)) < 0) + break; + } + } else { + /* do some slow I2C emulation kind of thing */ + while (len >= 2) { + reg = *data++; + if ((ret = bt819_write(client, reg, *data++)) < 0) + break; + len -= 2; + } + } + + return ret; +} + +static inline int +bt819_read (struct i2c_client *client, + u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int +bt819_init (struct i2c_client *client) +{ + struct bt819 *decoder = i2c_get_clientdata(client); + + static unsigned char init[] = { + //0x1f, 0x00, /* Reset */ + 0x01, 0x59, /* 0x01 input format */ + 0x02, 0x00, /* 0x02 temporal decimation */ + 0x03, 0x12, /* 0x03 Cropping msb */ + 0x04, 0x16, /* 0x04 Vertical Delay, lsb */ + 0x05, 0xe0, /* 0x05 Vertical Active lsb */ + 0x06, 0x80, /* 0x06 Horizontal Delay lsb */ + 0x07, 0xd0, /* 0x07 Horizontal Active lsb */ + 0x08, 0x00, /* 0x08 Horizontal Scaling msb */ + 0x09, 0xf8, /* 0x09 Horizontal Scaling lsb */ + 0x0a, 0x00, /* 0x0a Brightness control */ + 0x0b, 0x30, /* 0x0b Miscellaneous control */ + 0x0c, 0xd8, /* 0x0c Luma Gain lsb */ + 0x0d, 0xfe, /* 0x0d Chroma Gain (U) lsb */ + 0x0e, 0xb4, /* 0x0e Chroma Gain (V) msb */ + 0x0f, 0x00, /* 0x0f Hue control */ + 0x12, 0x04, /* 0x12 Output Format */ + 0x13, 0x20, /* 0x13 Vertial Scaling msb 0x00 + chroma comb OFF, line drop scaling, interlace scaling + BUG? Why does turning the chroma comb on fuck up color? + Bug in the bt819 stepping on my board? + */ + 0x14, 0x00, /* 0x14 Vertial Scaling lsb */ + 0x16, 0x07, /* 0x16 Video Timing Polarity + ACTIVE=active low + FIELD: high=odd, + vreset=active high, + hreset=active high */ + 0x18, 0x68, /* 0x18 AGC Delay */ + 0x19, 0x5d, /* 0x19 Burst Gate Delay */ + 0x1a, 0x80, /* 0x1a ADC Interface */ + }; + + struct timing *timing = &timing_data[decoder->norm]; + + init[0x03 * 2 - 1] = + (((timing->vdelay >> 8) & 0x03) << 6) | (((timing-> + vactive >> 8) & + 0x03) << 4) | + (((timing->hdelay >> 8) & 0x03) << 2) | ((timing-> + hactive >> 8) & + 0x03); + init[0x04 * 2 - 1] = timing->vdelay & 0xff; + init[0x05 * 2 - 1] = timing->vactive & 0xff; + init[0x06 * 2 - 1] = timing->hdelay & 0xff; + init[0x07 * 2 - 1] = timing->hactive & 0xff; + init[0x08 * 2 - 1] = timing->hscale >> 8; + init[0x09 * 2 - 1] = timing->hscale & 0xff; + /* 0x15 in array is address 0x19 */ + init[0x15 * 2 - 1] = (decoder->norm == 0) ? 115 : 93; /* Chroma burst delay */ + /* reset */ + bt819_write(client, 0x1f, 0x00); + mdelay(1); + + /* init */ + return bt819_write_block(client, init, sizeof(init)); + +} + +/* ----------------------------------------------------------------------- */ + +static int +bt819_command (struct i2c_client *client, + unsigned int cmd, + void *arg) +{ + int temp; + + struct bt819 *decoder = i2c_get_clientdata(client); + + if (!decoder->initialized) { // First call to bt819_init could be + bt819_init(client); // without #FRST = 0 + decoder->initialized = 1; + } + + switch (cmd) { + + case 0: + /* This is just for testing!!! */ + bt819_init(client); + break; + + case DECODER_GET_CAPABILITIES: + { + struct video_decoder_capability *cap = arg; + + cap->flags = VIDEO_DECODER_PAL | + VIDEO_DECODER_NTSC | + VIDEO_DECODER_AUTO | + VIDEO_DECODER_CCIR; + cap->inputs = 8; + cap->outputs = 1; + } + break; + + case DECODER_GET_STATUS: + { + int *iarg = arg; + int status; + int res; + + status = bt819_read(client, 0x00); + res = 0; + if ((status & 0x80)) { + res |= DECODER_STATUS_GOOD; + } + switch (decoder->norm) { + case VIDEO_MODE_NTSC: + res |= DECODER_STATUS_NTSC; + break; + case VIDEO_MODE_PAL: + res |= DECODER_STATUS_PAL; + break; + default: + case VIDEO_MODE_AUTO: + if ((status & 0x10)) { + res |= DECODER_STATUS_PAL; + } else { + res |= DECODER_STATUS_NTSC; + } + break; + } + res |= DECODER_STATUS_COLOR; + *iarg = res; + + dprintk(1, KERN_INFO "%s: get status %x\n", I2C_NAME(client), + *iarg); + } + break; + + case DECODER_SET_NORM: + { + int *iarg = arg; + struct timing *timing = NULL; + + dprintk(1, KERN_INFO "%s: set norm %x\n", I2C_NAME(client), + *iarg); + + switch (*iarg) { + case VIDEO_MODE_NTSC: + bt819_setbit(client, 0x01, 0, 1); + bt819_setbit(client, 0x01, 1, 0); + bt819_setbit(client, 0x01, 5, 0); + bt819_write(client, 0x18, 0x68); + bt819_write(client, 0x19, 0x5d); + //bt819_setbit(client, 0x1a, 5, 1); + timing = &timing_data[VIDEO_MODE_NTSC]; + break; + case VIDEO_MODE_PAL: + bt819_setbit(client, 0x01, 0, 1); + bt819_setbit(client, 0x01, 1, 1); + bt819_setbit(client, 0x01, 5, 1); + bt819_write(client, 0x18, 0x7f); + bt819_write(client, 0x19, 0x72); + //bt819_setbit(client, 0x1a, 5, 0); + timing = &timing_data[VIDEO_MODE_PAL]; + break; + case VIDEO_MODE_AUTO: + bt819_setbit(client, 0x01, 0, 0); + bt819_setbit(client, 0x01, 1, 0); + break; + default: + dprintk(1, + KERN_ERR + "%s: unsupported norm %d\n", + I2C_NAME(client), *iarg); + return -EINVAL; + } + + if (timing) { + bt819_write(client, 0x03, + (((timing->vdelay >> 8) & 0x03) << 6) | + (((timing->vactive >> 8) & 0x03) << 4) | + (((timing->hdelay >> 8) & 0x03) << 2) | + ((timing->hactive >> 8) & 0x03) ); + bt819_write(client, 0x04, timing->vdelay & 0xff); + bt819_write(client, 0x05, timing->vactive & 0xff); + bt819_write(client, 0x06, timing->hdelay & 0xff); + bt819_write(client, 0x07, timing->hactive & 0xff); + bt819_write(client, 0x08, (timing->hscale >> 8) & 0xff); + bt819_write(client, 0x09, timing->hscale & 0xff); + } + + decoder->norm = *iarg; + } + break; + + case DECODER_SET_INPUT: + { + int *iarg = arg; + + dprintk(1, KERN_INFO "%s: set input %x\n", I2C_NAME(client), + *iarg); + + if (*iarg < 0 || *iarg > 7) { + return -EINVAL; + } + + if (decoder->input != *iarg) { + decoder->input = *iarg; + /* select mode */ + if (decoder->input == 0) { + bt819_setbit(client, 0x0b, 6, 0); + bt819_setbit(client, 0x1a, 1, 1); + } else { + bt819_setbit(client, 0x0b, 6, 1); + bt819_setbit(client, 0x1a, 1, 0); + } + } + } + break; + + case DECODER_SET_OUTPUT: + { + int *iarg = arg; + + dprintk(1, KERN_INFO "%s: set output %x\n", I2C_NAME(client), + *iarg); + + /* not much choice of outputs */ + if (*iarg != 0) { + return -EINVAL; + } + } + break; + + case DECODER_ENABLE_OUTPUT: + { + int *iarg = arg; + int enable = (*iarg != 0); + + dprintk(1, KERN_INFO "%s: enable output %x\n", + I2C_NAME(client), *iarg); + + if (decoder->enable != enable) { + decoder->enable = enable; + + if (decoder->enable) { + bt819_setbit(client, 0x16, 7, 0); + } else { + bt819_setbit(client, 0x16, 7, 1); + } + } + } + break; + + case DECODER_SET_PICTURE: + { + struct video_picture *pic = arg; + + dprintk(1, + KERN_INFO + "%s: set picture brightness %d contrast %d colour %d\n", + I2C_NAME(client), pic->brightness, pic->contrast, + pic->colour); + + + if (decoder->bright != pic->brightness) { + /* We want -128 to 127 we get 0-65535 */ + decoder->bright = pic->brightness; + bt819_write(client, 0x0a, + (decoder->bright >> 8) - 128); + } + + if (decoder->contrast != pic->contrast) { + /* We want 0 to 511 we get 0-65535 */ + decoder->contrast = pic->contrast; + bt819_write(client, 0x0c, + (decoder->contrast >> 7) & 0xff); + bt819_setbit(client, 0x0b, 2, + ((decoder->contrast >> 15) & 0x01)); + } + + if (decoder->sat != pic->colour) { + /* We want 0 to 511 we get 0-65535 */ + decoder->sat = pic->colour; + bt819_write(client, 0x0d, + (decoder->sat >> 7) & 0xff); + bt819_setbit(client, 0x0b, 1, + ((decoder->sat >> 15) & 0x01)); + + temp = (decoder->sat * 201) / 237; + bt819_write(client, 0x0e, (temp >> 7) & 0xff); + bt819_setbit(client, 0x0b, 0, (temp >> 15) & 0x01); + } + + if (decoder->hue != pic->hue) { + /* We want -128 to 127 we get 0-65535 */ + decoder->hue = pic->hue; + bt819_write(client, 0x0f, + 128 - (decoder->hue >> 8)); + } + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ +static unsigned short normal_i2c[] = { + I2C_BT819 >> 1, + I2C_CLIENT_END, +}; + +static unsigned short ignore = I2C_CLIENT_END; + +static struct i2c_client_address_data addr_data = { + .normal_i2c = normal_i2c, + .probe = &ignore, + .ignore = &ignore, +}; + +static struct i2c_driver i2c_driver_bt819; + +static int +bt819_detect_client (struct i2c_adapter *adapter, + int address, + int kind) +{ + int i, id; + struct bt819 *decoder; + struct i2c_client *client; + + dprintk(1, + KERN_INFO + "saa7111.c: detecting bt819 client on address 0x%x\n", + address << 1); + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == 0) + return -ENOMEM; + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver_bt819; + + decoder = kzalloc(sizeof(struct bt819), GFP_KERNEL); + if (decoder == NULL) { + kfree(client); + return -ENOMEM; + } + decoder->norm = VIDEO_MODE_NTSC; + decoder->input = 0; + decoder->enable = 1; + decoder->bright = 32768; + decoder->contrast = 32768; + decoder->hue = 32768; + decoder->sat = 32768; + decoder->initialized = 0; + i2c_set_clientdata(client, decoder); + + id = bt819_read(client, 0x17); + switch (id & 0xf0) { + case 0x70: + strlcpy(I2C_NAME(client), "bt819a", sizeof(I2C_NAME(client))); + break; + case 0x60: + strlcpy(I2C_NAME(client), "bt817a", sizeof(I2C_NAME(client))); + break; + case 0x20: + strlcpy(I2C_NAME(client), "bt815a", sizeof(I2C_NAME(client))); + break; + default: + dprintk(1, + KERN_ERR + "bt819: unknown chip version 0x%x (ver 0x%x)\n", + id & 0xf0, id & 0x0f); + kfree(decoder); + kfree(client); + return 0; + } + + i = i2c_attach_client(client); + if (i) { + kfree(client); + kfree(decoder); + return i; + } + + i = bt819_init(client); + if (i < 0) { + dprintk(1, KERN_ERR "%s_attach: init status %d\n", + I2C_NAME(client), i); + } else { + dprintk(1, + KERN_INFO + "%s_attach: chip version 0x%x at address 0x%x\n", + I2C_NAME(client), id & 0x0f, + client->addr << 1); + } + + return 0; +} + +static int +bt819_attach_adapter (struct i2c_adapter *adapter) +{ + return i2c_probe(adapter, &addr_data, &bt819_detect_client); +} + +static int +bt819_detach_client (struct i2c_client *client) +{ + struct bt819 *decoder = i2c_get_clientdata(client); + int err; + + err = i2c_detach_client(client); + if (err) { + return err; + } + + kfree(decoder); + kfree(client); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver i2c_driver_bt819 = { + .driver = { + .name = "bt819", + }, + + .id = I2C_DRIVERID_BT819, + + .attach_adapter = bt819_attach_adapter, + .detach_client = bt819_detach_client, + .command = bt819_command, +}; + +static int __init +bt819_init_module (void) +{ + return i2c_add_driver(&i2c_driver_bt819); +} + +static void __exit +bt819_exit (void) +{ + i2c_del_driver(&i2c_driver_bt819); +} + +module_init(bt819_init_module); +module_exit(bt819_exit); diff --git a/linux/drivers/media/video/bt856.c b/linux/drivers/media/video/bt856.c new file mode 100644 index 000000000..cb5c2160f --- /dev/null +++ b/linux/drivers/media/video/bt856.c @@ -0,0 +1,426 @@ +/* + * bt856 - BT856A Digital Video Encoder (Rockwell Part) + * + * Copyright (C) 1999 Mike Bernson + * Copyright (C) 1998 Dave Perks + * + * Modifications for LML33/DC10plus unified driver + * Copyright (C) 2000 Serguei Miridonov + * + * This code was modify/ported from the saa7111 driver written + * by Dave Perks. + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (9/9/2002) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compat.h" +#include +#include + +MODULE_DESCRIPTION("Brooktree-856A video encoder driver"); +MODULE_AUTHOR("Mike Bernson & Dave Perks"); +MODULE_LICENSE("GPL"); + +#include + +#define I2C_NAME(s) (s)->name + +#include + +static int debug = 0; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define dprintk(num, format, args...) \ + do { \ + if (debug >= num) \ + printk(format, ##args); \ + } while (0) + +/* ----------------------------------------------------------------------- */ + +#define REG_OFFSET 0xDA +#define BT856_NR_REG 6 + +struct bt856 { + unsigned char reg[BT856_NR_REG]; + + int norm; + int enable; +}; + +#define I2C_BT856 0x88 + +/* ----------------------------------------------------------------------- */ + +static inline int +bt856_write (struct i2c_client *client, + u8 reg, + u8 value) +{ + struct bt856 *encoder = i2c_get_clientdata(client); + + encoder->reg[reg - REG_OFFSET] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int +bt856_setbit (struct i2c_client *client, + u8 reg, + u8 bit, + u8 value) +{ + struct bt856 *encoder = i2c_get_clientdata(client); + + return bt856_write(client, reg, + (encoder-> + reg[reg - REG_OFFSET] & ~(1 << bit)) | + (value ? (1 << bit) : 0)); +} + +static void +bt856_dump (struct i2c_client *client) +{ + int i; + struct bt856 *encoder = i2c_get_clientdata(client); + + printk(KERN_INFO "%s: register dump:", I2C_NAME(client)); + for (i = 0; i < BT856_NR_REG; i += 2) + printk(" %02x", encoder->reg[i]); + printk("\n"); +} + +/* ----------------------------------------------------------------------- */ + +static int +bt856_command (struct i2c_client *client, + unsigned int cmd, + void *arg) +{ + struct bt856 *encoder = i2c_get_clientdata(client); + + switch (cmd) { + + case 0: + /* This is just for testing!!! */ + dprintk(1, KERN_INFO "bt856: init\n"); + bt856_write(client, 0xdc, 0x18); + bt856_write(client, 0xda, 0); + bt856_write(client, 0xde, 0); + + bt856_setbit(client, 0xdc, 3, 1); + //bt856_setbit(client, 0xdc, 6, 0); + bt856_setbit(client, 0xdc, 4, 1); + + switch (encoder->norm) { + + case VIDEO_MODE_NTSC: + bt856_setbit(client, 0xdc, 2, 0); + break; + + case VIDEO_MODE_PAL: + bt856_setbit(client, 0xdc, 2, 1); + break; + } + + bt856_setbit(client, 0xdc, 1, 1); + bt856_setbit(client, 0xde, 4, 0); + bt856_setbit(client, 0xde, 3, 1); + if (debug != 0) + bt856_dump(client); + break; + + case ENCODER_GET_CAPABILITIES: + { + struct video_encoder_capability *cap = arg; + + dprintk(1, KERN_INFO "%s: get capabilities\n", + I2C_NAME(client)); + + cap->flags = VIDEO_ENCODER_PAL | + VIDEO_ENCODER_NTSC | + VIDEO_ENCODER_CCIR; + cap->inputs = 2; + cap->outputs = 1; + } + break; + + case ENCODER_SET_NORM: + { + int *iarg = arg; + + dprintk(1, KERN_INFO "%s: set norm %d\n", I2C_NAME(client), + *iarg); + + switch (*iarg) { + + case VIDEO_MODE_NTSC: + bt856_setbit(client, 0xdc, 2, 0); + break; + + case VIDEO_MODE_PAL: + bt856_setbit(client, 0xdc, 2, 1); + bt856_setbit(client, 0xda, 0, 0); + //bt856_setbit(client, 0xda, 0, 1); + break; + + default: + return -EINVAL; + + } + encoder->norm = *iarg; + if (debug != 0) + bt856_dump(client); + } + break; + + case ENCODER_SET_INPUT: + { + int *iarg = arg; + + dprintk(1, KERN_INFO "%s: set input %d\n", I2C_NAME(client), + *iarg); + + /* We only have video bus. + * iarg = 0: input is from bt819 + * iarg = 1: input is from ZR36060 */ + + switch (*iarg) { + + case 0: + bt856_setbit(client, 0xde, 4, 0); + bt856_setbit(client, 0xde, 3, 1); + bt856_setbit(client, 0xdc, 3, 1); + bt856_setbit(client, 0xdc, 6, 0); + break; + case 1: + bt856_setbit(client, 0xde, 4, 0); + bt856_setbit(client, 0xde, 3, 1); + bt856_setbit(client, 0xdc, 3, 1); + bt856_setbit(client, 0xdc, 6, 1); + break; + case 2: // Color bar + bt856_setbit(client, 0xdc, 3, 0); + bt856_setbit(client, 0xde, 4, 1); + break; + default: + return -EINVAL; + + } + + if (debug != 0) + bt856_dump(client); + } + break; + + case ENCODER_SET_OUTPUT: + { + int *iarg = arg; + + dprintk(1, KERN_INFO "%s: set output %d\n", I2C_NAME(client), + *iarg); + + /* not much choice of outputs */ + if (*iarg != 0) { + return -EINVAL; + } + } + break; + + case ENCODER_ENABLE_OUTPUT: + { + int *iarg = arg; + + encoder->enable = !!*iarg; + + dprintk(1, KERN_INFO "%s: enable output %d\n", + I2C_NAME(client), encoder->enable); + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ +static unsigned short normal_i2c[] = { I2C_BT856 >> 1, I2C_CLIENT_END }; + +static unsigned short ignore = I2C_CLIENT_END; + +static struct i2c_client_address_data addr_data = { + .normal_i2c = normal_i2c, + .probe = &ignore, + .ignore = &ignore, +}; + +static struct i2c_driver i2c_driver_bt856; + +static int +bt856_detect_client (struct i2c_adapter *adapter, + int address, + int kind) +{ + int i; + struct i2c_client *client; + struct bt856 *encoder; + + dprintk(1, + KERN_INFO + "bt856.c: detecting bt856 client on address 0x%x\n", + address << 1); + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == 0) + return -ENOMEM; + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver_bt856; + strlcpy(I2C_NAME(client), "bt856", sizeof(I2C_NAME(client))); + + encoder = kzalloc(sizeof(struct bt856), GFP_KERNEL); + if (encoder == NULL) { + kfree(client); + return -ENOMEM; + } + encoder->norm = VIDEO_MODE_NTSC; + encoder->enable = 1; + i2c_set_clientdata(client, encoder); + + i = i2c_attach_client(client); + if (i) { + kfree(client); + kfree(encoder); + return i; + } + + bt856_write(client, 0xdc, 0x18); + bt856_write(client, 0xda, 0); + bt856_write(client, 0xde, 0); + + bt856_setbit(client, 0xdc, 3, 1); + //bt856_setbit(client, 0xdc, 6, 0); + bt856_setbit(client, 0xdc, 4, 1); + + switch (encoder->norm) { + + case VIDEO_MODE_NTSC: + bt856_setbit(client, 0xdc, 2, 0); + break; + + case VIDEO_MODE_PAL: + bt856_setbit(client, 0xdc, 2, 1); + break; + } + + bt856_setbit(client, 0xdc, 1, 1); + bt856_setbit(client, 0xde, 4, 0); + bt856_setbit(client, 0xde, 3, 1); + + if (debug != 0) + bt856_dump(client); + + dprintk(1, KERN_INFO "%s_attach: at address 0x%x\n", I2C_NAME(client), + client->addr << 1); + + return 0; +} + +static int +bt856_attach_adapter (struct i2c_adapter *adapter) +{ + dprintk(1, + KERN_INFO + "bt856.c: starting probe for adapter %s (0x%x)\n", + I2C_NAME(adapter), adapter->id); + return i2c_probe(adapter, &addr_data, &bt856_detect_client); +} + +static int +bt856_detach_client (struct i2c_client *client) +{ + struct bt856 *encoder = i2c_get_clientdata(client); + int err; + + err = i2c_detach_client(client); + if (err) { + return err; + } + + kfree(encoder); + kfree(client); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver i2c_driver_bt856 = { + .driver = { + .name = "bt856", + }, + + .id = I2C_DRIVERID_BT856, + + .attach_adapter = bt856_attach_adapter, + .detach_client = bt856_detach_client, + .command = bt856_command, +}; + +static int __init +bt856_init (void) +{ + return i2c_add_driver(&i2c_driver_bt856); +} + +static void __exit +bt856_exit (void) +{ + i2c_del_driver(&i2c_driver_bt856); +} + +module_init(bt856_init); +module_exit(bt856_exit); diff --git a/linux/drivers/media/video/bw-qcam.c b/linux/drivers/media/video/bw-qcam.c new file mode 100644 index 000000000..0a276d967 --- /dev/null +++ b/linux/drivers/media/video/bw-qcam.c @@ -0,0 +1,1029 @@ +/* + * QuickCam Driver For Video4Linux. + * + * Video4Linux conversion work by Alan Cox. + * Parport compatibility by Phil Blundell. + * Busy loop avoidance by Mark Cooke. + * + * Module parameters: + * + * maxpoll=<1 - 5000> + * + * When polling the QuickCam for a response, busy-wait for a + * maximum of this many loops. The default of 250 gives little + * impact on interactive response. + * + * NOTE: If this parameter is set too high, the processor + * will busy wait until this loop times out, and then + * slowly poll for a further 5 seconds before failing + * the transaction. You have been warned. + * + * yieldlines=<1 - 250> + * + * When acquiring a frame from the camera, the data gathering + * loop will yield back to the scheduler after completing + * this many lines. The default of 4 provides a trade-off + * between increased frame acquisition time and impact on + * interactive response. + */ + +/* qcam-lib.c -- Library for programming with the Connectix QuickCam. + * See the included documentation for usage instructions and details + * of the protocol involved. */ + + +/* Version 0.5, August 4, 1996 */ +/* Version 0.7, August 27, 1996 */ +/* Version 0.9, November 17, 1996 */ + + +/****************************************************************** + +Copyright (C) 1996 by Scott Laird + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "compat.h" +#include +#include +#include + +#include "bw-qcam.h" + +static unsigned int maxpoll=250; /* Maximum busy-loop count for qcam I/O */ +static unsigned int yieldlines=4; /* Yield after this many during capture */ +static int video_nr = -1; + +module_param(maxpoll, int, 0); +module_param(yieldlines, int, 0); +module_param(video_nr, int, 0); + +static inline int read_lpstatus(struct qcam_device *q) +{ + return parport_read_status(q->pport); +} + +static inline int read_lpdata(struct qcam_device *q) +{ + return parport_read_data(q->pport); +} + +static inline void write_lpdata(struct qcam_device *q, int d) +{ + parport_write_data(q->pport, d); +} + +static inline void write_lpcontrol(struct qcam_device *q, int d) +{ + parport_write_control(q->pport, d); +} + +static int qc_waithand(struct qcam_device *q, int val); +static int qc_command(struct qcam_device *q, int command); +static int qc_readparam(struct qcam_device *q); +static int qc_setscanmode(struct qcam_device *q); +static int qc_readbytes(struct qcam_device *q, char buffer[]); + +static struct video_device qcam_template; + +static int qc_calibrate(struct qcam_device *q) +{ + /* + * Bugfix by Hanno Mueller hmueller@kabel.de, Mai 21 96 + * The white balance is an individiual value for each + * quickcam. + */ + + int value; + int count = 0; + + qc_command(q, 27); /* AutoAdjustOffset */ + qc_command(q, 0); /* Dummy Parameter, ignored by the camera */ + + /* GetOffset (33) will read 255 until autocalibration */ + /* is finished. After that, a value of 1-254 will be */ + /* returned. */ + + do { + qc_command(q, 33); + value = qc_readparam(q); + mdelay(1); + schedule(); + count++; + } while (value == 0xff && count<2048); + + q->whitebal = value; + return value; +} + +/* Initialize the QuickCam driver control structure. This is where + * defaults are set for people who don't have a config file.*/ + +static struct qcam_device *qcam_init(struct parport *port) +{ + struct qcam_device *q; + + q = kmalloc(sizeof(struct qcam_device), GFP_KERNEL); + if(q==NULL) + return NULL; + + q->pport = port; + q->pdev = parport_register_device(port, "bw-qcam", NULL, NULL, + NULL, 0, NULL); + if (q->pdev == NULL) + { + printk(KERN_ERR "bw-qcam: couldn't register for %s.\n", + port->name); + kfree(q); + return NULL; + } + + memcpy(&q->vdev, &qcam_template, sizeof(qcam_template)); + + mutex_init(&q->lock); + + q->port_mode = (QC_ANY | QC_NOTSET); + q->width = 320; + q->height = 240; + q->bpp = 4; + q->transfer_scale = 2; + q->contrast = 192; + q->brightness = 180; + q->whitebal = 105; + q->top = 1; + q->left = 14; + q->mode = -1; + q->status = QC_PARAM_CHANGE; + return q; +} + + +/* qc_command is probably a bit of a misnomer -- it's used to send + * bytes *to* the camera. Generally, these bytes are either commands + * or arguments to commands, so the name fits, but it still bugs me a + * bit. See the documentation for a list of commands. */ + +static int qc_command(struct qcam_device *q, int command) +{ + int n1, n2; + int cmd; + + write_lpdata(q, command); + write_lpcontrol(q, 6); + + n1 = qc_waithand(q, 1); + + write_lpcontrol(q, 0xe); + n2 = qc_waithand(q, 0); + + cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4); + return cmd; +} + +static int qc_readparam(struct qcam_device *q) +{ + int n1, n2; + int cmd; + + write_lpcontrol(q, 6); + n1 = qc_waithand(q, 1); + + write_lpcontrol(q, 0xe); + n2 = qc_waithand(q, 0); + + cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4); + return cmd; +} + +/* qc_waithand busy-waits for a handshake signal from the QuickCam. + * Almost all communication with the camera requires handshaking. */ + +static int qc_waithand(struct qcam_device *q, int val) +{ + int status; + int runs=0; + + if (val) + { + while (!((status = read_lpstatus(q)) & 8)) + { + /* 1000 is enough spins on the I/O for all normal + cases, at that point we start to poll slowly + until the camera wakes up. However, we are + busy blocked until the camera responds, so + setting it lower is much better for interactive + response. */ + + if(runs++>maxpoll) + { + msleep_interruptible(5); + } + if(runs>(maxpoll+1000)) /* 5 seconds */ + return -1; + } + } + else + { + while (((status = read_lpstatus(q)) & 8)) + { + /* 1000 is enough spins on the I/O for all normal + cases, at that point we start to poll slowly + until the camera wakes up. However, we are + busy blocked until the camera responds, so + setting it lower is much better for interactive + response. */ + + if(runs++>maxpoll) + { + msleep_interruptible(5); + } + if(runs++>(maxpoll+1000)) /* 5 seconds */ + return -1; + } + } + + return status; +} + +/* Waithand2 is used when the qcam is in bidirectional mode, and the + * handshaking signal is CamRdy2 (bit 0 of data reg) instead of CamRdy1 + * (bit 3 of status register). It also returns the last value read, + * since this data is useful. */ + +static unsigned int qc_waithand2(struct qcam_device *q, int val) +{ + unsigned int status; + int runs=0; + + do + { + status = read_lpdata(q); + /* 1000 is enough spins on the I/O for all normal + cases, at that point we start to poll slowly + until the camera wakes up. However, we are + busy blocked until the camera responds, so + setting it lower is much better for interactive + response. */ + + if(runs++>maxpoll) + { + msleep_interruptible(5); + } + if(runs++>(maxpoll+1000)) /* 5 seconds */ + return 0; + } + while ((status & 1) != val); + + return status; +} + + +/* Try to detect a QuickCam. It appears to flash the upper 4 bits of + the status register at 5-10 Hz. This is only used in the autoprobe + code. Be aware that this isn't the way Connectix detects the + camera (they send a reset and try to handshake), but this should be + almost completely safe, while their method screws up my printer if + I plug it in before the camera. */ + +static int qc_detect(struct qcam_device *q) +{ + int reg, lastreg; + int count = 0; + int i; + + lastreg = reg = read_lpstatus(q) & 0xf0; + + for (i = 0; i < 500; i++) + { + reg = read_lpstatus(q) & 0xf0; + if (reg != lastreg) + count++; + lastreg = reg; + mdelay(2); + } + + +#if 0 + /* Force camera detection during testing. Sometimes the camera + won't be flashing these bits. Possibly unloading the module + in the middle of a grab? Or some timeout condition? + I've seen this parameter as low as 19 on my 450Mhz box - mpc */ + printk("Debugging: QCam detection counter <30-200 counts as detected>: %d\n", count); + return 1; +#endif + + /* Be (even more) liberal in what you accept... */ + +/* if (count > 30 && count < 200) */ + if (count > 20 && count < 300) + return 1; /* found */ + else + return 0; /* not found */ +} + + +/* Reset the QuickCam. This uses the same sequence the Windows + * QuickPic program uses. Someone with a bi-directional port should + * check that bi-directional mode is detected right, and then + * implement bi-directional mode in qc_readbyte(). */ + +static void qc_reset(struct qcam_device *q) +{ + switch (q->port_mode & QC_FORCE_MASK) + { + case QC_FORCE_UNIDIR: + q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR; + break; + + case QC_FORCE_BIDIR: + q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR; + break; + + case QC_ANY: + write_lpcontrol(q, 0x20); + write_lpdata(q, 0x75); + + if (read_lpdata(q) != 0x75) { + q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR; + } else { + q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR; + } + break; + } + + write_lpcontrol(q, 0xb); + udelay(250); + write_lpcontrol(q, 0xe); + qc_setscanmode(q); /* in case port_mode changed */ +} + + +/* Decide which scan mode to use. There's no real requirement that + * the scanmode match the resolution in q->height and q-> width -- the + * camera takes the picture at the resolution specified in the + * "scanmode" and then returns the image at the resolution specified + * with the resolution commands. If the scan is bigger than the + * requested resolution, the upper-left hand corner of the scan is + * returned. If the scan is smaller, then the rest of the image + * returned contains garbage. */ + +static int qc_setscanmode(struct qcam_device *q) +{ + int old_mode = q->mode; + + switch (q->transfer_scale) + { + case 1: + q->mode = 0; + break; + case 2: + q->mode = 4; + break; + case 4: + q->mode = 8; + break; + } + + switch (q->bpp) + { + case 4: + break; + case 6: + q->mode += 2; + break; + } + + switch (q->port_mode & QC_MODE_MASK) + { + case QC_BIDIR: + q->mode += 1; + break; + case QC_NOTSET: + case QC_UNIDIR: + break; + } + + if (q->mode != old_mode) + q->status |= QC_PARAM_CHANGE; + + return 0; +} + + +/* Reset the QuickCam and program for brightness, contrast, + * white-balance, and resolution. */ + +static void qc_set(struct qcam_device *q) +{ + int val; + int val2; + + qc_reset(q); + + /* Set the brightness. Yes, this is repetitive, but it works. + * Shorter versions seem to fail subtly. Feel free to try :-). */ + /* I think the problem was in qc_command, not here -- bls */ + + qc_command(q, 0xb); + qc_command(q, q->brightness); + + val = q->height / q->transfer_scale; + qc_command(q, 0x11); + qc_command(q, val); + if ((q->port_mode & QC_MODE_MASK) == QC_UNIDIR && q->bpp == 6) { + /* The normal "transfers per line" calculation doesn't seem to work + as expected here (and yet it works fine in qc_scan). No idea + why this case is the odd man out. Fortunately, Laird's original + working version gives me a good way to guess at working values. + -- bls */ + val = q->width; + val2 = q->transfer_scale * 4; + } else { + val = q->width * q->bpp; + val2 = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) * + q->transfer_scale; + } + val = (val + val2 - 1) / val2; + qc_command(q, 0x13); + qc_command(q, val); + + /* Setting top and left -- bls */ + qc_command(q, 0xd); + qc_command(q, q->top); + qc_command(q, 0xf); + qc_command(q, q->left / 2); + + qc_command(q, 0x19); + qc_command(q, q->contrast); + qc_command(q, 0x1f); + qc_command(q, q->whitebal); + + /* Clear flag that we must update the grabbing parameters on the camera + before we grab the next frame */ + q->status &= (~QC_PARAM_CHANGE); +} + +/* Qc_readbytes reads some bytes from the QC and puts them in + the supplied buffer. It returns the number of bytes read, + or -1 on error. */ + +static inline int qc_readbytes(struct qcam_device *q, char buffer[]) +{ + int ret=1; + unsigned int hi, lo; + unsigned int hi2, lo2; + static int state = 0; + + if (buffer == NULL) + { + state = 0; + return 0; + } + + switch (q->port_mode & QC_MODE_MASK) + { + case QC_BIDIR: /* Bi-directional Port */ + write_lpcontrol(q, 0x26); + lo = (qc_waithand2(q, 1) >> 1); + hi = (read_lpstatus(q) >> 3) & 0x1f; + write_lpcontrol(q, 0x2e); + lo2 = (qc_waithand2(q, 0) >> 1); + hi2 = (read_lpstatus(q) >> 3) & 0x1f; + switch (q->bpp) + { + case 4: + buffer[0] = lo & 0xf; + buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3); + buffer[2] = (hi & 0x1e) >> 1; + buffer[3] = lo2 & 0xf; + buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3); + buffer[5] = (hi2 & 0x1e) >> 1; + ret = 6; + break; + case 6: + buffer[0] = lo & 0x3f; + buffer[1] = ((lo & 0x40) >> 6) | (hi << 1); + buffer[2] = lo2 & 0x3f; + buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1); + ret = 4; + break; + } + break; + + case QC_UNIDIR: /* Unidirectional Port */ + write_lpcontrol(q, 6); + lo = (qc_waithand(q, 1) & 0xf0) >> 4; + write_lpcontrol(q, 0xe); + hi = (qc_waithand(q, 0) & 0xf0) >> 4; + + switch (q->bpp) + { + case 4: + buffer[0] = lo; + buffer[1] = hi; + ret = 2; + break; + case 6: + switch (state) + { + case 0: + buffer[0] = (lo << 2) | ((hi & 0xc) >> 2); + q->saved_bits = (hi & 3) << 4; + state = 1; + ret = 1; + break; + case 1: + buffer[0] = lo | q->saved_bits; + q->saved_bits = hi << 2; + state = 2; + ret = 1; + break; + case 2: + buffer[0] = ((lo & 0xc) >> 2) | q->saved_bits; + buffer[1] = ((lo & 3) << 4) | hi; + state = 0; + ret = 2; + break; + } + break; + } + break; + } + return ret; +} + +/* requests a scan from the camera. It sends the correct instructions + * to the camera and then reads back the correct number of bytes. In + * previous versions of this routine the return structure contained + * the raw output from the camera, and there was a 'qc_convertscan' + * function that converted that to a useful format. In version 0.3 I + * rolled qc_convertscan into qc_scan and now I only return the + * converted scan. The format is just an one-dimensional array of + * characters, one for each pixel, with 0=black up to n=white, where + * n=2^(bit depth)-1. Ask me for more details if you don't understand + * this. */ + +static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long len) +{ + int i, j, k, yield; + int bytes; + int linestotrans, transperline; + int divisor; + int pixels_per_line; + int pixels_read = 0; + int got=0; + char buffer[6]; + int shift=8-q->bpp; + char invert; + + if (q->mode == -1) + return -ENXIO; + + qc_command(q, 0x7); + qc_command(q, q->mode); + + if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) + { + write_lpcontrol(q, 0x2e); /* turn port around */ + write_lpcontrol(q, 0x26); + (void) qc_waithand(q, 1); + write_lpcontrol(q, 0x2e); + (void) qc_waithand(q, 0); + } + + /* strange -- should be 15:63 below, but 4bpp is odd */ + invert = (q->bpp == 4) ? 16 : 63; + + linestotrans = q->height / q->transfer_scale; + pixels_per_line = q->width / q->transfer_scale; + transperline = q->width * q->bpp; + divisor = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) * + q->transfer_scale; + transperline = (transperline + divisor - 1) / divisor; + + for (i = 0, yield = yieldlines; i < linestotrans; i++) + { + for (pixels_read = j = 0; j < transperline; j++) + { + bytes = qc_readbytes(q, buffer); + for (k = 0; k < bytes && (pixels_read + k) < pixels_per_line; k++) + { + int o; + if (buffer[k] == 0 && invert == 16) + { + /* 4bpp is odd (again) -- inverter is 16, not 15, but output + must be 0-15 -- bls */ + buffer[k] = 16; + } + o=i*pixels_per_line + pixels_read + k; + if(o= yield) { + msleep_interruptible(5); + yield = i + yieldlines; + } + } + + if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) + { + write_lpcontrol(q, 2); + write_lpcontrol(q, 6); + udelay(3); + write_lpcontrol(q, 0xe); + } + if(gotname, "Quickcam"); + b->type = VID_TYPE_CAPTURE|VID_TYPE_SCALES|VID_TYPE_MONOCHROME; + b->channels = 1; + b->audios = 0; + b->maxwidth = 320; + b->maxheight = 240; + b->minwidth = 80; + b->minheight = 60; + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + if(v->channel!=0) + return -EINVAL; + v->flags=0; + v->tuners=0; + /* Good question.. its composite or SVHS so.. */ + v->type = VIDEO_TYPE_CAMERA; + strcpy(v->name, "Camera"); + return 0; + } + case VIDIOCSCHAN: + { + struct video_channel *v = arg; + if(v->channel!=0) + return -EINVAL; + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + if(v->tuner) + return -EINVAL; + strcpy(v->name, "Format"); + v->rangelow=0; + v->rangehigh=0; + v->flags= 0; + v->mode = VIDEO_MODE_AUTO; + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *v = arg; + if(v->tuner) + return -EINVAL; + if(v->mode!=VIDEO_MODE_AUTO) + return -EINVAL; + return 0; + } + case VIDIOCGPICT: + { + struct video_picture *p = arg; + p->colour=0x8000; + p->hue=0x8000; + p->brightness=qcam->brightness<<8; + p->contrast=qcam->contrast<<8; + p->whiteness=qcam->whitebal<<8; + p->depth=qcam->bpp; + p->palette=VIDEO_PALETTE_GREY; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture *p = arg; + if(p->palette!=VIDEO_PALETTE_GREY) + return -EINVAL; + if(p->depth!=4 && p->depth!=6) + return -EINVAL; + + /* + * Now load the camera. + */ + + qcam->brightness = p->brightness>>8; + qcam->contrast = p->contrast>>8; + qcam->whitebal = p->whiteness>>8; + qcam->bpp = p->depth; + + mutex_lock(&qcam->lock); + qc_setscanmode(qcam); + mutex_unlock(&qcam->lock); + qcam->status |= QC_PARAM_CHANGE; + + return 0; + } + case VIDIOCSWIN: + { + struct video_window *vw = arg; + if(vw->flags) + return -EINVAL; + if(vw->clipcount) + return -EINVAL; + if(vw->height<60||vw->height>240) + return -EINVAL; + if(vw->width<80||vw->width>320) + return -EINVAL; + + qcam->width = 320; + qcam->height = 240; + qcam->transfer_scale = 4; + + if(vw->width>=160 && vw->height>=120) + { + qcam->transfer_scale = 2; + } + if(vw->width>=320 && vw->height>=240) + { + qcam->width = 320; + qcam->height = 240; + qcam->transfer_scale = 1; + } + mutex_lock(&qcam->lock); + qc_setscanmode(qcam); + mutex_unlock(&qcam->lock); + + /* We must update the camera before we grab. We could + just have changed the grab size */ + qcam->status |= QC_PARAM_CHANGE; + + /* Ok we figured out what to use from our wide choice */ + return 0; + } + case VIDIOCGWIN: + { + struct video_window *vw = arg; + memset(vw, 0, sizeof(*vw)); + vw->width=qcam->width/qcam->transfer_scale; + vw->height=qcam->height/qcam->transfer_scale; + return 0; + } + case VIDIOCKEY: + return 0; + case VIDIOCCAPTURE: + case VIDIOCGFBUF: + case VIDIOCSFBUF: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int qcam_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, qcam_do_ioctl); +} + +static ssize_t qcam_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct video_device *v = video_devdata(file); + struct qcam_device *qcam=(struct qcam_device *)v; + int len; + parport_claim_or_block(qcam->pdev); + + mutex_lock(&qcam->lock); + + qc_reset(qcam); + + /* Update the camera parameters if we need to */ + if (qcam->status & QC_PARAM_CHANGE) + qc_set(qcam); + + len=qc_capture(qcam, buf,count); + + mutex_unlock(&qcam->lock); + + parport_release(qcam->pdev); + return len; +} + +static struct file_operations qcam_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = qcam_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .read = qcam_read, + .llseek = no_llseek, +}; +static struct video_device qcam_template= +{ + .owner = THIS_MODULE, + .name = "Connectix Quickcam", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_QCAM_BW, + .fops = &qcam_fops, +}; + +#define MAX_CAMS 4 +static struct qcam_device *qcams[MAX_CAMS]; +static unsigned int num_cams = 0; + +static int init_bwqcam(struct parport *port) +{ + struct qcam_device *qcam; + + if (num_cams == MAX_CAMS) + { + printk(KERN_ERR "Too many Quickcams (max %d)\n", MAX_CAMS); + return -ENOSPC; + } + + qcam=qcam_init(port); + if(qcam==NULL) + return -ENODEV; + + parport_claim_or_block(qcam->pdev); + + qc_reset(qcam); + + if(qc_detect(qcam)==0) + { + parport_release(qcam->pdev); + parport_unregister_device(qcam->pdev); + kfree(qcam); + return -ENODEV; + } + qc_calibrate(qcam); + + parport_release(qcam->pdev); + + printk(KERN_INFO "Connectix Quickcam on %s\n", qcam->pport->name); + + if(video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr)==-1) + { + parport_unregister_device(qcam->pdev); + kfree(qcam); + return -ENODEV; + } + + qcams[num_cams++] = qcam; + + return 0; +} + +static void close_bwqcam(struct qcam_device *qcam) +{ + video_unregister_device(&qcam->vdev); + parport_unregister_device(qcam->pdev); + kfree(qcam); +} + +/* The parport parameter controls which parports will be scanned. + * Scanning all parports causes some printers to print a garbage page. + * -- March 14, 1999 Billy Donahue */ +#ifdef MODULE +static char *parport[MAX_CAMS] = { NULL, }; +module_param_array(parport, charp, NULL, 0); +#endif + +static int accept_bwqcam(struct parport *port) +{ +#ifdef MODULE + int n; + + if (parport[0] && strncmp(parport[0], "auto", 4) != 0) { + /* user gave parport parameters */ + for(n=0; parport[n] && nnumber) + return 1; + } + return 0; + } +#endif + return 1; +} + +static void bwqcam_attach(struct parport *port) +{ + if (accept_bwqcam(port)) + init_bwqcam(port); +} + +static void bwqcam_detach(struct parport *port) +{ + int i; + for (i = 0; i < num_cams; i++) { + struct qcam_device *qcam = qcams[i]; + if (qcam && qcam->pdev->port == port) { + qcams[i] = NULL; + close_bwqcam(qcam); + } + } +} + +static struct parport_driver bwqcam_driver = { + .name = "bw-qcam", + .attach = bwqcam_attach, + .detach = bwqcam_detach, +}; + +static void __exit exit_bw_qcams(void) +{ + parport_unregister_driver(&bwqcam_driver); +} + +static int __init init_bw_qcams(void) +{ +#ifdef MODULE + /* Do some sanity checks on the module parameters. */ + if (maxpoll > 5000) { + printk("Connectix Quickcam max-poll was above 5000. Using 5000.\n"); + maxpoll = 5000; + } + + if (yieldlines < 1) { + printk("Connectix Quickcam yieldlines was less than 1. Using 1.\n"); + yieldlines = 1; + } +#endif + return parport_register_driver(&bwqcam_driver); +} + +module_init(init_bw_qcams); +module_exit(exit_bw_qcams); + +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/bw-qcam.h b/linux/drivers/media/video/bw-qcam.h new file mode 100644 index 000000000..6701dafbc --- /dev/null +++ b/linux/drivers/media/video/bw-qcam.h @@ -0,0 +1,68 @@ +/* + * Video4Linux bw-qcam driver + * + * Derived from code.. + */ + +/****************************************************************** + +Copyright (C) 1996 by Scott Laird + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +/* One from column A... */ +#define QC_NOTSET 0 +#define QC_UNIDIR 1 +#define QC_BIDIR 2 +#define QC_SERIAL 3 + +/* ... and one from column B */ +#define QC_ANY 0x00 +#define QC_FORCE_UNIDIR 0x10 +#define QC_FORCE_BIDIR 0x20 +#define QC_FORCE_SERIAL 0x30 +/* in the port_mode member */ + +#define QC_MODE_MASK 0x07 +#define QC_FORCE_MASK 0x70 + +#define MAX_HEIGHT 243 +#define MAX_WIDTH 336 + +/* Bit fields for status flags */ +#define QC_PARAM_CHANGE 0x01 /* Camera status change has occurred */ + +struct qcam_device { + struct video_device vdev; + struct pardevice *pdev; + struct parport *pport; + struct mutex lock; + int width, height; + int bpp; + int mode; + int contrast, brightness, whitebal; + int port_mode; + int transfer_scale; + int top, left; + int status; + unsigned int saved_bits; +}; diff --git a/linux/drivers/media/video/c-qcam.c b/linux/drivers/media/video/c-qcam.c new file mode 100644 index 000000000..c1957a143 --- /dev/null +++ b/linux/drivers/media/video/c-qcam.c @@ -0,0 +1,858 @@ +/* + * Video4Linux Colour QuickCam driver + * Copyright 1997-2000 Philip Blundell + * + * Module parameters: + * + * parport=auto -- probe all parports (default) + * parport=0 -- parport0 becomes qcam1 + * parport=2,0,1 -- parports 2,0,1 are tried in that order + * + * probe=0 -- do no probing, assume camera is present + * probe=1 -- use IEEE-1284 autoprobe data only (default) + * probe=2 -- probe aggressively for cameras + * + * force_rgb=1 -- force data format to RGB (default is BGR) + * + * The parport parameter controls which parports will be scanned. + * Scanning all parports causes some printers to print a garbage page. + * -- March 14, 1999 Billy Donahue + * + * Fixed data format to BGR, added force_rgb parameter. Added missing + * parport_unregister_driver() on module removal. + * -- May 28, 2000 Claudio Matsuoka + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "compat.h" +#include +#include + +#include + +struct qcam_device { + struct video_device vdev; + struct pardevice *pdev; + struct parport *pport; + int width, height; + int ccd_width, ccd_height; + int mode; + int contrast, brightness, whitebal; + int top, left; + unsigned int bidirectional; + struct mutex lock; +}; + +/* cameras maximum */ +#define MAX_CAMS 4 + +/* The three possible QuickCam modes */ +#define QC_MILLIONS 0x18 +#define QC_BILLIONS 0x10 +#define QC_THOUSANDS 0x08 /* with VIDEC compression (not supported) */ + +/* The three possible decimations */ +#define QC_DECIMATION_1 0 +#define QC_DECIMATION_2 2 +#define QC_DECIMATION_4 4 + +#define BANNER "Colour QuickCam for Video4Linux v0.05" + +static int parport[MAX_CAMS] = { [1 ... MAX_CAMS-1] = -1 }; +static int probe = 2; +static int force_rgb = 0; +static int video_nr = -1; + +static inline void qcam_set_ack(struct qcam_device *qcam, unsigned int i) +{ + /* note: the QC specs refer to the PCAck pin by voltage, not + software level. PC ports have builtin inverters. */ + parport_frob_control(qcam->pport, 8, i?8:0); +} + +static inline unsigned int qcam_ready1(struct qcam_device *qcam) +{ + return (parport_read_status(qcam->pport) & 0x8)?1:0; +} + +static inline unsigned int qcam_ready2(struct qcam_device *qcam) +{ + return (parport_read_data(qcam->pport) & 0x1)?1:0; +} + +static unsigned int qcam_await_ready1(struct qcam_device *qcam, + int value) +{ + unsigned long oldjiffies = jiffies; + unsigned int i; + + for (oldjiffies = jiffies; (jiffies - oldjiffies) < (HZ/25); ) + if (qcam_ready1(qcam) == value) + return 0; + + /* If the camera didn't respond within 1/25 second, poll slowly + for a while. */ + for (i = 0; i < 50; i++) + { + if (qcam_ready1(qcam) == value) + return 0; + msleep_interruptible(100); + } + + /* Probably somebody pulled the plug out. Not much we can do. */ + printk(KERN_ERR "c-qcam: ready1 timeout (%d) %x %x\n", value, + parport_read_status(qcam->pport), + parport_read_control(qcam->pport)); + return 1; +} + +static unsigned int qcam_await_ready2(struct qcam_device *qcam, int value) +{ + unsigned long oldjiffies = jiffies; + unsigned int i; + + for (oldjiffies = jiffies; (jiffies - oldjiffies) < (HZ/25); ) + if (qcam_ready2(qcam) == value) + return 0; + + /* If the camera didn't respond within 1/25 second, poll slowly + for a while. */ + for (i = 0; i < 50; i++) + { + if (qcam_ready2(qcam) == value) + return 0; + msleep_interruptible(100); + } + + /* Probably somebody pulled the plug out. Not much we can do. */ + printk(KERN_ERR "c-qcam: ready2 timeout (%d) %x %x %x\n", value, + parport_read_status(qcam->pport), + parport_read_control(qcam->pport), + parport_read_data(qcam->pport)); + return 1; +} + +static int qcam_read_data(struct qcam_device *qcam) +{ + unsigned int idata; + qcam_set_ack(qcam, 0); + if (qcam_await_ready1(qcam, 1)) return -1; + idata = parport_read_status(qcam->pport) & 0xf0; + qcam_set_ack(qcam, 1); + if (qcam_await_ready1(qcam, 0)) return -1; + idata |= (parport_read_status(qcam->pport) >> 4); + return idata; +} + +static int qcam_write_data(struct qcam_device *qcam, unsigned int data) +{ + unsigned int idata; + parport_write_data(qcam->pport, data); + idata = qcam_read_data(qcam); + if (data != idata) + { + printk(KERN_WARNING "cqcam: sent %x but received %x\n", data, + idata); + return 1; + } + return 0; +} + +static inline int qcam_set(struct qcam_device *qcam, unsigned int cmd, unsigned int data) +{ + if (qcam_write_data(qcam, cmd)) + return -1; + if (qcam_write_data(qcam, data)) + return -1; + return 0; +} + +static inline int qcam_get(struct qcam_device *qcam, unsigned int cmd) +{ + if (qcam_write_data(qcam, cmd)) + return -1; + return qcam_read_data(qcam); +} + +static int qc_detect(struct qcam_device *qcam) +{ + unsigned int stat, ostat, i, count = 0; + + /* The probe routine below is not very reliable. The IEEE-1284 + probe takes precedence. */ + /* XXX Currently parport provides no way to distinguish between + "the IEEE probe was not done" and "the probe was done, but + no device was found". Fix this one day. */ + if (qcam->pport->probe_info[0].class == PARPORT_CLASS_MEDIA + && qcam->pport->probe_info[0].model + && !strcmp(qcam->pdev->port->probe_info[0].model, + "Color QuickCam 2.0")) { + printk(KERN_DEBUG "QuickCam: Found by IEEE1284 probe.\n"); + return 1; + } + + if (probe < 2) + return 0; + + parport_write_control(qcam->pport, 0xc); + + /* look for a heartbeat */ + ostat = stat = parport_read_status(qcam->pport); + for (i=0; i<250; i++) + { + mdelay(1); + stat = parport_read_status(qcam->pport); + if (ostat != stat) + { + if (++count >= 3) return 1; + ostat = stat; + } + } + + /* Reset the camera and try again */ + parport_write_control(qcam->pport, 0xc); + parport_write_control(qcam->pport, 0x8); + mdelay(1); + parport_write_control(qcam->pport, 0xc); + mdelay(1); + count = 0; + + ostat = stat = parport_read_status(qcam->pport); + for (i=0; i<250; i++) + { + mdelay(1); + stat = parport_read_status(qcam->pport); + if (ostat != stat) + { + if (++count >= 3) return 1; + ostat = stat; + } + } + + /* no (or flatline) camera, give up */ + return 0; +} + +static void qc_reset(struct qcam_device *qcam) +{ + parport_write_control(qcam->pport, 0xc); + parport_write_control(qcam->pport, 0x8); + mdelay(1); + parport_write_control(qcam->pport, 0xc); + mdelay(1); +} + +/* Reset the QuickCam and program for brightness, contrast, + * white-balance, and resolution. */ + +static void qc_setup(struct qcam_device *q) +{ + qc_reset(q); + + /* Set the brightness. */ + qcam_set(q, 11, q->brightness); + + /* Set the height and width. These refer to the actual + CCD area *before* applying the selected decimation. */ + qcam_set(q, 17, q->ccd_height); + qcam_set(q, 19, q->ccd_width / 2); + + /* Set top and left. */ + qcam_set(q, 0xd, q->top); + qcam_set(q, 0xf, q->left); + + /* Set contrast and white balance. */ + qcam_set(q, 0x19, q->contrast); + qcam_set(q, 0x1f, q->whitebal); + + /* Set the speed. */ + qcam_set(q, 45, 2); +} + +/* Read some bytes from the camera and put them in the buffer. + nbytes should be a multiple of 3, because bidirectional mode gives + us three bytes at a time. */ + +static unsigned int qcam_read_bytes(struct qcam_device *q, unsigned char *buf, unsigned int nbytes) +{ + unsigned int bytes = 0; + + qcam_set_ack(q, 0); + if (q->bidirectional) + { + /* It's a bidirectional port */ + while (bytes < nbytes) + { + unsigned int lo1, hi1, lo2, hi2; + unsigned char r, g, b; + + if (qcam_await_ready2(q, 1)) return bytes; + lo1 = parport_read_data(q->pport) >> 1; + hi1 = ((parport_read_status(q->pport) >> 3) & 0x1f) ^ 0x10; + qcam_set_ack(q, 1); + if (qcam_await_ready2(q, 0)) return bytes; + lo2 = parport_read_data(q->pport) >> 1; + hi2 = ((parport_read_status(q->pport) >> 3) & 0x1f) ^ 0x10; + qcam_set_ack(q, 0); + r = (lo1 | ((hi1 & 1)<<7)); + g = ((hi1 & 0x1e)<<3) | ((hi2 & 0x1e)>>1); + b = (lo2 | ((hi2 & 1)<<7)); + if (force_rgb) { + buf[bytes++] = r; + buf[bytes++] = g; + buf[bytes++] = b; + } else { + buf[bytes++] = b; + buf[bytes++] = g; + buf[bytes++] = r; + } + } + } + else + { + /* It's a unidirectional port */ + int i = 0, n = bytes; + unsigned char rgb[3]; + + while (bytes < nbytes) + { + unsigned int hi, lo; + + if (qcam_await_ready1(q, 1)) return bytes; + hi = (parport_read_status(q->pport) & 0xf0); + qcam_set_ack(q, 1); + if (qcam_await_ready1(q, 0)) return bytes; + lo = (parport_read_status(q->pport) & 0xf0); + qcam_set_ack(q, 0); + /* flip some bits */ + rgb[(i = bytes++ % 3)] = (hi | (lo >> 4)) ^ 0x88; + if (i >= 2) { +get_fragment: + if (force_rgb) { + buf[n++] = rgb[0]; + buf[n++] = rgb[1]; + buf[n++] = rgb[2]; + } else { + buf[n++] = rgb[2]; + buf[n++] = rgb[1]; + buf[n++] = rgb[0]; + } + } + } + if (i) { + i = 0; + goto get_fragment; + } + } + return bytes; +} + +#define BUFSZ 150 + +static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long len) +{ + unsigned lines, pixelsperline, bitsperxfer; + unsigned int is_bi_dir = q->bidirectional; + size_t wantlen, outptr = 0; + char tmpbuf[BUFSZ]; + + if (!access_ok(VERIFY_WRITE, buf, len)) + return -EFAULT; + + /* Wait for camera to become ready */ + for (;;) + { + int i = qcam_get(q, 41); + if (i == -1) { + qc_setup(q); + return -EIO; + } + if ((i & 0x80) == 0) + break; + else + schedule(); + } + + if (qcam_set(q, 7, (q->mode | (is_bi_dir?1:0)) + 1)) + return -EIO; + + lines = q->height; + pixelsperline = q->width; + bitsperxfer = (is_bi_dir) ? 24 : 8; + + if (is_bi_dir) + { + /* Turn the port around */ + parport_data_reverse(q->pport); + mdelay(3); + qcam_set_ack(q, 0); + if (qcam_await_ready1(q, 1)) { + qc_setup(q); + return -EIO; + } + qcam_set_ack(q, 1); + if (qcam_await_ready1(q, 0)) { + qc_setup(q); + return -EIO; + } + } + + wantlen = lines * pixelsperline * 24 / 8; + + while (wantlen) + { + size_t t, s; + s = (wantlen > BUFSZ)?BUFSZ:wantlen; + t = qcam_read_bytes(q, tmpbuf, s); + if (outptr < len) + { + size_t sz = len - outptr; + if (sz > t) sz = t; + if (__copy_to_user(buf+outptr, tmpbuf, sz)) + break; + outptr += sz; + } + wantlen -= t; + if (t < s) + break; + cond_resched(); + } + + len = outptr; + + if (wantlen) + { + printk("qcam: short read.\n"); + if (is_bi_dir) + parport_data_forward(q->pport); + qc_setup(q); + return len; + } + + if (is_bi_dir) + { + int l; + do { + l = qcam_read_bytes(q, tmpbuf, 3); + cond_resched(); + } while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e)); + if (force_rgb) { + if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf) + printk("qcam: bad EOF\n"); + } else { + if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe) + printk("qcam: bad EOF\n"); + } + qcam_set_ack(q, 0); + if (qcam_await_ready1(q, 1)) + { + printk("qcam: no ack after EOF\n"); + parport_data_forward(q->pport); + qc_setup(q); + return len; + } + parport_data_forward(q->pport); + mdelay(3); + qcam_set_ack(q, 1); + if (qcam_await_ready1(q, 0)) + { + printk("qcam: no ack to port turnaround\n"); + qc_setup(q); + return len; + } + } + else + { + int l; + do { + l = qcam_read_bytes(q, tmpbuf, 1); + cond_resched(); + } while (l && tmpbuf[0] == 0x7e); + l = qcam_read_bytes(q, tmpbuf+1, 2); + if (force_rgb) { + if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf) + printk("qcam: bad EOF\n"); + } else { + if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe) + printk("qcam: bad EOF\n"); + } + } + + qcam_write_data(q, 0); + return len; +} + +/* + * Video4linux interfacing + */ + +static int qcam_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct qcam_device *qcam=(struct qcam_device *)dev; + + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability *b = arg; + strcpy(b->name, "Quickcam"); + b->type = VID_TYPE_CAPTURE|VID_TYPE_SCALES; + b->channels = 1; + b->audios = 0; + b->maxwidth = 320; + b->maxheight = 240; + b->minwidth = 80; + b->minheight = 60; + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + if(v->channel!=0) + return -EINVAL; + v->flags=0; + v->tuners=0; + /* Good question.. its composite or SVHS so.. */ + v->type = VIDEO_TYPE_CAMERA; + strcpy(v->name, "Camera"); + return 0; + } + case VIDIOCSCHAN: + { + struct video_channel *v = arg; + if(v->channel!=0) + return -EINVAL; + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + if(v->tuner) + return -EINVAL; + memset(v,0,sizeof(*v)); + strcpy(v->name, "Format"); + v->mode = VIDEO_MODE_AUTO; + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *v = arg; + if(v->tuner) + return -EINVAL; + if(v->mode!=VIDEO_MODE_AUTO) + return -EINVAL; + return 0; + } + case VIDIOCGPICT: + { + struct video_picture *p = arg; + p->colour=0x8000; + p->hue=0x8000; + p->brightness=qcam->brightness<<8; + p->contrast=qcam->contrast<<8; + p->whiteness=qcam->whitebal<<8; + p->depth=24; + p->palette=VIDEO_PALETTE_RGB24; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture *p = arg; + + /* + * Sanity check args + */ + if (p->depth != 24 || p->palette != VIDEO_PALETTE_RGB24) + return -EINVAL; + + /* + * Now load the camera. + */ + qcam->brightness = p->brightness>>8; + qcam->contrast = p->contrast>>8; + qcam->whitebal = p->whiteness>>8; + + mutex_lock(&qcam->lock); + parport_claim_or_block(qcam->pdev); + qc_setup(qcam); + parport_release(qcam->pdev); + mutex_unlock(&qcam->lock); + return 0; + } + case VIDIOCSWIN: + { + struct video_window *vw = arg; + + if(vw->flags) + return -EINVAL; + if(vw->clipcount) + return -EINVAL; + if(vw->height<60||vw->height>240) + return -EINVAL; + if(vw->width<80||vw->width>320) + return -EINVAL; + + qcam->width = 80; + qcam->height = 60; + qcam->mode = QC_DECIMATION_4; + + if(vw->width>=160 && vw->height>=120) + { + qcam->width = 160; + qcam->height = 120; + qcam->mode = QC_DECIMATION_2; + } + if(vw->width>=320 && vw->height>=240) + { + qcam->width = 320; + qcam->height = 240; + qcam->mode = QC_DECIMATION_1; + } + qcam->mode |= QC_MILLIONS; +#if 0 + if(vw->width>=640 && vw->height>=480) + { + qcam->width = 640; + qcam->height = 480; + qcam->mode = QC_BILLIONS | QC_DECIMATION_1; + } +#endif + /* Ok we figured out what to use from our + wide choice */ + mutex_lock(&qcam->lock); + parport_claim_or_block(qcam->pdev); + qc_setup(qcam); + parport_release(qcam->pdev); + mutex_unlock(&qcam->lock); + return 0; + } + case VIDIOCGWIN: + { + struct video_window *vw = arg; + memset(vw, 0, sizeof(*vw)); + vw->width=qcam->width; + vw->height=qcam->height; + return 0; + } + case VIDIOCKEY: + return 0; + case VIDIOCCAPTURE: + case VIDIOCGFBUF: + case VIDIOCSFBUF: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int qcam_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, qcam_do_ioctl); +} + +static ssize_t qcam_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct video_device *v = video_devdata(file); + struct qcam_device *qcam=(struct qcam_device *)v; + int len; + + mutex_lock(&qcam->lock); + parport_claim_or_block(qcam->pdev); + /* Probably should have a semaphore against multiple users */ + len = qc_capture(qcam, buf,count); + parport_release(qcam->pdev); + mutex_unlock(&qcam->lock); + return len; +} + +/* video device template */ +static struct file_operations qcam_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = qcam_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .read = qcam_read, + .llseek = no_llseek, +}; + +static struct video_device qcam_template= +{ + .owner = THIS_MODULE, + .name = "Colour QuickCam", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_QCAM_C, + .fops = &qcam_fops, +}; + +/* Initialize the QuickCam driver control structure. */ + +static struct qcam_device *qcam_init(struct parport *port) +{ + struct qcam_device *q; + + q = kmalloc(sizeof(struct qcam_device), GFP_KERNEL); + if(q==NULL) + return NULL; + + q->pport = port; + q->pdev = parport_register_device(port, "c-qcam", NULL, NULL, + NULL, 0, NULL); + + q->bidirectional = (q->pport->modes & PARPORT_MODE_TRISTATE)?1:0; + + if (q->pdev == NULL) + { + printk(KERN_ERR "c-qcam: couldn't register for %s.\n", + port->name); + kfree(q); + return NULL; + } + + memcpy(&q->vdev, &qcam_template, sizeof(qcam_template)); + + mutex_init(&q->lock); + q->width = q->ccd_width = 320; + q->height = q->ccd_height = 240; + q->mode = QC_MILLIONS | QC_DECIMATION_1; + q->contrast = 192; + q->brightness = 240; + q->whitebal = 128; + q->top = 1; + q->left = 14; + return q; +} + +static struct qcam_device *qcams[MAX_CAMS]; +static unsigned int num_cams = 0; + +static int init_cqcam(struct parport *port) +{ + struct qcam_device *qcam; + + if (parport[0] != -1) + { + /* The user gave specific instructions */ + int i, found = 0; + for (i = 0; i < MAX_CAMS && parport[i] != -1; i++) + { + if (parport[0] == port->number) + found = 1; + } + if (!found) + return -ENODEV; + } + + if (num_cams == MAX_CAMS) + return -ENOSPC; + + qcam = qcam_init(port); + if (qcam==NULL) + return -ENODEV; + + parport_claim_or_block(qcam->pdev); + + qc_reset(qcam); + + if (probe && qc_detect(qcam)==0) + { + parport_release(qcam->pdev); + parport_unregister_device(qcam->pdev); + kfree(qcam); + return -ENODEV; + } + + qc_setup(qcam); + + parport_release(qcam->pdev); + + if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr)==-1) + { + printk(KERN_ERR "Unable to register Colour QuickCam on %s\n", + qcam->pport->name); + parport_unregister_device(qcam->pdev); + kfree(qcam); + return -ENODEV; + } + + printk(KERN_INFO "video%d: Colour QuickCam found on %s\n", + qcam->vdev.minor, qcam->pport->name); + + qcams[num_cams++] = qcam; + + return 0; +} + +static void close_cqcam(struct qcam_device *qcam) +{ + video_unregister_device(&qcam->vdev); + parport_unregister_device(qcam->pdev); + kfree(qcam); +} + +static void cq_attach(struct parport *port) +{ + init_cqcam(port); +} + +static void cq_detach(struct parport *port) +{ + /* Write this some day. */ +} + +static struct parport_driver cqcam_driver = { + .name = "cqcam", + .attach = cq_attach, + .detach = cq_detach, +}; + +static int __init cqcam_init (void) +{ + printk(BANNER "\n"); + + return parport_register_driver(&cqcam_driver); +} + +static void __exit cqcam_cleanup (void) +{ + unsigned int i; + + for (i = 0; i < num_cams; i++) + close_cqcam(qcams[i]); + + parport_unregister_driver(&cqcam_driver); +} + +MODULE_AUTHOR("Philip Blundell "); +MODULE_DESCRIPTION(BANNER); +MODULE_LICENSE("GPL"); + +/* FIXME: parport=auto would never have worked, surely? --RR */ +MODULE_PARM_DESC(parport ,"parport= for port detection method\n\ +probe=<0|1|2> for camera detection method\n\ +force_rgb=<0|1> for RGB data format (default BGR)"); +module_param_array(parport, int, NULL, 0); +module_param(probe, int, 0); +module_param(force_rgb, bool, 0); +module_param(video_nr, int, 0); + +module_init(cqcam_init); +module_exit(cqcam_cleanup); diff --git a/linux/drivers/media/video/cpia.c b/linux/drivers/media/video/cpia.c new file mode 100644 index 000000000..85d84e89d --- /dev/null +++ b/linux/drivers/media/video/cpia.c @@ -0,0 +1,4073 @@ +/* + * cpia CPiA driver + * + * Supports CPiA based Video Camera's. + * + * (C) Copyright 1999-2000 Peter Pregler + * (C) Copyright 1999-2000 Scott J. Bertin + * (C) Copyright 1999-2000 Johannes Erdfelt + * (C) Copyright 2000 STMicroelectronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ +/* #define _CPIA_DEBUG_ 1 */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_KMOD +#include +#endif + +#include "cpia.h" + +#ifdef CONFIG_VIDEO_CPIA_PP +extern int cpia_pp_init(void); +#endif +#ifdef CONFIG_VIDEO_CPIA_USB +extern int cpia_usb_init(void); +#endif + +static int video_nr = -1; + +#ifdef MODULE +module_param(video_nr, int, 0); +MODULE_AUTHOR("Scott J. Bertin & Peter Pregler & Johannes Erdfelt "); +MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("video"); +#endif + +static unsigned short colorspace_conv; +module_param(colorspace_conv, ushort, 0444); +MODULE_PARM_DESC(colorspace_conv, + " Colorspace conversion:" + "\n 0 = disable, 1 = enable" + "\n Default value is 0" + ); + +#define ABOUT "V4L-Driver for Vision CPiA based cameras" + +#ifndef VID_HARDWARE_CPIA +#define VID_HARDWARE_CPIA 24 /* FIXME -> from linux/videodev.h */ +#endif + +#define CPIA_MODULE_CPIA (0<<5) +#define CPIA_MODULE_SYSTEM (1<<5) +#define CPIA_MODULE_VP_CTRL (5<<5) +#define CPIA_MODULE_CAPTURE (6<<5) +#define CPIA_MODULE_DEBUG (7<<5) + +#define INPUT (DATA_IN << 8) +#define OUTPUT (DATA_OUT << 8) + +#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1) +#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2) +#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3) +#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4) +#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5) +#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7) +#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8) +#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10) + +#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1) +#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2) +#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3) +#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4) +#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5) +#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6) +#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7) +#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8) +#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9) +#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10) +#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11) +#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12) +#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13) + +#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1) +#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2) +#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3) +#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4) +#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6) +#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7) +#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8) +#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9) +#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10) +#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11) +#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16) +#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17) +#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18) +#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19) +#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25) +#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30) +#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31) + +#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1) +#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2) +#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3) +#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4) +#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5) +#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6) +#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7) +#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8) +#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9) +#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10) +#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11) +#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12) +#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13) +#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14) +#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15) + +#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1) +#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4) +#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5) +#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6) +#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8) +#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9) +#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10) +#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11) + +enum { + FRAME_READY, /* Ready to grab into */ + FRAME_GRABBING, /* In the process of being grabbed into */ + FRAME_DONE, /* Finished grabbing, but not been synced yet */ + FRAME_UNUSED, /* Unused (no MCAPTURE) */ +}; + +#define COMMAND_NONE 0x0000 +#define COMMAND_SETCOMPRESSION 0x0001 +#define COMMAND_SETCOMPRESSIONTARGET 0x0002 +#define COMMAND_SETCOLOURPARAMS 0x0004 +#define COMMAND_SETFORMAT 0x0008 +#define COMMAND_PAUSE 0x0010 +#define COMMAND_RESUME 0x0020 +#define COMMAND_SETYUVTHRESH 0x0040 +#define COMMAND_SETECPTIMING 0x0080 +#define COMMAND_SETCOMPRESSIONPARAMS 0x0100 +#define COMMAND_SETEXPOSURE 0x0200 +#define COMMAND_SETCOLOURBALANCE 0x0400 +#define COMMAND_SETSENSORFPS 0x0800 +#define COMMAND_SETAPCOR 0x1000 +#define COMMAND_SETFLICKERCTRL 0x2000 +#define COMMAND_SETVLOFFSET 0x4000 +#define COMMAND_SETLIGHTS 0x8000 + +#define ROUND_UP_EXP_FOR_FLICKER 15 + +/* Constants for automatic frame rate adjustment */ +#define MAX_EXP 302 +#define MAX_EXP_102 255 +#define LOW_EXP 140 +#define VERY_LOW_EXP 70 +#define TC 94 +#define EXP_ACC_DARK 50 +#define EXP_ACC_LIGHT 90 +#define HIGH_COMP_102 160 +#define MAX_COMP 239 +#define DARK_TIME 3 +#define LIGHT_TIME 3 + +/* Maximum number of 10ms loops to wait for the stream to become ready */ +#define READY_TIMEOUT 100 + +/* Developer's Guide Table 5 p 3-34 + * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/ +static u8 flicker_jumps[2][2][4] = +{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } }, + { { 64, 32, 16, 8 }, { 76, 38, 19, 9} } +}; + +/* forward declaration of local function */ +static void reset_camera_struct(struct cam_data *cam); +static int find_over_exposure(int brightness); +static void set_flicker(struct cam_params *params, volatile u32 *command_flags, + int on); + + +/********************************************************************** + * + * Memory management + * + **********************************************************************/ +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + +static void rvfree(void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + +/********************************************************************** + * + * /proc interface + * + **********************************************************************/ +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *cpia_proc_root=NULL; + +static int cpia_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *out = page; + int len, tmp; + struct cam_data *cam = data; + char tmpstr[29]; + + /* IMPORTANT: This output MUST be kept under PAGE_SIZE + * or we need to get more sophisticated. */ + + out += sprintf(out, "read-only\n-----------------------\n"); + out += sprintf(out, "V4L Driver version: %d.%d.%d\n", + CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); + out += sprintf(out, "CPIA Version: %d.%02d (%d.%d)\n", + cam->params.version.firmwareVersion, + cam->params.version.firmwareRevision, + cam->params.version.vcVersion, + cam->params.version.vcRevision); + out += sprintf(out, "CPIA PnP-ID: %04x:%04x:%04x\n", + cam->params.pnpID.vendor, cam->params.pnpID.product, + cam->params.pnpID.deviceRevision); + out += sprintf(out, "VP-Version: %d.%d %04x\n", + cam->params.vpVersion.vpVersion, + cam->params.vpVersion.vpRevision, + cam->params.vpVersion.cameraHeadID); + + out += sprintf(out, "system_state: %#04x\n", + cam->params.status.systemState); + out += sprintf(out, "grab_state: %#04x\n", + cam->params.status.grabState); + out += sprintf(out, "stream_state: %#04x\n", + cam->params.status.streamState); + out += sprintf(out, "fatal_error: %#04x\n", + cam->params.status.fatalError); + out += sprintf(out, "cmd_error: %#04x\n", + cam->params.status.cmdError); + out += sprintf(out, "debug_flags: %#04x\n", + cam->params.status.debugFlags); + out += sprintf(out, "vp_status: %#04x\n", + cam->params.status.vpStatus); + out += sprintf(out, "error_code: %#04x\n", + cam->params.status.errorCode); + /* QX3 specific entries */ + if (cam->params.qx3.qx3_detected) { + out += sprintf(out, "button: %4d\n", + cam->params.qx3.button); + out += sprintf(out, "cradled: %4d\n", + cam->params.qx3.cradled); + } + out += sprintf(out, "video_size: %s\n", + cam->params.format.videoSize == VIDEOSIZE_CIF ? + "CIF " : "QCIF"); + out += sprintf(out, "roi: (%3d, %3d) to (%3d, %3d)\n", + cam->params.roi.colStart*8, + cam->params.roi.rowStart*4, + cam->params.roi.colEnd*8, + cam->params.roi.rowEnd*4); + out += sprintf(out, "actual_fps: %3d\n", cam->fps); + out += sprintf(out, "transfer_rate: %4dkB/s\n", + cam->transfer_rate); + + out += sprintf(out, "\nread-write\n"); + out += sprintf(out, "----------------------- current min" + " max default comment\n"); + out += sprintf(out, "brightness: %8d %8d %8d %8d\n", + cam->params.colourParams.brightness, 0, 100, 50); + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) + /* 1-02 firmware limits contrast to 80 */ + tmp = 80; + else + tmp = 96; + + out += sprintf(out, "contrast: %8d %8d %8d %8d" + " steps of 8\n", + cam->params.colourParams.contrast, 0, tmp, 48); + out += sprintf(out, "saturation: %8d %8d %8d %8d\n", + cam->params.colourParams.saturation, 0, 100, 50); + tmp = (25000+5000*cam->params.sensorFps.baserate)/ + (1<params.sensorFps.divisor); + out += sprintf(out, "sensor_fps: %4d.%03d %8d %8d %8d\n", + tmp/1000, tmp%1000, 3, 30, 15); + out += sprintf(out, "stream_start_line: %8d %8d %8d %8d\n", + 2*cam->params.streamStartLine, 0, + cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144, + cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120); + out += sprintf(out, "sub_sample: %8s %8s %8s %8s\n", + cam->params.format.subSample == SUBSAMPLE_420 ? + "420" : "422", "420", "422", "422"); + out += sprintf(out, "yuv_order: %8s %8s %8s %8s\n", + cam->params.format.yuvOrder == YUVORDER_YUYV ? + "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV"); + out += sprintf(out, "ecp_timing: %8s %8s %8s %8s\n", + cam->params.ecpTiming ? "slow" : "normal", "slow", + "normal", "normal"); + + if (cam->params.colourBalance.balanceMode == 2) { + sprintf(tmpstr, "auto"); + } else { + sprintf(tmpstr, "manual"); + } + out += sprintf(out, "color_balance_mode: %8s %8s %8s" + " %8s\n", tmpstr, "manual", "auto", "auto"); + out += sprintf(out, "red_gain: %8d %8d %8d %8d\n", + cam->params.colourBalance.redGain, 0, 212, 32); + out += sprintf(out, "green_gain: %8d %8d %8d %8d\n", + cam->params.colourBalance.greenGain, 0, 212, 6); + out += sprintf(out, "blue_gain: %8d %8d %8d %8d\n", + cam->params.colourBalance.blueGain, 0, 212, 92); + + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) + /* 1-02 firmware limits gain to 2 */ + sprintf(tmpstr, "%8d %8d %8d", 1, 2, 2); + else + sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2); + + if (cam->params.exposure.gainMode == 0) + out += sprintf(out, "max_gain: unknown %28s" + " powers of 2\n", tmpstr); + else + out += sprintf(out, "max_gain: %8d %28s" + " 1,2,4 or 8 \n", + 1<<(cam->params.exposure.gainMode-1), tmpstr); + + switch(cam->params.exposure.expMode) { + case 1: + case 3: + sprintf(tmpstr, "manual"); + break; + case 2: + sprintf(tmpstr, "auto"); + break; + default: + sprintf(tmpstr, "unknown"); + break; + } + out += sprintf(out, "exposure_mode: %8s %8s %8s" + " %8s\n", tmpstr, "manual", "auto", "auto"); + out += sprintf(out, "centre_weight: %8s %8s %8s %8s\n", + (2-cam->params.exposure.centreWeight) ? "on" : "off", + "off", "on", "on"); + out += sprintf(out, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n", + 1<params.exposure.gain, 1, 1); + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) + /* 1-02 firmware limits fineExp/2 to 127 */ + tmp = 254; + else + tmp = 510; + + out += sprintf(out, "fine_exp: %8d %8d %8d %8d\n", + cam->params.exposure.fineExp*2, 0, tmp, 0); + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) + /* 1-02 firmware limits coarseExpHi to 0 */ + tmp = MAX_EXP_102; + else + tmp = MAX_EXP; + + out += sprintf(out, "coarse_exp: %8d %8d %8d" + " %8d\n", cam->params.exposure.coarseExpLo+ + 256*cam->params.exposure.coarseExpHi, 0, tmp, 185); + out += sprintf(out, "red_comp: %8d %8d %8d %8d\n", + cam->params.exposure.redComp, COMP_RED, 255, COMP_RED); + out += sprintf(out, "green1_comp: %8d %8d %8d %8d\n", + cam->params.exposure.green1Comp, COMP_GREEN1, 255, + COMP_GREEN1); + out += sprintf(out, "green2_comp: %8d %8d %8d %8d\n", + cam->params.exposure.green2Comp, COMP_GREEN2, 255, + COMP_GREEN2); + out += sprintf(out, "blue_comp: %8d %8d %8d %8d\n", + cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE); + + out += sprintf(out, "apcor_gain1: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain1, 0, 0xff, 0x1c); + out += sprintf(out, "apcor_gain2: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain2, 0, 0xff, 0x1a); + out += sprintf(out, "apcor_gain4: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain4, 0, 0xff, 0x2d); + out += sprintf(out, "apcor_gain8: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain8, 0, 0xff, 0x2a); + out += sprintf(out, "vl_offset_gain1: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain1, 0, 255, 24); + out += sprintf(out, "vl_offset_gain2: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain2, 0, 255, 28); + out += sprintf(out, "vl_offset_gain4: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain4, 0, 255, 30); + out += sprintf(out, "vl_offset_gain8: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain8, 0, 255, 30); + out += sprintf(out, "flicker_control: %8s %8s %8s %8s\n", + cam->params.flickerControl.flickerMode ? "on" : "off", + "off", "on", "off"); + out += sprintf(out, "mains_frequency: %8d %8d %8d %8d" + " only 50/60\n", + cam->mainsFreq ? 60 : 50, 50, 60, 50); + if(cam->params.flickerControl.allowableOverExposure < 0) + out += sprintf(out, "allowable_overexposure: %4dauto auto %8d auto\n", + -cam->params.flickerControl.allowableOverExposure, + 255); + else + out += sprintf(out, "allowable_overexposure: %8d auto %8d auto\n", + cam->params.flickerControl.allowableOverExposure, + 255); + out += sprintf(out, "compression_mode: "); + switch(cam->params.compression.mode) { + case CPIA_COMPRESSION_NONE: + out += sprintf(out, "%8s", "none"); + break; + case CPIA_COMPRESSION_AUTO: + out += sprintf(out, "%8s", "auto"); + break; + case CPIA_COMPRESSION_MANUAL: + out += sprintf(out, "%8s", "manual"); + break; + default: + out += sprintf(out, "%8s", "unknown"); + break; + } + out += sprintf(out, " none,auto,manual auto\n"); + out += sprintf(out, "decimation_enable: %8s %8s %8s %8s\n", + cam->params.compression.decimation == + DECIMATION_ENAB ? "on":"off", "off", "on", + "off"); + out += sprintf(out, "compression_target: %9s %9s %9s %9s\n", + cam->params.compressionTarget.frTargeting == + CPIA_COMPRESSION_TARGET_FRAMERATE ? + "framerate":"quality", + "framerate", "quality", "quality"); + out += sprintf(out, "target_framerate: %8d %8d %8d %8d\n", + cam->params.compressionTarget.targetFR, 1, 30, 15); + out += sprintf(out, "target_quality: %8d %8d %8d %8d\n", + cam->params.compressionTarget.targetQ, 1, 64, 5); + out += sprintf(out, "y_threshold: %8d %8d %8d %8d\n", + cam->params.yuvThreshold.yThreshold, 0, 31, 6); + out += sprintf(out, "uv_threshold: %8d %8d %8d %8d\n", + cam->params.yuvThreshold.uvThreshold, 0, 31, 6); + out += sprintf(out, "hysteresis: %8d %8d %8d %8d\n", + cam->params.compressionParams.hysteresis, 0, 255, 3); + out += sprintf(out, "threshold_max: %8d %8d %8d %8d\n", + cam->params.compressionParams.threshMax, 0, 255, 11); + out += sprintf(out, "small_step: %8d %8d %8d %8d\n", + cam->params.compressionParams.smallStep, 0, 255, 1); + out += sprintf(out, "large_step: %8d %8d %8d %8d\n", + cam->params.compressionParams.largeStep, 0, 255, 3); + out += sprintf(out, "decimation_hysteresis: %8d %8d %8d %8d\n", + cam->params.compressionParams.decimationHysteresis, + 0, 255, 2); + out += sprintf(out, "fr_diff_step_thresh: %8d %8d %8d %8d\n", + cam->params.compressionParams.frDiffStepThresh, + 0, 255, 5); + out += sprintf(out, "q_diff_step_thresh: %8d %8d %8d %8d\n", + cam->params.compressionParams.qDiffStepThresh, + 0, 255, 3); + out += sprintf(out, "decimation_thresh_mod: %8d %8d %8d %8d\n", + cam->params.compressionParams.decimationThreshMod, + 0, 255, 2); + /* QX3 specific entries */ + if (cam->params.qx3.qx3_detected) { + out += sprintf(out, "toplight: %8s %8s %8s %8s\n", + cam->params.qx3.toplight ? "on" : "off", + "off", "on", "off"); + out += sprintf(out, "bottomlight: %8s %8s %8s %8s\n", + cam->params.qx3.bottomlight ? "on" : "off", + "off", "on", "off"); + } + + len = out - page; + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) return 0; + } else + len = count; + + *start = page + off; + return len; +} + + +static int match(char *checkstr, char **buffer, unsigned long *count, + int *find_colon, int *err) +{ + int ret, colon_found = 1; + int len = strlen(checkstr); + ret = (len <= *count && strncmp(*buffer, checkstr, len) == 0); + if (ret) { + *buffer += len; + *count -= len; + if (*find_colon) { + colon_found = 0; + while (*count && (**buffer == ' ' || **buffer == '\t' || + (!colon_found && **buffer == ':'))) { + if (**buffer == ':') + colon_found = 1; + --*count; + ++*buffer; + } + if (!*count || !colon_found) + *err = -EINVAL; + *find_colon = 0; + } + } + return ret; +} + +static unsigned long int value(char **buffer, unsigned long *count, int *err) +{ + char *p; + unsigned long int ret; + ret = simple_strtoul(*buffer, &p, 0); + if (p == *buffer) + *err = -EINVAL; + else { + *count -= p - *buffer; + *buffer = p; + } + return ret; +} + +static int cpia_write_proc(struct file *file, const char __user *buf, + unsigned long count, void *data) +{ + struct cam_data *cam = data; + struct cam_params new_params; + char *page, *buffer; + int retval, find_colon; + int size = count; + unsigned long val = 0; + u32 command_flags = 0; + u8 new_mains; + + /* + * This code to copy from buf to page is shamelessly copied + * from the comx driver + */ + if (count > PAGE_SIZE) { + printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE); + return -ENOSPC; + } + + if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM; + + if(copy_from_user(page, buf, count)) + { + retval = -EFAULT; + goto out; + } + + if (page[count-1] == '\n') + page[count-1] = '\0'; + else if (count < PAGE_SIZE) + page[count] = '\0'; + else if (page[count]) { + retval = -EINVAL; + goto out; + } + + buffer = page; + + if (mutex_lock_interruptible(&cam->param_lock)) + return -ERESTARTSYS; + + /* + * Skip over leading whitespace + */ + while (count && isspace(*buffer)) { + --count; + ++buffer; + } + + memcpy(&new_params, &cam->params, sizeof(struct cam_params)); + new_mains = cam->mainsFreq; + +#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval)) +#define VALUE (value(&buffer,&count, &retval)) +#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \ + new_params.version.firmwareRevision == (y)) + + retval = 0; + while (count && !retval) { + find_colon = 1; + if (MATCH("brightness")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 100) + new_params.colourParams.brightness = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURPARAMS; + if(new_params.flickerControl.allowableOverExposure < 0) + new_params.flickerControl.allowableOverExposure = + -find_over_exposure(new_params.colourParams.brightness); + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + + } else if (MATCH("contrast")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 100) { + /* contrast is in steps of 8, so round*/ + val = ((val + 3) / 8) * 8; + /* 1-02 firmware limits contrast to 80*/ + if (FIRMWARE_VERSION(1,2) && val > 80) + val = 80; + + new_params.colourParams.contrast = val; + } else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURPARAMS; + } else if (MATCH("saturation")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 100) + new_params.colourParams.saturation = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURPARAMS; + } else if (MATCH("sensor_fps")) { + if (!retval) + val = VALUE; + + if (!retval) { + /* find values so that sensorFPS is minimized, + * but >= val */ + if (val > 30) + retval = -EINVAL; + else if (val > 25) { + new_params.sensorFps.divisor = 0; + new_params.sensorFps.baserate = 1; + } else if (val > 15) { + new_params.sensorFps.divisor = 0; + new_params.sensorFps.baserate = 0; + } else if (val > 12) { + new_params.sensorFps.divisor = 1; + new_params.sensorFps.baserate = 1; + } else if (val > 7) { + new_params.sensorFps.divisor = 1; + new_params.sensorFps.baserate = 0; + } else if (val > 6) { + new_params.sensorFps.divisor = 2; + new_params.sensorFps.baserate = 1; + } else if (val > 3) { + new_params.sensorFps.divisor = 2; + new_params.sensorFps.baserate = 0; + } else { + new_params.sensorFps.divisor = 3; + /* Either base rate would work here */ + new_params.sensorFps.baserate = 1; + } + new_params.flickerControl.coarseJump = + flicker_jumps[new_mains] + [new_params.sensorFps.baserate] + [new_params.sensorFps.divisor]; + if (new_params.flickerControl.flickerMode) + command_flags |= COMMAND_SETFLICKERCTRL; + } + command_flags |= COMMAND_SETSENSORFPS; + cam->exposure_status = EXPOSURE_NORMAL; + } else if (MATCH("stream_start_line")) { + if (!retval) + val = VALUE; + + if (!retval) { + int max_line = 288; + + if (new_params.format.videoSize == VIDEOSIZE_QCIF) + max_line = 144; + if (val <= max_line) + new_params.streamStartLine = val/2; + else + retval = -EINVAL; + } + } else if (MATCH("sub_sample")) { + if (!retval && MATCH("420")) + new_params.format.subSample = SUBSAMPLE_420; + else if (!retval && MATCH("422")) + new_params.format.subSample = SUBSAMPLE_422; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETFORMAT; + } else if (MATCH("yuv_order")) { + if (!retval && MATCH("YUYV")) + new_params.format.yuvOrder = YUVORDER_YUYV; + else if (!retval && MATCH("UYVY")) + new_params.format.yuvOrder = YUVORDER_UYVY; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETFORMAT; + } else if (MATCH("ecp_timing")) { + if (!retval && MATCH("normal")) + new_params.ecpTiming = 0; + else if (!retval && MATCH("slow")) + new_params.ecpTiming = 1; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETECPTIMING; + } else if (MATCH("color_balance_mode")) { + if (!retval && MATCH("manual")) + new_params.colourBalance.balanceMode = 3; + else if (!retval && MATCH("auto")) + new_params.colourBalance.balanceMode = 2; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETCOLOURBALANCE; + } else if (MATCH("red_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 212) { + new_params.colourBalance.redGain = val; + new_params.colourBalance.balanceMode = 1; + } else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURBALANCE; + } else if (MATCH("green_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 212) { + new_params.colourBalance.greenGain = val; + new_params.colourBalance.balanceMode = 1; + } else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURBALANCE; + } else if (MATCH("blue_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 212) { + new_params.colourBalance.blueGain = val; + new_params.colourBalance.balanceMode = 1; + } else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURBALANCE; + } else if (MATCH("max_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + /* 1-02 firmware limits gain to 2 */ + if (FIRMWARE_VERSION(1,2) && val > 2) + val = 2; + switch(val) { + case 1: + new_params.exposure.gainMode = 1; + break; + case 2: + new_params.exposure.gainMode = 2; + break; + case 4: + new_params.exposure.gainMode = 3; + break; + case 8: + new_params.exposure.gainMode = 4; + break; + default: + retval = -EINVAL; + break; + } + } + command_flags |= COMMAND_SETEXPOSURE; + } else if (MATCH("exposure_mode")) { + if (!retval && MATCH("auto")) + new_params.exposure.expMode = 2; + else if (!retval && MATCH("manual")) { + if (new_params.exposure.expMode == 2) + new_params.exposure.expMode = 3; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + new_params.flickerControl.flickerMode = 0; + } else + retval = -EINVAL; + + command_flags |= COMMAND_SETEXPOSURE; + } else if (MATCH("centre_weight")) { + if (!retval && MATCH("on")) + new_params.exposure.centreWeight = 1; + else if (!retval && MATCH("off")) + new_params.exposure.centreWeight = 2; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETEXPOSURE; + } else if (MATCH("gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + switch(val) { + case 1: + new_params.exposure.gain = 0; + break; + case 2: + new_params.exposure.gain = 1; + break; + case 4: + new_params.exposure.gain = 2; + break; + case 8: + new_params.exposure.gain = 3; + break; + default: + retval = -EINVAL; + break; + } + new_params.exposure.expMode = 1; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETEXPOSURE; + if (new_params.exposure.gain > + new_params.exposure.gainMode-1) + retval = -EINVAL; + } + } else if (MATCH("fine_exp")) { + if (!retval) + val = VALUE/2; + + if (!retval) { + if (val < 256) { + /* 1-02 firmware limits fineExp/2 to 127*/ + if (FIRMWARE_VERSION(1,2) && val > 127) + val = 127; + new_params.exposure.fineExp = val; + new_params.exposure.expMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + } else + retval = -EINVAL; + } + } else if (MATCH("coarse_exp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= MAX_EXP) { + if (FIRMWARE_VERSION(1,2) && + val > MAX_EXP_102) + val = MAX_EXP_102; + new_params.exposure.coarseExpLo = + val & 0xff; + new_params.exposure.coarseExpHi = + val >> 8; + new_params.exposure.expMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + } else + retval = -EINVAL; + } + } else if (MATCH("red_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= COMP_RED && val <= 255) { + new_params.exposure.redComp = val; + new_params.exposure.compMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("green1_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= COMP_GREEN1 && val <= 255) { + new_params.exposure.green1Comp = val; + new_params.exposure.compMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("green2_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= COMP_GREEN2 && val <= 255) { + new_params.exposure.green2Comp = val; + new_params.exposure.compMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("blue_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= COMP_BLUE && val <= 255) { + new_params.exposure.blueComp = val; + new_params.exposure.compMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("apcor_gain1")) { + if (!retval) + val = VALUE; + + if (!retval) { + command_flags |= COMMAND_SETAPCOR; + if (val <= 0xff) + new_params.apcor.gain1 = val; + else + retval = -EINVAL; + } + } else if (MATCH("apcor_gain2")) { + if (!retval) + val = VALUE; + + if (!retval) { + command_flags |= COMMAND_SETAPCOR; + if (val <= 0xff) + new_params.apcor.gain2 = val; + else + retval = -EINVAL; + } + } else if (MATCH("apcor_gain4")) { + if (!retval) + val = VALUE; + + if (!retval) { + command_flags |= COMMAND_SETAPCOR; + if (val <= 0xff) + new_params.apcor.gain4 = val; + else + retval = -EINVAL; + } + } else if (MATCH("apcor_gain8")) { + if (!retval) + val = VALUE; + + if (!retval) { + command_flags |= COMMAND_SETAPCOR; + if (val <= 0xff) + new_params.apcor.gain8 = val; + else + retval = -EINVAL; + } + } else if (MATCH("vl_offset_gain1")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain1 = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETVLOFFSET; + } else if (MATCH("vl_offset_gain2")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain2 = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETVLOFFSET; + } else if (MATCH("vl_offset_gain4")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain4 = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETVLOFFSET; + } else if (MATCH("vl_offset_gain8")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain8 = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETVLOFFSET; + } else if (MATCH("flicker_control")) { + if (!retval && MATCH("on")) { + set_flicker(&new_params, &command_flags, 1); + } else if (!retval && MATCH("off")) { + set_flicker(&new_params, &command_flags, 0); + } else + retval = -EINVAL; + + command_flags |= COMMAND_SETFLICKERCTRL; + } else if (MATCH("mains_frequency")) { + if (!retval && MATCH("50")) { + new_mains = 0; + new_params.flickerControl.coarseJump = + flicker_jumps[new_mains] + [new_params.sensorFps.baserate] + [new_params.sensorFps.divisor]; + if (new_params.flickerControl.flickerMode) + command_flags |= COMMAND_SETFLICKERCTRL; + } else if (!retval && MATCH("60")) { + new_mains = 1; + new_params.flickerControl.coarseJump = + flicker_jumps[new_mains] + [new_params.sensorFps.baserate] + [new_params.sensorFps.divisor]; + if (new_params.flickerControl.flickerMode) + command_flags |= COMMAND_SETFLICKERCTRL; + } else + retval = -EINVAL; + } else if (MATCH("allowable_overexposure")) { + if (!retval && MATCH("auto")) { + new_params.flickerControl.allowableOverExposure = + -find_over_exposure(new_params.colourParams.brightness); + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + } else { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) { + new_params.flickerControl. + allowableOverExposure = val; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + } else + retval = -EINVAL; + } + } + } else if (MATCH("compression_mode")) { + if (!retval && MATCH("none")) + new_params.compression.mode = + CPIA_COMPRESSION_NONE; + else if (!retval && MATCH("auto")) + new_params.compression.mode = + CPIA_COMPRESSION_AUTO; + else if (!retval && MATCH("manual")) + new_params.compression.mode = + CPIA_COMPRESSION_MANUAL; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETCOMPRESSION; + } else if (MATCH("decimation_enable")) { + if (!retval && MATCH("off")) + new_params.compression.decimation = 0; + else if (!retval && MATCH("on")) + new_params.compression.decimation = 1; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETCOMPRESSION; + } else if (MATCH("compression_target")) { + if (!retval && MATCH("quality")) + new_params.compressionTarget.frTargeting = + CPIA_COMPRESSION_TARGET_QUALITY; + else if (!retval && MATCH("framerate")) + new_params.compressionTarget.frTargeting = + CPIA_COMPRESSION_TARGET_FRAMERATE; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETCOMPRESSIONTARGET; + } else if (MATCH("target_framerate")) { + if (!retval) + val = VALUE; + + if (!retval) { + if(val > 0 && val <= 30) + new_params.compressionTarget.targetFR = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONTARGET; + } else if (MATCH("target_quality")) { + if (!retval) + val = VALUE; + + if (!retval) { + if(val > 0 && val <= 64) + new_params.compressionTarget.targetQ = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONTARGET; + } else if (MATCH("y_threshold")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val < 32) + new_params.yuvThreshold.yThreshold = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETYUVTHRESH; + } else if (MATCH("uv_threshold")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val < 32) + new_params.yuvThreshold.uvThreshold = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETYUVTHRESH; + } else if (MATCH("hysteresis")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.hysteresis = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("threshold_max")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.threshMax = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("small_step")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.smallStep = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("large_step")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.largeStep = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("decimation_hysteresis")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.decimationHysteresis = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("fr_diff_step_thresh")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.frDiffStepThresh = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("q_diff_step_thresh")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.qDiffStepThresh = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("decimation_thresh_mod")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.decimationThreshMod = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("toplight")) { + if (!retval && MATCH("on")) + new_params.qx3.toplight = 1; + else if (!retval && MATCH("off")) + new_params.qx3.toplight = 0; + else + retval = -EINVAL; + command_flags |= COMMAND_SETLIGHTS; + } else if (MATCH("bottomlight")) { + if (!retval && MATCH("on")) + new_params.qx3.bottomlight = 1; + else if (!retval && MATCH("off")) + new_params.qx3.bottomlight = 0; + else + retval = -EINVAL; + command_flags |= COMMAND_SETLIGHTS; + } else { + DBG("No match found\n"); + retval = -EINVAL; + } + + if (!retval) { + while (count && isspace(*buffer) && *buffer != '\n') { + --count; + ++buffer; + } + if (count) { + if (*buffer == '\0' && count != 1) + retval = -EINVAL; + else if (*buffer != '\n' && *buffer != ';' && + *buffer != '\0') + retval = -EINVAL; + else { + --count; + ++buffer; + } + } + } + } +#undef MATCH +#undef VALUE +#undef FIRMWARE_VERSION + if (!retval) { + if (command_flags & COMMAND_SETCOLOURPARAMS) { + /* Adjust cam->vp to reflect these changes */ + cam->vp.brightness = + new_params.colourParams.brightness*65535/100; + cam->vp.contrast = + new_params.colourParams.contrast*65535/100; + cam->vp.colour = + new_params.colourParams.saturation*65535/100; + } + if((command_flags & COMMAND_SETEXPOSURE) && + new_params.exposure.expMode == 2) + cam->exposure_status = EXPOSURE_NORMAL; + + memcpy(&cam->params, &new_params, sizeof(struct cam_params)); + cam->mainsFreq = new_mains; + cam->cmd_queue |= command_flags; + retval = size; + } else + DBG("error: %d\n", retval); + + mutex_unlock(&cam->param_lock); + +out: + free_page((unsigned long)page); + return retval; +} + +static void create_proc_cpia_cam(struct cam_data *cam) +{ + char name[7]; + struct proc_dir_entry *ent; + + if (!cpia_proc_root || !cam) + return; + + sprintf(name, "video%d", cam->vdev.minor); + + ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root); + if (!ent) + return; + + ent->data = cam; + ent->read_proc = cpia_read_proc; + ent->write_proc = cpia_write_proc; + /* + size of the proc entry is 3736 bytes for the standard webcam; + the extra features of the QX3 microscope add 189 bytes. + (we have not yet probed the camera to see which type it is). + */ + ent->size = 3736 + 189; + cam->proc_entry = ent; +} + +static void destroy_proc_cpia_cam(struct cam_data *cam) +{ + char name[7]; + + if (!cam || !cam->proc_entry) + return; + + sprintf(name, "video%d", cam->vdev.minor); + remove_proc_entry(name, cpia_proc_root); + cam->proc_entry = NULL; +} + +static void proc_cpia_create(void) +{ + cpia_proc_root = proc_mkdir("cpia", NULL); + + if (cpia_proc_root) + cpia_proc_root->owner = THIS_MODULE; + else + LOG("Unable to initialise /proc/cpia\n"); +} + +static void __exit proc_cpia_destroy(void) +{ + remove_proc_entry("cpia", NULL); +} +#endif /* CONFIG_PROC_FS */ + +/* ----------------------- debug functions ---------------------- */ + +#define printstatus(cam) \ + DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\ + cam->params.status.systemState, cam->params.status.grabState, \ + cam->params.status.streamState, cam->params.status.fatalError, \ + cam->params.status.cmdError, cam->params.status.debugFlags, \ + cam->params.status.vpStatus, cam->params.status.errorCode); + +/* ----------------------- v4l helpers -------------------------- */ + +/* supported frame palettes and depths */ +static inline int valid_mode(u16 palette, u16 depth) +{ + if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) || + (palette == VIDEO_PALETTE_YUYV && depth == 16)) + return 1; + + if (colorspace_conv) + return (palette == VIDEO_PALETTE_GREY && depth == 8) || + (palette == VIDEO_PALETTE_RGB555 && depth == 16) || + (palette == VIDEO_PALETTE_RGB565 && depth == 16) || + (palette == VIDEO_PALETTE_RGB24 && depth == 24) || + (palette == VIDEO_PALETTE_RGB32 && depth == 32) || + (palette == VIDEO_PALETTE_UYVY && depth == 16); + + return 0; +} + +static int match_videosize( int width, int height ) +{ + /* return the best match, where 'best' is as always + * the largest that is not bigger than what is requested. */ + if (width>=352 && height>=288) + return VIDEOSIZE_352_288; /* CIF */ + + if (width>=320 && height>=240) + return VIDEOSIZE_320_240; /* SIF */ + + if (width>=288 && height>=216) + return VIDEOSIZE_288_216; + + if (width>=256 && height>=192) + return VIDEOSIZE_256_192; + + if (width>=224 && height>=168) + return VIDEOSIZE_224_168; + + if (width>=192 && height>=144) + return VIDEOSIZE_192_144; + + if (width>=176 && height>=144) + return VIDEOSIZE_176_144; /* QCIF */ + + if (width>=160 && height>=120) + return VIDEOSIZE_160_120; /* QSIF */ + + if (width>=128 && height>=96) + return VIDEOSIZE_128_96; + + if (width>=88 && height>=72) + return VIDEOSIZE_88_72; + + if (width>=64 && height>=48) + return VIDEOSIZE_64_48; + + if (width>=48 && height>=48) + return VIDEOSIZE_48_48; + + return -1; +} + +/* these are the capture sizes we support */ +static void set_vw_size(struct cam_data *cam) +{ + /* the col/row/start/end values are the result of simple math */ + /* study the SetROI-command in cpia developers guide p 2-22 */ + /* streamStartLine is set to the recommended value in the cpia */ + /* developers guide p 3-37 */ + switch(cam->video_size) { + case VIDEOSIZE_CIF: + cam->vw.width = 352; + cam->vw.height = 288; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=0; + cam->params.roi.rowStart=0; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_SIF: + cam->vw.width = 320; + cam->vw.height = 240; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=2; + cam->params.roi.rowStart=6; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_288_216: + cam->vw.width = 288; + cam->vw.height = 216; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=4; + cam->params.roi.rowStart=9; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_256_192: + cam->vw.width = 256; + cam->vw.height = 192; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=6; + cam->params.roi.rowStart=12; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_224_168: + cam->vw.width = 224; + cam->vw.height = 168; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=8; + cam->params.roi.rowStart=15; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_192_144: + cam->vw.width = 192; + cam->vw.height = 144; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=10; + cam->params.roi.rowStart=18; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_QCIF: + cam->vw.width = 176; + cam->vw.height = 144; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=0; + cam->params.roi.rowStart=0; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_QSIF: + cam->vw.width = 160; + cam->vw.height = 120; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=1; + cam->params.roi.rowStart=3; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_128_96: + cam->vw.width = 128; + cam->vw.height = 96; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=3; + cam->params.roi.rowStart=6; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_88_72: + cam->vw.width = 88; + cam->vw.height = 72; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=5; + cam->params.roi.rowStart=9; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_64_48: + cam->vw.width = 64; + cam->vw.height = 48; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=7; + cam->params.roi.rowStart=12; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_48_48: + cam->vw.width = 48; + cam->vw.height = 48; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=8; + cam->params.roi.rowStart=6; + cam->params.streamStartLine = 60; + break; + default: + LOG("bad videosize value: %d\n", cam->video_size); + return; + } + + if(cam->vc.width == 0) + cam->vc.width = cam->vw.width; + if(cam->vc.height == 0) + cam->vc.height = cam->vw.height; + + cam->params.roi.colStart += cam->vc.x >> 3; + cam->params.roi.colEnd = cam->params.roi.colStart + + (cam->vc.width >> 3); + cam->params.roi.rowStart += cam->vc.y >> 2; + cam->params.roi.rowEnd = cam->params.roi.rowStart + + (cam->vc.height >> 2); + + return; +} + +static int allocate_frame_buf(struct cam_data *cam) +{ + int i; + + cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE); + if (!cam->frame_buf) + return -ENOBUFS; + + for (i = 0; i < FRAME_NUM; i++) + cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE; + + return 0; +} + +static int free_frame_buf(struct cam_data *cam) +{ + int i; + + rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE); + cam->frame_buf = NULL; + for (i=0; i < FRAME_NUM; i++) + cam->frame[i].data = NULL; + + return 0; +} + + +static inline void free_frames(struct cpia_frame frame[FRAME_NUM]) +{ + int i; + + for (i=0; i < FRAME_NUM; i++) + frame[i].state = FRAME_UNUSED; + return; +} + +/********************************************************************** + * + * General functions + * + **********************************************************************/ +/* send an arbitrary command to the camera */ +static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d) +{ + int retval, datasize; + u8 cmd[8], data[8]; + + switch(command) { + case CPIA_COMMAND_GetCPIAVersion: + case CPIA_COMMAND_GetPnPID: + case CPIA_COMMAND_GetCameraStatus: + case CPIA_COMMAND_GetVPVersion: + datasize=8; + break; + case CPIA_COMMAND_GetColourParams: + case CPIA_COMMAND_GetColourBalance: + case CPIA_COMMAND_GetExposure: + mutex_lock(&cam->param_lock); + datasize=8; + break; + case CPIA_COMMAND_ReadMCPorts: + case CPIA_COMMAND_ReadVCRegs: + datasize = 4; + break; + default: + datasize=0; + break; + } + + cmd[0] = command>>8; + cmd[1] = command&0xff; + cmd[2] = a; + cmd[3] = b; + cmd[4] = c; + cmd[5] = d; + cmd[6] = datasize; + cmd[7] = 0; + + retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); + if (retval) { + DBG("%x - failed, retval=%d\n", command, retval); + if (command == CPIA_COMMAND_GetColourParams || + command == CPIA_COMMAND_GetColourBalance || + command == CPIA_COMMAND_GetExposure) + mutex_unlock(&cam->param_lock); + } else { + switch(command) { + case CPIA_COMMAND_GetCPIAVersion: + cam->params.version.firmwareVersion = data[0]; + cam->params.version.firmwareRevision = data[1]; + cam->params.version.vcVersion = data[2]; + cam->params.version.vcRevision = data[3]; + break; + case CPIA_COMMAND_GetPnPID: + cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8); + cam->params.pnpID.product = data[2]+(((u16)data[3])<<8); + cam->params.pnpID.deviceRevision = + data[4]+(((u16)data[5])<<8); + break; + case CPIA_COMMAND_GetCameraStatus: + cam->params.status.systemState = data[0]; + cam->params.status.grabState = data[1]; + cam->params.status.streamState = data[2]; + cam->params.status.fatalError = data[3]; + cam->params.status.cmdError = data[4]; + cam->params.status.debugFlags = data[5]; + cam->params.status.vpStatus = data[6]; + cam->params.status.errorCode = data[7]; + break; + case CPIA_COMMAND_GetVPVersion: + cam->params.vpVersion.vpVersion = data[0]; + cam->params.vpVersion.vpRevision = data[1]; + cam->params.vpVersion.cameraHeadID = + data[2]+(((u16)data[3])<<8); + break; + case CPIA_COMMAND_GetColourParams: + cam->params.colourParams.brightness = data[0]; + cam->params.colourParams.contrast = data[1]; + cam->params.colourParams.saturation = data[2]; + mutex_unlock(&cam->param_lock); + break; + case CPIA_COMMAND_GetColourBalance: + cam->params.colourBalance.redGain = data[0]; + cam->params.colourBalance.greenGain = data[1]; + cam->params.colourBalance.blueGain = data[2]; + mutex_unlock(&cam->param_lock); + break; + case CPIA_COMMAND_GetExposure: + cam->params.exposure.gain = data[0]; + cam->params.exposure.fineExp = data[1]; + cam->params.exposure.coarseExpLo = data[2]; + cam->params.exposure.coarseExpHi = data[3]; + cam->params.exposure.redComp = data[4]; + cam->params.exposure.green1Comp = data[5]; + cam->params.exposure.green2Comp = data[6]; + cam->params.exposure.blueComp = data[7]; + mutex_unlock(&cam->param_lock); + break; + + case CPIA_COMMAND_ReadMCPorts: + if (!cam->params.qx3.qx3_detected) + break; + /* test button press */ + cam->params.qx3.button = ((data[1] & 0x02) == 0); + if (cam->params.qx3.button) { + /* button pressed - unlock the latch */ + do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0); + do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0); + } + + /* test whether microscope is cradled */ + cam->params.qx3.cradled = ((data[2] & 0x40) == 0); + break; + + default: + break; + } + } + return retval; +} + +/* send a command to the camera with an additional data transaction */ +static int do_command_extended(struct cam_data *cam, u16 command, + u8 a, u8 b, u8 c, u8 d, + u8 e, u8 f, u8 g, u8 h, + u8 i, u8 j, u8 k, u8 l) +{ + int retval; + u8 cmd[8], data[8]; + + cmd[0] = command>>8; + cmd[1] = command&0xff; + cmd[2] = a; + cmd[3] = b; + cmd[4] = c; + cmd[5] = d; + cmd[6] = 8; + cmd[7] = 0; + data[0] = e; + data[1] = f; + data[2] = g; + data[3] = h; + data[4] = i; + data[5] = j; + data[6] = k; + data[7] = l; + + retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); + if (retval) + DBG("%x - failed\n", command); + + return retval; +} + +/********************************************************************** + * + * Colorspace conversion + * + **********************************************************************/ +#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) + +static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt, + int linesize, int mmap_kludge) +{ + int y, u, v, r, g, b, y1; + + /* Odd lines use the same u and v as the previous line. + * Because of compression, it is necessary to get this + * information from the decoded image. */ + switch(out_fmt) { + case VIDEO_PALETTE_RGB555: + y = (*yuv++ - 16) * 76310; + y1 = (*yuv - 16) * 76310; + r = ((*(rgb+1-linesize)) & 0x7c) << 1; + g = ((*(rgb-linesize)) & 0xe0) >> 4 | + ((*(rgb+1-linesize)) & 0x03) << 6; + b = ((*(rgb-linesize)) & 0x1f) << 3; + u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; + v = (157968 * r - 132278 * g - 25690 * b) / 5366159; + r = 104635 * v; + g = -25690 * u - 53294 * v; + b = 132278 * u; + *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3); + *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6); + *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3); + *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6); + return 4; + case VIDEO_PALETTE_RGB565: + y = (*yuv++ - 16) * 76310; + y1 = (*yuv - 16) * 76310; + r = (*(rgb+1-linesize)) & 0xf8; + g = ((*(rgb-linesize)) & 0xe0) >> 3 | + ((*(rgb+1-linesize)) & 0x07) << 5; + b = ((*(rgb-linesize)) & 0x1f) << 3; + u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; + v = (157968 * r - 132278 * g - 25690 * b) / 5366159; + r = 104635 * v; + g = -25690 * u - 53294 * v; + b = 132278 * u; + *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3); + *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5); + *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3); + *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5); + return 4; + break; + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_RGB32: + y = (*yuv++ - 16) * 76310; + y1 = (*yuv - 16) * 76310; + if (mmap_kludge) { + r = *(rgb+2-linesize); + g = *(rgb+1-linesize); + b = *(rgb-linesize); + } else { + r = *(rgb-linesize); + g = *(rgb+1-linesize); + b = *(rgb+2-linesize); + } + u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; + v = (157968 * r - 132278 * g - 25690 * b) / 5366159; + r = 104635 * v; + g = -25690 * u + -53294 * v; + b = 132278 * u; + if (mmap_kludge) { + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + if(out_fmt == VIDEO_PALETTE_RGB32) + rgb++; + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + if(out_fmt == VIDEO_PALETTE_RGB32) + rgb++; + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } + if(out_fmt == VIDEO_PALETTE_RGB32) + return 8; + return 6; + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + y = *yuv++; + u = *(rgb+1-linesize); + y1 = *yuv; + v = *(rgb+3-linesize); + *rgb++ = y; + *rgb++ = u; + *rgb++ = y1; + *rgb = v; + return 4; + case VIDEO_PALETTE_UYVY: + u = *(rgb-linesize); + y = *yuv++; + v = *(rgb+2-linesize); + y1 = *yuv; + *rgb++ = u; + *rgb++ = y; + *rgb++ = v; + *rgb = y1; + return 4; + case VIDEO_PALETTE_GREY: + *rgb++ = *yuv++; + *rgb = *yuv; + return 2; + default: + DBG("Empty: %d\n", out_fmt); + return 0; + } +} + + +static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt, + int in_uyvy, int mmap_kludge) +{ + int y, u, v, r, g, b, y1; + + switch(out_fmt) { + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_RGB32: + if (in_uyvy) { + u = *yuv++ - 128; + y = (*yuv++ - 16) * 76310; + v = *yuv++ - 128; + y1 = (*yuv - 16) * 76310; + } else { + y = (*yuv++ - 16) * 76310; + u = *yuv++ - 128; + y1 = (*yuv++ - 16) * 76310; + v = *yuv - 128; + } + r = 104635 * v; + g = -25690 * u + -53294 * v; + b = 132278 * u; + break; + default: + y = *yuv++; + u = *yuv++; + y1 = *yuv++; + v = *yuv; + /* Just to avoid compiler warnings */ + r = 0; + g = 0; + b = 0; + break; + } + switch(out_fmt) { + case VIDEO_PALETTE_RGB555: + *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3); + *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6); + *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3); + *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6); + return 4; + case VIDEO_PALETTE_RGB565: + *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3); + *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5); + *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3); + *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5); + return 4; + case VIDEO_PALETTE_RGB24: + if (mmap_kludge) { + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } + return 6; + case VIDEO_PALETTE_RGB32: + if (mmap_kludge) { + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + rgb++; + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + rgb++; + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } + return 8; + case VIDEO_PALETTE_GREY: + *rgb++ = y; + *rgb = y1; + return 2; + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + *rgb++ = y; + *rgb++ = u; + *rgb++ = y1; + *rgb = v; + return 4; + case VIDEO_PALETTE_UYVY: + *rgb++ = u; + *rgb++ = y; + *rgb++ = v; + *rgb = y1; + return 4; + default: + DBG("Empty: %d\n", out_fmt); + return 0; + } +} + +static int skipcount(int count, int fmt) +{ + switch(fmt) { + case VIDEO_PALETTE_GREY: + return count; + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + case VIDEO_PALETTE_UYVY: + return 2*count; + case VIDEO_PALETTE_RGB24: + return 3*count; + case VIDEO_PALETTE_RGB32: + return 4*count; + default: + return 0; + } +} + +static int parse_picture(struct cam_data *cam, int size) +{ + u8 *obuf, *ibuf, *end_obuf; + int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt; + int rows, cols, linesize, subsample_422; + + /* make sure params don't change while we are decoding */ + mutex_lock(&cam->param_lock); + + obuf = cam->decompressed_frame.data; + end_obuf = obuf+CPIA_MAX_FRAME_SIZE; + ibuf = cam->raw_image; + origsize = size; + out_fmt = cam->vp.palette; + + if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) { + LOG("header not found\n"); + mutex_unlock(&cam->param_lock); + return -1; + } + + if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) { + LOG("wrong video size\n"); + mutex_unlock(&cam->param_lock); + return -1; + } + + if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) { + LOG("illegal subtype %d\n",ibuf[17]); + mutex_unlock(&cam->param_lock); + return -1; + } + subsample_422 = ibuf[17] == SUBSAMPLE_422; + + if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) { + LOG("illegal yuvorder %d\n",ibuf[18]); + mutex_unlock(&cam->param_lock); + return -1; + } + in_uyvy = ibuf[18] == YUVORDER_UYVY; + + if ((ibuf[24] != cam->params.roi.colStart) || + (ibuf[25] != cam->params.roi.colEnd) || + (ibuf[26] != cam->params.roi.rowStart) || + (ibuf[27] != cam->params.roi.rowEnd)) { + LOG("ROI mismatch\n"); + mutex_unlock(&cam->param_lock); + return -1; + } + cols = 8*(ibuf[25] - ibuf[24]); + rows = 4*(ibuf[27] - ibuf[26]); + + + if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) { + LOG("illegal compression %d\n",ibuf[28]); + mutex_unlock(&cam->param_lock); + return -1; + } + compressed = (ibuf[28] == COMPRESSED); + + if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) { + LOG("illegal decimation %d\n",ibuf[29]); + mutex_unlock(&cam->param_lock); + return -1; + } + decimation = (ibuf[29] == DECIMATION_ENAB); + + cam->params.yuvThreshold.yThreshold = ibuf[30]; + cam->params.yuvThreshold.uvThreshold = ibuf[31]; + cam->params.status.systemState = ibuf[32]; + cam->params.status.grabState = ibuf[33]; + cam->params.status.streamState = ibuf[34]; + cam->params.status.fatalError = ibuf[35]; + cam->params.status.cmdError = ibuf[36]; + cam->params.status.debugFlags = ibuf[37]; + cam->params.status.vpStatus = ibuf[38]; + cam->params.status.errorCode = ibuf[39]; + cam->fps = ibuf[41]; + mutex_unlock(&cam->param_lock); + + linesize = skipcount(cols, out_fmt); + ibuf += FRAME_HEADER_SIZE; + size -= FRAME_HEADER_SIZE; + ll = ibuf[0] | (ibuf[1] << 8); + ibuf += 2; + even_line = 1; + + while (size > 0) { + size -= (ll+2); + if (size < 0) { + LOG("Insufficient data in buffer\n"); + return -1; + } + + while (ll > 1) { + if (!compressed || (compressed && !(*ibuf & 1))) { + if(subsample_422 || even_line) { + obuf += yuvconvert(ibuf, obuf, out_fmt, + in_uyvy, cam->mmap_kludge); + ibuf += 4; + ll -= 4; + } else { + /* SUBSAMPLE_420 on an odd line */ + obuf += convert420(ibuf, obuf, + out_fmt, linesize, + cam->mmap_kludge); + ibuf += 2; + ll -= 2; + } + } else { + /*skip compressed interval from previous frame*/ + obuf += skipcount(*ibuf >> 1, out_fmt); + if (obuf > end_obuf) { + LOG("Insufficient buffer size\n"); + return -1; + } + ++ibuf; + ll--; + } + } + if (ll == 1) { + if (*ibuf != EOL) { + DBG("EOL not found giving up after %d/%d" + " bytes\n", origsize-size, origsize); + return -1; + } + + ++ibuf; /* skip over EOL */ + + if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) && + (ibuf[2] == EOI) && (ibuf[3] == EOI)) { + size -= 4; + break; + } + + if(decimation) { + /* skip the odd lines for now */ + obuf += linesize; + } + + if (size > 1) { + ll = ibuf[0] | (ibuf[1] << 8); + ibuf += 2; /* skip over line length */ + } + if(!decimation) + even_line = !even_line; + } else { + LOG("line length was not 1 but %d after %d/%d bytes\n", + ll, origsize-size, origsize); + return -1; + } + } + + if(decimation) { + /* interpolate odd rows */ + int i, j; + u8 *prev, *next; + prev = cam->decompressed_frame.data; + obuf = prev+linesize; + next = obuf+linesize; + for(i=1; idecompressed_frame.count = obuf-cam->decompressed_frame.data; + + return cam->decompressed_frame.count; +} + +/* InitStreamCap wrapper to select correct start line */ +static inline int init_stream_cap(struct cam_data *cam) +{ + return do_command(cam, CPIA_COMMAND_InitStreamCap, + 0, cam->params.streamStartLine, 0, 0); +} + + +/* find_over_exposure + * Finds a suitable value of OverExposure for use with SetFlickerCtrl + * Some calculation is required because this value changes with the brightness + * set with SetColourParameters + * + * Parameters: Brightness - last brightness value set with SetColourParameters + * + * Returns: OverExposure value to use with SetFlickerCtrl + */ +#define FLICKER_MAX_EXPOSURE 250 +#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146 +#define FLICKER_BRIGHTNESS_CONSTANT 59 +static int find_over_exposure(int brightness) +{ + int MaxAllowableOverExposure, OverExposure; + + MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness - + FLICKER_BRIGHTNESS_CONSTANT; + + if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) { + OverExposure = MaxAllowableOverExposure; + } else { + OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE; + } + + return OverExposure; +} +#undef FLICKER_MAX_EXPOSURE +#undef FLICKER_ALLOWABLE_OVER_EXPOSURE +#undef FLICKER_BRIGHTNESS_CONSTANT + +/* update various camera modes and settings */ +static void dispatch_commands(struct cam_data *cam) +{ + mutex_lock(&cam->param_lock); + if (cam->cmd_queue==COMMAND_NONE) { + mutex_unlock(&cam->param_lock); + return; + } + DEB_BYTE(cam->cmd_queue); + DEB_BYTE(cam->cmd_queue>>8); + if (cam->cmd_queue & COMMAND_SETFORMAT) { + do_command(cam, CPIA_COMMAND_SetFormat, + cam->params.format.videoSize, + cam->params.format.subSample, + cam->params.format.yuvOrder, 0); + do_command(cam, CPIA_COMMAND_SetROI, + cam->params.roi.colStart, cam->params.roi.colEnd, + cam->params.roi.rowStart, cam->params.roi.rowEnd); + cam->first_frame = 1; + } + + if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS) + do_command(cam, CPIA_COMMAND_SetColourParams, + cam->params.colourParams.brightness, + cam->params.colourParams.contrast, + cam->params.colourParams.saturation, 0); + + if (cam->cmd_queue & COMMAND_SETAPCOR) + do_command(cam, CPIA_COMMAND_SetApcor, + cam->params.apcor.gain1, + cam->params.apcor.gain2, + cam->params.apcor.gain4, + cam->params.apcor.gain8); + + if (cam->cmd_queue & COMMAND_SETVLOFFSET) + do_command(cam, CPIA_COMMAND_SetVLOffset, + cam->params.vlOffset.gain1, + cam->params.vlOffset.gain2, + cam->params.vlOffset.gain4, + cam->params.vlOffset.gain8); + + if (cam->cmd_queue & COMMAND_SETEXPOSURE) { + do_command_extended(cam, CPIA_COMMAND_SetExposure, + cam->params.exposure.gainMode, + 1, + cam->params.exposure.compMode, + cam->params.exposure.centreWeight, + cam->params.exposure.gain, + cam->params.exposure.fineExp, + cam->params.exposure.coarseExpLo, + cam->params.exposure.coarseExpHi, + cam->params.exposure.redComp, + cam->params.exposure.green1Comp, + cam->params.exposure.green2Comp, + cam->params.exposure.blueComp); + if(cam->params.exposure.expMode != 1) { + do_command_extended(cam, CPIA_COMMAND_SetExposure, + 0, + cam->params.exposure.expMode, + 0, 0, + cam->params.exposure.gain, + cam->params.exposure.fineExp, + cam->params.exposure.coarseExpLo, + cam->params.exposure.coarseExpHi, + 0, 0, 0, 0); + } + } + + if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) { + if (cam->params.colourBalance.balanceMode == 1) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 1, + cam->params.colourBalance.redGain, + cam->params.colourBalance.greenGain, + cam->params.colourBalance.blueGain); + do_command(cam, CPIA_COMMAND_SetColourBalance, + 3, 0, 0, 0); + } + if (cam->params.colourBalance.balanceMode == 2) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 2, 0, 0, 0); + } + if (cam->params.colourBalance.balanceMode == 3) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 3, 0, 0, 0); + } + } + + if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET) + do_command(cam, CPIA_COMMAND_SetCompressionTarget, + cam->params.compressionTarget.frTargeting, + cam->params.compressionTarget.targetFR, + cam->params.compressionTarget.targetQ, 0); + + if (cam->cmd_queue & COMMAND_SETYUVTHRESH) + do_command(cam, CPIA_COMMAND_SetYUVThresh, + cam->params.yuvThreshold.yThreshold, + cam->params.yuvThreshold.uvThreshold, 0, 0); + + if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS) + do_command_extended(cam, CPIA_COMMAND_SetCompressionParams, + 0, 0, 0, 0, + cam->params.compressionParams.hysteresis, + cam->params.compressionParams.threshMax, + cam->params.compressionParams.smallStep, + cam->params.compressionParams.largeStep, + cam->params.compressionParams.decimationHysteresis, + cam->params.compressionParams.frDiffStepThresh, + cam->params.compressionParams.qDiffStepThresh, + cam->params.compressionParams.decimationThreshMod); + + if (cam->cmd_queue & COMMAND_SETCOMPRESSION) + do_command(cam, CPIA_COMMAND_SetCompression, + cam->params.compression.mode, + cam->params.compression.decimation, 0, 0); + + if (cam->cmd_queue & COMMAND_SETSENSORFPS) + do_command(cam, CPIA_COMMAND_SetSensorFPS, + cam->params.sensorFps.divisor, + cam->params.sensorFps.baserate, 0, 0); + + if (cam->cmd_queue & COMMAND_SETFLICKERCTRL) + do_command(cam, CPIA_COMMAND_SetFlickerCtrl, + cam->params.flickerControl.flickerMode, + cam->params.flickerControl.coarseJump, + abs(cam->params.flickerControl.allowableOverExposure), + 0); + + if (cam->cmd_queue & COMMAND_SETECPTIMING) + do_command(cam, CPIA_COMMAND_SetECPTiming, + cam->params.ecpTiming, 0, 0, 0); + + if (cam->cmd_queue & COMMAND_PAUSE) + do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0); + + if (cam->cmd_queue & COMMAND_RESUME) + init_stream_cap(cam); + + if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected) + { + int p1 = (cam->params.qx3.bottomlight == 0) << 1; + int p2 = (cam->params.qx3.toplight == 0) << 3; + do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0); + do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0); + } + + cam->cmd_queue = COMMAND_NONE; + mutex_unlock(&cam->param_lock); + return; +} + + + +static void set_flicker(struct cam_params *params, volatile u32 *command_flags, + int on) +{ + /* Everything in here is from the Windows driver */ +#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \ + params->version.firmwareRevision == (y)) +/* define for compgain calculation */ +#if 0 +#define COMPGAIN(base, curexp, newexp) \ + (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5) +#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ + (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128)) +#else + /* equivalent functions without floating point math */ +#define COMPGAIN(base, curexp, newexp) \ + (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) ) +#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ + (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128))) +#endif + + + int currentexp = params->exposure.coarseExpLo + + params->exposure.coarseExpHi*256; + int startexp; + if (on) { + int cj = params->flickerControl.coarseJump; + params->flickerControl.flickerMode = 1; + params->flickerControl.disabled = 0; + if(params->exposure.expMode != 2) + *command_flags |= COMMAND_SETEXPOSURE; + params->exposure.expMode = 2; + currentexp = currentexp << params->exposure.gain; + params->exposure.gain = 0; + /* round down current exposure to nearest value */ + startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj; + if(startexp < 1) + startexp = 1; + startexp = (startexp * cj) - 1; + if(FIRMWARE_VERSION(1,2)) + while(startexp > MAX_EXP_102) + startexp -= cj; + else + while(startexp > MAX_EXP) + startexp -= cj; + params->exposure.coarseExpLo = startexp & 0xff; + params->exposure.coarseExpHi = startexp >> 8; + if (currentexp > startexp) { + if (currentexp > (2 * startexp)) + currentexp = 2 * startexp; + params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp); + params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp); + params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp); + params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp); + } else { + params->exposure.redComp = COMP_RED; + params->exposure.green1Comp = COMP_GREEN1; + params->exposure.green2Comp = COMP_GREEN2; + params->exposure.blueComp = COMP_BLUE; + } + if(FIRMWARE_VERSION(1,2)) + params->exposure.compMode = 0; + else + params->exposure.compMode = 1; + + params->apcor.gain1 = 0x18; + params->apcor.gain2 = 0x18; + params->apcor.gain4 = 0x16; + params->apcor.gain8 = 0x14; + *command_flags |= COMMAND_SETAPCOR; + } else { + params->flickerControl.flickerMode = 0; + params->flickerControl.disabled = 1; + /* Coarse = average of equivalent coarse for each comp channel */ + startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp); + startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp); + startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp); + startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp); + startexp = startexp >> 2; + while(startexp > MAX_EXP && + params->exposure.gain < params->exposure.gainMode-1) { + startexp = startexp >> 1; + ++params->exposure.gain; + } + if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102) + startexp = MAX_EXP_102; + if(startexp > MAX_EXP) + startexp = MAX_EXP; + params->exposure.coarseExpLo = startexp&0xff; + params->exposure.coarseExpHi = startexp >> 8; + params->exposure.redComp = COMP_RED; + params->exposure.green1Comp = COMP_GREEN1; + params->exposure.green2Comp = COMP_GREEN2; + params->exposure.blueComp = COMP_BLUE; + params->exposure.compMode = 1; + *command_flags |= COMMAND_SETEXPOSURE; + params->apcor.gain1 = 0x18; + params->apcor.gain2 = 0x16; + params->apcor.gain4 = 0x24; + params->apcor.gain8 = 0x34; + *command_flags |= COMMAND_SETAPCOR; + } + params->vlOffset.gain1 = 20; + params->vlOffset.gain2 = 24; + params->vlOffset.gain4 = 26; + params->vlOffset.gain8 = 26; + *command_flags |= COMMAND_SETVLOFFSET; +#undef FIRMWARE_VERSION +#undef EXP_FROM_COMP +#undef COMPGAIN +} + +#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \ + cam->params.version.firmwareRevision == (y)) +/* monitor the exposure and adjust the sensor frame rate if needed */ +static void monitor_exposure(struct cam_data *cam) +{ + u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8]; + int retval, light_exp, dark_exp, very_dark_exp; + int old_exposure, new_exposure, framerate; + + /* get necessary stats and register settings from camera */ + /* do_command can't handle this, so do it ourselves */ + cmd[0] = CPIA_COMMAND_ReadVPRegs>>8; + cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff; + cmd[2] = 30; + cmd[3] = 4; + cmd[4] = 9; + cmd[5] = 8; + cmd[6] = 8; + cmd[7] = 0; + retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); + if (retval) { + LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n", + retval); + return; + } + exp_acc = data[0]; + bcomp = data[1]; + gain = data[2]; + coarseL = data[3]; + + mutex_lock(&cam->param_lock); + light_exp = cam->params.colourParams.brightness + + TC - 50 + EXP_ACC_LIGHT; + if(light_exp > 255) + light_exp = 255; + dark_exp = cam->params.colourParams.brightness + + TC - 50 - EXP_ACC_DARK; + if(dark_exp < 0) + dark_exp = 0; + very_dark_exp = dark_exp/2; + + old_exposure = cam->params.exposure.coarseExpHi * 256 + + cam->params.exposure.coarseExpLo; + + if(!cam->params.flickerControl.disabled) { + /* Flicker control on */ + int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102; + bcomp += 128; /* decode */ + if(bcomp >= max_comp && exp_acc < dark_exp) { + /* dark */ + if(exp_acc < very_dark_exp) { + /* very dark */ + if(cam->exposure_status == EXPOSURE_VERY_DARK) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_VERY_DARK; + cam->exposure_count = 1; + } + } else { + /* just dark */ + if(cam->exposure_status == EXPOSURE_DARK) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_DARK; + cam->exposure_count = 1; + } + } + } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) { + /* light */ + if(old_exposure <= VERY_LOW_EXP) { + /* very light */ + if(cam->exposure_status == EXPOSURE_VERY_LIGHT) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_VERY_LIGHT; + cam->exposure_count = 1; + } + } else { + /* just light */ + if(cam->exposure_status == EXPOSURE_LIGHT) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_LIGHT; + cam->exposure_count = 1; + } + } + } else { + /* not dark or light */ + cam->exposure_status = EXPOSURE_NORMAL; + } + } else { + /* Flicker control off */ + if(old_exposure >= MAX_EXP && exp_acc < dark_exp) { + /* dark */ + if(exp_acc < very_dark_exp) { + /* very dark */ + if(cam->exposure_status == EXPOSURE_VERY_DARK) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_VERY_DARK; + cam->exposure_count = 1; + } + } else { + /* just dark */ + if(cam->exposure_status == EXPOSURE_DARK) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_DARK; + cam->exposure_count = 1; + } + } + } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) { + /* light */ + if(old_exposure <= VERY_LOW_EXP) { + /* very light */ + if(cam->exposure_status == EXPOSURE_VERY_LIGHT) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_VERY_LIGHT; + cam->exposure_count = 1; + } + } else { + /* just light */ + if(cam->exposure_status == EXPOSURE_LIGHT) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_LIGHT; + cam->exposure_count = 1; + } + } + } else { + /* not dark or light */ + cam->exposure_status = EXPOSURE_NORMAL; + } + } + + framerate = cam->fps; + if(framerate > 30 || framerate < 1) + framerate = 1; + + if(!cam->params.flickerControl.disabled) { + /* Flicker control on */ + if((cam->exposure_status == EXPOSURE_VERY_DARK || + cam->exposure_status == EXPOSURE_DARK) && + cam->exposure_count >= DARK_TIME*framerate && + cam->params.sensorFps.divisor < 3) { + + /* dark for too long */ + ++cam->params.sensorFps.divisor; + cam->cmd_queue |= COMMAND_SETSENSORFPS; + + cam->params.flickerControl.coarseJump = + flicker_jumps[cam->mainsFreq] + [cam->params.sensorFps.baserate] + [cam->params.sensorFps.divisor]; + cam->cmd_queue |= COMMAND_SETFLICKERCTRL; + + new_exposure = cam->params.flickerControl.coarseJump-1; + while(new_exposure < old_exposure/2) + new_exposure += cam->params.flickerControl.coarseJump; + cam->params.exposure.coarseExpLo = new_exposure & 0xff; + cam->params.exposure.coarseExpHi = new_exposure >> 8; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + cam->exposure_status = EXPOSURE_NORMAL; + LOG("Automatically decreasing sensor_fps\n"); + + } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT || + cam->exposure_status == EXPOSURE_LIGHT) && + cam->exposure_count >= LIGHT_TIME*framerate && + cam->params.sensorFps.divisor > 0) { + + /* light for too long */ + int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ; + + --cam->params.sensorFps.divisor; + cam->cmd_queue |= COMMAND_SETSENSORFPS; + + cam->params.flickerControl.coarseJump = + flicker_jumps[cam->mainsFreq] + [cam->params.sensorFps.baserate] + [cam->params.sensorFps.divisor]; + cam->cmd_queue |= COMMAND_SETFLICKERCTRL; + + new_exposure = cam->params.flickerControl.coarseJump-1; + while(new_exposure < 2*old_exposure && + new_exposure+ + cam->params.flickerControl.coarseJump < max_exp) + new_exposure += cam->params.flickerControl.coarseJump; + cam->params.exposure.coarseExpLo = new_exposure & 0xff; + cam->params.exposure.coarseExpHi = new_exposure >> 8; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + cam->exposure_status = EXPOSURE_NORMAL; + LOG("Automatically increasing sensor_fps\n"); + } + } else { + /* Flicker control off */ + if((cam->exposure_status == EXPOSURE_VERY_DARK || + cam->exposure_status == EXPOSURE_DARK) && + cam->exposure_count >= DARK_TIME*framerate && + cam->params.sensorFps.divisor < 3) { + + /* dark for too long */ + ++cam->params.sensorFps.divisor; + cam->cmd_queue |= COMMAND_SETSENSORFPS; + + if(cam->params.exposure.gain > 0) { + --cam->params.exposure.gain; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + } + cam->exposure_status = EXPOSURE_NORMAL; + LOG("Automatically decreasing sensor_fps\n"); + + } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT || + cam->exposure_status == EXPOSURE_LIGHT) && + cam->exposure_count >= LIGHT_TIME*framerate && + cam->params.sensorFps.divisor > 0) { + + /* light for too long */ + --cam->params.sensorFps.divisor; + cam->cmd_queue |= COMMAND_SETSENSORFPS; + + if(cam->params.exposure.gain < + cam->params.exposure.gainMode-1) { + ++cam->params.exposure.gain; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + } + cam->exposure_status = EXPOSURE_NORMAL; + LOG("Automatically increasing sensor_fps\n"); + } + } + mutex_unlock(&cam->param_lock); +} + +/*-----------------------------------------------------------------*/ +/* if flicker is switched off, this function switches it back on.It checks, + however, that conditions are suitable before restarting it. + This should only be called for firmware version 1.2. + + It also adjust the colour balance when an exposure step is detected - as + long as flicker is running +*/ +static void restart_flicker(struct cam_data *cam) +{ + int cam_exposure, old_exp; + if(!FIRMWARE_VERSION(1,2)) + return; + mutex_lock(&cam->param_lock); + if(cam->params.flickerControl.flickerMode == 0 || + cam->raw_image[39] == 0) { + mutex_unlock(&cam->param_lock); + return; + } + cam_exposure = cam->raw_image[39]*2; + old_exp = cam->params.exposure.coarseExpLo + + cam->params.exposure.coarseExpHi*256; + /* + see how far away camera exposure is from a valid + flicker exposure value + */ + cam_exposure %= cam->params.flickerControl.coarseJump; + if(!cam->params.flickerControl.disabled && + cam_exposure <= cam->params.flickerControl.coarseJump - 3) { + /* Flicker control auto-disabled */ + cam->params.flickerControl.disabled = 1; + } + + if(cam->params.flickerControl.disabled && + cam->params.flickerControl.flickerMode && + old_exp > cam->params.flickerControl.coarseJump + + ROUND_UP_EXP_FOR_FLICKER) { + /* exposure is now high enough to switch + flicker control back on */ + set_flicker(&cam->params, &cam->cmd_queue, 1); + if((cam->cmd_queue & COMMAND_SETEXPOSURE) && + cam->params.exposure.expMode == 2) + cam->exposure_status = EXPOSURE_NORMAL; + + } + mutex_unlock(&cam->param_lock); +} +#undef FIRMWARE_VERSION + +static int clear_stall(struct cam_data *cam) +{ + /* FIXME: Does this actually work? */ + LOG("Clearing stall\n"); + + cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0); + do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); + return cam->params.status.streamState != STREAM_PAUSED; +} + +/* kernel thread function to read image from camera */ +static int fetch_frame(void *data) +{ + int image_size, retry; + struct cam_data *cam = (struct cam_data *)data; + unsigned long oldjif, rate, diff; + + /* Allow up to two bad images in a row to be read and + * ignored before an error is reported */ + for (retry = 0; retry < 3; ++retry) { + if (retry) + DBG("retry=%d\n", retry); + + if (!cam->ops) + continue; + + /* load first frame always uncompressed */ + if (cam->first_frame && + cam->params.compression.mode != CPIA_COMPRESSION_NONE) { + do_command(cam, CPIA_COMMAND_SetCompression, + CPIA_COMPRESSION_NONE, + NO_DECIMATION, 0, 0); + /* Trial & error - Discarding a frame prevents the + first frame from having an error in the data. */ + do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); + } + + /* init camera upload */ + if (do_command(cam, CPIA_COMMAND_GrabFrame, 0, + cam->params.streamStartLine, 0, 0)) + continue; + + if (cam->ops->wait_for_stream_ready) { + /* loop until image ready */ + int count = 0; + do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); + while (cam->params.status.streamState != STREAM_READY) { + if(++count > READY_TIMEOUT) + break; + if(cam->params.status.streamState == + STREAM_PAUSED) { + /* Bad news */ + if(!clear_stall(cam)) + return -EIO; + } + + cond_resched(); + + /* sleep for 10 ms, hopefully ;) */ + msleep_interruptible(10); + if (signal_pending(current)) + return -EINTR; + + do_command(cam, CPIA_COMMAND_GetCameraStatus, + 0, 0, 0, 0); + } + if(cam->params.status.streamState != STREAM_READY) { + continue; + } + } + + cond_resched(); + + /* grab image from camera */ + oldjif = jiffies; + image_size = cam->ops->streamRead(cam->lowlevel_data, + cam->raw_image, 0); + if (image_size <= 0) { + DBG("streamRead failed: %d\n", image_size); + continue; + } + + rate = image_size * HZ / 1024; + diff = jiffies-oldjif; + cam->transfer_rate = diff==0 ? rate : rate/diff; + /* diff==0 ? unlikely but possible */ + + /* Switch flicker control back on if it got turned off */ + restart_flicker(cam); + + /* If AEC is enabled, monitor the exposure and + adjust the sensor frame rate if needed */ + if(cam->params.exposure.expMode == 2) + monitor_exposure(cam); + + /* camera idle now so dispatch queued commands */ + dispatch_commands(cam); + + /* Update our knowledge of the camera state */ + do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); + do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); + do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0); + + /* decompress and convert image to by copying it from + * raw_image to decompressed_frame + */ + + cond_resched(); + + cam->image_size = parse_picture(cam, image_size); + if (cam->image_size <= 0) { + DBG("parse_picture failed %d\n", cam->image_size); + if(cam->params.compression.mode != + CPIA_COMPRESSION_NONE) { + /* Compression may not work right if we + had a bad frame, get the next one + uncompressed. */ + cam->first_frame = 1; + do_command(cam, CPIA_COMMAND_SetGrabMode, + CPIA_GRAB_SINGLE, 0, 0, 0); + /* FIXME: Trial & error - need up to 70ms for + the grab mode change to complete ? */ + msleep_interruptible(70); + if (signal_pending(current)) + return -EINTR; + } + } else + break; + } + + if (retry < 3) { + /* FIXME: this only works for double buffering */ + if (cam->frame[cam->curframe].state == FRAME_READY) { + memcpy(cam->frame[cam->curframe].data, + cam->decompressed_frame.data, + cam->decompressed_frame.count); + cam->frame[cam->curframe].state = FRAME_DONE; + } else + cam->decompressed_frame.state = FRAME_DONE; + + if (cam->first_frame) { + cam->first_frame = 0; + do_command(cam, CPIA_COMMAND_SetCompression, + cam->params.compression.mode, + cam->params.compression.decimation, 0, 0); + + /* Switch from single-grab to continuous grab */ + do_command(cam, CPIA_COMMAND_SetGrabMode, + CPIA_GRAB_CONTINUOUS, 0, 0, 0); + } + return 0; + } + return -EIO; +} + +static int capture_frame(struct cam_data *cam, struct video_mmap *vm) +{ + if (!cam->frame_buf) { + /* we do lazy allocation */ + int err; + if ((err = allocate_frame_buf(cam))) + return err; + } + + cam->curframe = vm->frame; + cam->frame[cam->curframe].state = FRAME_READY; + return fetch_frame(cam); +} + +static int goto_high_power(struct cam_data *cam) +{ + if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) + return -EIO; + msleep_interruptible(40); /* windows driver does it too */ + if(signal_pending(current)) + return -EINTR; + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + return -EIO; + if (cam->params.status.systemState == HI_POWER_STATE) { + DBG("camera now in HIGH power state\n"); + return 0; + } + printstatus(cam); + return -EIO; +} + +static int goto_low_power(struct cam_data *cam) +{ + if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0)) + return -1; + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + return -1; + if (cam->params.status.systemState == LO_POWER_STATE) { + DBG("camera now in LOW power state\n"); + return 0; + } + printstatus(cam); + return -1; +} + +static void save_camera_state(struct cam_data *cam) +{ + if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE)) + do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); + if(!(cam->cmd_queue & COMMAND_SETEXPOSURE)) + do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); + + DBG("%d/%d/%d/%d/%d/%d/%d/%d\n", + cam->params.exposure.gain, + cam->params.exposure.fineExp, + cam->params.exposure.coarseExpLo, + cam->params.exposure.coarseExpHi, + cam->params.exposure.redComp, + cam->params.exposure.green1Comp, + cam->params.exposure.green2Comp, + cam->params.exposure.blueComp); + DBG("%d/%d/%d\n", + cam->params.colourBalance.redGain, + cam->params.colourBalance.greenGain, + cam->params.colourBalance.blueGain); +} + +static int set_camera_state(struct cam_data *cam) +{ + cam->cmd_queue = COMMAND_SETCOMPRESSION | + COMMAND_SETCOMPRESSIONTARGET | + COMMAND_SETCOLOURPARAMS | + COMMAND_SETFORMAT | + COMMAND_SETYUVTHRESH | + COMMAND_SETECPTIMING | + COMMAND_SETCOMPRESSIONPARAMS | + COMMAND_SETEXPOSURE | + COMMAND_SETCOLOURBALANCE | + COMMAND_SETSENSORFPS | + COMMAND_SETAPCOR | + COMMAND_SETFLICKERCTRL | + COMMAND_SETVLOFFSET; + + do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0); + dispatch_commands(cam); + + /* Wait 6 frames for the sensor to get all settings and + AEC/ACB to settle */ + msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) * + (1 << cam->params.sensorFps.divisor) + 10); + + if(signal_pending(current)) + return -EINTR; + + save_camera_state(cam); + + return 0; +} + +static void get_version_information(struct cam_data *cam) +{ + /* GetCPIAVersion */ + do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0); + + /* GetPnPID */ + do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0); +} + +/* initialize camera */ +static int reset_camera(struct cam_data *cam) +{ + int err; + /* Start the camera in low power mode */ + if (goto_low_power(cam)) { + if (cam->params.status.systemState != WARM_BOOT_STATE) + return -ENODEV; + + /* FIXME: this is just dirty trial and error */ + err = goto_high_power(cam); + if(err) + return err; + do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); + if (goto_low_power(cam)) + return -ENODEV; + } + + /* procedure described in developer's guide p3-28 */ + + /* Check the firmware version. */ + cam->params.version.firmwareVersion = 0; + get_version_information(cam); + if (cam->params.version.firmwareVersion != 1) + return -ENODEV; + + /* A bug in firmware 1-02 limits gainMode to 2 */ + if(cam->params.version.firmwareRevision <= 2 && + cam->params.exposure.gainMode > 2) { + cam->params.exposure.gainMode = 2; + } + + /* set QX3 detected flag */ + cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 && + cam->params.pnpID.product == 0x0001); + + /* The fatal error checking should be done after + * the camera powers up (developer's guide p 3-38) */ + + /* Set streamState before transition to high power to avoid bug + * in firmware 1-02 */ + do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0, + STREAM_NOT_READY, 0); + + /* GotoHiPower */ + err = goto_high_power(cam); + if (err) + return err; + + /* Check the camera status */ + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + return -EIO; + + if (cam->params.status.fatalError) { + DBG("fatal_error: %#04x\n", + cam->params.status.fatalError); + DBG("vp_status: %#04x\n", + cam->params.status.vpStatus); + if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) { + /* Fatal error in camera */ + return -EIO; + } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) { + /* Firmware 1-02 may do this for parallel port cameras, + * just clear the flags (developer's guide p 3-38) */ + do_command(cam, CPIA_COMMAND_ModifyCameraStatus, + FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0); + } + } + + /* Check the camera status again */ + if (cam->params.status.fatalError) { + if (cam->params.status.fatalError) + return -EIO; + } + + /* VPVersion can't be retrieved before the camera is in HiPower, + * so get it here instead of in get_version_information. */ + do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0); + + /* set camera to a known state */ + return set_camera_state(cam); +} + +static void put_cam(struct cpia_camera_ops* ops) +{ + if (ops->owner) + module_put(ops->owner); +} + +/* ------------------------- V4L interface --------------------- */ +static int cpia_open(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct cam_data *cam = dev->priv; + int err; + + if (!cam) { + DBG("Internal error, cam_data not found!\n"); + return -ENODEV; + } + + if (cam->open_count > 0) { + DBG("Camera already open\n"); + return -EBUSY; + } + + if (!try_module_get(cam->ops->owner)) + return -ENODEV; + + mutex_lock(&cam->busy_lock); + err = -ENOMEM; + if (!cam->raw_image) { + cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE); + if (!cam->raw_image) + goto oops; + } + + if (!cam->decompressed_frame.data) { + cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE); + if (!cam->decompressed_frame.data) + goto oops; + } + + /* open cpia */ + err = -ENODEV; + if (cam->ops->open(cam->lowlevel_data)) + goto oops; + + /* reset the camera */ + if ((err = reset_camera(cam)) != 0) { + cam->ops->close(cam->lowlevel_data); + goto oops; + } + + err = -EINTR; + if(signal_pending(current)) + goto oops; + + /* Set ownership of /proc/cpia/videoX to current user */ + if(cam->proc_entry) + cam->proc_entry->uid = current->uid; + + /* set mark for loading first frame uncompressed */ + cam->first_frame = 1; + + /* init it to something */ + cam->mmap_kludge = 0; + + ++cam->open_count; + file->private_data = dev; + mutex_unlock(&cam->busy_lock); + return 0; + + oops: + if (cam->decompressed_frame.data) { + rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); + cam->decompressed_frame.data = NULL; + } + if (cam->raw_image) { + rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); + cam->raw_image = NULL; + } + mutex_unlock(&cam->busy_lock); + put_cam(cam->ops); + return err; +} + +static int cpia_close(struct inode *inode, struct file *file) +{ + struct video_device *dev = file->private_data; + struct cam_data *cam = dev->priv; + + if (cam->ops) { + /* Return ownership of /proc/cpia/videoX to root */ + if(cam->proc_entry) + cam->proc_entry->uid = 0; + + /* save camera state for later open (developers guide ch 3.5.3) */ + save_camera_state(cam); + + /* GotoLoPower */ + goto_low_power(cam); + + /* Update the camera status */ + do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); + + /* cleanup internal state stuff */ + free_frames(cam->frame); + + /* close cpia */ + cam->ops->close(cam->lowlevel_data); + + put_cam(cam->ops); + } + + if (--cam->open_count == 0) { + /* clean up capture-buffers */ + if (cam->raw_image) { + rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); + cam->raw_image = NULL; + } + + if (cam->decompressed_frame.data) { + rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); + cam->decompressed_frame.data = NULL; + } + + if (cam->frame_buf) + free_frame_buf(cam); + + if (!cam->ops) + kfree(cam); + } + file->private_data = NULL; + + return 0; +} + +static ssize_t cpia_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct video_device *dev = file->private_data; + struct cam_data *cam = dev->priv; + int err; + + /* make this _really_ smp and multithread-safe */ + if (mutex_lock_interruptible(&cam->busy_lock)) + return -EINTR; + + if (!buf) { + DBG("buf NULL\n"); + mutex_unlock(&cam->busy_lock); + return -EINVAL; + } + + if (!count) { + DBG("count 0\n"); + mutex_unlock(&cam->busy_lock); + return 0; + } + + if (!cam->ops) { + DBG("ops NULL\n"); + mutex_unlock(&cam->busy_lock); + return -ENODEV; + } + + /* upload frame */ + cam->decompressed_frame.state = FRAME_READY; + cam->mmap_kludge=0; + if((err = fetch_frame(cam)) != 0) { + DBG("ERROR from fetch_frame: %d\n", err); + mutex_unlock(&cam->busy_lock); + return err; + } + cam->decompressed_frame.state = FRAME_UNUSED; + + /* copy data to user space */ + if (cam->decompressed_frame.count > count) { + DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count, + (unsigned long) count); + mutex_unlock(&cam->busy_lock); + return -EFAULT; + } + if (copy_to_user(buf, cam->decompressed_frame.data, + cam->decompressed_frame.count)) { + DBG("copy_to_user failed\n"); + mutex_unlock(&cam->busy_lock); + return -EFAULT; + } + + mutex_unlock(&cam->busy_lock); + return cam->decompressed_frame.count; +} + +static int cpia_do_ioctl(struct inode *inode, struct file *file, + unsigned int ioctlnr, void *arg) +{ + struct video_device *dev = file->private_data; + struct cam_data *cam = dev->priv; + int retval = 0; + + if (!cam || !cam->ops) + return -ENODEV; + + /* make this _really_ smp-safe */ + if (mutex_lock_interruptible(&cam->busy_lock)) + return -EINTR; + + //DBG("cpia_ioctl: %u\n", ioctlnr); + + switch (ioctlnr) { + /* query capabilities */ + case VIDIOCGCAP: + { + struct video_capability *b = arg; + + DBG("VIDIOCGCAP\n"); + strcpy(b->name, "CPiA Camera"); + b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; + b->channels = 1; + b->audios = 0; + b->maxwidth = 352; /* VIDEOSIZE_CIF */ + b->maxheight = 288; + b->minwidth = 48; /* VIDEOSIZE_48_48 */ + b->minheight = 48; + break; + } + + /* get/set video source - we are a camera and nothing else */ + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + + DBG("VIDIOCGCHAN\n"); + if (v->channel != 0) { + retval = -EINVAL; + break; + } + + v->channel = 0; + strcpy(v->name, "Camera"); + v->tuners = 0; + v->flags = 0; + v->type = VIDEO_TYPE_CAMERA; + v->norm = 0; + break; + } + + case VIDIOCSCHAN: + { + struct video_channel *v = arg; + + DBG("VIDIOCSCHAN\n"); + if (v->channel != 0) + retval = -EINVAL; + break; + } + + /* image properties */ + case VIDIOCGPICT: + { + struct video_picture *pic = arg; + DBG("VIDIOCGPICT\n"); + *pic = cam->vp; + break; + } + + case VIDIOCSPICT: + { + struct video_picture *vp = arg; + + DBG("VIDIOCSPICT\n"); + + /* check validity */ + DBG("palette: %d\n", vp->palette); + DBG("depth: %d\n", vp->depth); + if (!valid_mode(vp->palette, vp->depth)) { + retval = -EINVAL; + break; + } + + mutex_lock(&cam->param_lock); + /* brightness, colour, contrast need no check 0-65535 */ + cam->vp = *vp; + /* update cam->params.colourParams */ + cam->params.colourParams.brightness = vp->brightness*100/65535; + cam->params.colourParams.contrast = vp->contrast*100/65535; + cam->params.colourParams.saturation = vp->colour*100/65535; + /* contrast is in steps of 8, so round */ + cam->params.colourParams.contrast = + ((cam->params.colourParams.contrast + 3) / 8) * 8; + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2 && + cam->params.colourParams.contrast > 80) { + /* 1-02 firmware limits contrast to 80 */ + cam->params.colourParams.contrast = 80; + } + + /* Adjust flicker control if necessary */ + if(cam->params.flickerControl.allowableOverExposure < 0) + cam->params.flickerControl.allowableOverExposure = + -find_over_exposure(cam->params.colourParams.brightness); + if(cam->params.flickerControl.flickerMode != 0) + cam->cmd_queue |= COMMAND_SETFLICKERCTRL; + + + /* queue command to update camera */ + cam->cmd_queue |= COMMAND_SETCOLOURPARAMS; + mutex_unlock(&cam->param_lock); + DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n", + vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour, + vp->contrast); + break; + } + + /* get/set capture window */ + case VIDIOCGWIN: + { + struct video_window *vw = arg; + DBG("VIDIOCGWIN\n"); + + *vw = cam->vw; + break; + } + + case VIDIOCSWIN: + { + /* copy_from_user, check validity, copy to internal structure */ + struct video_window *vw = arg; + DBG("VIDIOCSWIN\n"); + + if (vw->clipcount != 0) { /* clipping not supported */ + retval = -EINVAL; + break; + } + if (vw->clips != NULL) { /* clipping not supported */ + retval = -EINVAL; + break; + } + + /* we set the video window to something smaller or equal to what + * is requested by the user??? + */ + mutex_lock(&cam->param_lock); + if (vw->width != cam->vw.width || vw->height != cam->vw.height) { + int video_size = match_videosize(vw->width, vw->height); + + if (video_size < 0) { + retval = -EINVAL; + mutex_unlock(&cam->param_lock); + break; + } + cam->video_size = video_size; + + /* video size is changing, reset the subcapture area */ + memset(&cam->vc, 0, sizeof(cam->vc)); + + set_vw_size(cam); + DBG("%d / %d\n", cam->vw.width, cam->vw.height); + cam->cmd_queue |= COMMAND_SETFORMAT; + } + + mutex_unlock(&cam->param_lock); + + /* setformat ignored by camera during streaming, + * so stop/dispatch/start */ + if (cam->cmd_queue & COMMAND_SETFORMAT) { + DBG("\n"); + dispatch_commands(cam); + } + DBG("%d/%d:%d\n", cam->video_size, + cam->vw.width, cam->vw.height); + break; + } + + /* mmap interface */ + case VIDIOCGMBUF: + { + struct video_mbuf *vm = arg; + int i; + + DBG("VIDIOCGMBUF\n"); + memset(vm, 0, sizeof(*vm)); + vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM; + vm->frames = FRAME_NUM; + for (i = 0; i < FRAME_NUM; i++) + vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i; + break; + } + + case VIDIOCMCAPTURE: + { + struct video_mmap *vm = arg; + int video_size; + + DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame, + vm->width, vm->height); + if (vm->frame<0||vm->frame>=FRAME_NUM) { + retval = -EINVAL; + break; + } + + /* set video format */ + cam->vp.palette = vm->format; + switch(vm->format) { + case VIDEO_PALETTE_GREY: + cam->vp.depth=8; + break; + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + case VIDEO_PALETTE_UYVY: + cam->vp.depth = 16; + break; + case VIDEO_PALETTE_RGB24: + cam->vp.depth = 24; + break; + case VIDEO_PALETTE_RGB32: + cam->vp.depth = 32; + break; + default: + retval = -EINVAL; + break; + } + if (retval) + break; + + /* set video size */ + video_size = match_videosize(vm->width, vm->height); + if (video_size < 0) { + retval = -EINVAL; + break; + } + if (video_size != cam->video_size) { + cam->video_size = video_size; + + /* video size is changing, reset the subcapture area */ + memset(&cam->vc, 0, sizeof(cam->vc)); + + set_vw_size(cam); + cam->cmd_queue |= COMMAND_SETFORMAT; + dispatch_commands(cam); + } + /* according to v4l-spec we must start streaming here */ + cam->mmap_kludge = 1; + retval = capture_frame(cam, vm); + + break; + } + + case VIDIOCSYNC: + { + int *frame = arg; + + //DBG("VIDIOCSYNC: %d\n", *frame); + + if (*frame<0 || *frame >= FRAME_NUM) { + retval = -EINVAL; + break; + } + + switch (cam->frame[*frame].state) { + case FRAME_UNUSED: + case FRAME_READY: + case FRAME_GRABBING: + DBG("sync to unused frame %d\n", *frame); + retval = -EINVAL; + break; + + case FRAME_DONE: + cam->frame[*frame].state = FRAME_UNUSED; + //DBG("VIDIOCSYNC: %d synced\n", *frame); + break; + } + if (retval == -EINTR) { + /* FIXME - xawtv does not handle this nice */ + retval = 0; + } + break; + } + + case VIDIOCGCAPTURE: + { + struct video_capture *vc = arg; + + DBG("VIDIOCGCAPTURE\n"); + + *vc = cam->vc; + + break; + } + + case VIDIOCSCAPTURE: + { + struct video_capture *vc = arg; + + DBG("VIDIOCSCAPTURE\n"); + + if (vc->decimation != 0) { /* How should this be used? */ + retval = -EINVAL; + break; + } + if (vc->flags != 0) { /* Even/odd grab not supported */ + retval = -EINVAL; + break; + } + + /* Clip to the resolution we can set for the ROI + (every 8 columns and 4 rows) */ + vc->x = vc->x & ~(__u32)7; + vc->y = vc->y & ~(__u32)3; + vc->width = vc->width & ~(__u32)7; + vc->height = vc->height & ~(__u32)3; + + if(vc->width == 0 || vc->height == 0 || + vc->x + vc->width > cam->vw.width || + vc->y + vc->height > cam->vw.height) { + retval = -EINVAL; + break; + } + + DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height); + + mutex_lock(&cam->param_lock); + + cam->vc.x = vc->x; + cam->vc.y = vc->y; + cam->vc.width = vc->width; + cam->vc.height = vc->height; + + set_vw_size(cam); + cam->cmd_queue |= COMMAND_SETFORMAT; + + mutex_unlock(&cam->param_lock); + + /* setformat ignored by camera during streaming, + * so stop/dispatch/start */ + dispatch_commands(cam); + break; + } + + case VIDIOCGUNIT: + { + struct video_unit *vu = arg; + + DBG("VIDIOCGUNIT\n"); + + vu->video = cam->vdev.minor; + vu->vbi = VIDEO_NO_UNIT; + vu->radio = VIDEO_NO_UNIT; + vu->audio = VIDEO_NO_UNIT; + vu->teletext = VIDEO_NO_UNIT; + + break; + } + + + /* pointless to implement overlay with this camera */ + case VIDIOCCAPTURE: + case VIDIOCGFBUF: + case VIDIOCSFBUF: + case VIDIOCKEY: + /* tuner interface - we have none */ + case VIDIOCGTUNER: + case VIDIOCSTUNER: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + /* audio interface - we have none */ + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + retval = -EINVAL; + break; + default: + retval = -ENOIOCTLCMD; + break; + } + + mutex_unlock(&cam->busy_lock); + return retval; +} + +static int cpia_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, cpia_do_ioctl); +} + + +/* FIXME */ +static int cpia_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *dev = file->private_data; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; + unsigned long page, pos; + struct cam_data *cam = dev->priv; + int retval; + + if (!cam || !cam->ops) + return -ENODEV; + + DBG("cpia_mmap: %ld\n", size); + + if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE) + return -EINVAL; + + if (!cam || !cam->ops) + return -ENODEV; + + /* make this _really_ smp-safe */ + if (mutex_lock_interruptible(&cam->busy_lock)) + return -EINTR; + + if (!cam->frame_buf) { /* we do lazy allocation */ + if ((retval = allocate_frame_buf(cam))) { + mutex_unlock(&cam->busy_lock); + return retval; + } + } + + pos = (unsigned long)(cam->frame_buf); + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { + mutex_unlock(&cam->busy_lock); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + DBG("cpia_mmap: %ld\n", size); + mutex_unlock(&cam->busy_lock); + + return 0; +} + +static struct file_operations cpia_fops = { + .owner = THIS_MODULE, + .open = cpia_open, + .release = cpia_close, + .read = cpia_read, + .mmap = cpia_mmap, + .ioctl = cpia_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device cpia_template = { + .owner = THIS_MODULE, + .name = "CPiA Camera", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_CPIA, + .fops = &cpia_fops, +}; + +/* initialise cam_data structure */ +static void reset_camera_struct(struct cam_data *cam) +{ + /* The following parameter values are the defaults from + * "Software Developer's Guide for CPiA Cameras". Any changes + * to the defaults are noted in comments. */ + cam->params.colourParams.brightness = 50; + cam->params.colourParams.contrast = 48; + cam->params.colourParams.saturation = 50; + cam->params.exposure.gainMode = 4; + cam->params.exposure.expMode = 2; /* AEC */ + cam->params.exposure.compMode = 1; + cam->params.exposure.centreWeight = 1; + cam->params.exposure.gain = 0; + cam->params.exposure.fineExp = 0; + cam->params.exposure.coarseExpLo = 185; + cam->params.exposure.coarseExpHi = 0; + cam->params.exposure.redComp = COMP_RED; + cam->params.exposure.green1Comp = COMP_GREEN1; + cam->params.exposure.green2Comp = COMP_GREEN2; + cam->params.exposure.blueComp = COMP_BLUE; + cam->params.colourBalance.balanceMode = 2; /* ACB */ + cam->params.colourBalance.redGain = 32; + cam->params.colourBalance.greenGain = 6; + cam->params.colourBalance.blueGain = 92; + cam->params.apcor.gain1 = 0x18; + cam->params.apcor.gain2 = 0x16; + cam->params.apcor.gain4 = 0x24; + cam->params.apcor.gain8 = 0x34; + cam->params.flickerControl.flickerMode = 0; + cam->params.flickerControl.disabled = 1; + + cam->params.flickerControl.coarseJump = + flicker_jumps[cam->mainsFreq] + [cam->params.sensorFps.baserate] + [cam->params.sensorFps.divisor]; + cam->params.flickerControl.allowableOverExposure = + -find_over_exposure(cam->params.colourParams.brightness); + cam->params.vlOffset.gain1 = 20; + cam->params.vlOffset.gain2 = 24; + cam->params.vlOffset.gain4 = 26; + cam->params.vlOffset.gain8 = 26; + cam->params.compressionParams.hysteresis = 3; + cam->params.compressionParams.threshMax = 11; + cam->params.compressionParams.smallStep = 1; + cam->params.compressionParams.largeStep = 3; + cam->params.compressionParams.decimationHysteresis = 2; + cam->params.compressionParams.frDiffStepThresh = 5; + cam->params.compressionParams.qDiffStepThresh = 3; + cam->params.compressionParams.decimationThreshMod = 2; + /* End of default values from Software Developer's Guide */ + + cam->transfer_rate = 0; + cam->exposure_status = EXPOSURE_NORMAL; + + /* Set Sensor FPS to 15fps. This seems better than 30fps + * for indoor lighting. */ + cam->params.sensorFps.divisor = 1; + cam->params.sensorFps.baserate = 1; + + cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */ + cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */ + + cam->params.format.subSample = SUBSAMPLE_422; + cam->params.format.yuvOrder = YUVORDER_YUYV; + + cam->params.compression.mode = CPIA_COMPRESSION_AUTO; + cam->params.compressionTarget.frTargeting = + CPIA_COMPRESSION_TARGET_QUALITY; + cam->params.compressionTarget.targetFR = 15; /* From windows driver */ + cam->params.compressionTarget.targetQ = 5; /* From windows driver */ + + cam->params.qx3.qx3_detected = 0; + cam->params.qx3.toplight = 0; + cam->params.qx3.bottomlight = 0; + cam->params.qx3.button = 0; + cam->params.qx3.cradled = 0; + + cam->video_size = VIDEOSIZE_CIF; + + cam->vp.colour = 32768; /* 50% */ + cam->vp.hue = 32768; /* 50% */ + cam->vp.brightness = 32768; /* 50% */ + cam->vp.contrast = 32768; /* 50% */ + cam->vp.whiteness = 0; /* not used -> grayscale only */ + cam->vp.depth = 24; /* to be set by user */ + cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */ + + cam->vc.x = 0; + cam->vc.y = 0; + cam->vc.width = 0; + cam->vc.height = 0; + + cam->vw.x = 0; + cam->vw.y = 0; + set_vw_size(cam); + cam->vw.chromakey = 0; + cam->vw.flags = 0; + cam->vw.clipcount = 0; + cam->vw.clips = NULL; + + cam->cmd_queue = COMMAND_NONE; + cam->first_frame = 1; + + return; +} + +/* initialize cam_data structure */ +static void init_camera_struct(struct cam_data *cam, + struct cpia_camera_ops *ops ) +{ + int i; + + /* Default everything to 0 */ + memset(cam, 0, sizeof(struct cam_data)); + + cam->ops = ops; + mutex_init(&cam->param_lock); + mutex_init(&cam->busy_lock); + + reset_camera_struct(cam); + + cam->proc_entry = NULL; + + memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template)); + cam->vdev.priv = cam; + + cam->curframe = 0; + for (i = 0; i < FRAME_NUM; i++) { + cam->frame[i].width = 0; + cam->frame[i].height = 0; + cam->frame[i].state = FRAME_UNUSED; + cam->frame[i].data = NULL; + } + cam->decompressed_frame.width = 0; + cam->decompressed_frame.height = 0; + cam->decompressed_frame.state = FRAME_UNUSED; + cam->decompressed_frame.data = NULL; +} + +struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel) +{ + struct cam_data *camera; + + if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) + return NULL; + + + init_camera_struct( camera, ops ); + camera->lowlevel_data = lowlevel; + + /* register v4l device */ + if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { + kfree(camera); + printk(KERN_DEBUG "video_register_device failed\n"); + return NULL; + } + + /* get version information from camera: open/reset/close */ + + /* open cpia */ + if (camera->ops->open(camera->lowlevel_data)) + return camera; + + /* reset the camera */ + if (reset_camera(camera) != 0) { + camera->ops->close(camera->lowlevel_data); + return camera; + } + + /* close cpia */ + camera->ops->close(camera->lowlevel_data); + +#ifdef CONFIG_PROC_FS + create_proc_cpia_cam(camera); +#endif + + printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n", + camera->params.version.firmwareVersion, + camera->params.version.firmwareRevision, + camera->params.version.vcVersion, + camera->params.version.vcRevision); + printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n", + camera->params.pnpID.vendor, + camera->params.pnpID.product, + camera->params.pnpID.deviceRevision); + printk(KERN_INFO " VP-Version: %d.%d %04x\n", + camera->params.vpVersion.vpVersion, + camera->params.vpVersion.vpRevision, + camera->params.vpVersion.cameraHeadID); + + return camera; +} + +void cpia_unregister_camera(struct cam_data *cam) +{ + DBG("unregistering video\n"); + video_unregister_device(&cam->vdev); + if (cam->open_count) { + put_cam(cam->ops); + DBG("camera open -- setting ops to NULL\n"); + cam->ops = NULL; + } + +#ifdef CONFIG_PROC_FS + DBG("destroying /proc/cpia/video%d\n", cam->vdev.minor); + destroy_proc_cpia_cam(cam); +#endif + if (!cam->open_count) { + DBG("freeing camera\n"); + kfree(cam); + } +} + +static int __init cpia_init(void) +{ + printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, + CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); + + printk(KERN_WARNING "Since in-kernel colorspace conversion is not " + "allowed, it is disabled by default now. Users should fix the " + "applications in case they don't work without conversion " + "reenabled by setting the 'colorspace_conv' module " + "parameter to 1\n"); + +#ifdef CONFIG_PROC_FS + proc_cpia_create(); +#endif + +#ifdef CONFIG_VIDEO_CPIA_PP + cpia_pp_init(); +#endif +#ifdef CONFIG_VIDEO_CPIA_USB + cpia_usb_init(); +#endif + + return 0; +} + +static void __exit cpia_exit(void) +{ +#ifdef CONFIG_PROC_FS + proc_cpia_destroy(); +#endif +} + +module_init(cpia_init); +module_exit(cpia_exit); + +/* Exported symbols for modules. */ + +EXPORT_SYMBOL(cpia_register_camera); +EXPORT_SYMBOL(cpia_unregister_camera); diff --git a/linux/drivers/media/video/cpia.h b/linux/drivers/media/video/cpia.h new file mode 100644 index 000000000..120b66388 --- /dev/null +++ b/linux/drivers/media/video/cpia.h @@ -0,0 +1,432 @@ +#ifndef cpia_h +#define cpia_h + +/* + * CPiA Parallel Port Video4Linux driver + * + * Supports CPiA based parallel port Video Camera's. + * + * (C) Copyright 1999 Bas Huisman, + * Peter Pregler, + * Scott J. Bertin, + * VLSI Vision Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define CPIA_MAJ_VER 1 +#define CPIA_MIN_VER 2 +#define CPIA_PATCH_VER 3 + +#define CPIA_PP_MAJ_VER CPIA_MAJ_VER +#define CPIA_PP_MIN_VER CPIA_MIN_VER +#define CPIA_PP_PATCH_VER CPIA_PATCH_VER + +#define CPIA_USB_MAJ_VER CPIA_MAJ_VER +#define CPIA_USB_MIN_VER CPIA_MIN_VER +#define CPIA_USB_PATCH_VER CPIA_PATCH_VER + +#define CPIA_MAX_FRAME_SIZE_UNALIGNED (352 * 288 * 4) /* CIF at RGB32 */ +#define CPIA_MAX_FRAME_SIZE ((CPIA_MAX_FRAME_SIZE_UNALIGNED + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) /* align above to PAGE_SIZE */ + +#ifdef __KERNEL__ + +#include +#include "compat.h" +#include +#include +#include +#include + +struct cpia_camera_ops +{ + /* open sets privdata to point to structure for this camera. + * Returns negative value on error, otherwise 0. + */ + int (*open)(void *privdata); + + /* Registers callback function cb to be called with cbdata + * when an image is ready. If cb is NULL, only single image grabs + * should be used. cb should immediately call streamRead to read + * the data or data may be lost. Returns negative value on error, + * otherwise 0. + */ + int (*registerCallback)(void *privdata, void (*cb)(void *cbdata), + void *cbdata); + + /* transferCmd sends commands to the camera. command MUST point to + * an 8 byte buffer in kernel space. data can be NULL if no extra + * data is needed. The size of the data is given by the last 2 + * bytes of command. data must also point to memory in kernel space. + * Returns negative value on error, otherwise 0. + */ + int (*transferCmd)(void *privdata, u8 *command, u8 *data); + + /* streamStart initiates stream capture mode. + * Returns negative value on error, otherwise 0. + */ + int (*streamStart)(void *privdata); + + /* streamStop terminates stream capture mode. + * Returns negative value on error, otherwise 0. + */ + int (*streamStop)(void *privdata); + + /* streamRead reads a frame from the camera. buffer points to a + * buffer large enough to hold a complete frame in kernel space. + * noblock indicates if this should be a non blocking read. + * Returns the number of bytes read, or negative value on error. + */ + int (*streamRead)(void *privdata, u8 *buffer, int noblock); + + /* close disables the device until open() is called again. + * Returns negative value on error, otherwise 0. + */ + int (*close)(void *privdata); + + /* If wait_for_stream_ready is non-zero, wait until the streamState + * is STREAM_READY before calling streamRead. + */ + int wait_for_stream_ready; + + /* + * Used to maintain lowlevel module usage counts + */ + struct module *owner; +}; + +struct cpia_frame { + u8 *data; + int count; + int width; + int height; + volatile int state; +}; + +struct cam_params { + struct { + u8 firmwareVersion; + u8 firmwareRevision; + u8 vcVersion; + u8 vcRevision; + } version; + struct { + u16 vendor; + u16 product; + u16 deviceRevision; + } pnpID; + struct { + u8 vpVersion; + u8 vpRevision; + u16 cameraHeadID; + } vpVersion; + struct { + u8 systemState; + u8 grabState; + u8 streamState; + u8 fatalError; + u8 cmdError; + u8 debugFlags; + u8 vpStatus; + u8 errorCode; + } status; + struct { + u8 brightness; + u8 contrast; + u8 saturation; + } colourParams; + struct { + u8 gainMode; + u8 expMode; + u8 compMode; + u8 centreWeight; + u8 gain; + u8 fineExp; + u8 coarseExpLo; + u8 coarseExpHi; + u8 redComp; + u8 green1Comp; + u8 green2Comp; + u8 blueComp; + } exposure; + struct { + u8 balanceMode; + u8 redGain; + u8 greenGain; + u8 blueGain; + } colourBalance; + struct { + u8 divisor; + u8 baserate; + } sensorFps; + struct { + u8 gain1; + u8 gain2; + u8 gain4; + u8 gain8; + } apcor; + struct { + u8 disabled; + u8 flickerMode; + u8 coarseJump; + int allowableOverExposure; + } flickerControl; + struct { + u8 gain1; + u8 gain2; + u8 gain4; + u8 gain8; + } vlOffset; + struct { + u8 mode; + u8 decimation; + } compression; + struct { + u8 frTargeting; + u8 targetFR; + u8 targetQ; + } compressionTarget; + struct { + u8 yThreshold; + u8 uvThreshold; + } yuvThreshold; + struct { + u8 hysteresis; + u8 threshMax; + u8 smallStep; + u8 largeStep; + u8 decimationHysteresis; + u8 frDiffStepThresh; + u8 qDiffStepThresh; + u8 decimationThreshMod; + } compressionParams; + struct { + u8 videoSize; /* CIF/QCIF */ + u8 subSample; + u8 yuvOrder; + } format; + struct { /* Intel QX3 specific data */ + u8 qx3_detected; /* a QX3 is present */ + u8 toplight; /* top light lit , R/W */ + u8 bottomlight; /* bottom light lit, R/W */ + u8 button; /* snapshot button pressed (R/O) */ + u8 cradled; /* microscope is in cradle (R/O) */ + } qx3; + struct { + u8 colStart; /* skip first 8*colStart pixels */ + u8 colEnd; /* finish at 8*colEnd pixels */ + u8 rowStart; /* skip first 4*rowStart lines */ + u8 rowEnd; /* finish at 4*rowEnd lines */ + } roi; + u8 ecpTiming; + u8 streamStartLine; +}; + +enum v4l_camstates { + CPIA_V4L_IDLE = 0, + CPIA_V4L_ERROR, + CPIA_V4L_COMMAND, + CPIA_V4L_GRABBING, + CPIA_V4L_STREAMING, + CPIA_V4L_STREAMING_PAUSED, +}; + +#define FRAME_NUM 2 /* double buffering for now */ + +struct cam_data { + struct list_head cam_data_list; + + struct mutex busy_lock; /* guard against SMP multithreading */ + struct cpia_camera_ops *ops; /* lowlevel driver operations */ + void *lowlevel_data; /* private data for lowlevel driver */ + u8 *raw_image; /* buffer for raw image data */ + struct cpia_frame decompressed_frame; + /* buffer to hold decompressed frame */ + int image_size; /* sizeof last decompressed image */ + int open_count; /* # of process that have camera open */ + + /* camera status */ + int fps; /* actual fps reported by the camera */ + int transfer_rate; /* transfer rate from camera in kB/s */ + u8 mainsFreq; /* for flicker control */ + + /* proc interface */ + struct mutex param_lock; /* params lock for this camera */ + struct cam_params params; /* camera settings */ + struct proc_dir_entry *proc_entry; /* /proc/cpia/videoX */ + + /* v4l */ + int video_size; /* VIDEO_SIZE_ */ + volatile enum v4l_camstates camstate; /* v4l layer status */ + struct video_device vdev; /* v4l videodev */ + struct video_picture vp; /* v4l camera settings */ + struct video_window vw; /* v4l capture area */ + struct video_capture vc; /* v4l subcapture area */ + + /* mmap interface */ + int curframe; /* the current frame to grab into */ + u8 *frame_buf; /* frame buffer data */ + struct cpia_frame frame[FRAME_NUM]; + /* FRAME_NUM-buffering, so we need a array */ + + int first_frame; + int mmap_kludge; /* 'wrong' byte order for mmap */ + volatile u32 cmd_queue; /* queued commands */ + int exposure_status; /* EXPOSURE_* */ + int exposure_count; /* number of frames at this status */ +}; + +/* cpia_register_camera is called by low level driver for each camera. + * A unique camera number is returned, or a negative value on error */ +struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel); + +/* cpia_unregister_camera is called by low level driver when a camera + * is removed. This must not fail. */ +void cpia_unregister_camera(struct cam_data *cam); + +/* raw CIF + 64 byte header + (2 bytes line_length + EOL) per line + 4*EOI + + * one byte 16bit DMA alignment + */ +#define CPIA_MAX_IMAGE_SIZE ((352*288*2)+64+(288*3)+5) + +/* constant value's */ +#define MAGIC_0 0x19 +#define MAGIC_1 0x68 +#define DATA_IN 0xC0 +#define DATA_OUT 0x40 +#define VIDEOSIZE_QCIF 0 /* 176x144 */ +#define VIDEOSIZE_CIF 1 /* 352x288 */ +#define VIDEOSIZE_SIF 2 /* 320x240 */ +#define VIDEOSIZE_QSIF 3 /* 160x120 */ +#define VIDEOSIZE_48_48 4 /* where no one has gone before, iconsize! */ +#define VIDEOSIZE_64_48 5 +#define VIDEOSIZE_128_96 6 +#define VIDEOSIZE_160_120 VIDEOSIZE_QSIF +#define VIDEOSIZE_176_144 VIDEOSIZE_QCIF +#define VIDEOSIZE_192_144 7 +#define VIDEOSIZE_224_168 8 +#define VIDEOSIZE_256_192 9 +#define VIDEOSIZE_288_216 10 +#define VIDEOSIZE_320_240 VIDEOSIZE_SIF +#define VIDEOSIZE_352_288 VIDEOSIZE_CIF +#define VIDEOSIZE_88_72 11 /* quarter CIF */ +#define SUBSAMPLE_420 0 +#define SUBSAMPLE_422 1 +#define YUVORDER_YUYV 0 +#define YUVORDER_UYVY 1 +#define NOT_COMPRESSED 0 +#define COMPRESSED 1 +#define NO_DECIMATION 0 +#define DECIMATION_ENAB 1 +#define EOI 0xff /* End Of Image */ +#define EOL 0xfd /* End Of Line */ +#define FRAME_HEADER_SIZE 64 + +/* Image grab modes */ +#define CPIA_GRAB_SINGLE 0 +#define CPIA_GRAB_CONTINUOUS 1 + +/* Compression parameters */ +#define CPIA_COMPRESSION_NONE 0 +#define CPIA_COMPRESSION_AUTO 1 +#define CPIA_COMPRESSION_MANUAL 2 +#define CPIA_COMPRESSION_TARGET_QUALITY 0 +#define CPIA_COMPRESSION_TARGET_FRAMERATE 1 + +/* Return offsets for GetCameraState */ +#define SYSTEMSTATE 0 +#define GRABSTATE 1 +#define STREAMSTATE 2 +#define FATALERROR 3 +#define CMDERROR 4 +#define DEBUGFLAGS 5 +#define VPSTATUS 6 +#define ERRORCODE 7 + +/* SystemState */ +#define UNINITIALISED_STATE 0 +#define PASS_THROUGH_STATE 1 +#define LO_POWER_STATE 2 +#define HI_POWER_STATE 3 +#define WARM_BOOT_STATE 4 + +/* GrabState */ +#define GRAB_IDLE 0 +#define GRAB_ACTIVE 1 +#define GRAB_DONE 2 + +/* StreamState */ +#define STREAM_NOT_READY 0 +#define STREAM_READY 1 +#define STREAM_OPEN 2 +#define STREAM_PAUSED 3 +#define STREAM_FINISHED 4 + +/* Fatal Error, CmdError, and DebugFlags */ +#define CPIA_FLAG 1 +#define SYSTEM_FLAG 2 +#define INT_CTRL_FLAG 4 +#define PROCESS_FLAG 8 +#define COM_FLAG 16 +#define VP_CTRL_FLAG 32 +#define CAPTURE_FLAG 64 +#define DEBUG_FLAG 128 + +/* VPStatus */ +#define VP_STATE_OK 0x00 + +#define VP_STATE_FAILED_VIDEOINIT 0x01 +#define VP_STATE_FAILED_AECACBINIT 0x02 +#define VP_STATE_AEC_MAX 0x04 +#define VP_STATE_ACB_BMAX 0x08 + +#define VP_STATE_ACB_RMIN 0x10 +#define VP_STATE_ACB_GMIN 0x20 +#define VP_STATE_ACB_RMAX 0x40 +#define VP_STATE_ACB_GMAX 0x80 + +/* default (minimum) compensation values */ +#define COMP_RED 220 +#define COMP_GREEN1 214 +#define COMP_GREEN2 COMP_GREEN1 +#define COMP_BLUE 230 + +/* exposure status */ +#define EXPOSURE_VERY_LIGHT 0 +#define EXPOSURE_LIGHT 1 +#define EXPOSURE_NORMAL 2 +#define EXPOSURE_DARK 3 +#define EXPOSURE_VERY_DARK 4 + +/* ErrorCode */ +#define ERROR_FLICKER_BELOW_MIN_EXP 0x01 /*flicker exposure got below minimum exposure */ +#define ALOG(fmt,args...) printk(fmt, ##args) +#define LOG(fmt,args...) ALOG(KERN_INFO __FILE__ ":%s(%d):" fmt, __FUNCTION__ , __LINE__ , ##args) + +#ifdef _CPIA_DEBUG_ +#define ADBG(fmt,args...) printk(fmt, jiffies, ##args) +#define DBG(fmt,args...) ADBG(KERN_DEBUG __FILE__" (%ld):%s(%d):" fmt, __FUNCTION__, __LINE__ , ##args) +#else +#define DBG(fmn,args...) do {} while(0) +#endif + +#define DEB_BYTE(p)\ + DBG("%1d %1d %1d %1d %1d %1d %1d %1d \n",\ + (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\ + (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0); + +#endif /* __KERNEL__ */ + +#endif /* cpia_h */ diff --git a/linux/drivers/media/video/cpia_pp.c b/linux/drivers/media/video/cpia_pp.c new file mode 100644 index 000000000..0b00e6027 --- /dev/null +++ b/linux/drivers/media/video/cpia_pp.c @@ -0,0 +1,881 @@ +/* + * cpia_pp CPiA Parallel Port driver + * + * Supports CPiA based parallel port Video Camera's. + * + * (C) Copyright 1999 Bas Huisman + * (C) Copyright 1999-2000 Scott J. Bertin , + * (C) Copyright 1999-2000 Peter Pregler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ +/* #define _CPIA_DEBUG_ 1 */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* #define _CPIA_DEBUG_ define for verbose debug output */ +#include "cpia.h" + +static int cpia_pp_open(void *privdata); +static int cpia_pp_registerCallback(void *privdata, void (*cb) (void *cbdata), + void *cbdata); +static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data); +static int cpia_pp_streamStart(void *privdata); +static int cpia_pp_streamStop(void *privdata); +static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock); +static int cpia_pp_close(void *privdata); + + +#define ABOUT "Parallel port driver for Vision CPiA based cameras" + +#define PACKET_LENGTH 8 + +/* Magic numbers for defining port-device mappings */ +#define PPCPIA_PARPORT_UNSPEC -4 +#define PPCPIA_PARPORT_AUTO -3 +#define PPCPIA_PARPORT_OFF -2 +#define PPCPIA_PARPORT_NONE -1 + +#ifdef MODULE +static int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC}; +static char *parport[PARPORT_MAX] = {NULL,}; + +MODULE_AUTHOR("B. Huisman & Peter Pregler "); +MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras"); +MODULE_LICENSE("GPL"); + +module_param_array(parport, charp, NULL, 0); +MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp."); +#else +static int parport_nr[PARPORT_MAX] __initdata = + {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC}; +static int parport_ptr = 0; +#endif + +struct pp_cam_entry { + struct pardevice *pdev; + struct parport *port; + struct work_struct cb_task; + int open_count; + wait_queue_head_t wq_stream; + /* image state flags */ + int image_ready; /* we got an interrupt */ + int image_complete; /* we have seen 4 EOI */ + + int streaming; /* we are in streaming mode */ + int stream_irq; +}; + +static struct cpia_camera_ops cpia_pp_ops = +{ + cpia_pp_open, + cpia_pp_registerCallback, + cpia_pp_transferCmd, + cpia_pp_streamStart, + cpia_pp_streamStop, + cpia_pp_streamRead, + cpia_pp_close, + 1, + THIS_MODULE +}; + +static LIST_HEAD(cam_list); +static spinlock_t cam_list_lock_pp; + +/* FIXME */ +static void cpia_parport_enable_irq( struct parport *port ) { + parport_enable_irq(port); + mdelay(10); + return; +} + +static void cpia_parport_disable_irq( struct parport *port ) { + parport_disable_irq(port); + mdelay(10); + return; +} + +/* Special CPiA PPC modes: These are invoked by using the 1284 Extensibility + * Link Flag during negotiation */ +#define UPLOAD_FLAG 0x08 +#define NIBBLE_TRANSFER 0x01 +#define ECP_TRANSFER 0x03 + +#define PARPORT_CHUNK_SIZE PAGE_SIZE + + +/**************************************************************************** + * + * CPiA-specific low-level parport functions for nibble uploads + * + ***************************************************************************/ +/* CPiA nonstandard "Nibble" mode (no nDataAvail signal after each byte). */ +/* The standard kernel parport_ieee1284_read_nibble() fails with the CPiA... */ + +static size_t cpia_read_nibble (struct parport *port, + void *buffer, size_t len, + int flags) +{ + /* adapted verbatim, with one change, from + parport_ieee1284_read_nibble() in drivers/parport/ieee1284-ops.c */ + + unsigned char *buf = buffer; + int i; + unsigned char byte = 0; + + len *= 2; /* in nibbles */ + for (i=0; i < len; i++) { + unsigned char nibble; + + /* The CPiA firmware suppresses the use of nDataAvail (nFault LO) + * after every second nibble to signal that more + * data is available. (the total number of Bytes that + * should be sent is known; if too few are received, an error + * will be recorded after a timeout). + * This is incompatible with parport_ieee1284_read_nibble(), + * which expects to find nFault LO after every second nibble. + */ + + /* Solution: modify cpia_read_nibble to only check for + * nDataAvail before the first nibble is sent. + */ + + /* Does the error line indicate end of data? */ + if (((i /*& 1*/) == 0) && + (parport_read_status(port) & PARPORT_STATUS_ERROR)) { + DBG("%s: No more nibble data (%d bytes)\n", + port->name, i/2); + goto end_of_data; + } + + /* Event 7: Set nAutoFd low. */ + parport_frob_control (port, + PARPORT_CONTROL_AUTOFD, + PARPORT_CONTROL_AUTOFD); + + /* Event 9: nAck goes low. */ + port->ieee1284.phase = IEEE1284_PH_REV_DATA; + if (parport_wait_peripheral (port, + PARPORT_STATUS_ACK, 0)) { + /* Timeout -- no more data? */ + DBG("%s: Nibble timeout at event 9 (%d bytes)\n", + port->name, i/2); + parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); + break; + } + + + /* Read a nibble. */ + nibble = parport_read_status (port) >> 3; + nibble &= ~8; + if ((nibble & 0x10) == 0) + nibble |= 8; + nibble &= 0xf; + + /* Event 10: Set nAutoFd high. */ + parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); + + /* Event 11: nAck goes high. */ + if (parport_wait_peripheral (port, + PARPORT_STATUS_ACK, + PARPORT_STATUS_ACK)) { + /* Timeout -- no more data? */ + DBG("%s: Nibble timeout at event 11\n", + port->name); + break; + } + + if (i & 1) { + /* Second nibble */ + byte |= nibble << 4; + *buf++ = byte; + } else + byte = nibble; + } + + if (i == len) { + /* Read the last nibble without checking data avail. */ + if (parport_read_status (port) & PARPORT_STATUS_ERROR) { + end_of_data: + /* Go to reverse idle phase. */ + parport_frob_control (port, + PARPORT_CONTROL_AUTOFD, + PARPORT_CONTROL_AUTOFD); + port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE; + } + else + port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL; + } + + return i/2; +} + +/* CPiA nonstandard "Nibble Stream" mode (2 nibbles per cycle, instead of 1) + * (See CPiA Data sheet p. 31) + * + * "Nibble Stream" mode used by CPiA for uploads to non-ECP ports is a + * nonstandard variant of nibble mode which allows the same (mediocre) + * data flow of 8 bits per cycle as software-enabled ECP by TRISTATE-capable + * parallel ports, but works also for non-TRISTATE-capable ports. + * (Standard nibble mode only send 4 bits per cycle) + * + */ + +static size_t cpia_read_nibble_stream(struct parport *port, + void *buffer, size_t len, + int flags) +{ + int i; + unsigned char *buf = buffer; + int endseen = 0; + + for (i=0; i < len; i++) { + unsigned char nibble[2], byte = 0; + int j; + + /* Image Data is complete when 4 consecutive EOI bytes (0xff) are seen */ + if (endseen > 3 ) + break; + + /* Event 7: Set nAutoFd low. */ + parport_frob_control (port, + PARPORT_CONTROL_AUTOFD, + PARPORT_CONTROL_AUTOFD); + + /* Event 9: nAck goes low. */ + port->ieee1284.phase = IEEE1284_PH_REV_DATA; + if (parport_wait_peripheral (port, + PARPORT_STATUS_ACK, 0)) { + /* Timeout -- no more data? */ + DBG("%s: Nibble timeout at event 9 (%d bytes)\n", + port->name, i/2); + parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); + break; + } + + /* Read lower nibble */ + nibble[0] = parport_read_status (port) >>3; + + /* Event 10: Set nAutoFd high. */ + parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); + + /* Event 11: nAck goes high. */ + if (parport_wait_peripheral (port, + PARPORT_STATUS_ACK, + PARPORT_STATUS_ACK)) { + /* Timeout -- no more data? */ + DBG("%s: Nibble timeout at event 11\n", + port->name); + break; + } + + /* Read upper nibble */ + nibble[1] = parport_read_status (port) >>3; + + /* reassemble the byte */ + for (j = 0; j < 2 ; j++ ) { + nibble[j] &= ~8; + if ((nibble[j] & 0x10) == 0) + nibble[j] |= 8; + nibble[j] &= 0xf; + } + byte = (nibble[0] |(nibble[1] << 4)); + *buf++ = byte; + + if(byte == EOI) + endseen++; + else + endseen = 0; + } + return i; +} + +/**************************************************************************** + * + * EndTransferMode + * + ***************************************************************************/ +static void EndTransferMode(struct pp_cam_entry *cam) +{ + parport_negotiate(cam->port, IEEE1284_MODE_COMPAT); +} + +/**************************************************************************** + * + * ForwardSetup + * + ***************************************************************************/ +static int ForwardSetup(struct pp_cam_entry *cam) +{ + int retry; + + /* The CPiA uses ECP protocol for Downloads from the Host to the camera. + * This will be software-emulated if ECP hardware is not present + */ + + /* the usual camera maximum response time is 10ms, but after receiving + * some commands, it needs up to 40ms. (Data Sheet p. 32)*/ + + for(retry = 0; retry < 4; ++retry) { + if(!parport_negotiate(cam->port, IEEE1284_MODE_ECP)) { + break; + } + mdelay(10); + } + if(retry == 4) { + DBG("Unable to negotiate IEEE1284 ECP Download mode\n"); + return -1; + } + return 0; +} +/**************************************************************************** + * + * ReverseSetup + * + ***************************************************************************/ +static int ReverseSetup(struct pp_cam_entry *cam, int extensibility) +{ + int retry; + int upload_mode, mode = IEEE1284_MODE_ECP; + int transfer_mode = ECP_TRANSFER; + + if (!(cam->port->modes & PARPORT_MODE_ECP) && + !(cam->port->modes & PARPORT_MODE_TRISTATE)) { + mode = IEEE1284_MODE_NIBBLE; + transfer_mode = NIBBLE_TRANSFER; + } + + upload_mode = mode; + if(extensibility) mode = UPLOAD_FLAG|transfer_mode|IEEE1284_EXT_LINK; + + /* the usual camera maximum response time is 10ms, but after + * receiving some commands, it needs up to 40ms. */ + + for(retry = 0; retry < 4; ++retry) { + if(!parport_negotiate(cam->port, mode)) { + break; + } + mdelay(10); + } + if(retry == 4) { + if(extensibility) + DBG("Unable to negotiate upload extensibility mode\n"); + else + DBG("Unable to negotiate upload mode\n"); + return -1; + } + if(extensibility) cam->port->ieee1284.mode = upload_mode; + return 0; +} + +/**************************************************************************** + * + * WritePacket + * + ***************************************************************************/ +static int WritePacket(struct pp_cam_entry *cam, const u8 *packet, size_t size) +{ + int retval=0; + int size_written; + + if (packet == NULL) { + return -EINVAL; + } + if (ForwardSetup(cam)) { + DBG("Write failed in setup\n"); + return -EIO; + } + size_written = parport_write(cam->port, packet, size); + if(size_written != size) { + DBG("Write failed, wrote %d/%d\n", size_written, size); + retval = -EIO; + } + EndTransferMode(cam); + return retval; +} + +/**************************************************************************** + * + * ReadPacket + * + ***************************************************************************/ +static int ReadPacket(struct pp_cam_entry *cam, u8 *packet, size_t size) +{ + int retval=0; + + if (packet == NULL) { + return -EINVAL; + } + if (ReverseSetup(cam, 0)) { + return -EIO; + } + + /* support for CPiA variant nibble reads */ + if(cam->port->ieee1284.mode == IEEE1284_MODE_NIBBLE) { + if(cpia_read_nibble(cam->port, packet, size, 0) != size) + retval = -EIO; + } else { + if(parport_read(cam->port, packet, size) != size) + retval = -EIO; + } + EndTransferMode(cam); + return retval; +} + +/**************************************************************************** + * + * cpia_pp_streamStart + * + ***************************************************************************/ +static int cpia_pp_streamStart(void *privdata) +{ + struct pp_cam_entry *cam = privdata; + DBG("\n"); + cam->streaming=1; + cam->image_ready=0; + //if (ReverseSetup(cam,1)) return -EIO; + if(cam->stream_irq) cpia_parport_enable_irq(cam->port); + return 0; +} + +/**************************************************************************** + * + * cpia_pp_streamStop + * + ***************************************************************************/ +static int cpia_pp_streamStop(void *privdata) +{ + struct pp_cam_entry *cam = privdata; + + DBG("\n"); + cam->streaming=0; + cpia_parport_disable_irq(cam->port); + //EndTransferMode(cam); + + return 0; +} + +/**************************************************************************** + * + * cpia_pp_streamRead + * + ***************************************************************************/ +static int cpia_pp_read(struct parport *port, u8 *buffer, int len) +{ + int bytes_read; + + /* support for CPiA variant "nibble stream" reads */ + if(port->ieee1284.mode == IEEE1284_MODE_NIBBLE) + bytes_read = cpia_read_nibble_stream(port,buffer,len,0); + else { + int new_bytes; + for(bytes_read=0; bytes_readstreaming) DBG("%d / %d\n", cam->image_ready, noblock); + if( cam->stream_irq ) { + DBG("%d\n", cam->image_ready); + cam->image_ready--; + } + cam->image_complete=0; + if (0/*cam->streaming*/) { + if(!cam->image_ready) { + if(noblock) return -EWOULDBLOCK; + interruptible_sleep_on(&cam->wq_stream); + if( signal_pending(current) ) return -EINTR; + DBG("%d\n", cam->image_ready); + } + } else { + if (ReverseSetup(cam, 1)) { + DBG("unable to ReverseSetup\n"); + return -EIO; + } + } + endseen = 0; + block_size = PARPORT_CHUNK_SIZE; + while( !cam->image_complete ) { + cond_resched(); + + new_bytes = cpia_pp_read(cam->port, buffer, block_size ); + if( new_bytes <= 0 ) { + break; + } + i=-1; + while(++iimage_complete=1; + break; + } + if( CPIA_MAX_IMAGE_SIZE-read_bytes <= PARPORT_CHUNK_SIZE ) { + block_size=CPIA_MAX_IMAGE_SIZE-read_bytes; + } + } + EndTransferMode(cam); + return cam->image_complete ? read_bytes : -EIO; +} +/**************************************************************************** + * + * cpia_pp_transferCmd + * + ***************************************************************************/ +static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data) +{ + int err; + int retval=0; + int databytes; + struct pp_cam_entry *cam = privdata; + + if(cam == NULL) { + DBG("Internal driver error: cam is NULL\n"); + return -EINVAL; + } + if(command == NULL) { + DBG("Internal driver error: command is NULL\n"); + return -EINVAL; + } + databytes = (((int)command[7])<<8) | command[6]; + if ((err = WritePacket(cam, command, PACKET_LENGTH)) < 0) { + DBG("Error writing command\n"); + return err; + } + if(command[0] == DATA_IN) { + u8 buffer[8]; + if(data == NULL) { + DBG("Internal driver error: data is NULL\n"); + return -EINVAL; + } + if((err = ReadPacket(cam, buffer, 8)) < 0) { + DBG("Error reading command result\n"); + return err; + } + memcpy(data, buffer, databytes); + } else if(command[0] == DATA_OUT) { + if(databytes > 0) { + if(data == NULL) { + DBG("Internal driver error: data is NULL\n"); + retval = -EINVAL; + } else { + if((err=WritePacket(cam, data, databytes)) < 0){ + DBG("Error writing command data\n"); + return err; + } + } + } + } else { + DBG("Unexpected first byte of command: %x\n", command[0]); + retval = -EINVAL; + } + return retval; +} + +/**************************************************************************** + * + * cpia_pp_open + * + ***************************************************************************/ +static int cpia_pp_open(void *privdata) +{ + struct pp_cam_entry *cam = (struct pp_cam_entry *)privdata; + + if (cam == NULL) + return -EINVAL; + + if(cam->open_count == 0) { + if (parport_claim(cam->pdev)) { + DBG("failed to claim the port\n"); + return -EBUSY; + } + parport_negotiate(cam->port, IEEE1284_MODE_COMPAT); + parport_data_forward(cam->port); + parport_write_control(cam->port, PARPORT_CONTROL_SELECT); + udelay(50); + parport_write_control(cam->port, + PARPORT_CONTROL_SELECT + | PARPORT_CONTROL_INIT); + } + + ++cam->open_count; + + return 0; +} + +/**************************************************************************** + * + * cpia_pp_registerCallback + * + ***************************************************************************/ +static int cpia_pp_registerCallback(void *privdata, void (*cb)(void *cbdata), void *cbdata) +{ + struct pp_cam_entry *cam = privdata; + int retval = 0; + + if(cam->port->irq != PARPORT_IRQ_NONE) { + INIT_WORK(&cam->cb_task, cb, cbdata); + } else { + retval = -1; + } + return retval; +} + +/**************************************************************************** + * + * cpia_pp_close + * + ***************************************************************************/ +static int cpia_pp_close(void *privdata) +{ + struct pp_cam_entry *cam = privdata; + if (--cam->open_count == 0) { + parport_release(cam->pdev); + } + return 0; +} + +/**************************************************************************** + * + * cpia_pp_register + * + ***************************************************************************/ +static int cpia_pp_register(struct parport *port) +{ + struct pardevice *pdev = NULL; + struct pp_cam_entry *cam; + struct cam_data *cpia; + + if (!(port->modes & PARPORT_MODE_PCSPP)) { + LOG("port is not supported by CPiA driver\n"); + return -ENXIO; + } + + cam = kzalloc(sizeof(struct pp_cam_entry), GFP_KERNEL); + if (cam == NULL) { + LOG("failed to allocate camera structure\n"); + return -ENOMEM; + } + + pdev = parport_register_device(port, "cpia_pp", NULL, NULL, + NULL, 0, cam); + + if (!pdev) { + LOG("failed to parport_register_device\n"); + kfree(cam); + return -ENXIO; + } + + cam->pdev = pdev; + cam->port = port; + init_waitqueue_head(&cam->wq_stream); + + cam->streaming = 0; + cam->stream_irq = 0; + + if((cpia = cpia_register_camera(&cpia_pp_ops, cam)) == NULL) { + LOG("failed to cpia_register_camera\n"); + parport_unregister_device(pdev); + kfree(cam); + return -ENXIO; + } + spin_lock( &cam_list_lock_pp ); + list_add( &cpia->cam_data_list, &cam_list ); + spin_unlock( &cam_list_lock_pp ); + + return 0; +} + +static void cpia_pp_detach (struct parport *port) +{ + struct list_head *tmp; + struct cam_data *cpia = NULL; + struct pp_cam_entry *cam; + + spin_lock( &cam_list_lock_pp ); + list_for_each (tmp, &cam_list) { + cpia = list_entry(tmp, struct cam_data, cam_data_list); + cam = (struct pp_cam_entry *) cpia->lowlevel_data; + if (cam && cam->port->number == port->number) { + list_del(&cpia->cam_data_list); + break; + } + cpia = NULL; + } + spin_unlock( &cam_list_lock_pp ); + + if (!cpia) { + DBG("cpia_pp_detach failed to find cam_data in cam_list\n"); + return; + } + + cam = (struct pp_cam_entry *) cpia->lowlevel_data; + cpia_unregister_camera(cpia); + if(cam->open_count > 0) + cpia_pp_close(cam); + parport_unregister_device(cam->pdev); + cpia->lowlevel_data = NULL; + kfree(cam); +} + +static void cpia_pp_attach (struct parport *port) +{ + unsigned int i; + + switch (parport_nr[0]) + { + case PPCPIA_PARPORT_UNSPEC: + case PPCPIA_PARPORT_AUTO: + if (port->probe_info[0].class != PARPORT_CLASS_MEDIA || + port->probe_info[0].cmdset == NULL || + strncmp(port->probe_info[0].cmdset, "CPIA_1", 6) != 0) + return; + + cpia_pp_register(port); + + break; + + default: + for (i = 0; i < PARPORT_MAX; ++i) { + if (port->number == parport_nr[i]) { + cpia_pp_register(port); + break; + } + } + break; + } +} + +static struct parport_driver cpia_pp_driver = { + .name = "cpia_pp", + .attach = cpia_pp_attach, + .detach = cpia_pp_detach, +}; + +int cpia_pp_init(void) +{ + printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, + CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER); + + if(parport_nr[0] == PPCPIA_PARPORT_OFF) { + printk(" disabled\n"); + return 0; + } + + spin_lock_init( &cam_list_lock_pp ); + + if (parport_register_driver (&cpia_pp_driver)) { + LOG ("unable to register with parport\n"); + return -EIO; + } + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + if (parport[0]) { + /* The user gave some parameters. Let's see what they were. */ + if (!strncmp(parport[0], "auto", 4)) { + parport_nr[0] = PPCPIA_PARPORT_AUTO; + } else { + int n; + for (n = 0; n < PARPORT_MAX && parport[n]; n++) { + if (!strncmp(parport[n], "none", 4)) { + parport_nr[n] = PPCPIA_PARPORT_NONE; + } else { + char *ep; + unsigned long r = simple_strtoul(parport[n], &ep, 0); + if (ep != parport[n]) { + parport_nr[n] = r; + } else { + LOG("bad port specifier `%s'\n", parport[n]); + return -ENODEV; + } + } + } + } + } + return cpia_pp_init(); +} + +void cleanup_module(void) +{ + parport_unregister_driver (&cpia_pp_driver); + return; +} + +#else /* !MODULE */ + +static int __init cpia_pp_setup(char *str) +{ + if (!strncmp(str, "parport", 7)) { + int n = simple_strtoul(str + 7, NULL, 10); + if (parport_ptr < PARPORT_MAX) { + parport_nr[parport_ptr++] = n; + } else { + LOG("too many ports, %s ignored.\n", str); + } + } else if (!strcmp(str, "auto")) { + parport_nr[0] = PPCPIA_PARPORT_AUTO; + } else if (!strcmp(str, "none")) { + parport_nr[parport_ptr++] = PPCPIA_PARPORT_NONE; + } + + return 1; +} + +__setup("cpia_pp=", cpia_pp_setup); + +#endif /* !MODULE */ diff --git a/linux/drivers/media/video/cpia_usb.c b/linux/drivers/media/video/cpia_usb.c new file mode 100644 index 000000000..9c49a4b00 --- /dev/null +++ b/linux/drivers/media/video/cpia_usb.c @@ -0,0 +1,649 @@ +/* + * cpia_usb CPiA USB driver + * + * Supports CPiA based parallel port Video Camera's. + * + * Copyright (C) 1999 Jochen Scharrlach + * Copyright (C) 1999, 2000 Johannes Erdfelt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ +/* #define _CPIA_DEBUG_ 1 */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpia.h" + +#define USB_REQ_CPIA_GRAB_FRAME 0xC1 +#define USB_REQ_CPIA_UPLOAD_FRAME 0xC2 +#define WAIT_FOR_NEXT_FRAME 0 +#define FORCE_FRAME_UPLOAD 1 + +#define FRAMES_PER_DESC 10 +#define FRAME_SIZE_PER_DESC 960 /* Shouldn't be hardcoded */ +#define CPIA_NUMSBUF 2 +#define STREAM_BUF_SIZE (PAGE_SIZE * 4) +#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2) + +struct cpia_sbuf { + char *data; + struct urb *urb; +}; + +#define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100) +enum framebuf_status { + FRAME_EMPTY, + FRAME_READING, + FRAME_READY, + FRAME_ERROR, +}; + +struct framebuf { + int length; + enum framebuf_status status; + u8 data[FRAMEBUF_LEN]; + struct framebuf *next; +}; + +struct usb_cpia { + /* Device structure */ + struct usb_device *dev; + + unsigned char iface; + wait_queue_head_t wq_stream; + + int cursbuf; /* Current receiving sbuf */ + struct cpia_sbuf sbuf[CPIA_NUMSBUF]; /* Double buffering */ + + int streaming; + int open; + int present; + struct framebuf *buffers[3]; + struct framebuf *curbuff, *workbuff; +}; + +static int cpia_usb_open(void *privdata); +static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), + void *cbdata); +static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data); +static int cpia_usb_streamStart(void *privdata); +static int cpia_usb_streamStop(void *privdata); +static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock); +static int cpia_usb_close(void *privdata); + +#define ABOUT "USB driver for Vision CPiA based cameras" + +static struct cpia_camera_ops cpia_usb_ops = { + cpia_usb_open, + cpia_usb_registerCallback, + cpia_usb_transferCmd, + cpia_usb_streamStart, + cpia_usb_streamStop, + cpia_usb_streamRead, + cpia_usb_close, + 0, + THIS_MODULE +}; + +static LIST_HEAD(cam_list); +static spinlock_t cam_list_lock_usb; + +static void cpia_usb_complete(struct urb *urb, struct pt_regs *regs) +{ + int i; + char *cdata; + struct usb_cpia *ucpia; + + if (!urb || !urb->context) + return; + + ucpia = (struct usb_cpia *) urb->context; + + if (!ucpia->dev || !ucpia->streaming || !ucpia->present || !ucpia->open) + return; + + if (ucpia->workbuff->status == FRAME_EMPTY) { + ucpia->workbuff->status = FRAME_READING; + ucpia->workbuff->length = 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + + cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + if (st) + printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n", i, n, st); + + if (FRAMEBUF_LEN < ucpia->workbuff->length + n) { + printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n", ucpia->workbuff->length, n); + return; + } + + if (n) { + if ((ucpia->workbuff->length > 0) || + (0x19 == cdata[0] && 0x68 == cdata[1])) { + memcpy(ucpia->workbuff->data + ucpia->workbuff->length, cdata, n); + ucpia->workbuff->length += n; + } else + DBG("Ignoring packet!\n"); + } else { + if (ucpia->workbuff->length > 4 && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-1] && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-2] && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-3] && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-4]) { + ucpia->workbuff->status = FRAME_READY; + ucpia->curbuff = ucpia->workbuff; + ucpia->workbuff = ucpia->workbuff->next; + ucpia->workbuff->status = FRAME_EMPTY; + ucpia->workbuff->length = 0; + + if (waitqueue_active(&ucpia->wq_stream)) + wake_up_interruptible(&ucpia->wq_stream); + } + } + } + + /* resubmit */ + urb->dev = ucpia->dev; + if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) + printk(KERN_ERR "%s: usb_submit_urb ret %d\n", __FUNCTION__, i); +} + +static int cpia_usb_open(void *privdata) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + struct urb *urb; + int ret, retval = 0, fx, err; + + if (!ucpia) + return -EINVAL; + + ucpia->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!ucpia->sbuf[0].data) + return -EINVAL; + + ucpia->sbuf[1].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!ucpia->sbuf[1].data) { + retval = -EINVAL; + goto error_0; + } + + ret = usb_set_interface(ucpia->dev, ucpia->iface, 3); + if (ret < 0) { + printk(KERN_ERR "cpia_usb_open: usb_set_interface error (ret = %d)\n", ret); + retval = -EBUSY; + goto error_1; + } + + ucpia->buffers[0]->status = FRAME_EMPTY; + ucpia->buffers[0]->length = 0; + ucpia->buffers[1]->status = FRAME_EMPTY; + ucpia->buffers[1]->length = 0; + ucpia->buffers[2]->status = FRAME_EMPTY; + ucpia->buffers[2]->length = 0; + ucpia->curbuff = ucpia->buffers[0]; + ucpia->workbuff = ucpia->buffers[1]; + + /* We double buffer the Iso lists, and also know the polling + * interval is every frame (1 == (1 << (bInterval -1))). + */ + urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); + if (!urb) { + printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n"); + retval = -ENOMEM; + goto error_1; + } + + ucpia->sbuf[0].urb = urb; + urb->dev = ucpia->dev; + urb->context = ucpia; + urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = ucpia->sbuf[0].data; + urb->complete = cpia_usb_complete; + urb->number_of_packets = FRAMES_PER_DESC; + urb->interval = 1; + urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; + urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; + } + + urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); + if (!urb) { + printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 1\n"); + retval = -ENOMEM; + goto error_urb0; + } + + ucpia->sbuf[1].urb = urb; + urb->dev = ucpia->dev; + urb->context = ucpia; + urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = ucpia->sbuf[1].data; + urb->complete = cpia_usb_complete; + urb->number_of_packets = FRAMES_PER_DESC; + urb->interval = 1; + urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; + urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; + } + + /* queue the ISO urbs, and resubmit in the completion handler */ + err = usb_submit_urb(ucpia->sbuf[0].urb, GFP_KERNEL); + if (err) { + printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 0 ret %d\n", + err); + goto error_urb1; + } + err = usb_submit_urb(ucpia->sbuf[1].urb, GFP_KERNEL); + if (err) { + printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 1 ret %d\n", + err); + goto error_urb1; + } + + ucpia->streaming = 1; + ucpia->open = 1; + + return 0; + +error_urb1: /* free urb 1 */ + usb_free_urb(ucpia->sbuf[1].urb); + ucpia->sbuf[1].urb = NULL; +error_urb0: /* free urb 0 */ + usb_free_urb(ucpia->sbuf[0].urb); + ucpia->sbuf[0].urb = NULL; +error_1: + kfree (ucpia->sbuf[1].data); + ucpia->sbuf[1].data = NULL; +error_0: + kfree (ucpia->sbuf[0].data); + ucpia->sbuf[0].data = NULL; + + return retval; +} + +// +// convenience functions +// + +/**************************************************************************** + * + * WritePacket + * + ***************************************************************************/ +static int WritePacket(struct usb_device *udev, const u8 *packet, u8 *buf, size_t size) +{ + if (!packet) + return -EINVAL; + + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + packet[1] + (packet[0] << 8), + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + packet[2] + (packet[3] << 8), + packet[4] + (packet[5] << 8), buf, size, 1000); +} + +/**************************************************************************** + * + * ReadPacket + * + ***************************************************************************/ +static int ReadPacket(struct usb_device *udev, u8 *packet, u8 *buf, size_t size) +{ + if (!packet || size <= 0) + return -EINVAL; + + return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + packet[1] + (packet[0] << 8), + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + packet[2] + (packet[3] << 8), + packet[4] + (packet[5] << 8), buf, size, 1000); +} + +static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data) +{ + int err = 0; + int databytes; + struct usb_cpia *ucpia = (struct usb_cpia *)privdata; + struct usb_device *udev = ucpia->dev; + + if (!udev) { + DBG("Internal driver error: udev is NULL\n"); + return -EINVAL; + } + + if (!command) { + DBG("Internal driver error: command is NULL\n"); + return -EINVAL; + } + + databytes = (((int)command[7])<<8) | command[6]; + + if (command[0] == DATA_IN) { + u8 buffer[8]; + + if (!data) { + DBG("Internal driver error: data is NULL\n"); + return -EINVAL; + } + + err = ReadPacket(udev, command, buffer, 8); + if (err < 0) + return err; + + memcpy(data, buffer, databytes); + } else if(command[0] == DATA_OUT) + WritePacket(udev, command, data, databytes); + else { + DBG("Unexpected first byte of command: %x\n", command[0]); + err = -EINVAL; + } + + return 0; +} + +static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), + void *cbdata) +{ + return -ENODEV; +} + +static int cpia_usb_streamStart(void *privdata) +{ + return -ENODEV; +} + +static int cpia_usb_streamStop(void *privdata) +{ + return -ENODEV; +} + +static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + struct framebuf *mybuff; + + if (!ucpia || !ucpia->present) + return -1; + + if (ucpia->curbuff->status != FRAME_READY) + interruptible_sleep_on(&ucpia->wq_stream); + else + DBG("Frame already waiting!\n"); + + mybuff = ucpia->curbuff; + + if (!mybuff) + return -1; + + if (mybuff->status != FRAME_READY || mybuff->length < 4) { + DBG("Something went wrong!\n"); + return -1; + } + + memcpy(frame, mybuff->data, mybuff->length); + mybuff->status = FRAME_EMPTY; + +/* DBG("read done, %d bytes, Header: %x/%x, Footer: %x%x%x%x\n", */ +/* mybuff->length, frame[0], frame[1], */ +/* frame[mybuff->length-4], frame[mybuff->length-3], */ +/* frame[mybuff->length-2], frame[mybuff->length-1]); */ + + return mybuff->length; +} + +static void cpia_usb_free_resources(struct usb_cpia *ucpia, int try) +{ + if (!ucpia->streaming) + return; + + ucpia->streaming = 0; + + /* Set packet size to 0 */ + if (try) { + int ret; + + ret = usb_set_interface(ucpia->dev, ucpia->iface, 0); + if (ret < 0) { + printk(KERN_ERR "usb_set_interface error (ret = %d)\n", ret); + return; + } + } + + /* Unschedule all of the iso td's */ + if (ucpia->sbuf[1].urb) { + usb_kill_urb(ucpia->sbuf[1].urb); + usb_free_urb(ucpia->sbuf[1].urb); + ucpia->sbuf[1].urb = NULL; + } + + kfree(ucpia->sbuf[1].data); + ucpia->sbuf[1].data = NULL; + + if (ucpia->sbuf[0].urb) { + usb_kill_urb(ucpia->sbuf[0].urb); + usb_free_urb(ucpia->sbuf[0].urb); + ucpia->sbuf[0].urb = NULL; + } + + kfree(ucpia->sbuf[0].data); + ucpia->sbuf[0].data = NULL; +} + +static int cpia_usb_close(void *privdata) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + + if(!ucpia) + return -ENODEV; + + ucpia->open = 0; + + /* ucpia->present = 0 protects against trying to reset the + * alt setting if camera is physically disconnected while open */ + cpia_usb_free_resources(ucpia, ucpia->present); + + return 0; +} + +int cpia_usb_init(void) +{ + /* return -ENODEV; */ + return 0; +} + +/* Probing and initializing */ + +static int cpia_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_host_interface *interface; + struct usb_cpia *ucpia; + struct cam_data *cam; + int ret; + + /* A multi-config CPiA camera? */ + if (udev->descriptor.bNumConfigurations != 1) + return -ENODEV; + + interface = intf->cur_altsetting; + + printk(KERN_INFO "USB CPiA camera found\n"); + + ucpia = kzalloc(sizeof(*ucpia), GFP_KERNEL); + if (!ucpia) { + printk(KERN_ERR "couldn't kmalloc cpia struct\n"); + return -ENOMEM; + } + + ucpia->dev = udev; + ucpia->iface = interface->desc.bInterfaceNumber; + init_waitqueue_head(&ucpia->wq_stream); + + ucpia->buffers[0] = vmalloc(sizeof(*ucpia->buffers[0])); + if (!ucpia->buffers[0]) { + printk(KERN_ERR "couldn't vmalloc frame buffer 0\n"); + goto fail_alloc_0; + } + + ucpia->buffers[1] = vmalloc(sizeof(*ucpia->buffers[1])); + if (!ucpia->buffers[1]) { + printk(KERN_ERR "couldn't vmalloc frame buffer 1\n"); + goto fail_alloc_1; + } + + ucpia->buffers[2] = vmalloc(sizeof(*ucpia->buffers[2])); + if (!ucpia->buffers[2]) { + printk(KERN_ERR "couldn't vmalloc frame buffer 2\n"); + goto fail_alloc_2; + } + + ucpia->buffers[0]->next = ucpia->buffers[1]; + ucpia->buffers[1]->next = ucpia->buffers[2]; + ucpia->buffers[2]->next = ucpia->buffers[0]; + + ret = usb_set_interface(udev, ucpia->iface, 0); + if (ret < 0) { + printk(KERN_ERR "cpia_probe: usb_set_interface error (ret = %d)\n", ret); + /* goto fail_all; */ + } + + /* Before register_camera, important */ + ucpia->present = 1; + + cam = cpia_register_camera(&cpia_usb_ops, ucpia); + if (!cam) { + LOG("failed to cpia_register_camera\n"); + goto fail_all; + } + + spin_lock( &cam_list_lock_usb ); + list_add( &cam->cam_data_list, &cam_list ); + spin_unlock( &cam_list_lock_usb ); + + usb_set_intfdata(intf, cam); + return 0; + +fail_all: + vfree(ucpia->buffers[2]); + ucpia->buffers[2] = NULL; +fail_alloc_2: + vfree(ucpia->buffers[1]); + ucpia->buffers[1] = NULL; +fail_alloc_1: + vfree(ucpia->buffers[0]); + ucpia->buffers[0] = NULL; +fail_alloc_0: + kfree(ucpia); + return -EIO; +} + +static void cpia_disconnect(struct usb_interface *intf); + +static struct usb_device_id cpia_id_table [] = { + { USB_DEVICE(0x0553, 0x0002) }, + { USB_DEVICE(0x0813, 0x0001) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, cpia_id_table); +MODULE_LICENSE("GPL"); + + +static struct usb_driver cpia_driver = { + .name = "cpia", + .probe = cpia_probe, + .disconnect = cpia_disconnect, + .id_table = cpia_id_table, +}; + +static void cpia_disconnect(struct usb_interface *intf) +{ + struct cam_data *cam = usb_get_intfdata(intf); + struct usb_cpia *ucpia; + struct usb_device *udev; + + usb_set_intfdata(intf, NULL); + if (!cam) + return; + + ucpia = (struct usb_cpia *) cam->lowlevel_data; + spin_lock( &cam_list_lock_usb ); + list_del(&cam->cam_data_list); + spin_unlock( &cam_list_lock_usb ); + + ucpia->present = 0; + + cpia_unregister_camera(cam); + if(ucpia->open) + cpia_usb_close(cam->lowlevel_data); + + ucpia->curbuff->status = FRAME_ERROR; + + if (waitqueue_active(&ucpia->wq_stream)) + wake_up_interruptible(&ucpia->wq_stream); + + udev = interface_to_usbdev(intf); + + ucpia->curbuff = ucpia->workbuff = NULL; + + vfree(ucpia->buffers[2]); + ucpia->buffers[2] = NULL; + + vfree(ucpia->buffers[1]); + ucpia->buffers[1] = NULL; + + vfree(ucpia->buffers[0]); + ucpia->buffers[0] = NULL; + + cam->lowlevel_data = NULL; + kfree(ucpia); +} + +static int __init usb_cpia_init(void) +{ + printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, + CPIA_USB_MAJ_VER,CPIA_USB_MIN_VER,CPIA_USB_PATCH_VER); + + spin_lock_init(&cam_list_lock_usb); + return usb_register(&cpia_driver); +} + +static void __exit usb_cpia_cleanup(void) +{ + usb_deregister(&cpia_driver); +} + + +module_init (usb_cpia_init); +module_exit (usb_cpia_cleanup); + diff --git a/linux/drivers/media/video/cs8420.h b/linux/drivers/media/video/cs8420.h new file mode 100644 index 000000000..621c0c667 --- /dev/null +++ b/linux/drivers/media/video/cs8420.h @@ -0,0 +1,50 @@ +/* cs8420.h - cs8420 initializations + Copyright (C) 1999 Nathan Laredo (laredo@gnu.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, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ +#ifndef __CS8420_H__ +#define __CS8420_H__ + +/* Initialization Sequence */ + +static __u8 init8420[] = { + 1, 0x01, 2, 0x02, 3, 0x00, 4, 0x46, + 5, 0x24, 6, 0x84, 18, 0x18, 19, 0x13, +}; + +#define INIT8420LEN (sizeof(init8420)/2) + +static __u8 mode8420pro[] = { /* professional output mode */ + 32, 0xa1, 33, 0x00, 34, 0x00, 35, 0x00, + 36, 0x00, 37, 0x00, 38, 0x00, 39, 0x00, + 40, 0x00, 41, 0x00, 42, 0x00, 43, 0x00, + 44, 0x00, 45, 0x00, 46, 0x00, 47, 0x00, + 48, 0x00, 49, 0x00, 50, 0x00, 51, 0x00, + 52, 0x00, 53, 0x00, 54, 0x00, 55, 0x00, +}; +#define MODE8420LEN (sizeof(mode8420pro)/2) + +static __u8 mode8420con[] = { /* consumer output mode */ + 32, 0x20, 33, 0x00, 34, 0x00, 35, 0x48, + 36, 0x00, 37, 0x00, 38, 0x00, 39, 0x00, + 40, 0x00, 41, 0x00, 42, 0x00, 43, 0x00, + 44, 0x00, 45, 0x00, 46, 0x00, 47, 0x00, + 48, 0x00, 49, 0x00, 50, 0x00, 51, 0x00, + 52, 0x00, 53, 0x00, 54, 0x00, 55, 0x00, +}; + +#endif diff --git a/linux/drivers/media/video/dabfirmware.h b/linux/drivers/media/video/dabfirmware.h new file mode 100644 index 000000000..d14d80356 --- /dev/null +++ b/linux/drivers/media/video/dabfirmware.h @@ -0,0 +1,1408 @@ +/* + * dabdata.h - dab usb firmware and bitstream data + */ + +static INTEL_HEX_RECORD firmware[] = { + +{ 2, 0x0000, 0, {0x21,0x57} }, +{ 3, 0x0003, 0, {0x02,0x01,0x66} }, +{ 3, 0x000b, 0, {0x02,0x01,0x66} }, +{ 3, 0x0013, 0, {0x02,0x01,0x66} }, +{ 3, 0x001b, 0, {0x02,0x01,0x66} }, +{ 3, 0x0023, 0, {0x02,0x01,0x66} }, +{ 3, 0x002b, 0, {0x02,0x01,0x66} }, +{ 3, 0x0033, 0, {0x02,0x03,0x0f} }, +{ 3, 0x003b, 0, {0x02,0x01,0x66} }, +{ 3, 0x0043, 0, {0x02,0x01,0x00} }, +{ 3, 0x004b, 0, {0x02,0x01,0x66} }, +{ 3, 0x0053, 0, {0x02,0x01,0x66} }, +{ 3, 0x005b, 0, {0x02,0x04,0xbd} }, +{ 3, 0x0063, 0, {0x02,0x01,0x67} }, +{ 3, 0x0100, 0, {0x02,0x0c,0x5a} }, +{ 3, 0x0104, 0, {0x02,0x01,0xed} }, +{ 3, 0x0108, 0, {0x02,0x02,0x51} }, +{ 3, 0x010c, 0, {0x02,0x02,0x7c} }, +{ 3, 0x0110, 0, {0x02,0x02,0xe4} }, +{ 1, 0x0114, 0, {0x32} }, +{ 1, 0x0118, 0, {0x32} }, +{ 3, 0x011c, 0, {0x02,0x05,0xfd} }, +{ 3, 0x0120, 0, {0x02,0x00,0x00} }, +{ 3, 0x0124, 0, {0x02,0x00,0x00} }, +{ 3, 0x0128, 0, {0x02,0x04,0x3c} }, +{ 3, 0x012c, 0, {0x02,0x04,0x6a} }, +{ 3, 0x0130, 0, {0x02,0x00,0x00} }, +{ 3, 0x0134, 0, {0x02,0x00,0x00} }, +{ 3, 0x0138, 0, {0x02,0x00,0x00} }, +{ 3, 0x013c, 0, {0x02,0x00,0x00} }, +{ 3, 0x0140, 0, {0x02,0x00,0x00} }, +{ 3, 0x0144, 0, {0x02,0x00,0x00} }, +{ 3, 0x0148, 0, {0x02,0x00,0x00} }, +{ 3, 0x014c, 0, {0x02,0x00,0x00} }, +{ 3, 0x0150, 0, {0x02,0x00,0x00} }, +{ 3, 0x0154, 0, {0x02,0x00,0x00} }, +{ 10, 0x0157, 0, {0x75,0x81,0x7f,0xe5,0x82,0x60,0x03,0x02,0x01,0x61} }, +{ 5, 0x0161, 0, {0x12,0x07,0x6f,0x21,0x64} }, +{ 1, 0x0166, 0, {0x32} }, +{ 14, 0x0167, 0, {0xc0,0xd0,0xc0,0x86,0xc0,0x82,0xc0,0x83,0xc0,0xe0,0x90,0x7f,0x97,0xe0} }, +{ 14, 0x0175, 0, {0x44,0x80,0xf0,0x90,0x7f,0x69,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} }, +{ 14, 0x0183, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} }, +{ 14, 0x0191, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x90,0x7f,0x97,0xe0} }, +{ 3, 0x019f, 0, {0x55,0x7f,0xf0} }, +{ 14, 0x01a2, 0, {0x90,0x7f,0x9a,0xe0,0x30,0xe4,0x23,0x90,0x7f,0x68,0xf0,0xf0,0xf0,0xf0} }, +{ 14, 0x01b0, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} }, +{ 14, 0x01be, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} }, +{ 14, 0x01cc, 0, {0xe5,0xd8,0xc2,0xe3,0xf5,0xd8,0xd0,0xe0,0xd0,0x83,0xd0,0x82,0xd0,0x86} }, +{ 3, 0x01da, 0, {0xd0,0xd0,0x32} }, +{ 8, 0x01dd, 0, {0x75,0x86,0x00,0x90,0xff,0xc3,0x7c,0x05} }, +{ 7, 0x01e5, 0, {0xa3,0xe5,0x82,0x45,0x83,0x70,0xf9} }, +{ 1, 0x01ec, 0, {0x22} }, +{ 14, 0x01ed, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0xd0} }, +{ 14, 0x01fb, 0, {0x75,0xd0,0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91} }, +{ 13, 0x0209, 0, {0x90,0x88,0x00,0xe0,0xf5,0x41,0x90,0x7f,0xab,0x74,0x02,0xf0,0x90} }, +{ 9, 0x0216, 0, {0x7f,0xab,0x74,0x02,0xf0,0xe5,0x32,0x60,0x21} }, +{ 4, 0x021f, 0, {0x7a,0x00,0x7b,0x00} }, +{ 11, 0x0223, 0, {0xc3,0xea,0x94,0x18,0xeb,0x64,0x80,0x94,0x80,0x50,0x12} }, +{ 14, 0x022e, 0, {0x90,0x7f,0x69,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x0a,0xba,0x00} }, +{ 2, 0x023c, 0, {0x01,0x0b} }, +{ 2, 0x023e, 0, {0x80,0xe3} }, +{ 2, 0x0240, 0, {0xd0,0x86} }, +{ 14, 0x0242, 0, {0xd0,0xd0,0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0} }, +{ 1, 0x0250, 0, {0x32} }, +{ 14, 0x0251, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 14, 0x025f, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xab,0x74} }, +{ 4, 0x026d, 0, {0x04,0xf0,0xd0,0x86} }, +{ 11, 0x0271, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x027c, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} }, +{ 14, 0x028a, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} }, +{ 13, 0x0298, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} }, +{ 12, 0x02a5, 0, {0x7f,0xab,0x74,0x08,0xf0,0x75,0x6e,0x00,0x75,0x6f,0x02,0x12} }, +{ 6, 0x02b1, 0, {0x11,0x44,0x75,0x70,0x39,0x75} }, +{ 6, 0x02b7, 0, {0x71,0x0c,0x75,0x72,0x02,0x12} }, +{ 12, 0x02bd, 0, {0x11,0x75,0x90,0x7f,0xd6,0xe4,0xf0,0x75,0xd8,0x20,0xd0,0x86} }, +{ 14, 0x02c9, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} }, +{ 13, 0x02d7, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x02e4, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 14, 0x02f2, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xab,0x74} }, +{ 4, 0x0300, 0, {0x10,0xf0,0xd0,0x86} }, +{ 11, 0x0304, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x030f, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} }, +{ 14, 0x031d, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} }, +{ 12, 0x032b, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0x75,0x6e,0x00,0x75,0x6f,0x02} }, +{ 7, 0x0337, 0, {0x12,0x11,0x44,0x75,0x70,0x40,0x75} }, +{ 6, 0x033e, 0, {0x71,0x0c,0x75,0x72,0x02,0x12} }, +{ 14, 0x0344, 0, {0x11,0x75,0x90,0x7f,0xd6,0x74,0x02,0xf0,0x90,0x7f,0xd6,0x74,0x06,0xf0} }, +{ 5, 0x0352, 0, {0x75,0xd8,0x10,0xd0,0x86} }, +{ 14, 0x0357, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} }, +{ 13, 0x0365, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 13, 0x0372, 0, {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x90,0x7f,0xa6,0x74,0x9a,0xf0,0x12} }, +{ 12, 0x037f, 0, {0x10,0x1b,0x90,0x7f,0xa6,0xe5,0x42,0xf0,0x12,0x10,0x1b,0x90} }, +{ 13, 0x038b, 0, {0x7f,0xa6,0xe5,0x43,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa5,0x74,0x40} }, +{ 1, 0x0398, 0, {0xf0} }, +{ 1, 0x0399, 0, {0x22} }, +{ 13, 0x039a, 0, {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x90,0x7f,0xa6,0x74,0x9a,0xf0,0x12} }, +{ 12, 0x03a7, 0, {0x10,0x1b,0x90,0x7f,0xa6,0xe5,0x44,0xf0,0x12,0x10,0x1b,0x90} }, +{ 12, 0x03b3, 0, {0x7f,0xa6,0xe5,0x45,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa6,0xe5} }, +{ 11, 0x03bf, 0, {0x46,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa5,0x74,0x40,0xf0} }, +{ 1, 0x03ca, 0, {0x22} }, +{ 10, 0x03cb, 0, {0x75,0x44,0x02,0x75,0x45,0x00,0x75,0x46,0x00,0x12} }, +{ 9, 0x03d5, 0, {0x03,0x9a,0x75,0x42,0x03,0x75,0x43,0x00,0x12} }, +{ 2, 0x03de, 0, {0x03,0x72} }, +{ 1, 0x03e0, 0, {0x22} }, +{ 12, 0x03e1, 0, {0x90,0x88,0x00,0xe5,0x36,0xf0,0x90,0x88,0x00,0x74,0x10,0x25} }, +{ 9, 0x03ed, 0, {0x36,0xf0,0x12,0x01,0xdd,0x75,0x42,0x01,0x75} }, +{ 9, 0x03f6, 0, {0x43,0x18,0x12,0x03,0x72,0x75,0x44,0x02,0x75} }, +{ 9, 0x03ff, 0,{0x45,0x00,0x75,0x46,0x00,0x12,0x03,0x9a,0x75} }, +{ 8, 0x0408, 0,{0x42,0x03,0x75,0x43,0x44,0x12,0x03,0x72} }, +{ 1, 0x0410, 0,{0x22} }, +{ 14, 0x0411, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 14, 0x041f, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xaa,0x74} }, +{ 4, 0x042d, 0, {0x02,0xf0,0xd0,0x86} }, +{ 11, 0x0431, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x043c, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 14, 0x044a, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xa9,0x74} }, +{ 7, 0x0458, 0, {0x04,0xf0,0x75,0x30,0x01,0xd0,0x86} }, +{ 11, 0x045f, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x046a, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 14, 0x0478, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xaa,0x74} }, +{ 7, 0x0486, 0, {0x04,0xf0,0x75,0x31,0x01,0xd0,0x86} }, +{ 11, 0x048d, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x0498, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 12, 0x04a6, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe5,0xf5,0x91,0xd0,0x86} }, +{ 11, 0x04b2, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x04bd, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 12, 0x04cb, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe7,0xf5,0x91,0xd0,0x86} }, +{ 11, 0x04d7, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 12, 0x04e2, 0, {0x90,0x7f,0xea,0xe0,0xfa,0x8a,0x20,0x90,0x7f,0x96,0xe4,0xf0} }, +{ 1, 0x04ee, 0, {0x22} }, +{ 7, 0x04ef, 0, {0x90,0x7f,0xea,0xe0,0xfa,0x8a,0x21} }, +{ 1, 0x04f6, 0, {0x22} }, +{ 14, 0x04f7, 0, {0x90,0x17,0x13,0xe0,0xfa,0x90,0x17,0x15,0xe0,0xfb,0x74,0x80,0x2a,0xfa} }, +{ 14, 0x0505, 0, {0x74,0x80,0x2b,0xfb,0xea,0x03,0x03,0x54,0x3f,0xfc,0xea,0xc4,0x23,0x54} }, +{ 14, 0x0513, 0, {0x1f,0xfa,0x2c,0xfa,0xeb,0x03,0x03,0x54,0x3f,0xfc,0xeb,0xc4,0x23,0x54} }, +{ 11, 0x0521, 0, {0x1f,0xfb,0x2c,0xfb,0x90,0x17,0x0a,0xe0,0xfc,0x60,0x02} }, +{ 2, 0x052c, 0, {0x7a,0x00} }, +{ 7, 0x052e, 0, {0x90,0x17,0x0c,0xe0,0xfc,0x60,0x02} }, +{ 2, 0x0535, 0, {0x7b,0x00} }, +{ 11, 0x0537, 0, {0xea,0x2b,0xfc,0xc3,0x13,0xf5,0x3a,0x75,0x44,0x02,0x8b} }, +{ 7, 0x0542, 0, {0x45,0x8a,0x46,0x12,0x03,0x9a,0x75} }, +{ 9, 0x0549, 0, {0x6e,0x08,0x75,0x6f,0x00,0x12,0x11,0x44,0x75} }, +{ 4, 0x0552, 0, {0x70,0x47,0x75,0x71} }, +{ 8, 0x0556, 0, {0x0c,0x75,0x72,0x02,0x12,0x11,0x75,0x85} }, +{ 5, 0x055e, 0, {0x3a,0x73,0x12,0x11,0xa0} }, +{ 1, 0x0563, 0, {0x22} }, +{ 14, 0x0564, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x65,0x02,0xf0,0x90} }, +{ 14, 0x0572, 0, {0x7f,0xeb,0xe0,0xfa,0x90,0x7f,0xea,0xe0,0xfb,0x90,0x7f,0xef,0xe0,0xfc} }, +{ 14, 0x0580, 0, {0x33,0x95,0xe0,0xfd,0x8c,0x05,0x7c,0x00,0x90,0x7f,0xee,0xe0,0xfe,0x33} }, +{ 14, 0x058e, 0, {0x95,0xe0,0xff,0xec,0x2e,0xfc,0xed,0x3f,0xfd,0x90,0x7f,0xe9,0xe0,0xfe} }, +{ 5, 0x059c, 0, {0xbe,0x01,0x02,0x80,0x03} }, +{ 3, 0x05a1, 0, {0x02,0x05,0xf9} }, +{ 6, 0x05a4, 0, {0xbc,0x01,0x21,0xbd,0x00,0x1e} }, +{ 14, 0x05aa, 0, {0xea,0xc4,0x03,0x54,0xf8,0xfc,0xeb,0x25,0xe0,0xfd,0x2c,0x24,0x00,0xfc} }, +{ 14, 0x05b8, 0, {0xe4,0x34,0x17,0xfd,0x90,0x7e,0xc0,0xe0,0xfe,0x8c,0x82,0x8d,0x83,0xf0} }, +{ 2, 0x05c6, 0, {0x80,0x31} }, +{ 14, 0x05c8, 0, {0xea,0xc4,0x03,0x54,0xf8,0xfa,0xeb,0x25,0xe0,0xfb,0x2a,0xfa,0x24,0x00} }, +{ 14, 0x05d6, 0, {0xfb,0xe4,0x34,0x17,0xfc,0x90,0x7e,0xc0,0xe0,0xfd,0x8b,0x82,0x8c,0x83} }, +{ 14, 0x05e4, 0, {0xf0,0x74,0x01,0x2a,0x24,0x00,0xfa,0xe4,0x34,0x17,0xfb,0x90,0x7e,0xc1} }, +{ 7, 0x05f2, 0, {0xe0,0xfc,0x8a,0x82,0x8b,0x83,0xf0} }, +{ 3, 0x05f9, 0, {0x75,0x38,0x01} }, +{ 1, 0x05fc, 0, {0x22} }, +{ 14, 0x05fd, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} }, +{ 14, 0x060b, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} }, +{ 13, 0x0619, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} }, +{ 13, 0x0626, 0, {0x7f,0xaa,0x74,0x01,0xf0,0x12,0x05,0x64,0x75,0x37,0x00,0xd0,0x86} }, +{ 14, 0x0633, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} }, +{ 13, 0x0641, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x064e, 0, {0x90,0x7f,0xeb,0xe0,0xfa,0x90,0x7f,0xea,0xe0,0xfb,0x90,0x7f,0xee,0xe0} }, +{ 14, 0x065c, 0, {0xfc,0x33,0x95,0xe0,0xfd,0x90,0x7f,0x96,0xe0,0xfe,0x90,0x7f,0x96,0x74} }, +{ 14, 0x066a, 0, {0x80,0x65,0x06,0xf0,0x90,0x7f,0x00,0x74,0x01,0xf0,0xea,0xc4,0x03,0x54} }, +{ 14, 0x0678, 0, {0xf8,0xfe,0xeb,0x25,0xe0,0xfb,0x2e,0xfe,0x24,0x00,0xfb,0xe4,0x34,0x17} }, +{ 14, 0x0686, 0, {0xff,0x8b,0x82,0x8f,0x83,0xe0,0xfb,0x74,0x01,0x2e,0x24,0x00,0xfe,0xe4} }, +{ 14, 0x0694, 0, {0x34,0x17,0xff,0x8e,0x82,0x8f,0x83,0xe0,0xfe,0x90,0x7f,0xe9,0xe0,0xff} }, +{ 3, 0x06a2, 0, {0xbf,0x81,0x0a} }, +{ 10, 0x06a5, 0, {0x90,0x7f,0x00,0xeb,0xf0,0x90,0x7f,0x01,0xee,0xf0} }, +{ 8, 0x06af, 0, {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x82,0x1a} }, +{ 3, 0x06b7, 0, {0xba,0x01,0x0c} }, +{ 12, 0x06ba, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0b} }, +{ 11, 0x06c6, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0x74,0xb5,0xf0} }, +{ 8, 0x06d1, 0, {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x83,0x1b} }, +{ 3, 0x06d9, 0, {0xba,0x01,0x0d} }, +{ 13, 0x06dc, 0, {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0b} }, +{ 11, 0x06e9, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0x74,0x12,0xf0} }, +{ 8, 0x06f4, 0, {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x84,0x1c} }, +{ 3, 0x06fc, 0, {0xba,0x01,0x0d} }, +{ 13, 0x06ff, 0, {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0c} }, +{ 12, 0x070c, 0, {0x90,0x7f,0x00,0x74,0x80,0xf0,0x90,0x7f,0x01,0x74,0x01,0xf0} }, +{ 5, 0x0718, 0, {0x90,0x7f,0xb5,0xec,0xf0} }, +{ 1, 0x071d, 0, {0x22} }, +{ 12, 0x071e, 0, {0x75,0x36,0x0d,0x90,0x88,0x00,0x74,0x1d,0xf0,0x75,0x6b,0x80} }, +{ 10, 0x072a, 0, {0x75,0x6c,0x3c,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} }, +{ 9, 0x0734, 0, {0x6c,0x0f,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} }, +{ 9, 0x073d, 0, {0x6c,0x06,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} }, +{ 7, 0x0746, 0, {0x6c,0x01,0x12,0x10,0xe2,0x7a,0x00} }, +{ 3, 0x074d, 0, {0xba,0xff,0x00} }, +{ 2, 0x0750, 0, {0x50,0x0a} }, +{ 10, 0x0752, 0, {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xf1} }, +{ 10, 0x075c, 0, {0x75,0x6b,0x80,0x75,0x6c,0x3c,0x12,0x10,0xe2,0x75} }, +{ 8, 0x0766, 0, {0x6b,0x80,0x75,0x6c,0x0f,0x12,0x10,0xe2} }, +{ 1, 0x076e, 0, {0x22} }, +{ 14, 0x076f, 0, {0x90,0x7f,0xa1,0xe4,0xf0,0x90,0x7f,0xaf,0x74,0x01,0xf0,0x90,0x7f,0x92} }, +{ 14, 0x077d, 0, {0x74,0x02,0xf0,0x75,0x8e,0x31,0x75,0x89,0x21,0x75,0x88,0x00,0x75,0xc8} }, +{ 14, 0x078b, 0, {0x00,0x75,0x8d,0x40,0x75,0x98,0x40,0x75,0xc0,0x40,0x75,0x87,0x00,0x75} }, +{ 9, 0x0799, 0, {0x20,0x00,0x75,0x21,0x00,0x75,0x22,0x00,0x75} }, +{ 5, 0x07a2, 0, {0x23,0x00,0x75,0x47,0x00} }, +{ 7, 0x07a7, 0, {0xc3,0xe5,0x47,0x94,0x20,0x50,0x11} }, +{ 13, 0x07ae, 0, {0xe5,0x47,0x24,0x00,0xf5,0x82,0xe4,0x34,0x17,0xf5,0x83,0xe4,0xf0} }, +{ 4, 0x07bb, 0, {0x05,0x47,0x80,0xe8} }, +{ 9, 0x07bf, 0, {0xe4,0xf5,0x40,0xf5,0x3f,0xe4,0xf5,0x3c,0xf5} }, +{ 7, 0x07c8, 0, {0x3b,0xe4,0xf5,0x3e,0xf5,0x3d,0x75} }, +{ 11, 0x07cf, 0, {0x32,0x00,0x75,0x37,0x00,0x75,0x39,0x00,0x90,0x7f,0x93} }, +{ 14, 0x07da, 0, {0x74,0x3c,0xf0,0x90,0x7f,0x9c,0x74,0xff,0xf0,0x90,0x7f,0x96,0x74,0x80} }, +{ 14, 0x07e8, 0, {0xf0,0x90,0x7f,0x94,0x74,0x70,0xf0,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90} }, +{ 14, 0x07f6, 0, {0x7f,0x97,0xe4,0xf0,0x90,0x7f,0x95,0x74,0xc2,0xf0,0x90,0x7f,0x98,0x74} }, +{ 14, 0x0804, 0, {0x28,0xf0,0x90,0x7f,0x9e,0x74,0x28,0xf0,0x90,0x7f,0xf0,0xe4,0xf0,0x90} }, +{ 14, 0x0812, 0, {0x7f,0xf1,0xe4,0xf0,0x90,0x7f,0xf2,0xe4,0xf0,0x90,0x7f,0xf3,0xe4,0xf0} }, +{ 14, 0x0820, 0, {0x90,0x7f,0xf4,0xe4,0xf0,0x90,0x7f,0xf5,0xe4,0xf0,0x90,0x7f,0xf6,0xe4} }, +{ 14, 0x082e, 0, {0xf0,0x90,0x7f,0xf7,0xe4,0xf0,0x90,0x7f,0xf8,0xe4,0xf0,0x90,0x7f,0xf9} }, +{ 14, 0x083c, 0, {0x74,0x38,0xf0,0x90,0x7f,0xfa,0x74,0xa0,0xf0,0x90,0x7f,0xfb,0x74,0xa0} }, +{ 14, 0x084a, 0, {0xf0,0x90,0x7f,0xfc,0x74,0xa0,0xf0,0x90,0x7f,0xfd,0x74,0xa0,0xf0,0x90} }, +{ 14, 0x0858, 0, {0x7f,0xfe,0x74,0xa0,0xf0,0x90,0x7f,0xff,0x74,0xa0,0xf0,0x90,0x7f,0xe0} }, +{ 14, 0x0866, 0, {0x74,0x03,0xf0,0x90,0x7f,0xe1,0x74,0x01,0xf0,0x90,0x7f,0xdd,0x74,0x80} }, +{ 11, 0x0874, 0, {0xf0,0x12,0x12,0x43,0x12,0x07,0x1e,0x7a,0x00,0x7b,0x00} }, +{ 9, 0x087f, 0, {0xc3,0xea,0x94,0x1e,0xeb,0x94,0x00,0x50,0x17} }, +{ 12, 0x0888, 0, {0x90,0x88,0x00,0xe0,0xf5,0x47,0x90,0x88,0x0b,0xe0,0xf5,0x47} }, +{ 9, 0x0894, 0, {0x90,0x7f,0x68,0xf0,0x0a,0xba,0x00,0x01,0x0b} }, +{ 2, 0x089d, 0, {0x80,0xe0} }, +{ 12, 0x089f, 0, {0x12,0x03,0xe1,0x90,0x7f,0xd6,0xe4,0xf0,0x7a,0x00,0x7b,0x00} }, +{ 13, 0x08ab, 0, {0x8a,0x04,0x8b,0x05,0xc3,0xea,0x94,0xe0,0xeb,0x94,0x2e,0x50,0x1a} }, +{ 14, 0x08b8, 0, {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} }, +{ 10, 0x08c6, 0, {0x04,0xd0,0x03,0xd0,0x02,0x0a,0xba,0x00,0x01,0x0b} }, +{ 2, 0x08d0, 0, {0x80,0xd9} }, +{ 13, 0x08d2, 0, {0x90,0x7f,0xd6,0x74,0x02,0xf0,0x90,0x7f,0xd6,0x74,0x06,0xf0,0x90} }, +{ 14, 0x08df, 0, {0x7f,0xde,0x74,0x05,0xf0,0x90,0x7f,0xdf,0x74,0x05,0xf0,0x90,0x7f,0xac} }, +{ 14, 0x08ed, 0, {0xe4,0xf0,0x90,0x7f,0xad,0x74,0x05,0xf0,0x75,0xa8,0x80,0x75,0xf8,0x10} }, +{ 13, 0x08fb, 0, {0x90,0x7f,0xae,0x74,0x0b,0xf0,0x90,0x7f,0xe2,0x74,0x88,0xf0,0x90} }, +{ 12, 0x0908, 0, {0x7f,0xab,0x74,0x08,0xf0,0x75,0xe8,0x11,0x75,0x32,0x01,0x75} }, +{ 12, 0x0914, 0, {0x31,0x00,0x75,0x30,0x00,0xc0,0x04,0xc0,0x05,0x12,0x04,0xf7} }, +{ 10, 0x0920, 0, {0xd0,0x05,0xd0,0x04,0x75,0x34,0x00,0x75,0x35,0x01} }, +{ 13, 0x092a, 0, {0x90,0x7f,0xae,0x74,0x03,0xf0,0x8c,0x02,0xba,0x00,0x02,0x80,0x03} }, +{ 3, 0x0937, 0, {0x02,0x0a,0x3f} }, +{ 12, 0x093a, 0, {0x85,0x33,0x34,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90,0x7f,0x97} }, +{ 14, 0x0946, 0, {0x74,0x08,0xf0,0x90,0x7f,0x9d,0x74,0x88,0xf0,0x90,0x7f,0x9a,0xe0,0xfa} }, +{ 12, 0x0954, 0, {0x74,0x05,0x5a,0xf5,0x33,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90} }, +{ 13, 0x0960, 0, {0x7f,0x97,0x74,0x02,0xf0,0x90,0x7f,0x9d,0x74,0x82,0xf0,0xe5,0x33} }, +{ 13, 0x096d, 0, {0x25,0xe0,0xfa,0x90,0x7f,0x9a,0xe0,0x54,0x05,0xfb,0x4a,0xf5,0x33} }, +{ 2, 0x097a, 0, {0x60,0x0c} }, +{ 12, 0x097c, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x4a,0xf0} }, +{ 11, 0x0988, 0, {0x75,0x6e,0x00,0x75,0x6f,0x00,0xc0,0x04,0xc0,0x05,0x12} }, +{ 14, 0x0993, 0, {0x11,0x44,0xd0,0x05,0xd0,0x04,0x90,0x17,0x13,0xe0,0xfa,0x74,0x80,0x2a} }, +{ 6, 0x09a1, 0, {0xfa,0xe5,0x33,0xb4,0x04,0x29} }, +{ 3, 0x09a7, 0, {0xba,0xa0,0x00} }, +{ 2, 0x09aa, 0, {0x50,0x24} }, +{ 13, 0x09ac, 0, {0x90,0x17,0x13,0xe0,0x04,0xfb,0x0b,0x90,0x17,0x13,0xeb,0xf0,0x90} }, +{ 14, 0x09b9, 0, {0x17,0x13,0xe0,0xfb,0x90,0x17,0x15,0xf0,0xc0,0x02,0xc0,0x04,0xc0,0x05} }, +{ 9, 0x09c7, 0, {0x12,0x04,0xf7,0xd0,0x05,0xd0,0x04,0xd0,0x02} }, +{ 5, 0x09d0, 0, {0xe5,0x33,0xb4,0x02,0x26} }, +{ 6, 0x09d5, 0, {0xc3,0x74,0x04,0x9a,0x50,0x20} }, +{ 13, 0x09db, 0, {0x90,0x17,0x13,0xe0,0xfa,0x1a,0x1a,0x90,0x17,0x13,0xea,0xf0,0x90} }, +{ 13, 0x09e8, 0, {0x17,0x13,0xe0,0xfa,0x90,0x17,0x15,0xf0,0xc0,0x04,0xc0,0x05,0x12} }, +{ 6, 0x09f5, 0, {0x04,0xf7,0xd0,0x05,0xd0,0x04} }, +{ 5, 0x09fb, 0, {0xe5,0x33,0xb4,0x08,0x1d} }, +{ 4, 0x0a00, 0, {0xe5,0x34,0x70,0x19} }, +{ 10, 0x0a04, 0, {0x74,0x01,0x25,0x35,0x54,0x0f,0xf5,0x35,0x85,0x35} }, +{ 12, 0x0a0e, 0, {0x75,0x75,0x76,0x00,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0} }, +{ 3, 0x0a1a, 0, {0x05,0xd0,0x04} }, +{ 5, 0x0a1d, 0, {0xe5,0x33,0xb4,0x01,0x1d} }, +{ 4, 0x0a22, 0, {0xe5,0x34,0x70,0x19} }, +{ 10, 0x0a26, 0, {0xe5,0x35,0x24,0xff,0x54,0x0f,0xf5,0x35,0x85,0x35} }, +{ 12, 0x0a30, 0, {0x75,0x75,0x76,0x00,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0} }, +{ 3, 0x0a3c, 0, {0x05,0xd0,0x04} }, +{ 14, 0x0a3f, 0, {0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0,0x04,0x90,0x7f,0x96} }, +{ 14, 0x0a4d, 0, {0xe0,0xfa,0x90,0x7f,0x96,0x74,0x7f,0x5a,0xf0,0x90,0x7f,0x97,0x74,0x08} }, +{ 10, 0x0a5b, 0, {0xf0,0xc3,0xec,0x94,0x00,0xed,0x94,0x02,0x40,0x08} }, +{ 8, 0x0a65, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x20,0xe6,0x08} }, +{ 8, 0x0a6d, 0, {0xc3,0xe4,0x9c,0x74,0x08,0x9d,0x50,0x13} }, +{ 14, 0x0a75, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x40,0x65,0x02,0xf0,0x7c} }, +{ 5, 0x0a83, 0, {0x00,0x7d,0x00,0x80,0x05} }, +{ 5, 0x0a88, 0, {0x0c,0xbc,0x00,0x01,0x0d} }, +{ 5, 0x0a8d, 0, {0xe5,0x38,0xb4,0x01,0x0e} }, +{ 13, 0x0a92, 0, {0xc0,0x04,0xc0,0x05,0x12,0x04,0xf7,0xd0,0x05,0xd0,0x04,0x75,0x38} }, +{ 1, 0x0a9f, 0, {0x00} }, +{ 7, 0x0aa0, 0, {0xe5,0x31,0x70,0x03,0x02,0x09,0x2a} }, +{ 10, 0x0aa7, 0, {0x90,0x7f,0xc9,0xe0,0xfa,0x70,0x03,0x02,0x0c,0x2d} }, +{ 14, 0x0ab1, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x65,0x02,0xf0,0x90} }, +{ 9, 0x0abf, 0, {0x7d,0xc0,0xe0,0xfa,0xba,0x2c,0x02,0x80,0x03} }, +{ 3, 0x0ac8, 0, {0x02,0x0b,0x36} }, +{ 5, 0x0acb, 0, {0x75,0x32,0x00,0x7b,0x00} }, +{ 3, 0x0ad0, 0, {0xbb,0x64,0x00} }, +{ 2, 0x0ad3, 0, {0x50,0x1c} }, +{ 14, 0x0ad5, 0, {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} }, +{ 13, 0x0ae3, 0, {0x04,0xd0,0x03,0xd0,0x02,0x90,0x88,0x0f,0xe0,0xf5,0x47,0x0b,0x80} }, +{ 1, 0x0af0, 0, {0xdf} }, +{ 13, 0x0af1, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x07,0x1e,0x12,0x03,0xe1,0x12} }, +{ 12, 0x0afe, 0, {0x04,0xf7,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x75,0x6e,0x00,0x75} }, +{ 13, 0x0b0a, 0, {0x6f,0x01,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x11,0x44,0xd0,0x05} }, +{ 9, 0x0b17, 0, {0xd0,0x04,0xd0,0x02,0x75,0x70,0x4d,0x75,0x71} }, +{ 11, 0x0b20, 0, {0x0c,0x75,0x72,0x02,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12} }, +{ 11, 0x0b2b, 0, {0x11,0x75,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x02,0x0c,0x2d} }, +{ 3, 0x0b36, 0, {0xba,0x2a,0x3b} }, +{ 13, 0x0b39, 0, {0x90,0x7f,0x98,0x74,0x20,0xf0,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12} }, +{ 14, 0x0b46, 0, {0x01,0xdd,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x90,0x7f,0x98,0x74,0x28,0xf0} }, +{ 2, 0x0b54, 0, {0x7b,0x00} }, +{ 3, 0x0b56, 0, {0xbb,0x0a,0x00} }, +{ 5, 0x0b59, 0, {0x40,0x03,0x02,0x0c,0x2d} }, +{ 14, 0x0b5e, 0, {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} }, +{ 8, 0x0b6c, 0, {0x04,0xd0,0x03,0xd0,0x02,0x0b,0x80,0xe2} }, +{ 3, 0x0b74, 0, {0xba,0x2b,0x1a} }, +{ 8, 0x0b77, 0, {0x90,0x7f,0xc9,0xe0,0xfb,0xbb,0x40,0x12} }, +{ 14, 0x0b7f, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x12,0x05,0xd0,0x05,0xd0,0x04,0xd0} }, +{ 4, 0x0b8d, 0, {0x02,0x02,0x0c,0x2d} }, +{ 3, 0x0b91, 0, {0xba,0x10,0x1f} }, +{ 14, 0x0b94, 0, {0x90,0x7f,0x96,0xe0,0xfb,0x90,0x7f,0x96,0x74,0x80,0x65,0x03,0xf0,0xc0} }, +{ 14, 0x0ba2, 0, {0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x3d,0xd0,0x05,0xd0,0x04,0xd0,0x02} }, +{ 3, 0x0bb0, 0, {0x02,0x0c,0x2d} }, +{ 3, 0x0bb3, 0, {0xba,0x11,0x12} }, +{ 14, 0x0bb6, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x6a,0xd0,0x05,0xd0,0x04,0xd0} }, +{ 4, 0x0bc4, 0, {0x02,0x02,0x0c,0x2d} }, +{ 3, 0x0bc8, 0, {0xba,0x12,0x12} }, +{ 14, 0x0bcb, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x8f,0xd0,0x05,0xd0,0x04,0xd0} }, +{ 4, 0x0bd9, 0, {0x02,0x02,0x0c,0x2d} }, +{ 3, 0x0bdd, 0, {0xba,0x13,0x0b} }, +{ 11, 0x0be0, 0, {0x90,0x7d,0xc1,0xe0,0xfb,0x90,0x88,0x00,0xf0,0x80,0x42} }, +{ 3, 0x0beb, 0, {0xba,0x14,0x11} }, +{ 14, 0x0bee, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x11,0xdd,0xd0,0x05,0xd0,0x04,0xd0} }, +{ 3, 0x0bfc, 0, {0x02,0x80,0x2e} }, +{ 3, 0x0bff, 0, {0xba,0x15,0x1d} }, +{ 12, 0x0c02, 0, {0x90,0x7d,0xc1,0xe0,0xf5,0x75,0x90,0x7d,0xc2,0xe0,0xf5,0x76} }, +{ 14, 0x0c0e, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0,0x05,0xd0,0x04,0xd0} }, +{ 3, 0x0c1c, 0, {0x02,0x80,0x0e} }, +{ 3, 0x0c1f, 0, {0xba,0x16,0x0b} }, +{ 11, 0x0c22, 0, {0xc0,0x04,0xc0,0x05,0x12,0x13,0xa3,0xd0,0x05,0xd0,0x04} }, +{ 11, 0x0c2d, 0, {0x90,0x7f,0xc9,0xe4,0xf0,0x75,0x31,0x00,0x02,0x09,0x2a} }, +{ 1, 0x0c38, 0, {0x22} }, +{ 7, 0x0c39, 0, {0x53,0x55,0x50,0x45,0x4e,0x44,0x00} }, +{ 7, 0x0c40, 0, {0x52,0x45,0x53,0x55,0x4d,0x45,0x00} }, +{ 6, 0x0c47, 0, {0x20,0x56,0x6f,0x6c,0x20,0x00} }, +{ 13, 0x0c4d, 0, {0x44,0x41,0x42,0x55,0x53,0x42,0x20,0x76,0x31,0x2e,0x30,0x30,0x00} }, +{ 14, 0x0c5a, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} }, +{ 14, 0x0c68, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} }, +{ 13, 0x0c76, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} }, +{ 14, 0x0c83, 0, {0x7f,0xab,0x74,0x01,0xf0,0x90,0x7f,0xe8,0xe0,0xfa,0x90,0x7f,0xe9,0xe0} }, +{ 6, 0x0c91, 0, {0xfb,0xbb,0x00,0x02,0x80,0x03} }, +{ 3, 0x0c97, 0, {0x02,0x0d,0x38} }, +{ 3, 0x0c9a, 0, {0xba,0x80,0x14} }, +{ 14, 0x0c9d, 0, {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5} }, +{ 6, 0x0cab, 0, {0x74,0x02,0xf0,0x02,0x0e,0xcd} }, +{ 5, 0x0cb1, 0, {0xba,0x82,0x02,0x80,0x03} }, +{ 3, 0x0cb6, 0, {0x02,0x0d,0x1d} }, +{ 8, 0x0cb9, 0, {0x90,0x7f,0xec,0xe0,0xfc,0xbc,0x01,0x00} }, +{ 2, 0x0cc1, 0, {0x40,0x21} }, +{ 6, 0x0cc3, 0, {0xc3,0x74,0x07,0x9c,0x40,0x1b} }, +{ 14, 0x0cc9, 0, {0xec,0x24,0xff,0x25,0xe0,0xfd,0x24,0xc6,0xf5,0x82,0xe4,0x34,0x7f,0xf5} }, +{ 13, 0x0cd7, 0, {0x83,0xe0,0xfd,0x53,0x05,0x01,0x90,0x7f,0x00,0xed,0xf0,0x80,0x2b} }, +{ 3, 0x0ce4, 0, {0xbc,0x81,0x00} }, +{ 2, 0x0ce7, 0, {0x40,0x21} }, +{ 6, 0x0ce9, 0, {0xc3,0x74,0x87,0x9c,0x40,0x1b} }, +{ 14, 0x0cef, 0, {0xec,0x24,0x7f,0x25,0xe0,0xfc,0x24,0xb6,0xf5,0x82,0xe4,0x34,0x7f,0xf5} }, +{ 13, 0x0cfd, 0, {0x83,0xe0,0xfc,0x53,0x04,0x01,0x90,0x7f,0x00,0xec,0xf0,0x80,0x05} }, +{ 5, 0x0d0a, 0, {0x90,0x7f,0x00,0xe4,0xf0} }, +{ 14, 0x0d0f, 0, {0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5,0x74,0x02,0xf0,0x02,0x0e,0xcd} }, +{ 5, 0x0d1d, 0, {0xba,0x81,0x02,0x80,0x03} }, +{ 3, 0x0d22, 0, {0x02,0x0e,0xc5} }, +{ 14, 0x0d25, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5,0x74} }, +{ 5, 0x0d33, 0, {0x02,0xf0,0x02,0x0e,0xcd} }, +{ 3, 0x0d38, 0, {0xbb,0x01,0x2d} }, +{ 6, 0x0d3b, 0, {0xba,0x00,0x03,0x02,0x0e,0xcd} }, +{ 3, 0x0d41, 0, {0xba,0x02,0x11} }, +{ 13, 0x0d44, 0, {0x75,0x59,0x00,0xc0,0x02,0xc0,0x03,0x12,0x0e,0xf0,0xd0,0x03,0xd0} }, +{ 4, 0x0d51, 0, {0x02,0x02,0x0e,0xcd} }, +{ 5, 0x0d55, 0, {0xba,0x21,0x02,0x80,0x03} }, +{ 3, 0x0d5a, 0, {0x02,0x0e,0xcd} }, +{ 11, 0x0d5d, 0, {0x75,0x37,0x01,0x90,0x7f,0xc5,0xe4,0xf0,0x02,0x0e,0xcd} }, +{ 3, 0x0d68, 0, {0xbb,0x03,0x1f} }, +{ 6, 0x0d6b, 0, {0xba,0x00,0x03,0x02,0x0e,0xcd} }, +{ 5, 0x0d71, 0, {0xba,0x02,0x02,0x80,0x03} }, +{ 3, 0x0d76, 0, {0x02,0x0e,0xcd} }, +{ 13, 0x0d79, 0, {0x75,0x59,0x01,0xc0,0x02,0xc0,0x03,0x12,0x0e,0xf0,0xd0,0x03,0xd0} }, +{ 4, 0x0d86, 0, {0x02,0x02,0x0e,0xcd} }, +{ 3, 0x0d8a, 0, {0xbb,0x06,0x54} }, +{ 5, 0x0d8d, 0, {0xba,0x80,0x02,0x80,0x03} }, +{ 3, 0x0d92, 0, {0x02,0x0e,0xc5} }, +{ 8, 0x0d95, 0, {0x90,0x7f,0xeb,0xe0,0xfc,0xbc,0x01,0x15} }, +{ 12, 0x0d9d, 0, {0x7c,0xfb,0x7d,0x0f,0x8d,0x06,0x7f,0x00,0x90,0x7f,0xd4,0xee} }, +{ 9, 0x0da9, 0, {0xf0,0x90,0x7f,0xd5,0xec,0xf0,0x02,0x0e,0xcd} }, +{ 10, 0x0db2, 0, {0x90,0x7f,0xeb,0xe0,0xfc,0xbc,0x02,0x02,0x80,0x03} }, +{ 3, 0x0dbc, 0, {0x02,0x0e,0xc5} }, +{ 10, 0x0dbf, 0, {0x90,0x7f,0xea,0xe0,0xfc,0xbc,0x00,0x02,0x80,0x03} }, +{ 3, 0x0dc9, 0, {0x02,0x0e,0xc5} }, +{ 12, 0x0dcc, 0, {0x7c,0x3b,0x7d,0x0f,0x8d,0x06,0x7f,0x00,0x90,0x7f,0xd4,0xee} }, +{ 9, 0x0dd8, 0, {0xf0,0x90,0x7f,0xd5,0xec,0xf0,0x02,0x0e,0xcd} }, +{ 6, 0x0de1, 0, {0xbb,0x07,0x03,0x02,0x0e,0xc5} }, +{ 3, 0x0de7, 0, {0xbb,0x08,0x10} }, +{ 13, 0x0dea, 0, {0xac,0x48,0x90,0x7f,0x00,0xec,0xf0,0x90,0x7f,0xb5,0x74,0x01,0xf0} }, +{ 3, 0x0df7, 0, {0x02,0x0e,0xcd} }, +{ 3, 0x0dfa, 0, {0xbb,0x09,0x31} }, +{ 5, 0x0dfd, 0, {0xba,0x00,0x02,0x80,0x03} }, +{ 3, 0x0e02, 0, {0x02,0x0e,0xc5} }, +{ 14, 0x0e05, 0, {0x90,0x7f,0xea,0xe0,0xfc,0xc3,0x74,0x01,0x9c,0x50,0x03,0x02,0x0e,0xc5} }, +{ 8, 0x0e13, 0, {0x90,0x7f,0xea,0xe0,0xfc,0xbc,0x00,0x0a} }, +{ 10, 0x0e1b, 0, {0x90,0x17,0x21,0xe4,0xf0,0x90,0x17,0x22,0xe4,0xf0} }, +{ 9, 0x0e25, 0, {0x90,0x7f,0xea,0xe0,0xf5,0x48,0x02,0x0e,0xcd} }, +{ 3, 0x0e2e, 0, {0xbb,0x0a,0x27} }, +{ 5, 0x0e31, 0, {0xba,0x81,0x02,0x80,0x03} }, +{ 3, 0x0e36, 0, {0x02,0x0e,0xc5} }, +{ 14, 0x0e39, 0, {0x90,0x7f,0xec,0xe0,0xfa,0x24,0x20,0xfa,0xe4,0x34,0x17,0xfc,0x8a,0x82} }, +{ 14, 0x0e47, 0, {0x8c,0x83,0xe0,0xfa,0x90,0x7f,0x00,0xf0,0x90,0x7f,0xb5,0x74,0x01,0xf0} }, +{ 3, 0x0e55, 0, {0x02,0x0e,0xcd} }, +{ 5, 0x0e58, 0, {0xbb,0x0b,0x02,0x80,0x03} }, +{ 3, 0x0e5d, 0, {0x02,0x0e,0xa9} }, +{ 13, 0x0e60, 0, {0x90,0x17,0x20,0xe4,0xf0,0x90,0x7f,0xec,0xe0,0xfa,0xba,0x01,0x1a} }, +{ 8, 0x0e6d, 0, {0x90,0x7f,0xed,0xe0,0xfa,0xba,0x00,0x12} }, +{ 14, 0x0e75, 0, {0x90,0x7f,0xea,0xe0,0xfa,0x90,0x17,0x21,0xf0,0xc0,0x03,0x12,0x04,0xe2} }, +{ 4, 0x0e83, 0, {0xd0,0x03,0x80,0x46} }, +{ 8, 0x0e87, 0, {0x90,0x7f,0xec,0xe0,0xfa,0xba,0x02,0x3e} }, +{ 8, 0x0e8f, 0, {0x90,0x7f,0xed,0xe0,0xfa,0xba,0x00,0x36} }, +{ 13, 0x0e97, 0, {0xc0,0x03,0x12,0x04,0xef,0xd0,0x03,0x90,0x7f,0xea,0xe0,0xfa,0x90} }, +{ 5, 0x0ea4, 0, {0x17,0x22,0xf0,0x80,0x24} }, +{ 5, 0x0ea9, 0, {0xbb,0x12,0x02,0x80,0x17} }, +{ 5, 0x0eae, 0, {0xbb,0x81,0x02,0x80,0x0d} }, +{ 5, 0x0eb3, 0, {0xbb,0x83,0x02,0x80,0x08} }, +{ 5, 0x0eb8, 0, {0xbb,0x82,0x02,0x80,0x03} }, +{ 3, 0x0ebd, 0, {0xbb,0x84,0x05} }, +{ 5, 0x0ec0, 0, {0x12,0x06,0x4e,0x80,0x08} }, +{ 8, 0x0ec5, 0, {0x90,0x7f,0xb4,0x74,0x03,0xf0,0x80,0x06} }, +{ 6, 0x0ecd, 0, {0x90,0x7f,0xb4,0x74,0x02,0xf0} }, +{ 2, 0x0ed3, 0, {0xd0,0x86} }, +{ 14, 0x0ed5, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} }, +{ 13, 0x0ee3, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 11, 0x0ef0, 0, {0x90,0x7f,0xec,0xe0,0xf5,0x5a,0xc3,0x94,0x01,0x40,0x1d} }, +{ 7, 0x0efb, 0, {0xc3,0x74,0x07,0x95,0x5a,0x40,0x16} }, +{ 13, 0x0f02, 0, {0xe5,0x5a,0x24,0xff,0x25,0xe0,0xfa,0x24,0xc6,0xf5,0x82,0xe4,0x34} }, +{ 9, 0x0f0f, 0, {0x7f,0xf5,0x83,0xaa,0x59,0xea,0xf0,0x80,0x22} }, +{ 7, 0x0f18, 0, {0xc3,0xe5,0x5a,0x94,0x81,0x40,0x1b} }, +{ 7, 0x0f1f, 0, {0xc3,0x74,0x87,0x95,0x5a,0x40,0x14} }, +{ 13, 0x0f26, 0, {0xe5,0x5a,0x24,0xff,0x25,0xe0,0xfa,0x24,0xb6,0xf5,0x82,0xe4,0x34} }, +{ 7, 0x0f33, 0, {0x7f,0xf5,0x83,0xaa,0x59,0xea,0xf0} }, +{ 1, 0x0f3a, 0, {0x22} }, +{ 14, 0x0f3b, 0, {0x09,0x02,0xba,0x00,0x03,0x01,0x00,0x40,0x00,0x09,0x04,0x00,0x00,0x00} }, +{ 14, 0x0f49, 0, {0x01,0x01,0x00,0x00,0x09,0x24,0x01,0x00,0x01,0x3d,0x00,0x01,0x01,0x0c} }, +{ 14, 0x0f57, 0, {0x24,0x02,0x01,0x10,0x07,0x00,0x02,0x03,0x00,0x00,0x00,0x0d,0x24,0x06} }, +{ 14, 0x0f65, 0, {0x03,0x01,0x02,0x15,0x00,0x03,0x00,0x03,0x00,0x00,0x09,0x24,0x03,0x02} }, +{ 14, 0x0f73, 0, {0x01,0x01,0x00,0x01,0x00,0x09,0x24,0x03,0x04,0x02,0x03,0x00,0x03,0x00} }, +{ 14, 0x0f81, 0, {0x09,0x24,0x03,0x05,0x03,0x06,0x00,0x01,0x00,0x09,0x04,0x01,0x00,0x00} }, +{ 14, 0x0f8f, 0, {0x01,0x02,0x00,0x00,0x09,0x04,0x01,0x01,0x01,0x01,0x02,0x00,0x00,0x07} }, +{ 14, 0x0f9d, 0, {0x24,0x01,0x02,0x01,0x01,0x00,0x0b,0x24,0x02,0x01,0x02,0x02,0x10,0x01} }, +{ 14, 0x0fab, 0, {0x80,0xbb,0x00,0x09,0x05,0x88,0x05,0x00,0x01,0x01,0x00,0x00,0x07,0x25} }, +{ 14, 0x0fb9, 0, {0x01,0x00,0x00,0x00,0x00,0x09,0x04,0x02,0x00,0x02,0x00,0x00,0x00,0x00} }, +{ 14, 0x0fc7, 0, {0x07,0x05,0x82,0x02,0x40,0x00,0x00,0x07,0x05,0x02,0x02,0x40,0x00,0x00} }, +{ 14, 0x0fd5, 0, {0x09,0x04,0x02,0x01,0x03,0x00,0x00,0x00,0x00,0x07,0x05,0x82,0x02,0x40} }, +{ 14, 0x0fe3, 0, {0x00,0x00,0x07,0x05,0x02,0x02,0x40,0x00,0x00,0x09,0x05,0x89,0x05,0xa0} }, +{ 10, 0x0ff1, 0, {0x01,0x01,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00} }, +{ 14, 0x0ffb, 0, {0x12,0x01,0x00,0x01,0x00,0x00,0x00,0x40,0x47,0x05,0x99,0x99,0x00,0x01} }, +{ 14, 0x1009, 0, {0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x02,0xba} }, +{ 4, 0x1017, 0, {0x00,0x03,0x01,0x00} }, +{ 2, 0x101b, 0, {0x7a,0x00} }, +{ 3, 0x101d, 0, {0xba,0x05,0x00} }, +{ 2, 0x1020, 0, {0x50,0x17} }, +{ 8, 0x1022, 0, {0x90,0x7f,0xa5,0xe0,0xfb,0x30,0xe0,0x05} }, +{ 5, 0x102a, 0, {0x90,0x00,0x01,0x80,0x0d} }, +{ 10, 0x102f, 0, {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xe4} }, +{ 3, 0x1039, 0, {0x90,0x00,0x01} }, +{ 1, 0x103c, 0, {0x22} }, +{ 14, 0x103d, 0, {0x90,0x7d,0xc1,0xe0,0xf9,0xa3,0xe0,0xfa,0xa3,0xe0,0xfb,0x7c,0x00,0x7d} }, +{ 4, 0x104b, 0, {0x7e,0xeb,0x60,0x12} }, +{ 14, 0x104f, 0, {0x89,0x82,0x8a,0x83,0xe0,0xa3,0xa9,0x82,0xaa,0x83,0x8c,0x82,0x8d,0x83} }, +{ 4, 0x105d, 0, {0xf0,0x0c,0xdb,0xee} }, +{ 8, 0x1061, 0, {0x90,0x7d,0xc3,0xe0,0x90,0x7f,0xb9,0xf0} }, +{ 1, 0x1069, 0, {0x22} }, +{ 14, 0x106a, 0, {0x90,0x7d,0xc1,0xe0,0xf9,0xa3,0xe0,0xfa,0xa3,0xe0,0xfb,0x7c,0xc4,0x7d} }, +{ 4, 0x1078, 0, {0x7d,0xeb,0x60,0xe5} }, +{ 14, 0x107c, 0, {0x8c,0x82,0x8d,0x83,0xe0,0x0c,0x89,0x82,0x8a,0x83,0xf0,0xa3,0xa9,0x82} }, +{ 4, 0x108a, 0, {0xaa,0x83,0xdb,0xee} }, +{ 1, 0x108e, 0, {0x22} }, +{ 14, 0x108f, 0, {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x05,0x86,0x90,0x7d,0xc1,0xe0,0x05,0x86} }, +{ 14, 0x109d, 0, {0xa3,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa6,0x05,0x86,0xa3,0xa3,0xe0,0xf9} }, +{ 5, 0x10ab, 0, {0x60,0x16,0xa3,0x05,0x86} }, +{ 13, 0x10b0, 0, {0x90,0x7f,0xa6,0x05,0x86,0xe0,0xa3,0x05,0x86,0xf0,0xc0,0x01,0x12} }, +{ 6, 0x10bd, 0, {0x10,0x1b,0xd0,0x01,0xd9,0xed} }, +{ 6, 0x10c3, 0, {0x90,0x7f,0xa5,0x74,0x40,0xf0} }, +{ 1, 0x10c9, 0, {0x22} }, +{ 8, 0x10ca, 0, {0x90,0x88,0x02,0x74,0x01,0xf0,0x7a,0x00} }, +{ 3, 0x10d2, 0, {0xba,0xff,0x00} }, +{ 2, 0x10d5, 0, {0x50,0x0a} }, +{ 10, 0x10d7, 0, {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xf1} }, +{ 1, 0x10e1, 0, {0x22} }, +{ 5, 0x10e2, 0, {0xe5,0x6b,0xb4,0xc0,0x08} }, +{ 8, 0x10e7, 0, {0x90,0x88,0x03,0xe5,0x6c,0xf0,0x80,0x06} }, +{ 6, 0x10ef, 0, {0x90,0x88,0x02,0xe5,0x6c,0xf0} }, +{ 4, 0x10f5, 0, {0x7a,0x00,0x7b,0x00} }, +{ 11, 0x10f9, 0, {0xc3,0xea,0x94,0x32,0xeb,0x64,0x80,0x94,0x80,0x50,0x07} }, +{ 5, 0x1104, 0, {0x0a,0xba,0x00,0x01,0x0b} }, +{ 2, 0x1109, 0, {0x80,0xee} }, +{ 1, 0x110b, 0, {0x22} }, +{ 10, 0x110c, 0, {0x90,0x88,0x03,0xe5,0x6d,0xf0,0x05,0x39,0x7a,0x00} }, +{ 3, 0x1116, 0, {0xba,0x28,0x00} }, +{ 2, 0x1119, 0, {0x50,0x03} }, +{ 3, 0x111b, 0, {0x0a,0x80,0xf8} }, +{ 5, 0x111e, 0, {0xe5,0x39,0xb4,0x10,0x08} }, +{ 8, 0x1123, 0, {0x90,0x88,0x02,0x74,0xc0,0xf0,0x80,0x0e} }, +{ 5, 0x112b, 0, {0xe5,0x39,0xb4,0x20,0x09} }, +{ 9, 0x1130, 0, {0x90,0x88,0x02,0x74,0x80,0xf0,0x75,0x39,0x00} }, +{ 2, 0x1139, 0, {0x7a,0x00} }, +{ 3, 0x113b, 0, {0xba,0x28,0x00} }, +{ 2, 0x113e, 0, {0x50,0x03} }, +{ 3, 0x1140, 0, {0x0a,0x80,0xf8} }, +{ 1, 0x1143, 0, {0x22} }, +{ 4, 0x1144, 0, {0xe5,0x6f,0x60,0x02} }, +{ 2, 0x1148, 0, {0x80,0x07} }, +{ 7, 0x114a, 0, {0x7a,0x00,0x75,0x39,0x00,0x80,0x05} }, +{ 5, 0x1151, 0, {0x7a,0x40,0x75,0x39,0x10} }, +{ 9, 0x1156, 0, {0xe5,0x6e,0x2a,0xfa,0xe5,0x6e,0x25,0x39,0xf5} }, +{ 10, 0x115f, 0, {0x39,0x90,0x88,0x02,0x74,0x80,0x2a,0xf0,0x7a,0x00} }, +{ 8, 0x1169, 0, {0xc3,0xea,0x64,0x80,0x94,0xa8,0x50,0x03} }, +{ 3, 0x1171, 0, {0x0a,0x80,0xf5} }, +{ 1, 0x1174, 0, {0x22} }, +{ 6, 0x1175, 0, {0xaa,0x70,0xab,0x71,0xac,0x72} }, +{ 12, 0x117b, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x12,0x14,0xee,0xfd,0x60,0x18} }, +{ 13, 0x1187, 0, {0x8d,0x6d,0xc0,0x02,0xc0,0x03,0xc0,0x04,0x12,0x11,0x0c,0xd0,0x04} }, +{ 9, 0x1194, 0, {0xd0,0x03,0xd0,0x02,0x0a,0xba,0x00,0x01,0x0b} }, +{ 2, 0x119d, 0, {0x80,0xdc} }, +{ 1, 0x119f, 0, {0x22} }, +{ 13, 0x11a0, 0, {0xe5,0x73,0xc4,0x54,0x0f,0xfa,0x53,0x02,0x0f,0xc3,0x74,0x09,0x9a} }, +{ 2, 0x11ad, 0, {0x50,0x06} }, +{ 6, 0x11af, 0, {0x74,0x37,0x2a,0xfb,0x80,0x04} }, +{ 4, 0x11b5, 0, {0x74,0x30,0x2a,0xfb} }, +{ 12, 0x11b9, 0, {0x8b,0x6d,0xc0,0x03,0x12,0x11,0x0c,0xd0,0x03,0xaa,0x73,0x53} }, +{ 8, 0x11c5, 0, {0x02,0x0f,0xc3,0x74,0x09,0x9a,0x50,0x06} }, +{ 6, 0x11cd, 0, {0x74,0x37,0x2a,0xfb,0x80,0x04} }, +{ 4, 0x11d3, 0, {0x74,0x30,0x2a,0xfb} }, +{ 5, 0x11d7, 0, {0x8b,0x6d,0x12,0x11,0x0c} }, +{ 1, 0x11dc, 0, {0x22} }, +{ 7, 0x11dd, 0, {0x90,0x7d,0xc3,0xe0,0xfa,0x60,0x0f} }, +{ 12, 0x11e4, 0, {0x90,0x7d,0xc1,0xe0,0xf5,0x6e,0x90,0x7d,0xc2,0xe0,0xf5,0x6f} }, +{ 3, 0x11f0, 0, {0x12,0x11,0x44} }, +{ 12, 0x11f3, 0, {0x90,0x7d,0xff,0xe4,0xf0,0x75,0x70,0xc4,0x75,0x71,0x7d,0x75} }, +{ 5, 0x11ff, 0, {0x72,0x01,0x12,0x11,0x75} }, +{ 1, 0x1204, 0, {0x22} }, +{ 2, 0x1205, 0, {0x7a,0x04} }, +{ 3, 0x1207, 0, {0xba,0x40,0x00} }, +{ 2, 0x120a, 0, {0x50,0x36} }, +{ 14, 0x120c, 0, {0xea,0x24,0xc0,0xf5,0x82,0xe4,0x34,0x7d,0xf5,0x83,0xe0,0xfb,0x7c,0x00} }, +{ 3, 0x121a, 0, {0xbc,0x08,0x00} }, +{ 2, 0x121d, 0, {0x50,0x20} }, +{ 6, 0x121f, 0, {0x8b,0x05,0xed,0x30,0xe7,0x0b} }, +{ 11, 0x1225, 0, {0x90,0x7f,0x96,0x74,0x42,0xf0,0x74,0xc3,0xf0,0x80,0x08} }, +{ 8, 0x1230, 0, {0x90,0x7f,0x96,0xe4,0xf0,0x74,0x81,0xf0} }, +{ 7, 0x1238, 0, {0xeb,0x25,0xe0,0xfb,0x0c,0x80,0xdb} }, +{ 3, 0x123f, 0, {0x0a,0x80,0xc5} }, +{ 1, 0x1242, 0, {0x22} }, +{ 4, 0x1243, 0, {0x7a,0x00,0x7b,0xef} }, +{ 3, 0x1247, 0, {0xba,0x10,0x00} }, +{ 2, 0x124a, 0, {0x50,0x20} }, +{ 14, 0x124c, 0, {0x74,0x11,0x2b,0xfb,0x24,0x00,0xfc,0xe4,0x34,0x18,0xfd,0x8c,0x82,0x8d} }, +{ 14, 0x125a, 0, {0x83,0xe4,0xf0,0xea,0x24,0x00,0xf5,0x82,0xe4,0x34,0x19,0xf5,0x83,0xe4} }, +{ 4, 0x1268, 0, {0xf0,0x0a,0x80,0xdb} }, +{ 1, 0x126c, 0, {0x22} }, +{ 14, 0x126d, 0, {0x74,0xf8,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} }, +{ 14, 0x127b, 0, {0x74,0xf9,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} }, +{ 14, 0x1289, 0, {0x74,0xfa,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} }, +{ 14, 0x1297, 0, {0x74,0xfb,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} }, +{ 14, 0x12a5, 0, {0x74,0xff,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} }, +{ 1, 0x12b3, 0, {0x22} }, +{ 14, 0x12b4, 0, {0x12,0x03,0xcb,0x12,0x12,0x6d,0x7a,0xc0,0x7b,0x87,0x7c,0x01,0x74,0x01} }, +{ 14, 0x12c2, 0, {0x2a,0xfd,0xe4,0x3b,0xfe,0x8c,0x07,0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x74} }, +{ 14, 0x12d0, 0, {0x01,0x12,0x14,0xbf,0x2d,0xfa,0xe4,0x3e,0xfb,0x8f,0x04,0x8d,0x82,0x8e} }, +{ 14, 0x12de, 0, {0x83,0x8f,0xf0,0x74,0x06,0x12,0x14,0xbf,0x74,0x01,0x2a,0xfd,0xe4,0x3b} }, +{ 14, 0x12ec, 0, {0xfe,0x8c,0x07,0x8a,0x82,0x8b,0x83,0x8c,0xf0,0xe4,0x12,0x14,0xbf,0x74} }, +{ 14, 0x12fa, 0, {0x01,0x2d,0xfa,0xe4,0x3e,0xfb,0x8f,0x04,0x8d,0x82,0x8e,0x83,0x8f,0xf0} }, +{ 14, 0x1308, 0, {0x74,0x0b,0x12,0x14,0xbf,0x74,0x01,0x2a,0xfd,0xe4,0x3b,0xfe,0x8c,0x07} }, +{ 14, 0x1316, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x74,0x08,0x12,0x14,0xbf,0x74,0x01,0x2d} }, +{ 14, 0x1324, 0, {0xfa,0xe4,0x3e,0xfb,0x8f,0x04,0x8d,0x82,0x8e,0x83,0x8f,0xf0,0x74,0x01} }, +{ 14, 0x1332, 0, {0x12,0x14,0xbf,0x2a,0xfd,0xe4,0x3b,0xfe,0x8c,0x07,0x8a,0x82,0x8b,0x83} }, +{ 14, 0x1340, 0, {0x8c,0xf0,0xe4,0x12,0x14,0xbf,0x74,0x01,0x2d,0xfa,0xe4,0x3e,0xfb,0x8f} }, +{ 14, 0x134e, 0, {0x04,0x8d,0x82,0x8e,0x83,0x8f,0xf0,0x74,0x03,0x12,0x14,0xbf,0x7d,0x00} }, +{ 3, 0x135c, 0, {0xbd,0x06,0x00} }, +{ 2, 0x135f, 0, {0x50,0x12} }, +{ 11, 0x1361, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x0a,0xba,0x00,0x01,0x0b} }, +{ 7, 0x136c, 0, {0xe4,0x12,0x14,0xbf,0x0d,0x80,0xe9} }, +{ 13, 0x1373, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0xe5,0x74,0x12,0x14,0xbf,0x74,0xf9} }, +{ 14, 0x1380, 0, {0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0x74,0x0f,0xf0,0x74} }, +{ 14, 0x138e, 0, {0xfe,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0x74,0x01,0xf0} }, +{ 6, 0x139c, 0, {0x12,0x03,0xe1,0x12,0x04,0xf7} }, +{ 1, 0x13a2, 0, {0x22} }, +{ 13, 0x13a3, 0, {0x90,0x7d,0xc1,0xe0,0xfa,0x24,0x00,0xfb,0xe4,0x34,0x19,0xfc,0x90} }, +{ 14, 0x13b0, 0, {0x7d,0xc2,0xe0,0xfd,0x8b,0x82,0x8c,0x83,0xf0,0x75,0xf0,0x11,0xea,0xa4} }, +{ 3, 0x13be, 0, {0xfa,0x7b,0x00} }, +{ 3, 0x13c1, 0, {0xbb,0x10,0x00} }, +{ 2, 0x13c4, 0, {0x50,0x24} }, +{ 14, 0x13c6, 0, {0xea,0x24,0x00,0xfc,0xe4,0x34,0x18,0xfd,0xeb,0x2c,0xfc,0xe4,0x3d,0xfd} }, +{ 14, 0x13d4, 0, {0x74,0x04,0x2b,0x24,0xc0,0xf5,0x82,0xe4,0x34,0x7d,0xf5,0x83,0xe0,0xfe} }, +{ 8, 0x13e2, 0, {0x8c,0x82,0x8d,0x83,0xf0,0x0b,0x80,0xd7} }, +{ 14, 0x13ea, 0, {0xea,0x24,0x00,0xfa,0xe4,0x34,0x18,0xfb,0x74,0x10,0x2a,0xf5,0x82,0xe4} }, +{ 5, 0x13f8, 0, {0x3b,0xf5,0x83,0xe4,0xf0} }, +{ 1, 0x13fd, 0, {0x22} }, +{ 4, 0x13fe, 0, {0xe5,0x76,0x60,0x02} }, +{ 2, 0x1402, 0, {0x80,0x16} }, +{ 12, 0x1404, 0, {0x74,0x0f,0x55,0x75,0xfa,0x8a,0x75,0x24,0x00,0xf5,0x82,0xe4} }, +{ 10, 0x1410, 0, {0x34,0x19,0xf5,0x83,0xe0,0xf5,0x74,0x12,0x12,0xb4} }, +{ 10, 0x141a, 0, {0x12,0x10,0xca,0x75,0x6e,0x00,0x75,0x6f,0x00,0x12} }, +{ 6, 0x1424, 0, {0x11,0x44,0x75,0x70,0xb9,0x75} }, +{ 6, 0x142a, 0, {0x71,0x14,0x75,0x72,0x02,0x12} }, +{ 11, 0x1430, 0, {0x11,0x75,0xe5,0x76,0xb4,0x02,0x04,0x74,0x01,0x80,0x01} }, +{ 1, 0x143b, 0, {0xe4} }, +{ 3, 0x143c, 0, {0xfa,0x70,0x0f} }, +{ 12, 0x143f, 0, {0x74,0x01,0x25,0x75,0xf5,0x73,0xc0,0x02,0x12,0x11,0xa0,0xd0} }, +{ 3, 0x144b, 0, {0x02,0x80,0x0a} }, +{ 10, 0x144e, 0, {0x85,0x75,0x73,0xc0,0x02,0x12,0x11,0xa0,0xd0,0x02} }, +{ 12, 0x1458, 0, {0x75,0x6e,0x00,0x75,0x6f,0x01,0xc0,0x02,0x12,0x11,0x44,0xd0} }, +{ 4, 0x1464, 0, {0x02,0xea,0x70,0x1a} }, +{ 13, 0x1468, 0, {0x75,0xf0,0x11,0xe5,0x75,0xa4,0xfa,0x24,0x00,0xfa,0xe4,0x34,0x18} }, +{ 9, 0x1475, 0, {0xfb,0x8a,0x70,0x8b,0x71,0x75,0x72,0x01,0x12} }, +{ 4, 0x147e, 0, {0x11,0x75,0x80,0x36} }, +{ 2, 0x1482, 0, {0x7a,0x00} }, +{ 3, 0x1484, 0, {0xba,0x10,0x00} }, +{ 2, 0x1487, 0, {0x50,0x2f} }, +{ 13, 0x1489, 0, {0xea,0x24,0x00,0xf5,0x82,0xe4,0x34,0x19,0xf5,0x83,0xe0,0xfb,0xe5} }, +{ 4, 0x1496, 0, {0x75,0xb5,0x03,0x1b} }, +{ 14, 0x149a, 0, {0x75,0xf0,0x11,0xea,0xa4,0xfb,0x24,0x00,0xfb,0xe4,0x34,0x18,0xfc,0x8b} }, +{ 9, 0x14a8, 0, {0x70,0x8c,0x71,0x75,0x72,0x01,0xc0,0x02,0x12} }, +{ 4, 0x14b1, 0, {0x11,0x75,0xd0,0x02} }, +{ 3, 0x14b5, 0, {0x0a,0x80,0xcc} }, +{ 1, 0x14b8, 0, {0x22} }, +{ 6, 0x14b9, 0, {0x50,0x72,0x6f,0x67,0x20,0x00} }, +{ 14, 0x14bf, 0, {0xc8,0xc0,0xe0,0xc8,0xc0,0xe0,0xe5,0xf0,0x60,0x0b,0x14,0x60,0x0f,0x14} }, +{ 7, 0x14cd, 0, {0x60,0x11,0x14,0x60,0x12,0x80,0x15} }, +{ 7, 0x14d4, 0, {0xd0,0xe0,0xa8,0x82,0xf6,0x80,0x0e} }, +{ 5, 0x14db, 0, {0xd0,0xe0,0xf0,0x80,0x09} }, +{ 4, 0x14e0, 0, {0xd0,0xe0,0x80,0x05} }, +{ 5, 0x14e4, 0, {0xd0,0xe0,0xa8,0x82,0xf2} }, +{ 4, 0x14e9, 0, {0xc8,0xd0,0xe0,0xc8} }, +{ 1, 0x14ed, 0, {0x22} }, +{ 14, 0x14ee, 0, {0xc8,0xc0,0xe0,0xe5,0xf0,0x60,0x0d,0x14,0x60,0x0f,0x14,0x60,0x0f,0x14} }, +{ 6, 0x14fc, 0, {0x60,0x10,0x74,0xff,0x80,0x0f} }, +{ 5, 0x1502, 0, {0xa8,0x82,0xe6,0x80,0x0a} }, +{ 3, 0x1507, 0, {0xe0,0x80,0x07} }, +{ 4, 0x150a, 0, {0xe4,0x93,0x80,0x03} }, +{ 3, 0x150e, 0, {0xa8,0x82,0xe2} }, +{ 4, 0x1511, 0, {0xf8,0xd0,0xe0,0xc8} }, +{ 1, 0x1515, 0, {0x22} }, +{ 0, 0x0000, 1, {0} } + +}; + +static unsigned char bitstream[] = { + +0x00,0x09,0x0F,0xF0,0x0F,0xF0,0x0F,0xF0, 0x0F,0xF0,0x00,0x00,0x01,0x61,0x00,0x0D, +0x64,0x61,0x62,0x75,0x73,0x62,0x74,0x72, 0x2E,0x6E,0x63,0x64,0x00,0x62,0x00,0x0B, +0x73,0x31,0x30,0x78,0x6C,0x76,0x71,0x31, 0x30,0x30,0x00,0x63,0x00,0x0B,0x31,0x39, +0x39,0x39,0x2F,0x30,0x39,0x2F,0x32,0x34, 0x00,0x64,0x00,0x09,0x31,0x30,0x3A,0x34, +0x32,0x3A,0x34,0x36,0x00,0x65,0x00,0x00, 0x2E,0xC0,0xFF,0x20,0x17,0x5F,0x9F,0x5B, +0xFE,0xFB,0xBB,0xB7,0xBB,0xBB,0xFB,0xBF, 0xAF,0xEF,0xFB,0xDF,0xB7,0xFB,0xFB,0x7F, +0xBF,0xB7,0xEF,0xF2,0xFF,0xFB,0xFE,0xFF, 0xFF,0xEF,0xFF,0xFE,0xFF,0xBF,0xFF,0xFF, +0xFF,0xFF,0xAF,0xFF,0xFA,0xFF,0xFF,0xFF, 0xC9,0xFF,0xFF,0xFF,0xDF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFB,0xFF,0xA3,0xFF,0xFB, +0xFE,0xFF,0xBF,0xEF,0xE3,0xFE,0xFF,0xBF, 0xE3,0xFE,0xFF,0xBF,0x6F,0xFB,0xF6,0xFF, +0xBF,0xFF,0x47,0xFF,0xFF,0x9F,0xEE,0xF9, 0xFE,0xCF,0x9F,0xEF,0xFB,0xCF,0x9B,0xEE, +0xF8,0xFE,0xEF,0x8F,0xEE,0xFB,0xFE,0x0B, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xBF,0xFF,0xFF,0xFB,0xFF,0xFF, 0xBF,0xFF,0xFF,0xFC,0x17,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0x7F, 0xFF,0xFF,0xFB,0xFF,0xFF,0x7F,0xFF,0xFF, +0xFC,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFB,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x5F,0xFF, 0xFF,0xFD,0xFF,0xFF,0xDB,0xFF,0xFD,0xFF, +0x77,0xFF,0xFD,0xFF,0xFF,0xDF,0xFE,0xFD, 0xFF,0xFF,0xF2,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFD,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE1, +0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xE3,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBF, +0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x67,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0x7F,0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF, 0xFF,0xFF,0xDF,0xFF,0xFF,0xFF,0x2F,0xFF, +0xF3,0xFD,0xFF,0x7F,0xDE,0xF7,0xFD,0xFF, 0x7F,0xF7,0x7D,0xFF,0x7F,0xDF,0xF7,0xBD, +0xFF,0x7F,0xFF,0x1F,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xEF,0xFB, +0xFE,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFF, 0x3F,0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F, +0x9F,0xE7,0xFA,0x7F,0x9F,0xE7,0xF9,0xFE, 0x7F,0x9F,0xE7,0xFF,0xFC,0x7F,0xBF,0xBF, +0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0xB7, 0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, +0xFF,0xE0,0xFD,0xF9,0xFE,0x7F,0x9F,0xE7, 0xF9,0xFE,0x7F,0x9D,0xF9,0xFE,0x7D,0x9D, +0xE7,0xF9,0xFE,0x7F,0x9F,0xED,0xED,0xFF, 0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0x7F, +0xDF,0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF, 0x7F,0xDF,0xFF,0x9B,0xFF,0xEF,0xFB,0xFE, +0xFB,0xBF,0xEF,0xBB,0xFE,0xFF,0xAF,0xBB, 0xBE,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFF, +0xB7,0xBF,0xDB,0xF6,0xBD,0xBF,0x6B,0xDB, 0xF6,0xF9,0xBF,0x5B,0xD6,0xF9,0xBF,0x6F, +0xDB,0xF6,0xFD,0xBF,0xFF,0x0E,0xFF,0xFF, 0xFF,0xFF,0x5F,0xFF,0xF7,0xFF,0xFF,0x7F, +0xF7,0xBD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xDF,0x9F,0xFF,0xFF,0xFF,0xFE,0xFF, +0xFF,0xEF,0xFE,0xFE,0xFF,0xFF,0x77,0xFF, 0xFB,0xFB,0xFF,0xFF,0xFF,0xFF,0xF8,0x3F, +0xFF,0xFD,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xF4,0x7F,0xFF,0xFE,0xFD, 0xBE,0xFF,0xDF,0xFE,0xFF,0xFF,0xEF,0x7F, +0xFF,0xCF,0xFF,0xCF,0xFF,0xFF,0xFF,0xDF, 0xE6,0xFF,0xFF,0x7F,0xDF,0xF7,0xDD,0x7F, +0x7F,0xDF,0xF7,0xFF,0x7F,0xDF,0xD7,0xFD, 0xFF,0x7F,0xDF,0xF7,0xFF,0xCD,0xFF,0xF2, +0xFF,0xFF,0x4F,0x7F,0xF4,0xFF,0xFF,0xFF, 0xE7,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xBB,0xFF,0xEF,0xFF,0xFE,0xFF, 0xFF,0xFF,0xEF,0xFF,0xFF,0xEF,0xFF,0xFB, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x65, 0xEF,0xFF,0xFF,0x7F,0xFF,0xFD,0xEF,0xFF, +0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE,0xCF,0xDF,0xFE,0xFF, +0xFF,0xFB,0xFF,0xFF,0xFF,0xFF,0xF3,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFE,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xBF,0xFF, 0xFF,0xFF,0xE3,0x7F,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xEF,0xEB,0xFF,0xFE,0xBF,0xFF, 0xEB,0xFF,0xFC,0x7F,0xFF,0xFF,0xFF,0xEE, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xDD,0xFF, 0xD6,0xFF,0xFD,0xBF,0xFF,0xFB,0xFF,0xFE, +0xFD,0xFF,0xFF,0xFD,0xEF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xDE,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xBF,0xFF,0xFD,0xFF,0x7F,0xBF, 0xFF,0x5F,0xDF,0xFF,0xFF,0xBF,0x77,0xFF, +0xFF,0xFF,0x7F,0xD7,0xFF,0xFF,0xFF,0xFF, 0xFF,0xC3,0xFF,0xFF,0xFF,0xFF,0xDF,0xEF, +0xFF,0xFF,0xFE,0xFB,0xFF,0xFF,0xDF,0xBF, 0xFF,0xFF,0xFF,0xFF,0xED,0xFF,0xB7,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xAF,0x7F,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xDF,0xBF,0xDF,0xF3,0xFD,0xFB,0xFF,0x5B, +0xFD,0xFF,0xBF,0xEF,0xF7,0xFF,0xFF,0x7D, 0xFF,0xFF,0xFF,0xFF,0xF8,0x3B,0xFF,0xBF, +0x6F,0xFF,0xFE,0xFF,0xBF,0xFF,0xEB,0x7D, 0xFF,0xEF,0xFB,0xFE,0xFF,0xFF,0xFF,0xFF, +0xFF,0xF2,0x7F,0xFC,0xFF,0x3F,0xDF,0xED, 0xFE,0xFF,0xFF,0xFF,0xFF,0xEF,0x5F,0xF7, +0xB5,0xFF,0xEF,0xFF,0xFF,0xFF,0xE0,0x3F, 0x9F,0x9E,0xFF,0xFF,0xEF,0xFF,0xDF,0xFF, +0xBF,0x5F,0xBF,0xCF,0xF3,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0x69,0xAF,0x33,0xFD,0xFF, +0xFB,0xFF,0xFF,0xFF,0xFF,0xFC,0xFF,0x7F, 0xD9,0xFF,0xDF,0xFF,0xFF,0xFF,0xFF,0xF5, +0xA3,0xDF,0x6E,0xDE,0xFF,0xFF,0xBD,0xFF, 0xFF,0xFE,0xFF,0xFF,0xFF,0xFE,0xE7,0xFD, +0xFF,0xFF,0xFF,0xF9,0xEF,0xC6,0xFE,0xB7, 0xAD,0xE5,0xF9,0xFF,0xFF,0xFF,0xCF,0xFF, +0xFF,0xFF,0xCD,0xFB,0x7F,0xFF,0xFF,0xFF, 0xF9,0xF6,0x0F,0xDF,0xEC,0xCF,0x7F,0xFF, +0xFB,0x7F,0xFF,0xFF,0xFF,0xFD,0xFF,0xFE, 0xF9,0xFD,0x7F,0xFF,0x7F,0xFF,0xF9,0x5B, +0xFF,0x73,0xDC,0xFD,0x7B,0xDF,0xFF,0xFF, 0xFF,0x7B,0xFF,0xFF,0xF7,0x53,0xD6,0xFF, +0xFF,0xFF,0xFF,0xD8,0x9F,0xFE,0xFF,0xEF, 0x7F,0xEE,0xFF,0xFF,0xFF,0xFB,0xED,0xED, +0xFD,0xFF,0xFE,0xFF,0xFF,0xFB,0x7F,0xFF, 0xE2,0x7F,0xFF,0x6F,0xD8,0x57,0xF7,0xFF, +0xFF,0xFF,0xDF,0xFF,0xE8,0xFF,0xFF,0xFD, 0xFF,0xFF,0xFC,0x7F,0xFF,0xE4,0xFF,0xFB, +0xEF,0xFB,0xFE,0xDF,0xB7,0xED,0xFF,0xFE, 0xDF,0x7F,0xFF,0xFE,0x7F,0xB7,0xFF,0xFF, +0xFF,0xFF,0x89,0xFF,0xFF,0xCF,0xF3,0xFE, 0x7F,0xFF,0xEF,0xFF,0xFE,0x7E,0x7F,0xFB, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF1, 0xFF,0xEB,0x7A,0xD5,0xBF,0x6F,0xDB,0xBE, +0xFD,0xB7,0xD8,0xF6,0xE5,0xBF,0x6F,0xFB, 0xFE,0xF5,0xBD,0x7E,0x06,0xFF,0xDF,0xF7, +0xFB,0xF6,0xFF,0x3F,0xFF,0xDB,0xFF,0xFF, 0x6F,0xFB,0xF7,0xFF,0xFF,0xFF,0xFB,0xFE, +0xF7,0xAF,0xFF,0xB7,0xED,0xEF,0xF7,0xFE, 0xFF,0xFF,0xDF,0xFF,0xFE,0xFF,0xEF,0xFF, +0xFF,0xFF,0xFF,0xBF,0xF7,0xFC,0x1F,0xEE, 0xFB,0xFE,0xBD,0xFF,0x7F,0x5F,0xD7,0xFD, +0xFB,0x43,0xFF,0xFF,0xFD,0xFF,0x5F,0xFF, 0xF7,0xFF,0xF9,0x3F,0xFF,0xCF,0xF3,0xFD, +0xF7,0x7E,0xEF,0xA7,0xF9,0xFE,0x8F,0xA7, 0xE9,0xF3,0x7E,0x9F,0xFB,0xF8,0xFF,0xFF, +0x3F,0xFD,0x7F,0x5F,0xDF,0xFD,0xFF,0xFF, 0x5F,0xFF,0xFD,0x5F,0xFF,0xFF,0x7F,0xFD, +0x7F,0xFD,0x9F,0xFF,0xE0,0xFF,0xFA,0xF8, 0xBE,0x6F,0x9F,0xE6,0xF8,0xBE,0x3F,0x9A, +0xF9,0xBE,0x6F,0x9F,0xE2,0xF9,0xFE,0x6F, 0x9F,0xF9,0xFF,0xF5,0xFD,0x7F,0xCF,0xDF, +0xFD,0xFD,0x7F,0xFF,0xF5,0xFF,0xFF,0xFF, 0xF7,0xF5,0xFD,0x0F,0xDB,0xFF,0xD3,0xFF, +0xEB,0xFA,0xFF,0xFF,0xBF,0xFF,0xFA,0xFF, 0xFF,0xCB,0xFB,0xFE,0xFF,0xFF,0xEB,0xFA, +0xFE,0xFF,0xFF,0xB7,0xFF,0xFF,0xFF,0xFF, 0xBF,0xFF,0xDF,0xF5,0xFF,0xFF,0xD7,0xFF, +0xFF,0xFF,0xDF,0xD7,0xF5,0xFF,0x7F,0xFE, 0x4F,0xFF,0xFD,0xFF,0x7F,0x7F,0xFF,0xAD, +0xEB,0xFB,0xFF,0xAD,0xFF,0xFF,0xFF,0xFF, 0xAF,0xEB,0xFB,0xFF,0xFC,0x0D,0xFF,0xFF, +0xDF,0xD2,0xFD,0xFF,0xFF,0xFD,0xF6,0xFF, 0xFF,0x7F,0xFF,0xFF,0x1F,0xFF,0xFF,0xFF, +0xFF,0xFB,0x3F,0x7D,0xEB,0x32,0xFE,0xBF, 0x2F,0xEB,0xFA,0xAE,0xBD,0xE0,0xFA,0x7E, +0xBF,0xAD,0xEB,0xFA,0xFE,0xBF,0xF5,0x7F, 0xFF,0xDE,0xFE,0xE3,0xFB,0xFF,0xFF,0xFF, +0xDF,0xEF,0x4F,0xDF,0xFF,0x7F,0xDF,0xFF, 0xF7,0xFF,0xFF,0xF8,0x7F,0xFF,0xFF,0xEF, +0xFB,0xFF,0xFF,0xFF,0xEF,0xFF,0xFF,0xDF, 0xED,0xFB,0xDF,0xFF,0xBF,0xFF,0xFF,0xFF, +0x81,0xFF,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF, 0xFF,0xFF,0xFE,0xDD,0xFE,0xEF,0xFD,0xFF, +0xFF,0xFB,0xFE,0xF7,0xFF,0x93,0xFD,0xFB, 0x7E,0xFF,0xFE,0x87,0xE9,0xFF,0x7F,0xB3, +0x9F,0xFE,0xFE,0xFF,0xAF,0xFD,0xFE,0x7E, 0x3F,0xFE,0x67,0xFF,0xFF,0xF7,0xFF,0xFF, +0xFC,0xF7,0xDF,0xFD,0xFF,0x7F,0xFF,0xFF, 0x7F,0x6D,0xFF,0xFF,0xFE,0xFF,0xFF,0x2F, +0xFF,0xBF,0xFF,0xFF,0xEE,0xFF,0xBE,0xFF, 0xFF,0xFE,0xFF,0xEF,0xFF,0xFF,0xFE,0xFF, +0xEF,0xFF,0xFF,0xFA,0x5F,0xFF,0xFF,0xFB, 0xFF,0xFF,0xEF,0xFF,0xFB,0xFE,0xFD,0xFF, +0xFE,0xFF,0xFB,0xFF,0xFF,0xFF,0x7F,0xFF, 0xFE,0xBF,0xDF,0xFF,0xFB,0xFF,0xFF,0xF7, +0xFC,0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F, 0xFF,0xFF,0xFF,0xFF,0xFF,0xF2,0x7F,0xFF, +0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF, 0xF3,0xFF,0xFF,0xFF,0xEF,0xFB,0xFF,0xFF, +0xFF,0xDF,0xE2,0xFF,0xFF,0xFB,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFB,0xE7,0xFF,0xFD, +0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xED, 0xEF,0xFD,0xFF,0xFF,0xDF,0xD7,0xF5,0xFD, +0x7F,0x5D,0xFD,0xFF,0x7F,0xDF,0x97,0xF4, 0xFD,0x7B,0x5F,0xFF,0xC9,0xFF,0xFB,0xFE, +0xFF,0xBF,0xFF,0x5F,0xFF,0xFF,0xF7,0xFF, 0xEF,0xFD,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xF7,0xFF,0xD7,0xFD,0x7D,0x7F,0xFF, 0xFF,0xFF,0xFF,0xEF,0xDF,0xF7,0xFD,0xFF, +0xBB,0xFF,0xFF,0x7F,0xFF,0xFE,0xE3,0xFF, 0xF9,0xFE,0x7F,0xBF,0xEF,0xFB,0xFE,0xFF, +0xBF,0xF9,0xFE,0xFF,0x9F,0xEF,0xF9,0xFE, 0xFF,0xBF,0xF3,0xDA,0xFF,0x37,0xCD,0xF3, +0x7C,0xDF,0x37,0xCD,0xF3,0x7F,0x37,0xCD, 0xF3,0x7C,0xDF,0x37,0xCC,0xF3,0x7F,0x5A, +0xBD,0xF6,0xFD,0xBF,0x6F,0xDB,0xF6,0xFD, 0xBF,0x6F,0xDE,0xFD,0xBF,0x6F,0xDB,0xF6, +0xFD,0xBF,0x6F,0xFE,0xF1,0x6F,0xEB,0x7A, 0xDE,0xB7,0xAD,0xEB,0x7A,0xDE,0xB7,0xAF, +0x7A,0xDE,0xB7,0xAD,0xEB,0x7A,0xDE,0xB7, 0xFF,0x7E,0xFF,0xFE,0xCD,0xB3,0x6C,0xDB, +0x36,0xCD,0xB3,0x6C,0xDE,0xCD,0xB3,0x6C, 0xDB,0x36,0xCD,0xB3,0x6C,0xDF,0xC9,0xBF, +0xF7,0xBD,0xEF,0x7A,0x9E,0xA7,0xA9,0xEA, 0x7A,0xB7,0xBD,0xEA,0x7B,0xDE,0xA7,0xBD, +0xCA,0x72,0x8D,0x91,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xEF,0xFB,0xFE,0xF7,0xEF,0xFB, +0xFE,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFE, 0x87,0xFF,0xF6,0xFD,0xBF,0x6F,0xDB,0xF6, +0xFD,0xBF,0x6F,0xF6,0xFD,0xBF,0x6F,0xDB, 0xF6,0xFD,0xBF,0x6F,0xFE,0x4F,0xFF,0xBF, +0xEF,0xBB,0xEE,0xFB,0xBE,0xEF,0xBB,0xEF, 0xBE,0xEF,0xBB,0xEE,0xFB,0xBE,0xEF,0xBB, +0xEF,0xFC,0x5F,0xFF,0xFF,0xFF,0x3F,0xCF, 0xF3,0xFC,0xFF,0x3F,0xCF,0xFC,0xFF,0x3F, +0xCF,0xF3,0xFC,0xFF,0x3F,0xCF,0xFD,0x9F, 0xFE,0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF, +0xEB,0xFE,0xBF,0xAF,0xEB,0xFA,0xFE,0xBF, 0xAF,0xEB,0xFF,0xE1,0x6F,0xFD,0xFF,0x7F, +0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFD,0xFF, 0x7F,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFF, +0x7A,0xBF,0xFB,0xFE,0xDF,0xB7,0xED,0xFB, 0x7E,0xDF,0xB7,0xFB,0x7E,0xDF,0xB7,0xED, +0xFB,0x7E,0xDF,0xB7,0xFF,0xC9,0xFF,0xFF, 0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, +0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEE, 0xFB,0xFE,0xBB,0xFF,0xFE,0xFF,0xBF,0xEF, +0xFB,0xFE,0xFF,0xBF,0xEF,0xFE,0xFF,0xBF, 0xEF,0xFB,0xFE,0xFF,0x3F,0xCF,0xFF,0xE7, +0xFE,0xFF,0xF5,0xFD,0x77,0x5D,0xD7,0x35, 0xDD,0x77,0xD7,0xF5,0xCD,0x7B,0x5D,0xD7, +0xF5,0xDD,0x77,0xFE,0x27,0xFF,0xFF,0x8B, 0xE2,0xF8,0xBE,0x2F,0x8B,0xE2,0xF9,0xAF, +0x8B,0xE2,0xF8,0xBE,0x2F,0x8B,0xE2,0xF9, 0xFE,0x1F,0xFF,0x5F,0xD7,0xF5,0xFD,0x7F, +0x5F,0xD7,0xF5,0xFF,0x5F,0xD7,0xF5,0xFD, 0x7F,0x5F,0xD7,0xF5,0xFF,0xFA,0x3F,0xFE, +0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF,0xEB, 0xEC,0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF, +0xEB,0xFF,0xFE,0x7F,0xFD,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE6, 0xFF,0xFA,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF, +0xF7,0xFC,0xFF,0xDF,0xF7,0xFD,0xFF,0x7F, 0xDF,0xF7,0xFD,0xFF,0xF5,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0x02,0xFF,0xFE,0xBF,0xAB,0xEB,0xFA, 0xBE,0xBF,0x23,0xEB,0xDE,0x1F,0xAF,0xEA, +0xFA,0xFE,0xAF,0xAF,0xEB,0xFD,0x97,0xFF, 0xF3,0xFC,0x7B,0x1F,0xCF,0xF1,0xFC,0x7F, +0x1F,0xF1,0xFC,0x77,0x1F,0xCD,0xF1,0xFC, 0xFF,0x1F,0xFE,0x87,0xFF,0xAF,0xEF,0xFA, +0xFE,0xFF,0xAF,0xEF,0xFA,0xFD,0xBF,0x2B, 0xFB,0x7E,0xBF,0xBF,0xEB,0xFB,0xFB,0xFB, +0xDF,0xFF,0xFB,0xF7,0xFF,0xFF,0x7F,0xF7, 0xF7,0xFF,0xFD,0xDF,0xFE,0xFC,0xDF,0xFF, +0xDF,0xFF,0xFD,0xFF,0xDA,0xBF,0xFF,0xBB, 0xEF,0xFB,0xF9,0xFF,0xBE,0xEF,0xFB,0xFB, +0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFF,0xF7,0x7F,0xFD,0xD7,0xFF,0xFF,0x7F, +0xFF,0xFF,0xFF,0xFE,0xF7,0xFF,0xFE,0xFF, 0xF7,0xFF,0xFF,0x7F,0xFF,0xFF,0xEC,0xFF, +0xFF,0xFE,0xDF,0xBF,0xFF,0xFB,0xFE,0xFF, 0xBB,0x68,0xAE,0x1F,0xAE,0xFB,0xFB,0xFF, +0xFF,0xBF,0xFF,0xD5,0xFF,0x7F,0xFF,0xFF, 0xF7,0xFE,0xFE,0xFF,0xBF,0xEF,0x9F,0xFD, +0x7F,0xFF,0xCB,0xFF,0xFF,0xDF,0xFF,0xFF, 0xBB,0xF7,0xBF,0xFF,0xFF,0xFF,0xFF,0xDF, +0xFF,0xBF,0xFB,0xFF,0xFF,0xFF,0xDE,0x3F, 0xFF,0xFF,0xFF,0xFF,0xFF,0xA7,0xFF,0xFF, +0xFF,0xFF,0xEF,0xFF,0x7F,0xFB,0xFD,0xFB, 0x7F,0xFF,0xFF,0xFF,0xFF,0xCF,0xF3,0x7C, +0xFF,0x7F,0x8D,0x7F,0xFF,0xFF,0xFF,0xFF, 0xFB,0xFF,0xF7,0xFB,0xFE,0xFD,0xFF,0xFF, +0xFF,0xFF,0xF7,0xFD,0xFF,0x7F,0xFD,0x1F, 0xFD,0xFF,0xFF,0xFF,0xFF,0xBF,0xDF,0xFF, +0xFF,0xFE,0x5C,0xFF,0x6D,0xFF,0x7F,0xAB, 0xE7,0xF1,0xFF,0xFD,0x9F,0xFF,0xFF,0xAD, +0xEB,0x7A,0x3F,0x1F,0xFF,0xFF,0xFE,0xBF, 0xAF,0xF3,0xDE,0xF5,0xFF,0x8F,0xFB,0xDF, +0xE6,0x7F,0xFF,0xDF,0xF3,0xFD,0xFF,0x7E, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0xF7,0xF3, +0x7F,0xDF,0xF7,0xEF,0xFF,0xF6,0x3F,0x9F, 0xDF,0xFF,0xFF,0xEE,0xFF,0xFF,0xEF,0xFB, +0xFF,0xFF,0xF9,0xFB,0xFE,0x4F,0xBF,0xEF, 0xBB,0xFF,0x69,0xAF,0xAF,0xFC,0xFF,0x3F, +0xDD,0xFF,0xFC,0xBF,0x8F,0xFF,0xFD,0xF3, 0xBF,0xED,0x9E,0xFC,0xBF,0x6F,0xF5,0xD3, +0xDF,0xFF,0xDB,0xD6,0xF5,0xEF,0xFD,0xFE, 0xFF,0xB9,0xFF,0x1F,0xD2,0xA9,0xAF,0xFF, +0xDB,0xF7,0xBF,0xEF,0x46,0xFF,0xFF,0xAD, 0xEB,0x7A,0xDF,0xEF,0xF7,0xFF,0x7F,0xF7, +0x9F,0xED,0xFF,0x7F,0xFF,0xAD,0xEB,0x7F, 0xF5,0x6F,0xFF,0xFD,0xFB,0xD6,0xF4,0xF7, +0xFB,0xF9,0x7E,0x7F,0xFF,0x5F,0xC2,0xFE, 0xBF,0xFD,0xFB,0x33,0xDF,0xF9,0x5B,0xFF, +0xFF,0xDD,0x67,0x7D,0xCF,0xEF,0xDB,0xEC, 0xFF,0x77,0xDD,0xF7,0xFD,0xFF,0xFF,0xDE, +0xA7,0xBF,0xD4,0x9F,0xFF,0xFF,0xBF,0xEF, 0xFE,0xFF,0xDF,0xEF,0xBB,0xFF,0xFF,0xEF, +0xEB,0xFA,0xFF,0xEF,0xBD,0xFB,0xFF,0xE2, 0x7F,0xFF,0xDF,0xDF,0xF7,0xFD,0xBF,0xBB, +0x73,0xF7,0xFD,0x7F,0xDF,0xDE,0xF7,0xBF, 0xEA,0xDB,0xF6,0xFF,0xD6,0xFF,0xFF,0x66, +0xFF,0xBE,0xFF,0xBF,0x6B,0xD9,0xF6,0xDF, 0xFF,0xFB,0x7E,0x7F,0xB7,0x7E,0xFF,0xFE, +0xFF,0xCD,0xFF,0xFE,0x7F,0xFF,0xFC,0xFD, 0x3F,0xFB,0xFB,0xF7,0xFF,0xFF,0xFB,0xF6, +0x7D,0xFE,0x7F,0xFF,0xFC,0xFF,0xB9,0xFF, 0xF9,0xFA,0xFE,0xBF,0xAF,0x5B,0xD6,0xED, +0xAD,0x7B,0xF6,0xF9,0xBF,0xEF,0xF8,0xFA, 0xFE,0xBF,0xFE,0xE6,0xFF,0xFF,0xF7,0xFD, +0xFF,0x7F,0xBF,0xEF,0xF3,0xFF,0xFF,0x6F, 0xF7,0xFE,0xFF,0xFF,0xF7,0xFD,0xFE,0xF7, +0xEF,0xFF,0xFB,0xEF,0xFB,0x7E,0xDE,0xFE, 0xFF,0xBF,0xFF,0xFE,0xFF,0xFF,0xFB,0xFF, +0xFF,0xEF,0xFB,0x6F,0xFC,0x1F,0xFE,0xE7, 0xFF,0xFF,0xFF,0xEF,0xFF,0xD3,0xB4,0xBB, +0xFF,0xFF,0xFD,0xBF,0x6F,0xE3,0xFE,0xFF, 0xBF,0xFC,0xBF,0xF7,0xCF,0xF7,0xFD,0xFF, +0x2F,0xDF,0xAB,0xEA,0xFF,0xDF,0xE7,0xEA, 0x9A,0xAF,0xEF,0xFB,0xFE,0xFF,0xF5,0x3F, +0xFD,0x7E,0xFF,0xD7,0xF5,0xFB,0xFF,0xFD, 0xF7,0xFF,0x7F,0xFE,0xF7,0xFD,0xFF,0xD7, +0xFF,0xD7,0x7F,0xEE,0x7F,0xFA,0x79,0xFE, 0x2F,0x8B,0xE6,0xF9,0xFE,0x3F,0x9E,0xF9, +0xBE,0x2F,0x0B,0xE7,0xF9,0xFE,0x2F,0x9F, 0xFD,0xFF,0xFE,0x7D,0x7F,0x5F,0xD7,0xFF, +0xFF,0x7F,0xFF,0xFD,0xFF,0x7F,0x5F,0x97, 0xFF,0xFD,0x7F,0x5F,0xFF,0xE3,0xFF,0xFF, +0xFA,0xFE,0xBF,0xAF,0xFB,0xFB,0xFF,0xFF, 0xCF,0xEB,0xFE,0xBF,0xAF,0xFF,0xFA,0xFE, +0xBF,0xFF,0x87,0xFF,0xFF,0xF5,0xFF,0xFF, 0xFF,0xFF,0xFD,0xFF,0x7F,0xFF,0xFF,0xFF, +0xFB,0xFF,0xFF,0xF5,0xFF,0xFF,0xFE,0x0F, 0xFF,0xFD,0xEB,0xFF,0xFF,0xF7,0xFF,0xEF, +0x7B,0xDF,0xFE,0xFF,0xFF,0xDF,0xF7,0xFD, 0xEB,0x7F,0xDF,0xFF,0x5F,0xFF,0xFF,0xFF, +0xFF,0xFD,0xBF,0xFF,0x7E,0xFA,0xBF,0xC7, 0xDB,0xF7,0xBD,0x3F,0xFB,0xFF,0xF6,0xFF, +0xFA,0xAF,0xFF,0xEB,0xFA,0xFE,0x3F,0x2F, 0xEA,0xFA,0x3E,0xAD,0xC9,0xBA,0xF6,0xAD, +0xAF,0xEB,0xFA,0xF6,0xBF,0xFE,0x7F,0xFF, 0xFF,0xFD,0xFF,0xF1,0x7F,0x3F,0xCF,0xF1, +0xEF,0xFF,0x7F,0xFF,0xBC,0xDF,0xDF,0xF7, 0xDD,0xFF,0xE0,0x7F,0xFF,0xFF,0xFE,0xFF, +0xFA,0xEC,0xBB,0x7F,0x5F,0xFF,0xFB,0xEC, 0xFF,0xEF,0xB7,0xFF,0xF7,0xFF,0xFF,0xB5, +0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xEE,0xDF, 0x5F,0xDF,0xDE,0xFF,0xAE,0xE7,0x77,0xFF, +0xFF,0xDF,0xF7,0xFF,0xE3,0xFF,0xFA,0xBB, 0xFE,0xFF,0xAF,0xFD,0xFB,0xFE,0xBF,0xAB, +0xF9,0xFE,0xFF,0xBF,0x7F,0xBF,0xFE,0xBD, 0xFE,0xD7,0xFF,0x9F,0xFD,0xFF,0xBE,0xEF, +0xFF,0xEE,0xFD,0xBB,0x5B,0xEF,0xFF,0x7F, 0xEF,0xFF,0xEF,0xFF,0x7F,0xFF,0x4F,0xFF, +0xEF,0xFB,0xBC,0xFC,0xFF,0xFF,0xFF,0xFE, 0xFE,0xFD,0xFA,0xFE,0xFB,0xFF,0xFD,0xF3, +0xFB,0xFF,0xF8,0x5F,0xFF,0xFF,0xD7,0xF5, 0xFD,0xDF,0xEF,0xFF,0xF3,0xDC,0x5F,0xCE, +0xF5,0xBD,0xFF,0xFF,0xD7,0xFF,0xFF,0xF9, 0x3F,0xFF,0xDF,0xF7,0xFF,0xFE,0xFF,0xFD, +0xFF,0xFB,0xFF,0xF7,0xB9,0x7D,0xFE,0xDF, 0xFF,0xFF,0xFF,0xFF,0xF9,0x7F,0xFF,0xFE, +0xFF,0xFF,0x7F,0xFF,0xFE,0xFF,0xFF,0xF7, 0xF6,0xFF,0xBF,0xF1,0xF8,0xFF,0xFF,0xFF, +0xFF,0xE0,0xFF,0xFF,0xFF,0xFF,0xF9,0xFF, 0xFF,0xFF,0xFF,0xFF,0xEF,0xEF,0xFF,0xFF, +0x9B,0xFB,0x7F,0xFF,0xFF,0xFF,0xC1,0xFF, 0xDF,0xFF,0x3F,0x5F,0xD7,0xBF,0xEF,0xBB, +0xDE,0xEE,0xFF,0x7F,0xDF,0xFF,0xFE,0xF5, 0x7F,0xDF,0xFF,0x99,0xFF,0xFF,0xFA,0xFF, +0xBF,0xFD,0xEB,0x7A,0xFF,0xB7,0xFE,0xFE, 0xFF,0xFF,0xEF,0xFF,0xFF,0xFD,0xBF,0xFF, +0x97,0xFF,0xFD,0xF7,0xFF,0x7F,0xF7,0xFF, 0xFF,0xFD,0x5F,0xFE,0xF3,0xF9,0xDF,0xDF, +0xFF,0xFF,0xFC,0xFF,0xFF,0x83,0xFF,0xFF, 0xFE,0xFF,0x9E,0xEC,0xFB,0xEE,0xFF,0x9F, +0xBF,0xEF,0xFF,0xFE,0xED,0x7B,0xFF,0xFF, 0xFF,0xF1,0x5A,0xFF,0xFF,0xFD,0xFF,0x7C, +0x69,0x3B,0xDF,0xFF,0x7F,0x1F,0xDF,0xFF, 0xFD,0xBA,0xFF,0xFF,0xFB,0xFF,0x5B,0xBD, +0xFF,0xFF,0xFF,0xFF,0xD7,0xB6,0xED,0xE9, 0xFF,0xD6,0xBD,0x6F,0x5F,0xFB,0xFF,0xEF, +0xFF,0x5F,0xFE,0xF6,0x6F,0xFF,0xFF,0xFF, 0xFF,0xF7,0xEB,0x7A,0xDF,0xFF,0x9F,0x7F, +0x7F,0xFF,0xB7,0xFF,0xFF,0xFE,0xDF,0xFF, 0x6C,0xFF,0xFB,0xFF,0xBB,0x6F,0xEB,0xFE, +0xCC,0xF7,0xA5,0xFA,0x5C,0xF5,0x75,0xBB, 0xB7,0xDF,0xFE,0x6F,0x5F,0xC5,0xBF,0xFD, +0x7B,0xFE,0xFF,0x95,0xE7,0x29,0xCF,0x4F, 0xF5,0x91,0xEE,0x6B,0xDF,0xEF,0xFD,0x54, +0xF5,0xBD,0xB1,0xFF,0xEF,0xEE,0xFB,0xBE, 0xBF,0xAF,0xFE,0xDE,0xBD,0x6F,0xDA,0xF2, +0xFF,0xAF,0xBE,0xFF,0xFF,0xFD,0x7E,0xA7, 0xFF,0xF7,0xFF,0xBF,0xEF,0x7B,0xF6,0xFD, +0xBD,0x4A,0xF2,0x85,0x85,0xBF,0x5B,0xFE, 0xB5,0xFD,0xFA,0xFF,0x4F,0xFF,0xFE,0xDF, +0xFF,0xED,0xFF,0xBF,0xFF,0xBF,0x7F,0xFE, 0xFF,0xB7,0x6D,0xFF,0xF7,0xBF,0xBF,0xEF, +0xFD,0x1F,0xFF,0xFE,0x7D,0xFF,0x67,0xFF, 0xFF,0xFF,0x3F,0x7F,0xFE,0xBF,0xFF,0xE7, +0xDF,0xE7,0xFF,0xEF,0x6B,0xFC,0x1F,0xFF, 0xBF,0xEF,0xFB,0xFE,0xDE,0xBF,0xAF,0xFA, +0xFF,0xB6,0xEF,0xF9,0xFE,0xFF,0x8F,0xEF, 0xDB,0xEF,0xAB,0x6F,0xFB,0xFE,0xFF,0xFF, +0xEF,0xFD,0xFF,0x7F,0xFF,0xFF,0xDE,0xFF, 0xFF,0xEF,0xFF,0xFF,0xFF,0x3F,0xFF,0x6C, +0xFF,0xBF,0xFB,0xFF,0xFE,0xFF,0xFB,0xFE, 0xDF,0xFF,0xFF,0xEF,0xFF,0xFF,0xBF,0xFF, +0xFF,0xFE,0xFB,0xFF,0xD5,0x7F,0xFF,0xFF, 0xEF,0xFB,0xFF,0xFF,0xBF,0xEF,0x43,0xB5, +0xFD,0x6F,0xCF,0xD6,0xBE,0x3F,0x7F,0xDB, 0xFE,0xC3,0xFF,0xFD,0xFF,0xAF,0xEB,0xFB, +0xFC,0xFF,0x3E,0xEF,0xE8,0xFA,0xBD,0xCD, 0xAA,0xFE,0xFE,0x7D,0xCF,0xFF,0xB7,0xFF, +0xF7,0xFF,0xFF,0xFF,0xFD,0xFF,0x75,0xCD, 0x52,0xD7,0xFD,0xFB,0xF7,0xDD,0xFB,0xEF, +0xEB,0xFF,0xFF,0x4F,0xFF,0xBF,0x9F,0xE7, 0xF9,0xFC,0x7F,0x8B,0xC3,0xF9,0xAF,0x8F, +0xE7,0xE9,0xBE,0x7F,0x9F,0xE6,0xF9,0xFC, 0x5F,0xFF,0xFF,0xF7,0xFD,0xFF,0x7A,0x5F, +0xD7,0xED,0xFF,0xFF,0xD7,0xFF,0xDD,0x7F, 0xE7,0xFF,0xFC,0xFF,0xFC,0x3F,0xFF,0xFF, +0xFF,0xFB,0xFF,0xFE,0xBF,0xAF,0xFF,0xFD, 0xFF,0xEF,0xFF,0xEB,0xFF,0xFF,0xFF,0xFF, +0xFF,0xF7,0x7F,0xFF,0x7F,0xDF,0xFF,0xFD, 0xFD,0x7F,0xFE,0xF7,0xFD,0x7F,0xDF,0xFF, +0xFD,0xFF,0xFF,0xDF,0xFB,0xFF,0xEE,0xFF, 0xFB,0xFF,0xF7,0xFD,0xFF,0x7A,0xDF,0xF5, +0xFD,0xFA,0xDF,0xF7,0xFC,0xFF,0x7F,0xDF, 0xBF,0xED,0xFF,0xC9,0xFF,0xDF,0xFF,0xBF, +0x2F,0xFB,0xFF,0xBC,0xAD,0xFF,0xF7,0xFF, 0xFF,0xEF,0xD3,0xFF,0x7D,0xBF,0x6F,0xFF, +0xFA,0xFF,0xFE,0xBF,0xAE,0xEA,0xFA,0xBE, 0xAD,0xA5,0xEB,0xCE,0xBF,0xA7,0xEB,0x5A, +0xDE,0xBD,0xAF,0x6B,0xFD,0x57,0xFF,0xFF, 0xF4,0x7F,0x1F,0x7F,0xFD,0xFF,0x7F,0x36, +0xF0,0xDF,0x79,0xFF,0xFF,0xFF,0xF7,0xFD, 0xBF,0xFF,0x87,0xFF,0xFB,0xF3,0xFC,0xFF, +0xFF,0xFF,0xFF,0x7E,0xFF,0xBF,0xDF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0xBF,0xF8,0x9F, +0xFF,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFD, 0xF7,0xFC,0xBD,0xFF,0xFE,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFB,0xF9,0xBF,0xFF,0xFF,0xEB, 0xE2,0xFE,0xFF,0xBF,0xEF,0xA9,0xBA,0x2F, +0xEB,0xF9,0xFE,0x77,0xDF,0xF7,0xFF,0xFF, 0xF9,0x7F,0xFF,0xFF,0x7F,0xEF,0xD7,0xFF, +0xFD,0xFF,0xFB,0xF5,0xFF,0xBF,0x6F,0xDF, 0xFF,0xFF,0xFD,0xFF,0xFF,0xF0,0xFF,0xFF, +0xFF,0x3F,0xCF,0xFF,0xBA,0xEE,0x9B,0xBF, 0xEE,0xD7,0xFE,0xCD,0xEF,0xFF,0xDF,0xBF, +0xFF,0xFF,0xC5,0xFF,0xFF,0xFD,0x7F,0x4F, 0xFD,0xF6,0xD9,0xFF,0x4F,0xD6,0xFD,0xBF, +0x6E,0xFF,0xFF,0xF4,0x7F,0xFF,0x7F,0x8B, 0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xF9,0xFE, +0x37,0xFF,0xD9,0xFB,0xF5,0xAF,0xFD,0xFF, 0xFF,0xFB,0xFF,0xFF,0x07,0xFF,0xFF,0xFF, +0xFB,0xF7,0xFF,0xFD,0xFF,0x7C,0xFA,0x7E, 0x4F,0xFC,0xDF,0x1D,0xC7,0xFF,0xFF,0xFF, +0xFF,0xAE,0xFF,0xFF,0xFF,0xFF,0xFD,0xFB, 0xFF,0xFF,0xFE,0xFE,0xFC,0xFF,0x7F,0x7F, +0xBF,0xEF,0xFE,0xFF,0xFF,0xFF,0x5F,0xFD, 0xFF,0xFF,0xFF,0xFD,0x6F,0x5A,0xD7,0x7B, +0xBE,0x5F,0xFE,0x39,0xFF,0xF7,0xFF,0xF7, 0xFD,0xFE,0xAA,0x1F,0xFF,0xFF,0xFF,0xFF, +0xFE,0xFE,0xAB,0xAF,0xFD,0xFE,0xBF,0xFF, 0xF7,0xFF,0x7F,0xFE,0x8F,0xE3,0xFB,0xEE, +0x7F,0xFF,0xFF,0xFF,0xFF,0xEB,0xFB,0xFF, 0xFD,0xBF,0xEF,0xDF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFB,0xE4,0x3F,0xFF,0xDF, 0xFF,0xFF,0xFF,0xFF,0xF3,0xEF,0xBB,0xFB, +0xBF,0xEF,0xBB,0xFF,0xD7,0xBF,0xFF,0xFF, 0xFF,0x29,0xAF,0xF7,0xFF,0xFF,0xFB,0xFF, +0xFB,0xE6,0xFF,0x0F,0xFB,0x3F,0xDF,0x0F, 0xFF,0xAF,0xFF,0xFF,0xFF,0xF5,0xC3,0xDF, +0x5F,0xFF,0xFF,0xFF,0xFE,0x6B,0xCA,0xBE, 0xBC,0xFF,0x9F,0xF2,0xBF,0xFF,0xFE,0xFA, +0xFF,0xFF,0xEF,0x16,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFC,0xDF,0x97,0xFD,0x79,0xFF,0x37, +0xE7,0x7F,0xFF,0xFF,0xB5,0xFF,0xFF,0xF6, 0x2F,0xFF,0xFD,0xFB,0xFE,0xFF,0xFF,0xFD, +0x5F,0x57,0x5F,0xFF,0xDB,0x52,0xDF,0xFF, 0xFD,0xBF,0xFF,0xFF,0xFC,0xDB,0xFF,0x7B, +0xB5,0xFD,0x7F,0xFF,0x71,0x9C,0x6E,0xFF, 0xF6,0x35,0xA5,0x9B,0xFF,0xFF,0xFD,0xFF, +0xFF,0xDB,0x9E,0x7F,0xFE,0xEF,0xFB,0xFF, 0xFF,0xBD,0xEF,0xFF,0xDE,0xB7,0xF9,0x4B, +0xFF,0xF5,0xEF,0xFF,0xFF,0xFF,0xE8,0x7E, 0xFF,0xEA,0xDF,0xF7,0xFF,0xFD,0x69,0x5B, +0xFC,0x9F,0xEF,0x78,0xD6,0xFF,0xEB,0xEF, 0xFF,0xFF,0xFF,0xE8,0xFF,0xFF,0xED,0xFF, +0xFF,0xFF,0xFF,0xE3,0xF9,0xF6,0xBF,0xFF, 0xFF,0xFE,0xDF,0xFF,0x7F,0xFF,0xFF,0xFF, +0xD1,0xFF,0xFF,0xE7,0xFF,0xFF,0xFF,0xFF, 0xE7,0xF9,0xFF,0xBF,0x7F,0xD9,0xFF,0xFD, +0xFE,0x7F,0xFF,0xFE,0xFF,0xF9,0xFF,0xFB, 0xD6,0xDF,0xBF,0xEF,0x5B,0xD6,0xFF,0xBF, +0xFB,0xF6,0xFF,0xBF,0xEF,0xF8,0xF6,0xDD, 0xBE,0xFE,0x16,0xFF,0xBF,0xEF,0xFF,0xFE, +0xFF,0xBF,0xEF,0xFF,0xFF,0xFF,0x6F,0xFB, 0xFF,0xFF,0xFF,0x6F,0xF3,0xFF,0xF7,0xEF, +0xFB,0xFF,0xBF,0xFF,0xEF,0xFE,0xFF,0xBF, 0xFF,0xFF,0xFF,0xBE,0xBF,0xFF,0xEF,0xFF, +0x7F,0xEF,0xFF,0xFD,0x17,0xFB,0x7B,0xFF, 0xFF,0xFD,0x7F,0xDB,0xF6,0xF4,0x7F,0xFA, +0xFE,0xF5,0xBF,0xEB,0xE3,0xF7,0xFF,0xFF, 0xE9,0xBF,0xFF,0xAF,0xF7,0xFD,0xF3,0x7E, +0x8F,0xA3,0xEA,0xFF,0xCB,0xF3,0xEE,0xFF, 0xBF,0xEF,0xF7,0xF9,0xFF,0xFE,0x7F,0xFF, +0xFF,0xFF,0xFF,0xF5,0xFB,0xF6,0xFF,0xF5, 0x2F,0xFE,0xFB,0xD7,0xBF,0xFF,0xBE,0xDF, +0x9F,0xFF,0xF0,0xFF,0xFF,0xF9,0xFE,0x7F, 0x8F,0xA3,0xF8,0xFE,0x6F,0x9F,0xF9,0xF6, +0x2F,0x9F,0xE7,0xF9,0xFE,0x2F,0x9F,0xE1, 0xFF,0xFF,0xFF,0x7F,0xDF,0xF7,0xF5,0xFD, +0x7F,0x7F,0xF5,0xFF,0x9F,0x5F,0xFB,0xFE, 0xFF,0x7F,0xFF,0xFF,0xCB,0xFF,0xFF,0xFB, +0xFE,0xFF,0xBF,0xAF,0xFB,0xFE,0xFF,0xDF, 0xFE,0xFE,0xBF,0xF7,0xFF,0xFF,0xFF,0xFF, +0xFF,0xC7,0xFF,0xFF,0xFD,0xFF,0x7F,0xDD, 0xF7,0xFD,0xFF,0xFF,0xD7,0xFF,0xFD,0x7F, +0xFF,0xFB,0xFD,0xFF,0xFF,0xFE,0xEF,0x7F, 0xFD,0xEF,0xFB,0xFE,0xFB,0xFD,0xFF,0x7F, +0xDF,0xFD,0xFF,0x7A,0xDF,0xF7,0xFD,0xFF, 0xFF,0xFF,0xFF,0x1F,0xFF,0xFF,0xD3,0xF7, +0xFF,0xFF,0x6F,0xDB,0xFF,0xFF,0xEF,0xCB, 0xF4,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, +0x29,0xFF,0xE8,0xDA,0x76,0x9F,0xAF,0x6A, 0xDA,0xFE,0x35,0xEB,0xDA,0xD6,0xBF,0xAB, +0xEB,0x7A,0xDE,0xBF,0xD7,0x7F,0xFF,0xFE, 0xFF,0xBF,0xEF,0xFD,0xDF,0x77,0xBF,0xFD, +0x37,0xEF,0xFF,0xEF,0xFF,0x3F,0xFF,0xFF, 0xFF,0xFE,0x7F,0xFF,0xFF,0xFF,0xF7,0x7E, +0xDF,0xFF,0xFF,0xFF,0xFA,0xB7,0x7F,0xFF, 0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0x89,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x9F,0xFB,0xFF,0xFF,0xFF,0xE7,0xFF, +0xFF,0xFF,0xFF,0xAA,0xFF,0xAB,0xFB,0xFA, 0xEF,0xBF,0xFF,0xDF,0xFA,0x7B,0xB9,0xFE, +0xFE,0xFF,0xFD,0xFF,0xF7,0xFE,0x3F,0xFF, 0xB7,0xFF,0xF7,0xEE,0xFF,0x7F,0xEF,0xFF, +0xFF,0x7F,0xFF,0x1F,0xFB,0xFF,0xBF,0xFB, 0xFE,0xFF,0xBD,0xFF,0xFF,0x2F,0xFF,0xBF, +0xFF,0x7F,0xDF,0xFA,0xFF,0xFF,0xFC,0xEE, 0xF5,0xF3,0xBE,0xFB,0x0F,0xEF,0xF3,0xBE, +0xEF,0xFC,0x5F,0xFF,0x5A,0xFF,0xF7,0xDF, 0xFF,0xFF,0xFE,0xD5,0xFC,0x5F,0xFB,0xF2, +0xFF,0xFF,0x2F,0xBB,0xF3,0xFF,0xFF,0xBF, 0xFF,0xEF,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF, +0xBF,0xFF,0xFF,0xFD,0x7B,0xFF,0xDF,0xB9, 0xFF,0xFB,0xFF,0xD8,0x7F,0xFF,0xFF,0xFF, +0xFB,0xFF,0xFC,0x7F,0x1F,0xBF,0xE0,0xDF, 0xF7,0xEF,0xFF,0xFD,0x7F,0xFE,0xDF,0xFF, +0xE0,0xFF,0xFF,0xFD,0xEF,0xFB,0xFF,0xFE, 0xF7,0xDF,0xFF,0xEB,0x5F,0xFF,0xF7,0xFF, +0xFF,0xFF,0xFF,0xBF,0xFF,0xFD,0xFF,0xFD, 0xFF,0xFF,0xFF,0xF7,0xFD,0xFF,0x3B,0xDC, +0xFD,0x6D,0x7B,0x5F,0x57,0xF5,0xFD,0x7F, 0x5F,0xFF,0xB1,0xFF,0xEB,0xFF,0xFF,0xFF, +0xFB,0xFB,0xFE,0xFF,0xBF,0xFB,0xBE,0xFF, 0xBF,0xEF,0xFB,0xFE,0xFF,0xAF,0xFE,0xF7, +0xDF,0xDF,0xFF,0xFF,0xFF,0x7F,0xCF,0xF3, 0xF8,0xFF,0xD7,0xFB,0xFF,0x5F,0xBF,0xF7, +0xFB,0xFF,0x7F,0xFE,0x23,0xFF,0xFF,0xFE, 0x7F,0xF3,0xFF,0xFB,0xFE,0xFF,0xFF,0xF3, +0xFF,0xFF,0xF5,0xF9,0xFF,0x3F,0xFF,0xFF, 0xF0,0x9A,0xFF,0xBE,0x7F,0xFF,0xFC,0xF9, +0xFF,0xFD,0xAF,0xEB,0xFE,0xBF,0xFF,0xCF, 0xF3,0xFE,0x7F,0xFF,0xFF,0x5B,0xBD,0xFF, +0xBC,0xEB,0xFF,0xD7,0xD4,0xAF,0xAF,0xFD, 0xFF,0xCF,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7, +0xFD,0xFE,0xFF,0x6F,0xFF,0xFB,0xFF,0xFF, 0xFF,0xFD,0x7F,0x5E,0xFD,0xBF,0xDB,0xF6, +0xFD,0xBF,0x6F,0xFB,0xEE,0xFD,0xFF,0x7A, 0xFF,0xFA,0xFB,0xFF,0x3F,0xFB,0xB7,0x5F, +0xD6,0xF7,0x1F,0x71,0xDC,0x77,0x1D,0xC7, 0x31,0xDC,0x77,0xDF,0xF9,0xBF,0xF5,0x5B, +0xF4,0xD7,0x9D,0xAE,0xFF,0xBF,0xFD,0xBF, 0xDB,0xF6,0xFD,0xBF,0x6F,0xDB,0xF6,0xFE, +0x3D,0x81,0xFF,0xEB,0xFE,0xFE,0xFE,0xFF, 0xEB,0x7A,0xDF,0x7D,0x77,0x7D,0xF5,0x79, +0xDF,0x57,0xDD,0xF5,0x7D,0x7E,0xE6,0xFF, 0xD6,0x3F,0xBF,0x7F,0xFF,0xD4,0xF5,0x3F, +0xBF,0xFB,0xBE,0xEF,0xB3,0xEE,0xFB,0x9E, 0xEF,0xBB,0xFE,0x8B,0xFF,0xFE,0xDF,0xB7, +0xED,0xFF,0xF7,0xFD,0xFE,0xFF,0xEF,0xBB, 0xEE,0xFF,0xBE,0xEF,0xBB,0xEE,0xEB,0xFC, +0x1F,0xFF,0xFF,0xFD,0xFF,0xE7,0xFF,0xF7, 0xFD,0xFF,0xEF,0xFE,0xFF,0xBF,0xEF,0xFB, +0xFE,0xFF,0xBF,0xEB,0xFA,0x1F,0xFF,0xB7, 0xEF,0x5B,0xFE,0xFF,0xAF,0xEB,0xDD,0xE7, +0xDE,0x77,0x9D,0xE7,0x79,0xDE,0x77,0x9D, 0xBF,0xE6,0x6F,0xFF,0xFE,0xFF,0xBF,0xEF, +0xFB,0xFE,0xFD,0xBF,0x6F,0xF6,0xFD,0xBF, 0x6F,0xDB,0xF6,0xFD,0xBF,0xFF,0x7E,0xFF, +0xFF,0xFB,0xFE,0xFE,0xFF,0xEF,0xFB,0xFD, 0xEF,0x7E,0xF7,0xBD,0xEF,0x7B,0xDE,0xF7, +0xBD,0xEF,0xFF,0xD5,0xFF,0xBF,0xFF,0xEF, 0xFE,0xFF,0xFC,0x3F,0x0F,0xE7,0xFE,0x7F, +0x9F,0xE7,0xF9,0xFE,0x7F,0x9F,0xE7,0xFE, 0xF3,0xFF,0xFE,0xDF,0xAD,0xDF,0x67,0xEE, +0xFB,0xBF,0xEF,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFE,0xFF,0xBF,0xEF,0xFF,0x23,0xFF,0xFF, +0xFF,0xFF,0x7F,0xFF,0xF3,0xBC,0xDB,0xFE, 0xFB,0xFF,0xFB,0xBE,0xF7,0xFB,0xFF,0x7F, +0xDF,0xFF,0xCF,0xFB,0xFF,0x9F,0xE3,0xF9, 0xBE,0x3F,0x8F,0xE7,0x79,0xFF,0x9D,0xE7, +0xF9,0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x5F, 0xFF,0xCF,0xF7,0xFF,0xFF,0xFF,0xDF,0xF7, +0xFE,0x7F,0xE7,0xF9,0xFE,0x7F,0xFF,0xFF, 0xFB,0xFE,0xFF,0xFF,0xBF,0xFF,0xBF,0xBF, +0xFF,0xFE,0xFF,0xBF,0xEF,0xFF,0xFD,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFD,0xFF, +0xFF,0x3F,0xFF,0xBF,0xFF,0xF7,0xFF,0xFF, 0x7F,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xE8,0xEF,0xFF, 0x5F,0xF7,0xBF,0xF9,0xFE,0xDF,0xB7,0xFD, +0xFF,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7, 0xFD,0xFF,0xDD,0xFF,0xF2,0xFF,0xBF,0xFF, +0xFF,0xBF,0xFF,0xFF,0x2F,0xF2,0xFF,0xBF, 0x2F,0x7B,0xD2,0xF7,0xBF,0x2F,0xFF,0xBB, +0xFF,0xEE,0x8F,0xAF,0xEB,0xFA,0xFE,0x3F, 0xA7,0x69,0xCE,0x8F,0xA4,0xEA,0xFA,0xEE, +0xB7,0xAE,0xEB,0xFD,0xC7,0xFF,0xF7,0xF7, 0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x3E,0xF3, +0x74,0xFF,0x3F,0x4F,0xFF,0xE7,0xFF,0x3F, 0xFE,0xA7,0xFF,0xFF,0xDF,0xF7,0xB7,0xFF, +0xF7,0xFF,0xBA,0xEF,0x37,0xEB,0xFB,0xFE, 0xBF,0xFB,0xFE,0xF3,0xFF,0xF9,0xDF,0xFF, +0xBF,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF, 0xFD,0xDF,0xFF,0xFD,0xFF,0xFF,0xFB,0xFE, +0xFD,0xFF,0xFB,0xBF,0xFE,0x3F,0xED,0xFF, 0xDF,0xBE,0x3D,0xA7,0xFB,0xFA,0x3F,0xE6, +0xE1,0xFE,0xFE,0x3F,0xEF,0xE3,0xDF,0xF5, 0x7F,0xFE,0xFF,0x7E,0xFF,0xFF,0xFF,0xFF, +0xEF,0x6F,0xF6,0xFF,0x7D,0xEF,0xD7,0xDE, 0xFF,0x7D,0xEF,0xFF,0xF2,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0x7B,0xDE,0xFB,0xE6,0xEE, 0xEF,0x37,0x6E,0xF3,0x7E,0xEB,0x37,0xEF, +0xFF,0xC1,0xFF,0xFE,0xFF,0xF7,0xEF,0xFF, 0xFF,0xFF,0xBF,0x3F,0xD2,0xDF,0xBF,0x2F, +0x7B,0xE2,0xFF,0xFE,0x3B,0xBD,0xDB,0xFF, 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFE, +0xFF,0xFB,0xFF,0xFF,0xBF,0xFF,0xFB,0xDF, 0xFF,0xBF,0xFF,0xB7,0xFF,0xFF,0xBF,0xEF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF, 0x7F,0xFF,0x1F,0xEF,0xF1,0xFD,0xFF,0xF6, +0xAF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFF, 0xFF,0xFF,0xFE,0x9F,0xFF,0xFF,0xFF,0x77, +0xEF,0xF7,0xFB,0xFF,0xFE,0x5F,0xFF,0xFF, 0xBF,0xCF,0xFB,0xF7,0xDD,0xF7,0xF5,0xFF, +0x5F,0xD5,0xF5,0xFD,0x7F,0x5F,0xD7,0xF5, 0xFF,0xFB,0x0F,0xFF,0xFF,0xA9,0xEA,0x7A, +0xFF,0xAF,0x8F,0xFE,0xDF,0xAF,0xEF,0xFB, 0xFE,0xFF,0xBF,0xEF,0xFB,0xDF,0xE5,0x5F, +0xFF,0xFF,0xFF,0xFF,0xFF,0xBD,0x57,0xFF, 0xFF,0x6F,0x77,0xBF,0xF7,0xFB,0xFF,0x7F, +0xBF,0xF7,0xFF,0xFC,0xBF,0xFF,0x9F,0xFF, 0xFF,0xEF,0xFF,0xFE,0xFF,0xFF,0xFF,0x1F, +0xCF,0xFF,0xFC,0xFF,0xFF,0xFF,0xFF,0xFB, 0x65,0xAF,0xF3,0x7C,0xFF,0x3F,0xDF,0xFF, +0xFD,0xE9,0xFE,0x7F,0xE7,0xFF,0xFE,0x7F, 0xFF,0xFF,0xFF,0xFF,0xFD,0xE3,0xDF,0xFB, +0xDB,0xF6,0xFD,0xEF,0x5B,0xFB,0xFF,0xDF, 0xFC,0xFF,0x3F,0xDF,0xF3,0xFD,0xFF,0x7F, +0xDF,0xEF,0x66,0xFF,0xDF,0xAD,0xEB,0x7A, 0xDE,0xF7,0xF7,0xE7,0xD9,0xFD,0x9F,0x67, +0xD9,0xF6,0x7D,0x9F,0xE7,0xDF,0xF5,0x47, 0xFD,0x65,0x5B,0xD6,0xF4,0xFE,0xFF,0xEF, +0xFF,0x6D,0xF6,0xDD,0xB7,0x6D,0xDB,0x76, 0xDC,0xB7,0x7D,0xFA,0x9B,0xF6,0x6D,0x9D, +0x67,0x59,0xDF,0xF7,0xDD,0xFF,0xEB,0xFE, 0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF,0xE3, +0xD1,0x9F,0xFF,0xBD,0xBF,0xEF,0xFE,0xF7, 0xBF,0xBF,0xF7,0xD7,0x7F,0xDD,0xF7,0x9D, +0xDF,0x7F,0xDF,0xF7,0xFF,0xE0,0x7F,0xFD, 0xC1,0xDF,0xF7,0xFD,0xC7,0x7F,0x7F,0xFB, +0xFF,0xBB,0xEC,0xFB,0x3E,0xFF,0xBF,0xEC, 0xFB,0xFF,0xD8,0x7F,0xBF,0x6C,0xFF,0xBE, +0xFF,0xBF,0xED,0xFF,0xEF,0xFE,0xFB,0xBF, 0xEF,0xFB,0xFE,0xFF,0xBF,0xEE,0xFF,0xC5, +0xFF,0xAF,0x6F,0xFF,0xFC,0xFD,0x3F,0xE7, 0xFF,0xFE,0xFF,0xEF,0xFB,0xFE,0xFF,0xBF, +0xEF,0xFB,0xFE,0xBF,0x89,0xFE,0xFA,0xBA, 0xFE,0xBF,0xAF,0xFB,0xF6,0xF5,0xD9,0x7D, +0x97,0x65,0xD9,0x74,0x5D,0x97,0x65,0xD3, 0xFE,0xD6,0xFF,0xBF,0xF7,0xFD,0xFF,0x7F, +0xBF,0xCF,0xFB,0xFE,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xEF,0xFB,0xFF,0xF6,0x8F,0xFB, +0xFF,0xEF,0xFB,0x7E,0xDB,0xFE,0xFF,0xBE, 0xEF,0xEE,0xFB,0xBE,0xEF,0xBB,0xEE,0xFB, +0xBE,0xFF,0xFF,0xDF,0xFF,0x43,0xFF,0xFF, 0xFB,0xEF,0x5F,0xB7,0xFE,0x7F,0xE7,0xF9, +0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0xF9, 0xBF,0xFE,0xAF,0x77,0xFD,0xFF,0x2F,0xAF, +0xA7,0xFE,0xFF,0xEF,0xFB,0xFE,0xFF,0xBF, 0xEF,0xFB,0xFE,0xFF,0xF1,0x7F,0xEF,0xDF, +0xFF,0x97,0xF5,0xEF,0xFF,0xDF,0xFF,0xFF, 0xBF,0xFF,0xBF,0xFF,0xFF,0xFE,0xFF,0xFF, +0xFF,0xE0,0xFF,0xFF,0xF9,0xFE,0x2F,0x8B, 0xE3,0xF8,0xBE,0x77,0x9F,0xF9,0xDA,0x77, +0x9D,0xE7,0x79,0xDE,0x77,0x9F,0xDD,0xFF, 0xFD,0xFD,0x7F,0x5F,0xD7,0xFD,0xFF,0x7F, +0xE7,0xFE,0x7F,0x97,0xE7,0xFB,0xFE,0xFF, 0xBF,0xEF,0xFF,0xAB,0xFF,0xEF,0xFA,0xFE, +0xBF,0xAF,0xFF,0xFA,0xFF,0xFF,0xDF,0xFF, 0xFB,0xFF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFF, +0x67,0xFF,0xF7,0xF5,0xFF,0xFF,0xFF,0xDF, 0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFF,0xBD, 0xEB,0xFF,0xFF,0xF7,0xAD,0xEB,0xFF,0xDF, +0xFD,0xFF,0x3F,0xDF,0xF7,0xFD,0xFF,0x7F, 0xDF,0xFF,0x5F,0xFF,0xF7,0xFF,0xFF,0xFD, +0xBF,0xFF,0xCB,0xF4,0xFF,0x7F,0xD3,0xF7, 0xFD,0x3F,0x7F,0xD3,0xF7,0xFF,0xFC,0x3F, +0xFF,0xEA,0xFA,0xBE,0xAF,0xAB,0xEB,0xBA, 0xF4,0x95,0x6B,0x52,0xD4,0xAD,0x2F,0x4A, +0xD2,0xF6,0xBF,0xD2,0x7F,0xF7,0x3F,0xFF, 0xFF,0xF3,0x7F,0xFF,0xFF,0xF7,0xFF,0xBA, +0xDF,0xFB,0xFD,0xFF,0xBF,0xFF,0xFB,0xFF, 0xF8,0x7F,0xEA,0xFF,0xFE,0xFE,0xDF,0xFF, +0xF7,0xFF,0x7F,0xBB,0xFF,0xFF,0xBF,0xDF, 0xFB,0xFF,0xFF,0xBF,0xFF,0xB1,0x7F,0xFF, +0xFB,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBF, 0xCF,0xFE,0xFF,0xFF,0xEF,0xFF,0xF7,0xFF, +0xFF,0xFF,0xF1,0xFF,0x69,0xBE,0xFA,0xBF, 0xAF,0xE2,0xFF,0xFE,0xFD,0xAF,0xF3,0xFE, +0xFF,0xBF,0xEF,0xFB,0xFC,0xFF,0xFF,0x07, 0xFD,0x95,0xDB,0xDF,0x7F,0xDF,0xAF,0xFF, +0xF7,0xAF,0x36,0xFE,0xBF,0x65,0xEB,0xF6, 0xFE,0x9F,0x6F,0xFE,0x07,0xFF,0xCF,0xFF, +0xF8,0xFE,0xFF,0xCF,0xFF,0xF6,0xFA,0xE7, 0xFB,0xFE,0xFF,0xBB,0xED,0xF9,0xFF,0xFF, +0xFF,0x5F,0xFF,0xFF,0xFF,0x75,0xFF,0xEF, 0x7E,0xFD,0xE0,0xE8,0x5E,0xD3,0xE5,0xF9, +0x3E,0x5F,0xD7,0xF7,0xFF,0xFA,0x2F,0xFB, 0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0x7F, +0x7F,0xD7,0xF5,0x7D,0x5F,0x57,0xD5,0xF5, 0xEF,0xFF,0xF3,0x7F,0xFC,0x7F,0xFF,0xC7, +0xF1,0xFF,0xFF,0x1F,0xCF,0xB0,0xFF,0x3F, 0xCF,0xF3,0xFC,0xFF,0x3F,0xCE,0xFF,0xE4, +0xFF,0xDF,0x7F,0xFE,0xF7,0xBB,0xFF,0xFF, 0xDF,0xEF,0xEE,0xFF,0xBF,0xEF,0xFB,0xFE, +0xBF,0xBF,0xEF,0xFF,0xD1,0xFF,0xFF,0xFF, 0xFD,0xFB,0xFF,0xFD,0xFF,0xFB,0x9F,0xE9, +0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0xBF, 0xFF,0xB3,0xFF,0xFF,0xF7,0xFF,0xFF,0xAF, +0xF7,0xFF,0xB6,0x3F,0xEB,0xFA,0xFE,0xBF, 0xAF,0xEB,0xFA,0xFE,0xBF,0xFE,0xA7,0xFF, +0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xFF,0xFF, 0xFE,0x9F,0xF7,0xF9,0xFF,0x7F,0x9F,0xE7, +0xFF,0xFF,0xFE,0xAF,0x6F,0xFF,0xFF,0xFF, 0x9F,0xFF,0xDF,0xFF,0x7D,0x5F,0xDD,0xFF, +0xFB,0xBF,0xE7,0xBB,0xFF,0xFB,0xDF,0x6D, 0x5F,0x7E,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xEB,0xF7,0xFF,0xE7,0xEF,0xF7,0xFF,0xFF, 0x7F,0xFF,0xF7,0xFF,0xFC,0x8F,0xFF,0xEF, +0xFD,0xFE,0xFF,0xBE,0xF4,0xF2,0x7D,0xD7, 0xCF,0xFF,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xCF,0x6B,0xFF,0xBF,0x3F,0xFB,0xF2, 0xFC,0x7F,0xEB,0xFF,0x9F,0xFA,0xFF,0xFF, +0x3F,0xFF,0xF3,0xFF,0xFF,0xFD,0x70,0xF7, 0xFF,0xFF,0xBF,0xFF,0xFB,0xD7,0xFE,0xF5, +0x77,0xFF,0x15,0xDD,0x77,0xFD,0xFF,0x7F, 0xDF,0xF7,0xFB,0xCD,0xBF,0xFF,0xFD,0xFF, +0xFF,0xDF,0x37,0xCD,0xF9,0xEC,0xFE,0xEF, 0xBB,0xF4,0xFB,0x3F,0x4F,0xB3,0xFF,0xFD, +0xCB,0xFF,0xE9,0x7E,0x54,0x9F,0xE5,0x4B, 0xB7,0xFF,0xDD,0x7D,0xC7,0x71,0xDD,0x77, +0x5D,0xD7,0x75,0xCD,0x7F,0xD6,0xFF,0xD3, 0xF6,0xF9,0x3F,0x6D,0x95,0xAF,0x7F,0xFE, +0xFF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFE,0xF6,0xC7,0xFF,0xAD,0x7B,0xCA,0xFF, +0xBF,0xBF,0xEF,0xFD,0xE3,0xDF,0xB7,0xED, 0xFB,0x7E,0xDF,0x37,0xED,0xE3,0xFB,0xDF, +0xFF,0x52,0x5C,0x15,0xFD,0xCF,0x7F,0xDF, 0xFE,0xEF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEC, +0x7B,0xFE,0xFF,0xFE,0x3E,0x7F,0xDA,0xF7, 0xFD,0xFF,0x7F,0xFF,0xFF,0xFB,0xEF,0xBB, +0x6F,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0xFF, 0xF7,0x7D,0xFF,0xD8,0xFF,0xFD,0xBF,0x7F, +0xFB,0xFF,0xFF,0x9F,0xFB,0xFE,0x7F,0x9F, 0xE7,0xF9,0xFE,0x7F,0x9F,0xEA,0x7F,0xF6, +0xBF,0xBD,0x6A,0x5A,0xF6,0xE5,0xBF,0x77, 0x5F,0x6D,0xDD,0x77,0x5D,0xD7,0x75,0xDD, +0x77,0xFF,0xA5,0xBF,0xCF,0xFB,0xFF,0xFF, 0xBF,0xCF,0xFB,0xFD,0xFF,0xBF,0xF3,0xFE, +0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFD,0xAB, 0xFF,0xBF,0xBF,0xFF,0xFB,0xFF,0x7F,0xEF, +0xFF,0xBE,0xFB,0xEE,0xFB,0xBE,0xEF,0xBB, 0xEE,0xFB,0xBF,0xFF,0xB5,0xFF,0xD0,0xBC, +0xFD,0x2F,0x4B,0xF7,0xFF,0xFF,0x9F,0xF9, 0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0x9F, +0xFA,0x8F,0xFD,0xAB,0xFA,0xDA,0xBF,0xAF, 0xB3,0xFD,0xFF,0xBF,0xFB,0xFE,0xFF,0xBF, +0xEF,0xFB,0xFE,0xF7,0xBF,0xFF,0x9F,0xFF, 0x77,0xF7,0xBD,0xFD,0x77,0xDF,0xFF,0x7E, +0xDF,0xED,0xBB,0xFE,0xFF,0xBE,0xEF,0xFB, 0xFE,0xFF,0xFA,0x3F,0xFF,0xBE,0x6F,0x8F, +0xE6,0xF9,0xFE,0x7F,0x9F,0xC7,0xFE,0x7F, 0x9F,0xE7,0xF9,0xFE,0x7F,0x9F,0xE7,0xFB, +0x7F,0xFF,0x7F,0xCF,0xFF,0xFD,0xFF,0xFF, 0xDF,0xFB,0xAF,0xBF,0xEF,0xFF,0xFE,0xFF, +0x9F,0xEF,0xFB,0xFF,0xFC,0xFF,0xFB,0xFE, 0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xF7, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xF5,0xFF,0xFF,0xFF,0x3F,0xDF,0xF7, +0xFF,0xFF,0x7F,0xEF,0xFE,0xFF,0xBF,0xFF, 0xFB,0xFF,0xFF,0xBF,0xEF,0xFF,0xB3,0x7F, +0xFF,0x7B,0x5E,0xF7,0xFD,0xFF,0x7B,0x7F, 0xF7,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0x7F, +0xDF,0xF7,0xFF,0x17,0xFF,0xFF,0xFF,0x7F, 0xFF,0xFF,0xDD,0xF6,0xFC,0xBF,0xCB,0xF2, +0xBC,0xBF,0x2F,0xCB,0xF2,0xFC,0xBF,0xFE, 0x8F,0xFF,0xFA,0x7E,0xBF,0xA7,0xEB,0xDA, +0xFC,0xBF,0xAF,0x7A,0xFE,0xBF,0xAF,0xEA, 0xFA,0xFE,0xBF,0xAF,0xF4,0xDF,0xFE,0xFF, +0xF3,0x3C,0x7F,0x3E,0xFF,0xCF,0xF8,0xBF, 0x8F,0xE3,0xF8,0xFE,0x3F,0x8F,0xE7,0xE8, +0xFF,0xFC,0x9F,0xFF,0xFF,0xCF,0xEB,0xB3, 0xE7,0xFB,0x7B,0xF3,0xFE,0xFF,0xCF,0xDB, +0xFB,0xFB,0xBF,0x6F,0x6F,0xDF,0xEC,0x7F, 0xFF,0xFF,0xF7,0xFD,0xFD,0xFF,0xFF,0xFF, +0xFF,0xB2,0xBF,0xFF,0xDE,0xFD,0xBD,0xEF, 0xFB,0xF6,0xDF,0xEA,0xE7,0xDB,0xFE,0xBB, +0xFF,0xEB,0xFB,0xBF,0x9F,0x8F,0xE8,0xFE, 0x3F,0x8F,0xA3,0xF8,0xFE,0x3F,0x8F,0xFF, +0xF8,0x7E,0xFD,0xFD,0x7F,0xFF,0xFB,0xCD, 0xFF,0xFD,0xFF,0x5F,0xEF,0xFD,0xFF,0xFF, +0xDF,0xF7,0xFD,0xFF,0xBE,0x90,0xFF,0xFF, 0xEE,0xFF,0x3F,0xBF,0xF3,0xBB,0xFE,0xB7, +0xAB,0xFA,0xFE,0xAF,0xAD,0xEA,0xFA,0xDE, 0xAB,0xFF,0x63,0xFF,0xFE,0xF2,0xFF,0xB3, +0xFF,0xDF,0xEE,0x7D,0xFF,0x03,0xF1,0xF4, 0x3F,0x1F,0xC3,0xF1,0xEC,0x7F,0xFE,0x6F, +0xFF,0xFB,0xFB,0xFF,0x9F,0xFF,0xBF,0xFF, 0x7B,0x5F,0xFD,0xFF,0xDF,0xF7,0xFD,0xFD, +0x7F,0x7F,0xDF,0xFE,0xCF,0xFB,0xFF,0xFF, 0xAF,0xFB,0xFF,0x1F,0xEF,0xA5,0xFD,0xBF, +0xDF,0xFB,0x7D,0xFF,0xBF,0xDF,0xFB,0xFF, 0xFD,0x3B,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, +0xAF,0xF3,0xFF,0xFB,0x7F,0xBF,0xD7,0xFB, 0xBF,0x7F,0xBB,0xF7,0xFF,0xF8,0x7F,0xFF, +0xFA,0x5F,0xD7,0xFF,0xDF,0x7F,0xEF,0xFF, 0xFF,0x7F,0xDB,0xF7,0xFD,0xFF,0x7F,0xDF, +0xB7,0xFB,0xEC,0xFF,0xFF,0xF7,0xBF,0xEF, 0xFD,0xFC,0xFB,0xFF,0xEF,0xF0,0xFE,0x3F, +0x8F,0xE3,0xF8,0xFE,0x3F,0x8F,0xEF,0x8D, 0xFF,0xFF,0xEF,0x7F,0xBF,0xFF,0xFB,0xFF, +0xDB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xEF,0xD8,0xFF,0x2E,0x7F, +0xBE,0xEF,0xFE,0x6E,0xFF,0xBF,0xF9,0xFF, 0xFF,0xF3,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFC,0x66,0xBE,0x47,0xF3,0x7F,0xDF,0xFE, 0x87,0x9F,0xFF,0xFF,0xFF,0xFF,0xE7,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xD6,0x6F,0x7C, 0xFB,0x4F,0xD2,0xFF,0xFD,0x2B,0xFE,0xFF, +0xFF,0xFD,0x5F,0xD7,0xD5,0xF5,0x7D,0xFF, 0xFF,0xFF,0xBF,0x9B,0xFF,0xFF,0xDF,0xB7, +0xFF,0xFF,0xDF,0xFF,0x3F,0xCF,0xFE,0x7F, 0xBF,0xEF,0xFB,0xFC,0xFF,0x3F,0xFF,0xD9, +0xBF,0xFE,0x97,0xEC,0x8F,0xB7,0xFE,0x9B, 0x7D,0xFD,0xB7,0xDD,0x77,0x1D,0xC7,0x71, +0xDD,0x77,0x5D,0xD7,0xF3,0x6F,0xFD,0x3F, 0x73,0xDD,0xAF,0xFD,0x7A,0xFF,0xFF,0xAF, +0xFE,0xFD,0xBF,0xEF,0xFB,0xFE,0xFF,0xBF, 0xEF,0x66,0x7F,0xFF,0xFF,0xBF,0xBF,0xFF, +0xFB,0xFF,0xF7,0xDF,0xFD,0xFB,0x7D,0xDF, 0xB7,0xCD,0xF3,0x7C,0x5F,0x3F,0x91,0x3F, +0xFF,0x3D,0xEF,0x7B,0xFF,0xFC,0xFF,0xCA, 0xEF,0xFE,0xFF,0xBD,0xEF,0xFB,0x1E,0xE7, +0xBB,0xEC,0x7F,0xB3,0xFF,0xFD,0x9F,0xFF, 0xFF,0xFE,0xFF,0xFF,0x7F,0xBF,0xFB,0xFE, +0xFF,0xBF,0xEF,0xFB,0xEE,0xFB,0xBF,0xDF, 0x67,0xFF,0xFF,0xBF,0xEF,0xDB,0xFF,0xBC, +0xFE,0x7F,0xFB,0xFF,0x9F,0xEF,0xF9,0xFE, 0x7F,0x9F,0xE7,0xF9,0xFE,0x87,0xFF,0xEE, +0xFB,0xBE,0xE5,0xBF,0xEF,0xF9,0xD7,0x65, 0xF7,0xDD,0xE7,0x7D,0xDF,0x77,0x5D,0xD7, +0x7F,0xF8,0x9B,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFF,0xFF,0xBF,0xEF,0xFB,0xFF,0x7F,0xCF, +0xF3,0xFC,0xFF,0xBF,0xEF,0xFF,0xDB,0x3F, 0xEF,0xFB,0xFE,0xFF,0xDF,0xFF,0xFE,0xFB, +0xBB,0xEF,0xBF,0xEF,0xBB,0xEE,0xFB,0xBE, 0xEF,0xBB,0xFF,0xFC,0x7F,0xFD,0x3B,0x5B, +0xD6,0xE5,0xFD,0x4F,0xC3,0xFB,0xFF,0xBF, 0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0xFF, +0xB4,0xFF,0xFA,0xBC,0x8F,0xB2,0xE9,0xD2, 0x2E,0xCF,0xFB,0xFF,0xBF,0xEF,0xFB,0xFE, +0xFF,0xBF,0xEF,0xFB,0xFF,0xEC,0xFF,0xFD, 0xFD,0x7F,0xDF,0xF7,0xE4,0xDF,0x5F,0xFF, +0xFF,0xFB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xC3,0xFF,0xEF,0xE6,0xF8,0xFE, +0x3F,0x8B,0x83,0xF9,0xFE,0x7F,0xE7,0xF9, 0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0x17, +0xFD,0xFF,0xFF,0xFF,0x7F,0x5F,0xF7,0x2C, 0xFF,0xFF,0xFF,0xFE,0x7F,0xFF,0xE7,0xF9, +0xFE,0x7F,0x9F,0xFE,0x2F,0xFF,0xFF,0xEF, 0xFF,0xFE,0xBF,0xEF,0xAD,0xFF,0xFF,0x7F, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFE,0xDF,0xFF,0xDF,0xFF,0xFD,0xFD,0x7F, +0xDF,0xF7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFA,0x3F,0xFE, +0xF7,0xFD,0xEF,0x7A,0xFF,0xB1,0xBD,0xFF, 0x7F,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7,0xFD, +0xFF,0x7F,0xF3,0x27,0xFF,0xDF,0xFF,0xDD, 0xFF,0xFC,0x9B,0xFF,0xCB,0xFC,0xBF,0x2F, +0xCB,0xF2,0xFC,0xBF,0x2F,0xC9,0xFF,0xDE, 0xFF,0xDF,0xAF,0xEB,0xDA,0xFE,0xBB,0xAF, +0xEB,0xF8,0xF7,0xAF,0xE8,0xFA,0xFE,0xBF, 0xAF,0xEB,0xF2,0xFF,0xFD,0xFF,0xFF,0xEF, +0xBD,0xD7,0xBF,0xFF,0xFF,0xDE,0x8F,0xB8, 0xDE,0x37,0x8D,0xA3,0x78,0xDA,0x3F,0x8F, +0xFF,0xA1,0xFF,0xFF,0xFB,0xFB,0xFF,0xFF, 0xFF,0xFF,0xA7,0xBD,0xFB,0x76,0xFD,0xBF, +0xEF,0xDB,0xFE,0xBB,0xBF,0xFE,0x27,0x7F, 0xFF,0xFE,0xFE,0xFD,0xF5,0xFF,0xEF,0xF5, +0xDF,0x1F,0xE7,0xFD,0xFF,0x7F,0xDF,0xF7, 0xFD,0xFF,0xFF,0xCD,0xFD,0xAE,0xFF,0xFA, +0x3E,0x3F,0xAB,0xFD,0xF8,0x7E,0x8F,0xE3, 0xF8,0xFE,0x3E,0x8F,0xE3,0xF8,0xFF,0xFE, +0x1F,0xEF,0xDF,0xBF,0xFE,0xDE,0xDF,0xD9, 0xFF,0xDF,0xBC,0xFF,0xFF,0x7F,0xFF,0xEF, +0xFD,0x7F,0xDF,0xF7,0xF9,0x3F,0xFE,0xFF, 0xFF,0x6F,0xFE,0xDE,0xBF,0xF7,0xED,0xEA, +0xFD,0x8F,0x83,0xF8,0xEA,0x3F,0x8F,0xEF, 0xFF,0xF4,0x7F,0xFF,0xEF,0xEF,0x7B,0xF3, +0xF1,0x5F,0xFF,0xFF,0xF1,0x3B,0x7F,0xDF, 0xF7,0xFD,0xFF,0xFF,0xFF,0xFF,0xE0,0xFF, +0xFF,0xFF,0xF7,0xFF,0x6F,0xFF,0x7F,0xFF, 0xFF,0xF7,0xDE,0xF7,0xBF,0xEF,0xFB,0xF7, +0xFD,0xFF,0xFF,0xF5,0xFA,0xFF,0xFF,0xFB, 0xE7,0xFF,0xF3,0xF8,0x7F,0xF3,0xDF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F,0xEF, 0xBB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, +0xFF,0x7F,0xFF,0x9F,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xCF,0xFF,0x37,0xFF,0xFF, +0x7F,0xDF,0x77,0x5D,0xE7,0xFC,0xFF,0xBF, 0xF7,0xF5,0xFB,0xFF,0xFF,0xD7,0xF5,0xFB, +0xFF,0xFF,0x45,0xFD,0x7F,0xEA,0xFD,0xBE, 0xBF,0xDF,0xF7,0xFF,0xFF,0xDB,0xFB,0xFE, +0xFF,0xBF,0xEF,0xFF,0xFF,0xFF,0xFB,0x5F, 0x7F,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFE,0xFF,0xEF,0xFD,0xFF,0x7F,0xDF, 0xFF,0xEF,0xFB,0xF8,0x0F,0xF3,0xFF,0xF9, +0x2E,0xFB,0xFE,0xFC,0xF3,0xEF,0xFF,0xFF, 0xBF,0xFF,0xFB,0xE7,0xFF,0xFE,0x7E,0xFF, +0xC0,0x6B,0xCF,0xFF,0x34,0xDF,0xF1,0xFD, 0xFF,0xEF,0xFF,0xFF,0xFF,0xDF,0xF7,0xFD, +0xCF,0x7F,0x9C,0xFD,0xFD,0x6C,0xF7,0xFF, 0xF6,0xFD,0xEB,0x2B,0x9F,0xFF,0xFC,0xFE, +0x7E,0xFF,0xFF,0xFF,0xFF,0xD7,0xF3,0xF7, 0xFF,0xFB,0xE1,0xBF,0xFF,0xEB,0x7A,0xDE, +0xD7,0xFB,0xFF,0xF9,0xFE,0xFF,0xFF,0xF3, 0xDE,0x7F,0xFD,0xE7,0x7F,0xFF,0xFD,0xBB, +0xFF,0xFF,0x7E,0xCC,0xF6,0xAF,0x5F,0x7F, 0xFE,0xF4,0x7D,0xF7,0xFD,0xBB,0x6E,0xDB, +0xB7,0xFF,0xF7,0xDF,0x66,0xFF,0xFF,0xF7, 0x3D,0xCF,0xDE,0xBD,0xFF,0xFF,0xDE,0xDB, +0x8D,0xF7,0x7E,0xDF,0xB7,0xEF,0x7F,0xFF, 0xF6,0x87,0xFF,0xFF,0xEF,0xFE,0xDE,0xBF, +0xFF,0xFF,0xFF,0xBB,0xEF,0xFD,0xFF,0x7B, 0xDE,0xF7,0x3F,0xFF,0xBF,0xFB,0xDB,0xFF, +0xF2,0xB6,0xFD,0xBD,0x7F,0xE7,0xFF,0xFF, 0xFF,0x6F,0xF7,0xFF,0xFF,0xFF,0xFE,0x77, +0xFF,0xBF,0xF8,0xAF,0xFF,0xDF,0xBF,0xFF, 0xBF,0x7F,0xFB,0xFF,0xFF,0xFF,0xDB,0xFE, +0xFF,0xBF,0xFF,0xFA,0xFF,0xFD,0xFF,0xF6, 0x7F,0xFF,0x9F,0xFF,0xFF,0x3F,0xEF,0xF8, +0xEE,0x7E,0x9F,0xBA,0xFE,0xBF,0x8F,0xEF, 0xFE,0xFE,0xF9,0xFF,0xFA,0x7F,0xFE,0x7E, +0xBF,0xAF,0xFB,0x96,0xFD,0x9F,0xEF,0x5E, 0x65,0xBE,0xEF,0x5B,0xB6,0xFF,0xBE,0xE3, +0xFF,0xB5,0xBF,0xFF,0xFD,0xFF,0x7F,0xFF, 0xEF,0xDF,0xFE,0xFF,0xBF,0xFB,0xFE,0xFF, +0xBF,0xCF,0xFF,0xFF,0xFF,0xFD,0x9B,0xFF, 0xFE,0xFB,0xFE,0xDF,0xFF,0x7F,0xFF,0xF7, +0xFE,0xFF,0xDF,0xFB,0xFB,0xFE,0xFF,0xFF, 0xFF,0xFF,0xFF,0xB7,0xFE,0xFA,0xFF,0xAB, +0xEF,0xFF,0xFD,0xB5,0x7B,0x7F,0xFB,0xF7, 0xFD,0xFF,0xFF,0xDD,0xFF,0xEF,0x8F,0xFF, +0x2F,0xFF,0xFB,0x7C,0xFF,0x3F,0xDF,0x73, 0xEB,0xFE,0x3F,0xFF,0xEF,0xFB,0xFE,0xFF, +0xEF,0xFD,0xFF,0xBF,0xFD,0x0F,0xFF,0xFF, 0xFF,0xF5,0xF9,0xFF,0x7F,0xD7,0xFD,0xFF, +0xDF,0xFF,0xF7,0xFB,0xFF,0x7F,0xBF,0xFF, 0xFF,0xF0,0x9F,0xFF,0xFE,0x7F,0x8B,0xE3, +0xF9,0xDE,0x27,0x9B,0xE6,0xBE,0x7F,0x9B, 0xC3,0xF8,0xDE,0x7F,0x9D,0xE7,0xFE,0x7F, +0xFF,0xFF,0x5F,0xD7,0xFF,0xFF,0xFF,0x4F, 0xFB,0xFF,0xFF,0x7F,0xFF,0xAF,0xFF,0x9F, +0x7F,0xFB,0xFF,0xE8,0xFF,0xFF,0xFE,0xBF, 0xAF,0xFF,0xFF,0xFE,0xBF,0xEF,0xF7,0xFF, +0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF, 0xFC,0xFF,0xFF,0xFD,0x7F,0xFF,0xFF,0xFF, +0xFD,0x3F,0xCF,0xFF,0xFF,0xFF,0xFF,0xF7, 0xFF,0xFD,0x7F,0xFF,0xFF,0x93,0xFF,0xFF, +0x7A,0xDF,0xF7,0xFF,0xFF,0x7B,0x7F,0xB7, 0xEF,0xFF,0xFF,0xFD,0xBF,0xFD,0xFB,0xFF, +0xF7,0xFF,0xD7,0xFF,0xFF,0xFF,0xFC,0x9F, 0x6F,0xCB,0xFF,0xF4,0xBB,0xDF,0xD6,0xFD, +0xBF,0x2F,0xD3,0xF7,0xFF,0xDF,0xFF,0xCF, 0xFF,0xFA,0xBE,0xBD,0xAF,0x6A,0xDA,0xBE, +0xBB,0xAB,0x3A,0xBE,0x2D,0xAE,0xEB,0xDA, 0xF6,0x3F,0xAD,0xF5,0xDD,0xFF,0xCF,0xF1, +0xFF,0xF9,0x7F,0xFF,0x73,0xFE,0xFF,0xCF, 0xC3,0xF4,0xF7,0x2F,0xF3,0xFF,0xFC,0xFF, +0x7C,0x1F,0xFF,0x3F,0x4F,0xFF,0x7E,0xFF, 0xEF,0xBD,0xF6,0xFE,0xFF,0x2B,0xEF,0xDC, +0xFB,0xFD,0xFF,0xFB,0xFF,0xEA,0x7B,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFB,0xF7,0xDF,0xFF, +0xE3,0x7D,0xFF,0xB7,0xFF,0xBF,0xFF,0xFF, 0xDF,0xFF,0xF8,0xFF,0xBF,0xFF,0xBF,0xEB, +0xE7,0xFA,0xFE,0x3D,0xBF,0xE9,0xFC,0xBF, 0xFF,0xFA,0xFB,0xFE,0xFF,0xFF,0xFF,0xD9, +0xFF,0xFF,0xFF,0xF6,0x7F,0xFF,0xF6,0x7D, 0xFF,0xDF,0xCF,0xFD,0xBF,0xFB,0xEF,0x7E, +0xFF,0x7F,0xFF,0xFF,0xD3,0xFF,0xFD,0xFB, 0xFF,0xFB,0xFF,0xFF,0xFF,0xEF,0xFF,0xBF, +0xFE,0xFF,0xF7,0xEF,0xFF,0xFF,0xFF,0xFB, 0xFF,0x87,0xFF,0xFD,0xFF,0xFF,0xFF,0xFF, +0x7B,0xFE,0xFF,0xFE,0x3B,0xF7,0xF7,0xFF, 0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF, +0xFF,0xFF,0xFF,0xFB,0xFF,0xFF,0xFF,0xF7, 0xFF,0xFF,0xAD,0xFF,0xFE,0xF7,0xFF,0xFF, +0x5F,0xFF,0xFF,0xDF,0xFF,0xFD,0xFF,0xF5, 0xFF,0xDF,0xFF,0xBD,0xFF,0xE9,0xFF,0xC7, +0xF3,0xFF,0xFF,0xF7,0xFF,0xF3,0xFF,0xF8, 0x3B,0xFF,0xFF,0x7B,0xDF,0xBF,0xFB,0xEF, +0xFB,0xFF,0xFB,0xF7,0xF7,0xBB,0xFF,0xFF, 0xFF,0xFF,0xFB,0xFF,0xFE,0x7F,0xF3,0x7F, +0x5E,0xB7,0xBF,0xFD,0x7F,0xFF,0xF9,0x7F, 0xFB,0xFF,0xEB,0xFD,0x7F,0x7F,0xFF,0xEF, +0xFB,0xE0,0x3F,0xFE,0xBF,0xBF,0xDF,0xFF, 0x7E,0xFF,0xF7,0xFF,0xFF,0xFE,0xBF,0xFF, +0xDB,0x78,0xFF,0xFF,0xFF,0xEE,0xA1,0xBF, 0xF5,0xDE,0xFB,0xF7,0xFF,0xFB,0xFF,0xFF, +0xFF,0xFF,0xFB,0xFF,0xFF,0xD7,0xFF,0xFF, 0xFF,0xFF,0xEF,0xF0,0xFF,0xFF,0xFF,0xF3, +0xF7,0xFF,0xEF,0xFF,0xE7,0xCF,0xFF,0xFB, 0xFF,0xEF,0xFF,0xFF,0x9F,0x9F,0xEF,0xFC, +0x16,0xBF,0xFE,0xF3,0xE4,0xFF,0xFF,0xC6, 0xFF,0xE7,0xFF,0xFF,0xFD,0xFF,0xBF,0xFF, +0xFF,0x3F,0xFF,0xBF,0xD6,0xAF,0x7F,0xFE, 0x6B,0x7E,0x7F,0xFF,0xAF,0xFF,0xFF,0xBF, +0xFF,0x5F,0xFF,0xFE,0xFF,0xFF,0xFE,0xFF, 0xFF,0xBD,0xDB,0xFF,0xFE,0x5F,0xF2,0xFF, +0xFF,0x5F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xEF,0x7F,0xFF,0xFF,0xFF,0xFF,0xDE,0xBF, +0xFF,0xFF,0xEF,0xFB,0x77,0xFE,0xBD,0x7F, 0x5F,0xFF,0xFF,0xFF,0xDF,0x6F,0xED,0xFF, +0xFD,0xFF,0x7F,0xFD,0x6F,0xFF,0xFF,0x77, 0xDA,0xCF,0xFD,0x5F,0xFF,0xBF,0xFF,0xFF, +0xDF,0x7F,0xFF,0xFB,0xFF,0xFF,0xFF,0xFF, 0x66,0x7F,0xFF,0xFE,0xBF,0xE7,0xBF,0xFA, +0xFF,0xFE,0xFF,0xFF,0xFF,0xDF,0xFF,0x59, 0xEF,0xFF,0xEF,0xFB,0x7F,0x89,0xFF,0xFF, +0xE9,0xFF,0x6F,0xFF,0xF5,0xFF,0xFF,0xFF, 0xFF,0xFF,0x7F,0xF2,0xF7,0xFF,0xFF,0xEF, +0xF8,0x7F,0xFB,0xFF,0xFD,0xFF,0xFF,0xD9, 0xFF,0xEF,0xBB,0xFF,0xFF,0xFF,0xBF,0xEF, +0xDE,0xFF,0xFF,0x9F,0x7F,0xDF,0xFF,0xF7, 0xFF,0xFF,0xFF,0xFF,0xDF,0xFF,0xFF,0xAF, +0xFF,0xFF,0xF7,0x3F,0xEB,0x9F,0xFE,0x7F, 0x9E,0x7F,0x9F,0xFE,0x87,0xFF,0xED,0xDB, +0x56,0xFF,0xBF,0xAF,0x0B,0xD2,0xFF,0xEF, 0xDB,0x6E,0x7D,0xBD,0x6F,0xF8,0xFE,0x3F, +0xFA,0x5B,0xFF,0xFD,0xBF,0xEF,0xFF,0xBF, 0x6F,0xDB,0xE6,0xFF,0xFF,0x3F,0xFF,0xDF, +0xFE,0xFF,0xFF,0xFF,0xFF,0xDA,0x3F,0xFF, 0xFB,0xFE,0xFE,0xFF,0xFF,0xDF,0xF7,0xBD, +0xFF,0xFD,0xFF,0xFE,0xFF,0xFB,0xFF,0xFF, 0xFF,0xFF,0xF1,0x5F,0xFD,0x9F,0xDF,0xFD, +0xFF,0xFD,0x7F,0xFF,0xFF,0xFF,0xFF,0x76, 0xFA,0xFF,0xFF,0x7F,0xE3,0xF8,0xFF,0xAE, +0xFF,0xFB,0x7E,0x9D,0x73,0xFF,0xFA,0x7F, 0xDF,0xFF,0xFF,0x7F,0xFF,0xFB,0xCD,0xFF, +0x7F,0xEF,0xFB,0xFF,0xFD,0xFF,0xF7,0x7F, 0x7F,0xEF,0xFF,0xED,0xFF,0xFF,0xFF,0xB5, +0xFF,0xBF,0xFF,0xBF,0xFD,0xEF,0xDB,0xF7, 0xFF,0x93,0xFF,0xEF,0xE2,0xF9,0xBE,0x7F, +0x8B,0xE7,0xF9,0xFE,0x6B,0xE7,0xF9,0xFE, 0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0x47,0xFF, +0xFF,0xFD,0xFF,0x9F,0xFF,0xD7,0xFF,0xFF, 0xFF,0xFF,0xF5,0xFF,0x9F,0xFF,0xF7,0xFE, +0xFF,0xBF,0xFE,0x6F,0xFF,0xFF,0xFB,0xFF, 0xFF,0xFF,0xAF,0xFF,0xFF,0xFF,0x7F,0xFB, +0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, 0xDF,0xFF,0xFF,0xF7,0xFF,0xFF,0xFF,0xDF, +0xFF,0xFF,0xFF,0x5F,0xFF,0xFF,0xFF,0xFF, 0x5F,0xFB,0xFE,0xFF,0xF8,0x37,0xFF,0xFF, +0xEF,0xFF,0x7F,0xFE,0xBF,0xFF,0xFF,0xFE, 0xBF,0xFF,0xFF,0x7F,0xFF,0xBF,0xFD,0xFF, +0x7F,0xFA,0x7F,0xFF,0xFF,0x6F,0xFF,0xFF, 0x7D,0xFF,0xCF,0xFF,0xFF,0xFF,0x4F,0xFF, +0xF2,0xFF,0xFF,0xFF,0xFF,0xFF,0xFA,0xBF, 0xFF,0xAE,0xEB,0xFA,0xFE,0xBB,0xAD,0xEB, +0xFA,0xF7,0xAF,0x6B,0xFA,0xF6,0xBF,0x25, 0xE9,0xF2,0x7F,0x45,0xFF,0xFF,0xFD,0xF7, +0xF7,0xBF,0xFF,0xDF,0xFF,0xFF,0xBF,0xFB, 0xFF,0xDF,0xF3,0xFF,0xF7,0x3F,0xCF,0xFF, +0xA1,0xFF,0xFF,0xBF,0xE7,0xFF,0xFF,0x7F, 0xFF,0x3D,0xFF,0xFF,0xFF,0xF7,0xFF,0x2F, +0xFF,0xFB,0xF5,0x7F,0xFE,0x57,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7, +0x3F,0xFF,0xFE,0xFF,0xFF,0xFF,0xFD,0xFE, 0xF7,0xEE,0xAF,0xFE,0xEE,0xE7,0xFA,0xFF, +0xFE,0x9D,0xF9,0x5E,0xFE,0xFF,0xEB,0xFF, 0xFF,0xDF,0xA7,0xFF,0xFF,0xFF,0xFC,0xDB, +0xFF,0xFF,0xFF,0x7E,0xFB,0xFF,0xFF,0xEF, 0xFB,0xFD,0xFF,0xDB,0xFF,0xFF,0xFF,0xEF, +0xFF,0xFF,0xFF,0xFD,0xBF,0xFE,0xBF,0xFF, 0x6F,0x7F,0xFF,0xF7,0xFF,0xFF,0xF9,0xFF, +0xF7,0xFF,0xBF,0xDE,0xF7,0xFF,0xFF,0xFF, 0xFA,0x7F,0xFD,0xBF,0x5F,0xFF,0xFF,0xBF, +0xFF,0xED,0xFF,0xF7,0xBF,0xFF,0xFF,0xEF, 0xFF,0xDF,0xFF,0xFF,0xFF,0xE6,0xFF,0xFB, +0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEB,0xFF, +0xFD,0xFF,0xF5,0xFF,0xF6,0x7F,0xDF,0xBD, 0xCF,0xFF,0xFF,0xFF,0xFF,0xDF,0xFF,0xFF, +0xFF,0xF9,0xFF,0xFF,0xFF,0xFF,0xFF,0xE3, 0xFF,0xEE,0xBF,0xFF,0x7D,0xEF,0xFE,0xFF, +0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFE, 0xFF,0xFF,0xFF,0xFF,0xE7,0xFF,0xB5,0xAE, +0xFF,0xFF,0xB6,0xFE,0xBF,0xFF,0xFF,0xBF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0x27,0xFF,0xEF,0xFE,0x7F,0xDF,0xFF, 0x7E,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFD,0xFF,0xF7,0xF9,0x9F,0xFF, 0x5F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F, +0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0x0F,0xFF,0xE7,0xBF,0xFE, +0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFC,0xBF, 0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xC4, +0x6B,0xFF,0x29,0x1F,0xFB,0xAF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xEF,0x1B,0xFE,0xFF,0xFC, +0x6F,0xFF,0xFF,0xFD,0x6A,0xF7,0xD7,0xF5, 0xBF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFE,0xBF,0xFF,0xFF,0xFA,0xFF,0xFF,0xF7, 0xFB,0xDD,0xBF,0xFF,0xE7,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0x7F,0xFF, 0xFF,0xF5,0xFF,0xFF,0xF7,0xFD,0xB3,0xEF, +0xFD,0x7E,0x5D,0xFF,0xFD,0xFF,0xFF,0xFF, 0xFD,0x7F,0xD2,0xF5,0xFB,0x7E,0xCB,0xB7, +0xFF,0xFF,0xFF,0xC6,0xFF,0xFD,0xEE,0x63, 0xFF,0xFF,0xFF,0xFF,0xFF,0xF6,0xFD,0x65, +0x5B,0xDF,0xFF,0xD5,0xFF,0xFF,0xFF,0xF6, 0xE7,0xBF,0xF7,0xA9,0xFF,0xFF,0xED,0xFF, +0xFF,0xFF,0xFF,0xFF,0xEB,0xFF,0xFF,0xFF, 0xAF,0xFF,0xFF,0xFF,0xF8,0x1B,0xFF,0xE3, +0xD0,0xBF,0xFF,0xE1,0xFF,0xFF,0xFF,0xFF, 0xFF,0xD7,0xFF,0xFF,0xFF,0x5F,0xFF,0xFF, +0xFF,0xFF,0xAF,0xFF,0xDB,0x76,0xBF,0xFF, 0x7F,0xFF,0xBF,0xEF,0xFE,0xFF,0xBF,0xEF, +0xFB,0xFE,0xFF,0xFF,0xFF,0xBF,0xF2,0x7F, 0xFF,0x9F,0xFE,0xBD,0xFE,0x7F,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xF7,0x3F,0xEC,0x7F,0xF6,0x95,0xBB, +0xEF,0xF8,0xFE,0xFC,0xBF,0x2F,0xDA,0xFC, 0xBF,0x2F,0xCB,0xF2,0xFC,0xBF,0xEF,0xFF, +0xA9,0xBF,0xCF,0xFB,0xFF,0xFF,0xFF,0xFE, 0xDD,0xB7,0x6D,0xF6,0xD9,0xB6,0x6D,0x9B, +0x76,0xD9,0xBF,0xFB,0xFD,0xA3,0xFF,0xBF, 0xEF,0xFF,0xEF,0xFF,0xFF,0xFF,0x7F,0xDF, +0xFD,0xEF,0x7B,0xDE,0xF7,0xFD,0xEF,0x7F, 0xFF,0xFF,0x05,0xFF,0xFA,0xFE,0x7F,0xEF, +0xE3,0xFF,0xFF,0xFD,0x7F,0xFF,0xFF,0xFF, 0xFF,0x5F,0xFF,0xFF,0xFD,0x7F,0xFB,0xAF, +0xFF,0x63,0xC8,0xFF,0xBF,0xEF,0xFF,0xFF, 0xFA,0x7F,0xFF,0xFF,0xFF,0xFE,0x9F,0xF7, +0xFF,0xFA,0xBF,0xFE,0x9F,0xFB,0x7F,0xFF, 0xFF,0xEF,0xD7,0xFF,0xFF,0xF5,0xFF,0xFF, +0xFF,0xFF,0xFD,0x7F,0xFF,0xFF,0xBF,0xFF, 0xF9,0xBF,0xFF,0xBE,0x27,0x9F,0xE7,0xF9, +0xFE,0x7F,0x8B,0xE7,0xFE,0x7F,0x9F,0xE2, 0xF9,0xFE,0x7F,0x9F,0xE7,0xF1,0x7F,0xFF, +0xFF,0xFF,0xFB,0xFE,0xFF,0xFF,0xFF,0xD7, 0xFF,0xFF,0xFF,0xFF,0xF5,0xFF,0xFF,0xFF, +0xD7,0xFF,0xFA,0xFF,0xFE,0xFF,0xFF,0xFF, 0xFD,0xFF,0xFF,0xFF,0xAF,0xF7,0xFF,0xFF, +0xFF,0xEB,0xFF,0xFF,0xFF,0xAF,0xFF,0xC4, 0xFF,0xF7,0xFF,0xFF,0xEF,0xFF,0xFF,0xFF, +0xFF,0x5F,0xFF,0xFF,0xFF,0xFF,0xD7,0xFF, 0xFF,0xFF,0xFF,0xFF,0xEB,0xFF,0xFB,0x7A, +0xDF,0xF7,0xFD,0xFF,0xFF,0xFE,0xBF,0xFF, 0xFF,0x7F,0xFF,0xAF,0xFF,0xFF,0xFF,0xF7, +0xEF,0xE3,0xFF,0xDD,0xD2,0xFF,0xDF,0xFF, 0xFF,0xF2,0xFC,0xBF,0xCB,0xF6,0xFD,0xBF, +0x2F,0xCB,0xFF,0x7F,0xDF,0xDE,0xAF,0xFF, 0xDA,0xEE,0xBF,0xAF,0xE9,0xFA,0xF4,0xBD, +0xAF,0x5A,0xAE,0xBB,0xAB,0x6B,0xDA,0xDE, 0xBF,0xAD,0xD7,0x5E,0xFF,0xFF,0xBF,0xFC, +0xFF,0xDF,0xFD,0xFF,0xFF,0xFF,0xFF,0xDF, 0xF7,0xFF,0xFF,0xFF,0xFF,0xFD,0xFF,0xFA, +0x1F,0xFF,0xFE,0xFB,0xEF,0xBF,0xFD,0xFF, 0xFD,0xBD,0x77,0xFF,0xFF,0xFF,0xFF,0x9D, +0xEF,0xFF,0xFF,0xFF,0xEF,0x7D,0xFF,0xFB, 0xFE,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEE, 0xBF,0xE4,0xFB,0xFF,0xFE,0x3F,0xFE,0xFF, +0xFF,0xFF,0xFF,0xAF,0xEA,0xFE,0xBF,0xAF, 0xEB,0xFA,0xFE,0xFF,0xFF,0xFF,0x55,0xF6, +0xFF,0xFE,0xF7,0xFF,0x7F,0xFF,0xEB,0xF7, 0x5F,0xC5,0xFD,0x7F,0x5F,0xD7,0xF5,0xFF, +0x6F,0xFB,0xFF,0x8A,0xFF,0xFF,0xFF,0xFF, 0xEB,0xFF,0xFF,0xFF,0xFF,0xFB,0xBF,0xBF, +0xEF,0xFB,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF, 0x77,0xDF,0xFB,0xFF,0xFD,0x7F,0xEF,0xFF, +0xFF,0xFF,0xBF,0x7F,0xFF,0xDF,0xBF,0xFF, 0xFB,0xFF,0xFF,0xFF,0xFE,0xEF,0xDF,0xFF, +0xFE,0xFF,0x9F,0xEF,0x7D,0xFF,0xF7,0xFF, 0x7F,0xFF,0xFF,0xDF,0xF7,0xFD,0xFF,0xEF, +0xDF,0xFF,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF,0xFB, +0xFD,0xFF,0xBF,0xDF,0xD1,0xFF,0xF8,0x3B, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0x7E,0xDB,0xFD,0xFF,0x77,0xDB,0xB7,0x7D, 0xBF,0xFB,0xFF,0xF8,0x7F,0xED,0x7B,0x5E, +0xFF,0xFE,0xFF,0xFF,0x4F,0xD7,0xFD,0x7F, 0xDF,0xD7,0xF5,0xFF,0x7F,0xFF,0xFF,0xFF, +0xF2,0x3F,0xFE,0xFF,0xBF,0xFF,0xFF,0xFF, 0xFF,0xBF,0xEF,0xFE,0xFF,0x3B,0xEE,0xFF, +0xFC,0xEF,0xFF,0xFF,0xFF,0x85,0xFF,0xFD, 0xFE,0xFF,0xF5,0xFF,0xFF,0xFE,0xFF,0xDF, +0xFB,0xFF,0x5F,0xBF,0xFF,0xFD,0xFF,0xFF, 0xFF,0xFF,0xA8,0xFF,0xFF,0x9F,0x9E,0xFF, +0xFF,0xFF,0x7F,0xF3,0xFF,0xFF,0xCF,0xFF, 0xF7,0xFD,0xFF,0x7F,0xFF,0xFF,0xFC,0x16, +0xBF,0xCF,0xA3,0xE5,0xEF,0x7F,0xFF,0xF3, 0xE4,0xFF,0xCF,0x93,0xFC,0xFF,0x3F,0xCF, +0xFF,0xFF,0xFF,0xD6,0x0F,0x7D,0xBF,0x6E, 0xFB,0xF4,0xFC,0xAF,0x6D,0xDB,0x77,0xB7, +0x6D,0xDB,0xF6,0xFD,0xBF,0xFF,0xFF,0xFF, 0xBF,0x9B,0xFA,0xDE,0xB7,0xB7,0xED,0xF9, +0x7E,0xB7,0xAC,0xEB,0xD6,0xB3,0xAD,0xEB, 0x7A,0xDF,0xFF,0xFF,0xFF,0xD8,0xBF,0xFF, +0xB7,0xED,0x9F,0x6F,0xDD,0xF7,0x68,0xDB, 0x37,0xB3,0x6C,0xDB,0x36,0xCD,0xB3,0x7F, +0xFF,0x7F,0xF5,0x6F,0xFD,0xEF,0x79,0x3D, 0xF7,0x93,0xE4,0x7A,0x9E,0xAD,0xEA,0x7A, +0x9E,0xF7,0xBD,0xEF,0xFF,0xFF,0xFF,0x76, 0x7F,0xFB,0xC6,0xFF,0xBB,0xEF,0xDA,0xFE, +0xFD,0xBF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFF,0xFF,0xFB,0xFF,0xA5,0xFF,0xFD,0xAB, +0x6F,0x78,0xDE,0x17,0x8F,0x79,0xDF,0xFD, 0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0xFF,0xFB, +0xFF,0xFB,0xFF,0xEF,0xFB,0xEF,0xFB,0xFE, 0xFF,0xBB,0xDA,0xF3,0xEF,0x3B,0xCE,0xF3, +0xBC,0xEF,0x3F,0xCF,0xDF,0xFF,0xB7,0xFF, 0xFF,0xFF,0xCF,0x73,0xFF,0xBF,0xEF,0xFF, +0xF3,0xFF,0x3F,0xCF,0xF3,0xFC,0xFF,0x3D, 0xCF,0x9F,0xFE,0x07,0xFF,0xAF,0xEB,0xFE, +0xFD,0xBF,0xEF,0xEB,0xFA,0xFF,0xAF,0xEB, 0xFA,0xFE,0xBF,0xAF,0xFB,0xFE,0x3F,0xFB, +0x9B,0xFF,0x7F,0xDF,0xFF,0xF3,0xFE,0xFF, 0xDE,0xF7,0xBF,0x7B,0xDE,0xF7,0xBD,0xEF, +0x7B,0xFE,0xFF,0xFF,0xDF,0x3F,0xFE,0xFF, 0xB7,0xFF,0xEF,0xF7,0xFF,0xBF,0xED,0xFE, +0xDF,0xB7,0xED,0xFB,0x7E,0xDF,0xFF,0xFF, 0xFF,0xFD,0x5F,0xEF,0xEB,0xFA,0xFE,0xF5, +0xBF,0x6F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE,0xF8,0xFF,0xA8,0xFF, +0xFF,0xBF,0xEF,0xFB,0x6A,0xFB,0xB7,0xEF, 0xFB,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xBF, +0xEF,0xFB,0xFF,0xE0,0xFF,0xFF,0xFD,0x7F, 0x5C,0xD7,0x7D,0xDF,0xF3,0x5C,0xF5,0xCD, +0x73,0x5E,0xD7,0xB5,0xFD,0x7F,0xEF,0xFF, 0xDB,0xFF,0xFF,0xE2,0xF8,0xBE,0x2F,0x8F, +0xE7,0xF8,0xBE,0x6B,0xE2,0xF8,0xBE,0x2F, 0x8B,0xE2,0xF9,0xFE,0x7F,0xE7,0xFF,0xD7, +0xF5,0xFD,0x7F,0xFF,0xF7,0xF5,0xFD,0x7F, 0xD7,0xF5,0xFD,0x7F,0x5F,0xD7,0xF5,0xFF, +0xFF,0xFF,0x8F,0xFF,0xAF,0xEB,0xFA,0xFF, 0xFF,0xBF,0xEB,0xFA,0xFF,0x2F,0xEB,0xFA, +0xFE,0xBF,0xAF,0xEB,0xFF,0xFF,0xFE,0x5F, 0xFF,0x5F,0xFF,0xFF,0xFD,0xFF,0xFF,0xD7, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xBF,0xFE,0xB7,0xFD, +0xFF,0x7E,0xDF,0xF7,0xAD,0xFF,0x7F,0xF7, 0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0x7F, +0xF6,0x7F,0xFF,0xFF,0xFF,0xDB,0xF6,0xFC, 0xAF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xEC,0xBF,0xFF, 0xAF,0xEB,0xFA,0xF6,0xAB,0x8F,0xEB,0xFA, +0xF7,0xA5,0xEB,0xFA,0xBE,0xBF,0xAF,0xEB, 0xFA,0xFF,0x6D,0xFF,0xFF,0x7F,0xDF,0x33, +0xDD,0xFF,0x7F,0xFE,0xF7,0xFC,0x7F,0xFB, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xA9, +0xFF,0xFD,0xFF,0xFF,0xFE,0xFF,0xFF,0xDF, 0xFF,0xFF,0xEF,0xEF,0xFD,0xFF,0x7F,0xFF, +0xFF,0xFF,0xFF,0xFE,0xA7,0xFF,0xFF,0xFF, 0x77,0xDF,0xF7,0xFD,0x9F,0x7F,0xFE,0x77, +0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xAF,0xBF,0xAF,0xFF,0xF9,0xBE,0xBF, +0x8F,0xFB,0xFE,0xFE,0xEF,0xFB,0xFE,0xFF, 0xBF,0xEF,0xFB,0xFF,0xFF,0xFD,0xDF,0x6F, +0xEF,0xFF,0x7F,0xFF,0xBF,0xBF,0xDF,0xFF, 0xFC,0xFF,0xDF,0xF7,0xFD,0xEF,0x7F,0xDF, +0xFF,0xFF,0xFF,0x3F,0xF6,0xFF,0xCF,0xFF, 0xDB,0xFB,0xF7,0xFF,0xEB,0x7A,0xFF,0xFF, +0xFF,0xBF,0xEF,0xFB,0xFF,0xFF,0xFF,0xFE, 0x6D,0xFD,0xFF,0x5F,0xFB,0xFF,0xFF,0xF7, +0xFF,0x5F,0xF5,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xF8,0xFF,0xFB,0xFF, +0xFF,0xFD,0xFF,0xFF,0xFF,0xFF,0xE7,0xF6, 0xBF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF,0xFF, +0xFF,0xC9,0xFF,0xFF,0xFF,0xBD,0xFF,0xBF, 0xAF,0xEF,0xEF,0x3F,0xD1,0xFC,0x7F,0xFB, +0xC7,0xFF,0xFF,0xFF,0xFF,0xFF,0xE3,0xFF, 0xFF,0xFF,0xFF,0xFD,0xFF,0xFF,0x77,0xFF, +0xDF,0xB7,0xFD,0xF7,0xFD,0xF7,0xFF,0xFF, 0xFF,0xFF,0xFF,0x57,0xFF,0xF7,0xA5,0xFD, +0x3F,0xDF,0xBF,0xBF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xDF,0xFA,0xFD,0xFF,0xFF,0xFF,0xFE, +0x87,0xFF,0xE9,0xFF,0xFE,0xEF,0xBF,0xEF, 0xFE,0xFE,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFA,0x9F,0xFF,0x3F, 0xFF,0xFD,0xFD,0x57,0xDF,0xFD,0xF3,0xFF, +0xDF,0xFD,0xFF,0x5F,0xDF,0xF5,0xFD,0xFF, 0xFF,0xF9,0x8F,0xFF,0xFF,0xFF,0xEE,0x7F, +0xFF,0xFF,0xBF,0x5E,0xFE,0xEC,0xFB,0x3F, 0x7F,0x9F,0xEF,0xF9,0xFF,0xFF,0xCD,0x6B, +0xFF,0xFF,0xFF,0xC5,0xF3,0xFC,0xFA,0x38, 0xFF,0xAF,0x3F,0xEE,0x7F,0x9F,0xFF,0xD9, +0xFF,0xFF,0xFD,0x7A,0xF7,0xFF,0xF3,0xFF, 0xAF,0x6F,0xDB,0xF2,0xB9,0xE9,0xFB,0xFF, +0xFF,0xFF,0xFE,0xFF,0xFF,0xEF,0xFF,0xFB, 0xC5,0xBF,0xFF,0xEF,0xFF,0x5E,0xB7,0xAD, +0xCD,0x79,0x7C,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0x93,0xFF,0xEF, +0xEA,0xFE,0xBF,0xEF,0x5B,0xD2,0xCD,0xF5, 0x6D,0x77,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF, +0xFF,0xFF,0x66,0xFF,0xD5,0x65,0x7D,0x5F, 0x75,0x9D,0x65,0x7F,0xD6,0xFB,0x4F,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF6,0xC7, 0xFF,0xBF,0xEF,0xFA,0xFE,0xFF,0xBF,0xEB, +0xFF,0xDF,0xFF,0x7E,0xFF,0xFF,0xEF,0xFD, 0x7E,0xD7,0xFF,0x78,0xDF,0xFF,0x5F,0xDF, +0xF5,0xBF,0x7F,0xDF,0xC5,0xFF,0x3F,0xF6, 0x7E,0xFF,0x0F,0xEF,0xF2,0x3E,0xBF,0xFF, +0xFB,0x3F,0xFF,0xFB,0x7F,0xFF,0xB3,0xFE, 0xFB,0xF6,0xFD,0xFF,0xDA,0xF7,0xFD,0xFF, +0x7F,0xDF,0xF7,0xBF,0xFF,0xFA,0x7F,0xFF, 0xFF,0xFF,0xFF,0x9F,0xFF,0xF3,0xDC,0xF9, +0xBF,0xCE,0xE7,0xF9,0xFE,0x7F,0x9F,0xE7, 0xFF,0xFF,0xE2,0x7F,0xFE,0xFF,0xBF,0xEF, +0xEB,0xFA,0xFF,0x9F,0x67,0x1E,0xFF,0x8F, 0xE7,0xF8,0xFE,0x7F,0x8F,0xEF,0xFF,0xBD, +0xBF,0xFF,0xFB,0xFF,0xFF,0xDF,0xF7,0xFF, 0xFC,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFD,0xB3,0xFF,0xFF,0xEF, 0xFF,0xFF,0xBF,0xED,0xFF,0xFB,0xEE,0xFE, +0xFF,0xFF,0xEF,0xFF,0xFE,0xFF,0xFF,0xFF, 0xFF,0xB5,0xFF,0xB7,0xFD,0xFD,0x6E,0xFF, +0xFF,0xFE,0xFD,0x2F,0xD8,0xFE,0xBF,0x8F, 0xEB,0xF9,0xFE,0x3F,0xFF,0xFA,0xCF,0xFF, +0xE7,0xD9,0xFA,0xBF,0xDF,0x77,0xFC,0xFB, 0x3F,0xAB,0xFE,0xFF,0xBF,0xEF,0xFB,0xFE, +0xFF,0xFF,0xEE,0x1F,0xFF,0xDF,0xF7,0xFF, 0xFF,0xFF,0x5F,0x97,0x35,0xBF,0x5E,0xFE, +0xBF,0xEF,0xFF,0xF7,0xFD,0xFF,0xFF,0xFA, 0xBF,0xFF,0xBE,0x6F,0x9F,0xE7,0xF8,0xBE, +0x2F,0x8B,0x66,0x94,0x7D,0x9D,0xE7,0xF9, 0xFE,0x7F,0x9F,0xE7,0xF1,0x7F,0xFF,0xFF, +0xFF,0xF7,0xF5,0xFD,0x7F,0x5F,0xFB,0xFD, 0x9E,0xFF,0xFB,0xFE,0xFF,0xFF,0xEF,0xFF, +0xFF,0xA0,0xFF,0xFF,0xFF,0xBF,0xEF,0xEB, 0xFA,0xFE,0xBF,0xB7,0xF7,0xF7,0xFF,0xFF, +0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xDD,0xFF, 0xFD,0xFF,0xFF,0xFF,0xD7,0xFF,0xFF,0xFF, +0x7F,0xF5,0xFF,0xFF,0xEF,0xFF,0xFF,0xFF, 0xBF,0xFF,0xFF,0xAB,0xFE,0xFB,0xFE,0xFF, +0xF7,0xAF,0xFF,0xFF,0xDE,0xF7,0xEB,0x5F, 0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFF,0xFF, +0xB3,0xFF,0xC9,0xFE,0xFF,0xFF,0xFF,0xFF, 0xD6,0xFF,0xFF,0xCB,0xFF,0xFF,0xDF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFC,0x8F,0xFF,0xBA, 0xBE,0xBF,0xAF,0xEB,0x78,0xFE,0xB7,0xAD, +0x3A,0xFE,0xB7,0xAF,0xEB,0x7A,0xFE,0xBF, 0xAF,0xFF,0x9F,0xFF,0xFF,0xDF,0xFC,0xFF, +0xFF,0xFE,0xC3,0xFE,0xFF,0xFF,0x33,0xFC, 0xFF,0xBF,0xDF,0xF3,0xFF,0xFF,0xBB,0x9F, +0xFF,0xFF,0xFF,0xEB,0xDF,0xFF,0xFF,0xAF, 0xF7,0x6F,0xF9,0xBF,0xEF,0xFD,0xFF,0xFF, +0xFF,0xFF,0xFF,0xE3,0x7F,0xFF,0xFF,0xFF, 0xFB,0xFF,0xFF,0xBF,0xFD,0xFB,0xF7,0xFF, +0xDF,0xF7,0xFF,0xFE,0xEF,0x5F,0xBD,0xFF, 0xFA,0xFF,0xF8,0xFF,0xBF,0xAF,0xFB,0xFE, +0xFE,0x3F,0xEF,0xE8,0xFF,0xDF,0xF3,0xFD, 0xFF,0xFF,0xFF,0xFF,0xFF,0xED,0xFF,0xFB, +0xFD,0xFF,0xAF,0xFF,0xFF,0xFE,0xFE,0xBF, 0xDB,0xFF,0xFF,0xFF,0xBF,0xFF,0xDF,0xFF, +0xFD,0xFF,0xCB,0xFF,0xFF,0xFF,0xFF,0xFF, 0xBF,0x6F,0xFF,0x7F,0xB7,0xB3,0xFF,0xFF, +0xDF,0xFF,0xFB,0xEF,0xFF,0xFF,0xFF,0x07, 0xFF,0xFB,0xFF,0xFF,0xFF,0xED,0xFF,0xF5, +0x7C,0xFF,0x7F,0xFE,0xFF,0xFF,0xEF,0xCF, 0xFF,0xFB,0xFF,0xFF,0x2F,0xFF,0xFF,0xFF, +0xFF,0xF3,0xFF,0xFB,0xFF,0xFE,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF, +0xFD,0x1B,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7C,0xFF,0xFF,0xFF,0xFF, +0xEF,0xFF,0xFF,0xFF,0xFF,0xFB,0xBF,0x7F, 0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xDB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, 0xFF,0xFF,0xF0,0x7F,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF,0xDF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0xBF,0xFE, +0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xEF,0xFE,0xFF,0xBF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xEF,0xFA,0xB5,0xFF,0xFF,0xFF, 0xF7,0xF7,0xFF,0xFF,0xFF,0xFF,0xDF,0xFB, +0xFC,0xFF,0xFF,0xFE,0xFF,0x7F,0xDF,0xBF, 0xFF,0xCB,0xBF,0xF9,0xFE,0x7F,0x9F,0xE7, +0xF9,0xFE,0x7F,0x97,0xE1,0xFE,0x79,0x9F, 0xE7,0xFD,0xFE,0x7F,0xDF,0xFE,0x37,0xFF, +0xFB,0xDE,0xDE,0xBD,0xEF,0xF3,0xFE,0xFB, 0xAF,0xEB,0xFE,0xFF,0xFF,0xCF,0xFF,0xFE, +0xFF,0xBF,0xFF,0x8F,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xE7,0xF9,0x5E,0x7F,0xEF,0xFB, +0xDA,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFD, 0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xDF, +0xFF,0xFF,0x7F,0xFF,0xFF,0xF7,0xFB,0x7F, 0xFF,0xFF,0xFF,0xFF,0xFC,0x3F,0xFF,0xBF, +0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0x7B,0x7F, 0xBF,0xEF,0xFB,0xFE,0xFF,0xB5,0xEF,0xFB, +0xBF,0xFA,0x7F,0xFC,0xFF,0x3F,0xCF,0xF3, 0xFC,0xFF,0x3F,0xCF,0xBC,0xFF,0x3F,0xEF, +0xF3,0xFC,0xFE,0x3F,0xCF,0xFF,0xEE,0xEF, 0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0x6A,0xD7, +0xB7,0xFB,0xF8,0xFF,0xB7,0xEF,0xBA,0xFE, 0xFF,0xBF,0x7F,0xE9,0xFF,0xF9,0x7E,0x5F, +0x97,0xE5,0xF9,0xFE,0x7F,0xBF,0xF9,0x7E, 0x5F,0x9F,0xE5,0xFB,0xFE,0x5F,0xB7,0xFF, +0xA3,0xFF,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7, 0xFD,0xFF,0x5E,0xF7,0x7D,0xFF,0x77,0xDF, +0xF7,0xFD,0xFF,0x7F,0xFF,0xD7,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0xDF,0xFB,0x7F, +0xFF,0xFF,0xEF,0xFF,0xFE,0xFB,0xFF,0xFF, 0xBF,0xFE,0x8F,0xFF,0xDF,0xF7,0xFD,0xFD, +0x7F,0xDF,0xF7,0xFD,0x3E,0xDF,0xF5,0xBD, 0xFF,0x7F,0xDF,0xF7,0xFD,0xF7,0xFF,0x9F, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFD,0xFF,0xBE,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFD,0x3F,0xFF,0xDF,0xF7, 0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0xCF, +0x77,0xFC,0xFF,0x5F,0xDF,0xF7,0xFD,0xFF, 0xF4,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFD,0xFF,0xFF,0xFF,0xEE,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xED,0xFB,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xE9,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFB,0xFF,0xFF,0xFF,0xD3,0xFF,0xFF, +0xBF,0x3F,0xFB,0xFF,0xFF,0xFF,0xFB,0xF3, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xF7, 0xFF,0xFF,0xFF,0xFF,0x17,0xFF,0xFF,0xFF, +0xDF,0xFF,0xFD,0xFF,0xFF,0xFF,0xFF,0xFF, 0xDF,0xDF,0xFF,0xFD,0xFF,0xFF,0xDF,0xF7, +0xFF,0x4F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFD, +0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0x9F,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF, 0xFF,0xFF,0x7A,0x3F,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF2, +0x7F,0xFF,0xFB,0xFE,0xFF,0xBF,0xEF,0xF8, 0xFE,0xFF,0xBF,0xFB,0xFE,0xFF,0x8F,0xEC, +0xFB,0xFE,0xFF,0xBF,0xF8,0xF7,0xFE,0xFF, 0xBF,0xEF,0xFB,0xFE,0xFD,0xBF,0xCF,0xEC, +0xFF,0x3F,0xEF,0xDB,0xF8,0xFF,0xBF,0xCF, 0xFF,0xF9,0xFF,0xFF,0xBF,0xFF,0xFB,0xFF, +0xFF,0xFF,0xEF,0xFB,0xDF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xBB,0xFF, +0xEF,0xFB,0xFE,0xEF,0xBF,0xEE,0xEB,0xFB, 0xFE,0xFF,0xEF,0xFE,0xEE,0xBF,0xFE,0xEB, +0xFF,0xEF,0xFF,0x17,0xFF,0x7E,0xEB,0xBB, 0xFE,0xBF,0xBE,0xFB,0xEF,0x5B,0xF7,0xBD, +0xFB,0xCF,0xBF,0xBF,0xBB,0xFB,0x7E,0xCC, 0xEF,0xFF + +}; diff --git a/linux/drivers/media/video/dabusb.c b/linux/drivers/media/video/dabusb.c new file mode 100644 index 000000000..b9ba95f5e --- /dev/null +++ b/linux/drivers/media/video/dabusb.c @@ -0,0 +1,874 @@ +/*****************************************************************************/ + +/* + * dabusb.c -- dab usb driver. + * + * Copyright (C) 1999 Deti Fliegl (deti@fliegl.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * + * $Id: dabusb.c,v 1.54 2000/07/24 21:39:39 deti Exp $ + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dabusb.h" +#include "dabfirmware.h" + +/* + * Version Information + */ +#define DRIVER_VERSION "v1.54" +#define DRIVER_AUTHOR "Deti Fliegl, deti@fliegl.de" +#define DRIVER_DESC "DAB-USB Interface Driver for Linux (c)1999" + +/* --------------------------------------------------------------------- */ + +#ifdef CONFIG_USB_DYNAMIC_MINORS +#define NRDABUSB 256 +#else +#define NRDABUSB 4 +#endif + +/*-------------------------------------------------------------------*/ + +static dabusb_t dabusb[NRDABUSB]; +static int buffers = 256; +static struct usb_driver dabusb_driver; + +/*-------------------------------------------------------------------*/ + +static int dabusb_add_buf_tail (pdabusb_t s, struct list_head *dst, struct list_head *src) +{ + unsigned long flags; + struct list_head *tmp; + int ret = 0; + + spin_lock_irqsave (&s->lock, flags); + + if (list_empty (src)) { + // no elements in source buffer + ret = -1; + goto err; + } + tmp = src->next; + list_move_tail (tmp, dst); + + err: spin_unlock_irqrestore (&s->lock, flags); + return ret; +} +/*-------------------------------------------------------------------*/ +#ifdef DEBUG +static void dump_urb (struct urb *urb) +{ + dbg("urb :%p", urb); + dbg("dev :%p", urb->dev); + dbg("pipe :%08X", urb->pipe); + dbg("status :%d", urb->status); + dbg("transfer_flags :%08X", urb->transfer_flags); + dbg("transfer_buffer :%p", urb->transfer_buffer); + dbg("transfer_buffer_length:%d", urb->transfer_buffer_length); + dbg("actual_length :%d", urb->actual_length); + dbg("setup_packet :%p", urb->setup_packet); + dbg("start_frame :%d", urb->start_frame); + dbg("number_of_packets :%d", urb->number_of_packets); + dbg("interval :%d", urb->interval); + dbg("error_count :%d", urb->error_count); + dbg("context :%p", urb->context); + dbg("complete :%p", urb->complete); +} +#endif +/*-------------------------------------------------------------------*/ +static int dabusb_cancel_queue (pdabusb_t s, struct list_head *q) +{ + unsigned long flags; + pbuff_t b; + + dbg("dabusb_cancel_queue"); + + spin_lock_irqsave (&s->lock, flags); + + list_for_each_entry(b, q, buff_list) { +#ifdef DEBUG + dump_urb(b->purb); +#endif + usb_unlink_urb (b->purb); + } + spin_unlock_irqrestore (&s->lock, flags); + return 0; +} +/*-------------------------------------------------------------------*/ +static int dabusb_free_queue (struct list_head *q) +{ + struct list_head *tmp; + struct list_head *p; + pbuff_t b; + + dbg("dabusb_free_queue"); + for (p = q->next; p != q;) { + b = list_entry (p, buff_t, buff_list); + +#ifdef DEBUG + dump_urb(b->purb); +#endif + kfree(b->purb->transfer_buffer); + usb_free_urb(b->purb); + tmp = p->next; + list_del (p); + kfree (b); + p = tmp; + } + + return 0; +} +/*-------------------------------------------------------------------*/ +static int dabusb_free_buffers (pdabusb_t s) +{ + unsigned long flags; + dbg("dabusb_free_buffers"); + + spin_lock_irqsave(&s->lock, flags); + + dabusb_free_queue (&s->free_buff_list); + dabusb_free_queue (&s->rec_buff_list); + + spin_unlock_irqrestore(&s->lock, flags); + + s->got_mem = 0; + return 0; +} +/*-------------------------------------------------------------------*/ +static void dabusb_iso_complete (struct urb *purb, struct pt_regs *regs) +{ + pbuff_t b = purb->context; + pdabusb_t s = b->s; + int i; + int len; + int dst = 0; + void *buf = purb->transfer_buffer; + + dbg("dabusb_iso_complete"); + + // process if URB was not killed + if (purb->status != -ENOENT) { + unsigned int pipe = usb_rcvisocpipe (purb->dev, _DABUSB_ISOPIPE); + int pipesize = usb_maxpacket (purb->dev, pipe, usb_pipeout (pipe)); + for (i = 0; i < purb->number_of_packets; i++) + if (!purb->iso_frame_desc[i].status) { + len = purb->iso_frame_desc[i].actual_length; + if (len <= pipesize) { + memcpy (buf + dst, buf + purb->iso_frame_desc[i].offset, len); + dst += len; + } + else + err("dabusb_iso_complete: invalid len %d", len); + } + else + warn("dabusb_iso_complete: corrupted packet status: %d", purb->iso_frame_desc[i].status); + if (dst != purb->actual_length) + err("dst!=purb->actual_length:%d!=%d", dst, purb->actual_length); + } + + if (atomic_dec_and_test (&s->pending_io) && !s->remove_pending && s->state != _stopped) { + s->overruns++; + err("overrun (%d)", s->overruns); + } + wake_up (&s->wait); +} +/*-------------------------------------------------------------------*/ +static int dabusb_alloc_buffers (pdabusb_t s) +{ + int buffers = 0; + pbuff_t b; + unsigned int pipe = usb_rcvisocpipe (s->usbdev, _DABUSB_ISOPIPE); + int pipesize = usb_maxpacket (s->usbdev, pipe, usb_pipeout (pipe)); + int packets = _ISOPIPESIZE / pipesize; + int transfer_buffer_length = packets * pipesize; + int i; + + dbg("dabusb_alloc_buffers pipesize:%d packets:%d transfer_buffer_len:%d", + pipesize, packets, transfer_buffer_length); + + while (buffers < (s->total_buffer_size << 10)) { + b = (pbuff_t) kzalloc (sizeof (buff_t), GFP_KERNEL); + if (!b) { + err("kzalloc(sizeof(buff_t))==NULL"); + goto err; + } + b->s = s; + b->purb = usb_alloc_urb(packets, GFP_KERNEL); + if (!b->purb) { + err("usb_alloc_urb == NULL"); + kfree (b); + goto err; + } + + b->purb->transfer_buffer = kmalloc (transfer_buffer_length, GFP_KERNEL); + if (!b->purb->transfer_buffer) { + kfree (b->purb); + kfree (b); + err("kmalloc(%d)==NULL", transfer_buffer_length); + goto err; + } + + b->purb->transfer_buffer_length = transfer_buffer_length; + b->purb->number_of_packets = packets; + b->purb->complete = dabusb_iso_complete; + b->purb->context = b; + b->purb->dev = s->usbdev; + b->purb->pipe = pipe; + b->purb->transfer_flags = URB_ISO_ASAP; + + for (i = 0; i < packets; i++) { + b->purb->iso_frame_desc[i].offset = i * pipesize; + b->purb->iso_frame_desc[i].length = pipesize; + } + + buffers += transfer_buffer_length; + list_add_tail (&b->buff_list, &s->free_buff_list); + } + s->got_mem = buffers; + + return 0; + + err: + dabusb_free_buffers (s); + return -ENOMEM; +} +/*-------------------------------------------------------------------*/ +static int dabusb_bulk (pdabusb_t s, pbulk_transfer_t pb) +{ + int ret; + unsigned int pipe; + int actual_length; + + dbg("dabusb_bulk"); + + if (!pb->pipe) + pipe = usb_rcvbulkpipe (s->usbdev, 2); + else + pipe = usb_sndbulkpipe (s->usbdev, 2); + + ret=usb_bulk_msg(s->usbdev, pipe, pb->data, pb->size, &actual_length, 100); + if(ret<0) { + err("dabusb: usb_bulk_msg failed(%d)",ret); + + if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) { + err("set_interface failed"); + return -EINVAL; + } + + } + + if( ret == -EPIPE ) { + warn("CLEAR_FEATURE request to remove STALL condition."); + if(usb_clear_halt(s->usbdev, usb_pipeendpoint(pipe))) + err("request failed"); + } + + pb->size = actual_length; + return ret; +} +/* --------------------------------------------------------------------- */ +static int dabusb_writemem (pdabusb_t s, int pos, unsigned char *data, int len) +{ + int ret; + unsigned char *transfer_buffer = kmalloc (len, GFP_KERNEL); + + if (!transfer_buffer) { + err("dabusb_writemem: kmalloc(%d) failed.", len); + return -ENOMEM; + } + + memcpy (transfer_buffer, data, len); + + ret=usb_control_msg(s->usbdev, usb_sndctrlpipe( s->usbdev, 0 ), 0xa0, 0x40, pos, 0, transfer_buffer, len, 300); + + kfree (transfer_buffer); + return ret; +} +/* --------------------------------------------------------------------- */ +static int dabusb_8051_reset (pdabusb_t s, unsigned char reset_bit) +{ + dbg("dabusb_8051_reset: %d",reset_bit); + return dabusb_writemem (s, CPUCS_REG, &reset_bit, 1); +} +/* --------------------------------------------------------------------- */ +static int dabusb_loadmem (pdabusb_t s, const char *fname) +{ + int ret; + PINTEL_HEX_RECORD ptr = firmware; + + dbg("Enter dabusb_loadmem (internal)"); + + ret = dabusb_8051_reset (s, 1); + while (ptr->Type == 0) { + + dbg("dabusb_writemem: %04X %p %d)", ptr->Address, ptr->Data, ptr->Length); + + ret = dabusb_writemem (s, ptr->Address, ptr->Data, ptr->Length); + if (ret < 0) { + err("dabusb_writemem failed (%d %04X %p %d)", ret, ptr->Address, ptr->Data, ptr->Length); + break; + } + ptr++; + } + ret = dabusb_8051_reset (s, 0); + + dbg("dabusb_loadmem: exit"); + + return ret; +} +/* --------------------------------------------------------------------- */ +static int dabusb_fpga_clear (pdabusb_t s, pbulk_transfer_t b) +{ + b->size = 4; + b->data[0] = 0x2a; + b->data[1] = 0; + b->data[2] = 0; + b->data[3] = 0; + + dbg("dabusb_fpga_clear"); + + return dabusb_bulk (s, b); +} +/* --------------------------------------------------------------------- */ +static int dabusb_fpga_init (pdabusb_t s, pbulk_transfer_t b) +{ + b->size = 4; + b->data[0] = 0x2c; + b->data[1] = 0; + b->data[2] = 0; + b->data[3] = 0; + + dbg("dabusb_fpga_init"); + + return dabusb_bulk (s, b); +} +/* --------------------------------------------------------------------- */ +static int dabusb_fpga_download (pdabusb_t s, const char *fname) +{ + pbulk_transfer_t b = kmalloc (sizeof (bulk_transfer_t), GFP_KERNEL); + unsigned int blen, n; + int ret; + unsigned char *buf = bitstream; + + dbg("Enter dabusb_fpga_download (internal)"); + + if (!b) { + err("kmalloc(sizeof(bulk_transfer_t))==NULL"); + return -ENOMEM; + } + + b->pipe = 1; + ret = dabusb_fpga_clear (s, b); + mdelay (10); + blen = buf[73] + (buf[72] << 8); + + dbg("Bitstream len: %i", blen); + + b->data[0] = 0x2b; + b->data[1] = 0; + b->data[2] = 0; + b->data[3] = 60; + + for (n = 0; n <= blen + 60; n += 60) { + // some cclks for startup + b->size = 64; + memcpy (b->data + 4, buf + 74 + n, 60); + ret = dabusb_bulk (s, b); + if (ret < 0) { + err("dabusb_bulk failed."); + break; + } + mdelay (1); + } + + ret = dabusb_fpga_init (s, b); + kfree (b); + + dbg("exit dabusb_fpga_download"); + + return ret; +} + +static int dabusb_stop (pdabusb_t s) +{ + dbg("dabusb_stop"); + + s->state = _stopped; + dabusb_cancel_queue (s, &s->rec_buff_list); + + dbg("pending_io: %d", s->pending_io.counter); + + s->pending_io.counter = 0; + return 0; +} + +static int dabusb_startrek (pdabusb_t s) +{ + if (!s->got_mem && s->state != _started) { + + dbg("dabusb_startrek"); + + if (dabusb_alloc_buffers (s) < 0) + return -ENOMEM; + dabusb_stop (s); + s->state = _started; + s->readptr = 0; + } + + if (!list_empty (&s->free_buff_list)) { + pbuff_t end; + int ret; + + while (!dabusb_add_buf_tail (s, &s->rec_buff_list, &s->free_buff_list)) { + + dbg("submitting: end:%p s->rec_buff_list:%p", s->rec_buff_list.prev, &s->rec_buff_list); + + end = list_entry (s->rec_buff_list.prev, buff_t, buff_list); + + ret = usb_submit_urb (end->purb, GFP_KERNEL); + if (ret) { + err("usb_submit_urb returned:%d", ret); + if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list)) + err("startrek: dabusb_add_buf_tail failed"); + break; + } + else + atomic_inc (&s->pending_io); + } + dbg("pending_io: %d",s->pending_io.counter); + } + + return 0; +} + +static ssize_t dabusb_read (struct file *file, char __user *buf, size_t count, loff_t * ppos) +{ + pdabusb_t s = (pdabusb_t) file->private_data; + unsigned long flags; + unsigned ret = 0; + int rem; + int cnt; + pbuff_t b; + struct urb *purb = NULL; + + dbg("dabusb_read"); + + if (*ppos) + return -ESPIPE; + + if (s->remove_pending) + return -EIO; + + + if (!s->usbdev) + return -EIO; + + while (count > 0) { + dabusb_startrek (s); + + spin_lock_irqsave (&s->lock, flags); + + if (list_empty (&s->rec_buff_list)) { + + spin_unlock_irqrestore(&s->lock, flags); + + err("error: rec_buf_list is empty"); + goto err; + } + + b = list_entry (s->rec_buff_list.next, buff_t, buff_list); + purb = b->purb; + + spin_unlock_irqrestore(&s->lock, flags); + + if (purb->status == -EINPROGRESS) { + if (file->f_flags & O_NONBLOCK) // return nonblocking + { + if (!ret) + ret = -EAGAIN; + goto err; + } + + interruptible_sleep_on (&s->wait); + + if (signal_pending (current)) { + if (!ret) + ret = -ERESTARTSYS; + goto err; + } + + spin_lock_irqsave (&s->lock, flags); + + if (list_empty (&s->rec_buff_list)) { + spin_unlock_irqrestore(&s->lock, flags); + err("error: still no buffer available."); + goto err; + } + spin_unlock_irqrestore(&s->lock, flags); + s->readptr = 0; + } + if (s->remove_pending) { + ret = -EIO; + goto err; + } + + rem = purb->actual_length - s->readptr; // set remaining bytes to copy + + if (count >= rem) + cnt = rem; + else + cnt = count; + + dbg("copy_to_user:%p %p %d",buf, purb->transfer_buffer + s->readptr, cnt); + + if (copy_to_user (buf, purb->transfer_buffer + s->readptr, cnt)) { + err("read: copy_to_user failed"); + if (!ret) + ret = -EFAULT; + goto err; + } + + s->readptr += cnt; + count -= cnt; + buf += cnt; + ret += cnt; + + if (s->readptr == purb->actual_length) { + // finished, take next buffer + if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list)) + err("read: dabusb_add_buf_tail failed"); + s->readptr = 0; + } + } + err: //mutex_unlock(&s->mutex); + return ret; +} + +static int dabusb_open (struct inode *inode, struct file *file) +{ + int devnum = iminor(inode); + pdabusb_t s; + + if (devnum < DABUSB_MINOR || devnum >= (DABUSB_MINOR + NRDABUSB)) + return -EIO; + + s = &dabusb[devnum - DABUSB_MINOR]; + + dbg("dabusb_open"); + mutex_lock(&s->mutex); + + while (!s->usbdev || s->opened) { + mutex_unlock(&s->mutex); + + if (file->f_flags & O_NONBLOCK) { + return -EBUSY; + } + msleep_interruptible(500); + + if (signal_pending (current)) { + return -EAGAIN; + } + mutex_lock(&s->mutex); + } + if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) { + mutex_unlock(&s->mutex); + err("set_interface failed"); + return -EINVAL; + } + s->opened = 1; + mutex_unlock(&s->mutex); + + file->f_pos = 0; + file->private_data = s; + + return nonseekable_open(inode, file); +} + +static int dabusb_release (struct inode *inode, struct file *file) +{ + pdabusb_t s = (pdabusb_t) file->private_data; + + dbg("dabusb_release"); + + mutex_lock(&s->mutex); + dabusb_stop (s); + dabusb_free_buffers (s); + mutex_unlock(&s->mutex); + + if (!s->remove_pending) { + if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) + err("set_interface failed"); + } + else + wake_up (&s->remove_ok); + + s->opened = 0; + return 0; +} + +static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + pdabusb_t s = (pdabusb_t) file->private_data; + pbulk_transfer_t pbulk; + int ret = 0; + int version = DABUSB_VERSION; + + dbg("dabusb_ioctl"); + + if (s->remove_pending) + return -EIO; + + mutex_lock(&s->mutex); + + if (!s->usbdev) { + mutex_unlock(&s->mutex); + return -EIO; + } + + switch (cmd) { + + case IOCTL_DAB_BULK: + pbulk = (pbulk_transfer_t) kmalloc (sizeof (bulk_transfer_t), GFP_KERNEL); + + if (!pbulk) { + ret = -ENOMEM; + break; + } + + if (copy_from_user (pbulk, (void __user *) arg, sizeof (bulk_transfer_t))) { + ret = -EFAULT; + kfree (pbulk); + break; + } + + ret=dabusb_bulk (s, pbulk); + if(ret==0) + if (copy_to_user((void __user *)arg, pbulk, + sizeof(bulk_transfer_t))) + ret = -EFAULT; + kfree (pbulk); + break; + + case IOCTL_DAB_OVERRUNS: + ret = put_user (s->overruns, (unsigned int __user *) arg); + break; + + case IOCTL_DAB_VERSION: + ret = put_user (version, (unsigned int __user *) arg); + break; + + default: + ret = -ENOIOCTLCMD; + break; + } + mutex_unlock(&s->mutex); + return ret; +} + +static struct file_operations dabusb_fops = +{ + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = dabusb_read, + .ioctl = dabusb_ioctl, + .open = dabusb_open, + .release = dabusb_release, +}; + +static struct usb_class_driver dabusb_class = { + .name = "dabusb%d", + .fops = &dabusb_fops, + .minor_base = DABUSB_MINOR, +}; + + +/* --------------------------------------------------------------------- */ +static int dabusb_probe (struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *usbdev = interface_to_usbdev(intf); + int retval; + pdabusb_t s; + + dbg("dabusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d", + le16_to_cpu(usbdev->descriptor.idVendor), + le16_to_cpu(usbdev->descriptor.idProduct), + intf->altsetting->desc.bInterfaceNumber); + + /* We don't handle multiple configurations */ + if (usbdev->descriptor.bNumConfigurations != 1) + return -ENODEV; + + if (intf->altsetting->desc.bInterfaceNumber != _DABUSB_IF && + le16_to_cpu(usbdev->descriptor.idProduct) == 0x9999) + return -ENODEV; + + + + s = &dabusb[intf->minor]; + + mutex_lock(&s->mutex); + s->remove_pending = 0; + s->usbdev = usbdev; + s->devnum = intf->minor; + + if (usb_reset_configuration (usbdev) < 0) { + err("reset_configuration failed"); + goto reject; + } + if (le16_to_cpu(usbdev->descriptor.idProduct) == 0x2131) { + dabusb_loadmem (s, NULL); + goto reject; + } + else { + dabusb_fpga_download (s, NULL); + + if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) { + err("set_interface failed"); + goto reject; + } + } + dbg("bound to interface: %d", intf->altsetting->desc.bInterfaceNumber); + usb_set_intfdata (intf, s); + mutex_unlock(&s->mutex); + + retval = usb_register_dev(intf, &dabusb_class); + if (retval) { + usb_set_intfdata (intf, NULL); + return -ENOMEM; + } + + return 0; + + reject: + mutex_unlock(&s->mutex); + s->usbdev = NULL; + return -ENODEV; +} + +static void dabusb_disconnect (struct usb_interface *intf) +{ + wait_queue_t __wait; + pdabusb_t s = usb_get_intfdata (intf); + + dbg("dabusb_disconnect"); + + init_waitqueue_entry(&__wait, current); + + usb_set_intfdata (intf, NULL); + if (s) { + usb_deregister_dev (intf, &dabusb_class); + s->remove_pending = 1; + wake_up (&s->wait); + add_wait_queue(&s->remove_ok, &__wait); + set_current_state(TASK_UNINTERRUPTIBLE); + if (s->state == _started) + schedule(); + current->state = TASK_RUNNING; + remove_wait_queue(&s->remove_ok, &__wait); + + s->usbdev = NULL; + s->overruns = 0; + } +} + +static struct usb_device_id dabusb_ids [] = { + // { USB_DEVICE(0x0547, 0x2131) }, /* An2131 chip, no boot ROM */ + { USB_DEVICE(0x0547, 0x9999) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, dabusb_ids); + +static struct usb_driver dabusb_driver = { + .name = "dabusb", + .probe = dabusb_probe, + .disconnect = dabusb_disconnect, + .id_table = dabusb_ids, +}; + +/* --------------------------------------------------------------------- */ + +static int __init dabusb_init (void) +{ + int retval; + unsigned u; + + /* initialize struct */ + for (u = 0; u < NRDABUSB; u++) { + pdabusb_t s = &dabusb[u]; + memset (s, 0, sizeof (dabusb_t)); + mutex_init (&s->mutex); + s->usbdev = NULL; + s->total_buffer_size = buffers; + init_waitqueue_head (&s->wait); + init_waitqueue_head (&s->remove_ok); + spin_lock_init (&s->lock); + INIT_LIST_HEAD (&s->free_buff_list); + INIT_LIST_HEAD (&s->rec_buff_list); + } + + /* register misc device */ + retval = usb_register(&dabusb_driver); + if (retval) + goto out; + + dbg("dabusb_init: driver registered"); + + info(DRIVER_VERSION ":" DRIVER_DESC); + +out: + return retval; +} + +static void __exit dabusb_cleanup (void) +{ + dbg("dabusb_cleanup"); + + usb_deregister (&dabusb_driver); +} + +/* --------------------------------------------------------------------- */ + +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_LICENSE("GPL"); + +module_param(buffers, int, 0); +MODULE_PARM_DESC (buffers, "Number of buffers (default=256)"); + +module_init (dabusb_init); +module_exit (dabusb_cleanup); + +/* --------------------------------------------------------------------- */ diff --git a/linux/drivers/media/video/dabusb.h b/linux/drivers/media/video/dabusb.h new file mode 100644 index 000000000..00eb34c86 --- /dev/null +++ b/linux/drivers/media/video/dabusb.h @@ -0,0 +1,85 @@ +#define _BULK_DATA_LEN 64 +typedef struct +{ + unsigned char data[_BULK_DATA_LEN]; + unsigned int size; + unsigned int pipe; +}bulk_transfer_t,*pbulk_transfer_t; + +#define DABUSB_MINOR 240 /* some unassigned USB minor */ +#define DABUSB_VERSION 0x1000 +#define IOCTL_DAB_BULK _IOWR('d', 0x30, bulk_transfer_t) +#define IOCTL_DAB_OVERRUNS _IOR('d', 0x15, int) +#define IOCTL_DAB_VERSION _IOR('d', 0x3f, int) + +#ifdef __KERNEL__ + +typedef enum { _stopped=0, _started } driver_state_t; + +typedef struct +{ + struct mutex mutex; + struct usb_device *usbdev; + wait_queue_head_t wait; + wait_queue_head_t remove_ok; + spinlock_t lock; + atomic_t pending_io; + driver_state_t state; + int remove_pending; + int got_mem; + int total_buffer_size; + unsigned int overruns; + int readptr; + int opened; + int devnum; + struct list_head free_buff_list; + struct list_head rec_buff_list; +} dabusb_t,*pdabusb_t; + +typedef struct +{ + pdabusb_t s; + struct urb *purb; + struct list_head buff_list; +} buff_t,*pbuff_t; + +typedef struct +{ + wait_queue_head_t wait; +} bulk_completion_context_t, *pbulk_completion_context_t; + + +#define _DABUSB_IF 2 +#define _DABUSB_ISOPIPE 0x09 +#define _ISOPIPESIZE 16384 + +#define _BULK_DATA_LEN 64 +// Vendor specific request code for Anchor Upload/Download +// This one is implemented in the core +#define ANCHOR_LOAD_INTERNAL 0xA0 + +// EZ-USB Control and Status Register. Bit 0 controls 8051 reset +#define CPUCS_REG 0x7F92 +#define _TOTAL_BUFFERS 384 + +#define MAX_INTEL_HEX_RECORD_LENGTH 16 + +#ifndef _BYTE_DEFINED +#define _BYTE_DEFINED +typedef unsigned char BYTE; +#endif // !_BYTE_DEFINED + +#ifndef _WORD_DEFINED +#define _WORD_DEFINED +typedef unsigned short WORD; +#endif // !_WORD_DEFINED + +typedef struct _INTEL_HEX_RECORD +{ + BYTE Length; + WORD Address; + BYTE Type; + BYTE Data[MAX_INTEL_HEX_RECORD_LENGTH]; +} INTEL_HEX_RECORD, *PINTEL_HEX_RECORD; + +#endif diff --git a/linux/drivers/media/video/dsbr100.c b/linux/drivers/media/video/dsbr100.c new file mode 100644 index 000000000..a4cee9886 --- /dev/null +++ b/linux/drivers/media/video/dsbr100.c @@ -0,0 +1,430 @@ +/* A driver for the D-Link DSB-R100 USB radio. The R100 plugs + into both the USB and an analog audio input, so this thing + only deals with initialisation and frequency setting, the + audio data has to be handled by a sound driver. + + Major issue: I can't find out where the device reports the signal + strength, and indeed the windows software appearantly just looks + at the stereo indicator as well. So, scanning will only find + stereo stations. Sad, but I can't help it. + + Also, the windows program sends oodles of messages over to the + device, and I couldn't figure out their meaning. My suspicion + is that they don't have any:-) + + You might find some interesting stuff about this module at + http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr + + Copyright (c) 2000 Markus Demleitner + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + History: + + Version 0.40: + Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing + + Version 0.30: + Markus: Updates for 2.5.x kernel and more ISO compliant source + + Version 0.25: + PSL and Markus: Cleanup, radio now doesn't stop on device close + + Version 0.24: + Markus: Hope I got these silly VIDEO_TUNER_LOW issues finally + right. Some minor cleanup, improved standalone compilation + + Version 0.23: + Markus: Sign extension bug fixed by declaring transfer_buffer unsigned + + Version 0.22: + Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns, + thanks to Mike Cox for pointing the problem out. + + Version 0.21: + Markus: Minor cleanup, warnings if something goes wrong, lame attempt + to adhere to Documentation/CodingStyle + + Version 0.2: + Brad Hards : Fixes to make it work as non-module + Markus: Copyright clarification + + Version 0.01: Markus: initial release + +*/ + + +#include +#include +#include +#include +#include +#include "compat.h" +#include +#include +#include + +/* + * Version Information + */ +#define DRIVER_VERSION "v0.40" +#define DRIVER_AUTHOR "Markus Demleitner " +#define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver" + +#define DSB100_VENDOR 0x04b4 +#define DSB100_PRODUCT 0x1002 + +/* Commands the device appears to understand */ +#define DSB100_TUNE 1 +#define DSB100_ONOFF 2 + +#define TB_LEN 16 + +/* Frequency limits in MHz -- these are European values. For Japanese +devices, that would be 76 and 91. */ +#define FREQ_MIN 87.5 +#define FREQ_MAX 108.0 +#define FREQ_MUL 16000 + + +static int usb_dsbr100_probe(struct usb_interface *intf, + const struct usb_device_id *id); +static void usb_dsbr100_disconnect(struct usb_interface *intf); +static int usb_dsbr100_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static int usb_dsbr100_open(struct inode *inode, struct file *file); +static int usb_dsbr100_close(struct inode *inode, struct file *file); + +static int radio_nr = -1; +module_param(radio_nr, int, 0); + +/* Data for one (physical) device */ +typedef struct { + struct usb_device *usbdev; + struct video_device *videodev; + unsigned char transfer_buffer[TB_LEN]; + int curfreq; + int stereo; + int users; + int removed; +} dsbr100_device; + + +/* File system interface */ +static struct file_operations usb_dsbr100_fops = { + .owner = THIS_MODULE, + .open = usb_dsbr100_open, + .release = usb_dsbr100_close, + .ioctl = usb_dsbr100_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +/* V4L interface */ +static struct video_device dsbr100_videodev_template= +{ + .owner = THIS_MODULE, + .name = "D-Link DSB-R 100", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_AZTECH, + .fops = &usb_dsbr100_fops, + .release = video_device_release, +}; + +static struct usb_device_id usb_dsbr100_device_table [] = { + { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table); + +/* USB subsystem interface */ +static struct usb_driver usb_dsbr100_driver = { + .name = "dsbr100", + .probe = usb_dsbr100_probe, + .disconnect = usb_dsbr100_disconnect, + .id_table = usb_dsbr100_device_table, +}; + +/* Low-level device interface begins here */ + +/* switch on radio */ +static int dsbr100_start(dsbr100_device *radio) +{ + if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + USB_REQ_GET_STATUS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + DSB100_ONOFF, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x01, 0x00, radio->transfer_buffer, 8, 300)<0) + return -1; + return (radio->transfer_buffer)[0]; +} + + +/* switch off radio */ +static int dsbr100_stop(dsbr100_device *radio) +{ + if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + USB_REQ_GET_STATUS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + DSB100_ONOFF, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x00, 0x00, radio->transfer_buffer, 8, 300)<0) + return -1; + return (radio->transfer_buffer)[0]; +} + +/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ +static int dsbr100_setfreq(dsbr100_device *radio, int freq) +{ + freq = (freq/16*80)/1000+856; + if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + DSB100_TUNE, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + (freq>>8)&0x00ff, freq&0xff, + radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + USB_REQ_GET_STATUS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + USB_REQ_GET_STATUS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) { + radio->stereo = -1; + return -1; + } + radio->stereo = ! ((radio->transfer_buffer)[0]&0x01); + return (radio->transfer_buffer)[0]; +} + +/* return the device status. This is, in effect, just whether it +sees a stereo signal or not. Pity. */ +static void dsbr100_getstat(dsbr100_device *radio) +{ + if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), + USB_REQ_GET_STATUS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0) + radio->stereo = -1; + else + radio->stereo = ! (radio->transfer_buffer[0]&0x01); +} + + +/* USB subsystem interface begins here */ + +/* check if the device is present and register with v4l and +usb if it is */ +static int usb_dsbr100_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + dsbr100_device *radio; + + if (!(radio = kmalloc(sizeof(dsbr100_device), GFP_KERNEL))) + return -ENOMEM; + if (!(radio->videodev = video_device_alloc())) { + kfree(radio); + return -ENOMEM; + } + memcpy(radio->videodev, &dsbr100_videodev_template, + sizeof(dsbr100_videodev_template)); + radio->removed = 0; + radio->users = 0; + radio->usbdev = interface_to_usbdev(intf); + radio->curfreq = FREQ_MIN*FREQ_MUL; + video_set_drvdata(radio->videodev, radio); + if (video_register_device(radio->videodev, VFL_TYPE_RADIO, + radio_nr)) { + warn("Could not register video device"); + video_device_release(radio->videodev); + kfree(radio); + return -EIO; + } + usb_set_intfdata(intf, radio); + return 0; +} + +/* handle unplugging of the device, release data structures +if nothing keeps us from doing it. If something is still +keeping us busy, the release callback of v4l will take care +of releasing it. stv680.c does not relase its private +data, so I don't do this here either. Checking out the +code I'd expect I better did that, but if there's a memory +leak here it's tiny (~50 bytes per disconnect) */ +static void usb_dsbr100_disconnect(struct usb_interface *intf) +{ + dsbr100_device *radio = usb_get_intfdata(intf); + + usb_set_intfdata (intf, NULL); + if (radio) { + video_unregister_device(radio->videodev); + radio->videodev = NULL; + if (radio->users) { + kfree(radio); + } else { + radio->removed = 1; + } + } +} + + +/* Video for Linux interface */ + +static int usb_dsbr100_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + dsbr100_device *radio=video_get_drvdata(video_devdata(file)); + + if (!radio) + return -EIO; + + switch(cmd) { + case VIDIOCGCAP: { + struct video_capability *v = arg; + + memset(v, 0, sizeof(*v)); + v->type = VID_TYPE_TUNER; + v->channels = 1; + v->audios = 1; + strcpy(v->name, "D-Link R-100 USB FM Radio"); + return 0; + } + case VIDIOCGTUNER: { + struct video_tuner *v = arg; + + dsbr100_getstat(radio); + if(v->tuner) /* Only 1 tuner */ + return -EINVAL; + v->rangelow = FREQ_MIN*FREQ_MUL; + v->rangehigh = FREQ_MAX*FREQ_MUL; + v->flags = VIDEO_TUNER_LOW; + v->mode = VIDEO_MODE_AUTO; + v->signal = radio->stereo*0x7000; + /* Don't know how to get signal strength */ + v->flags |= VIDEO_TUNER_STEREO_ON*radio->stereo; + strcpy(v->name, "DSB R-100"); + return 0; + } + case VIDIOCSTUNER: { + struct video_tuner *v = arg; + + if(v->tuner!=0) + return -EINVAL; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: { + int *freq = arg; + + if (radio->curfreq==-1) + return -EINVAL; + *freq = radio->curfreq; + return 0; + } + case VIDIOCSFREQ: { + int *freq = arg; + + radio->curfreq = *freq; + if (dsbr100_setfreq(radio, radio->curfreq)==-1) + warn("Set frequency failed"); + return 0; + } + case VIDIOCGAUDIO: { + struct video_audio *v = arg; + + memset(v, 0, sizeof(*v)); + v->flags |= VIDEO_AUDIO_MUTABLE; + v->mode = VIDEO_SOUND_STEREO; + v->volume = 1; + v->step = 1; + strcpy(v->name, "Radio"); + return 0; + } + case VIDIOCSAUDIO: { + struct video_audio *v = arg; + + if (v->audio) + return -EINVAL; + if (v->flags&VIDEO_AUDIO_MUTE) { + if (dsbr100_stop(radio)==-1) + warn("Radio did not respond properly"); + } + else + if (dsbr100_start(radio)==-1) + warn("Radio did not respond properly"); + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int usb_dsbr100_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, usb_dsbr100_do_ioctl); +} + +static int usb_dsbr100_open(struct inode *inode, struct file *file) +{ + dsbr100_device *radio=video_get_drvdata(video_devdata(file)); + + radio->users = 1; + if (dsbr100_start(radio)<0) { + warn("Radio did not start up properly"); + radio->users = 0; + return -EIO; + } + dsbr100_setfreq(radio, radio->curfreq); + return 0; +} + +static int usb_dsbr100_close(struct inode *inode, struct file *file) +{ + dsbr100_device *radio=video_get_drvdata(video_devdata(file)); + + if (!radio) + return -ENODEV; + radio->users = 0; + if (radio->removed) { + kfree(radio); + } + return 0; +} + +static int __init dsbr100_init(void) +{ + int retval = usb_register(&usb_dsbr100_driver); + info(DRIVER_VERSION ":" DRIVER_DESC); + return retval; +} + +static void __exit dsbr100_exit(void) +{ + usb_deregister(&usb_dsbr100_driver); +} + +module_init (dsbr100_init); +module_exit (dsbr100_exit); + +MODULE_AUTHOR( DRIVER_AUTHOR ); +MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/ibmmpeg2.h b/linux/drivers/media/video/ibmmpeg2.h new file mode 100644 index 000000000..68e10387c --- /dev/null +++ b/linux/drivers/media/video/ibmmpeg2.h @@ -0,0 +1,94 @@ +/* ibmmpeg2.h - IBM MPEGCD21 definitions */ + +#ifndef __IBM_MPEG2__ +#define __IBM_MPEG2__ + +/* Define all MPEG Decoder registers */ +/* Chip Control and Status */ +#define IBM_MP2_CHIP_CONTROL 0x200*2 +#define IBM_MP2_CHIP_MODE 0x201*2 +/* Timer Control and Status */ +#define IBM_MP2_SYNC_STC2 0x202*2 +#define IBM_MP2_SYNC_STC1 0x203*2 +#define IBM_MP2_SYNC_STC0 0x204*2 +#define IBM_MP2_SYNC_PTS2 0x205*2 +#define IBM_MP2_SYNC_PTS1 0x206*2 +#define IBM_MP2_SYNC_PTS0 0x207*2 +/* Video FIFO Control */ +#define IBM_MP2_FIFO 0x208*2 +#define IBM_MP2_FIFOW 0x100*2 +#define IBM_MP2_FIFO_STAT 0x209*2 +#define IBM_MP2_RB_THRESHOLD 0x22b*2 +/* Command buffer */ +#define IBM_MP2_COMMAND 0x20a*2 +#define IBM_MP2_CMD_DATA 0x20b*2 +#define IBM_MP2_CMD_STAT 0x20c*2 +#define IBM_MP2_CMD_ADDR 0x20d*2 +/* Internal Processor Control and Status */ +#define IBM_MP2_PROC_IADDR 0x20e*2 +#define IBM_MP2_PROC_IDATA 0x20f*2 +#define IBM_MP2_WR_PROT 0x235*2 +/* DRAM Access */ +#define IBM_MP2_DRAM_ADDR 0x210*2 +#define IBM_MP2_DRAM_DATA 0x212*2 +#define IBM_MP2_DRAM_CMD_STAT 0x213*2 +#define IBM_MP2_BLOCK_SIZE 0x23b*2 +#define IBM_MP2_SRC_ADDR 0x23c*2 +/* Onscreen Display */ +#define IBM_MP2_OSD_ADDR 0x214*2 +#define IBM_MP2_OSD_DATA 0x215*2 +#define IBM_MP2_OSD_MODE 0x217*2 +#define IBM_MP2_OSD_LINK_ADDR 0x229*2 +#define IBM_MP2_OSD_SIZE 0x22a*2 +/* Interrupt Control */ +#define IBM_MP2_HOST_INT 0x218*2 +#define IBM_MP2_MASK0 0x219*2 +#define IBM_MP2_HOST_INT1 0x23e*2 +#define IBM_MP2_MASK1 0x23f*2 +/* Audio Control */ +#define IBM_MP2_AUD_IADDR 0x21a*2 +#define IBM_MP2_AUD_IDATA 0x21b*2 +#define IBM_MP2_AUD_FIFO 0x21c*2 +#define IBM_MP2_AUD_FIFOW 0x101*2 +#define IBM_MP2_AUD_CTL 0x21d*2 +#define IBM_MP2_BEEP_CTL 0x21e*2 +#define IBM_MP2_FRNT_ATTEN 0x22d*2 +/* Display Control */ +#define IBM_MP2_DISP_MODE 0x220*2 +#define IBM_MP2_DISP_DLY 0x221*2 +#define IBM_MP2_VBI_CTL 0x222*2 +#define IBM_MP2_DISP_LBOR 0x223*2 +#define IBM_MP2_DISP_TBOR 0x224*2 +/* Polarity Control */ +#define IBM_MP2_INFC_CTL 0x22c*2 + +/* control commands */ +#define IBM_MP2_PLAY 0 +#define IBM_MP2_PAUSE 1 +#define IBM_MP2_SINGLE_FRAME 2 +#define IBM_MP2_FAST_FORWARD 3 +#define IBM_MP2_SLOW_MOTION 4 +#define IBM_MP2_IMED_NORM_PLAY 5 +#define IBM_MP2_RESET_WINDOW 6 +#define IBM_MP2_FREEZE_FRAME 7 +#define IBM_MP2_RESET_VID_RATE 8 +#define IBM_MP2_CONFIG_DECODER 9 +#define IBM_MP2_CHANNEL_SWITCH 10 +#define IBM_MP2_RESET_AUD_RATE 11 +#define IBM_MP2_PRE_OP_CHN_SW 12 +#define IBM_MP2_SET_STILL_MODE 14 + +/* Define Xilinx FPGA Internal Registers */ + +/* general control register 0 */ +#define XILINX_CTL0 0x600 +/* genlock delay resister 1 */ +#define XILINX_GLDELAY 0x602 +/* send 16 bits to CS3310 port */ +#define XILINX_CS3310 0x604 +/* send 16 bits to CS3310 and complete */ +#define XILINX_CS3310_CMPLT 0x60c +/* pulse width modulator control */ +#define XILINX_PWM 0x606 + +#endif diff --git a/linux/drivers/media/video/indycam.c b/linux/drivers/media/video/indycam.c new file mode 100644 index 000000000..7420b79e9 --- /dev/null +++ b/linux/drivers/media/video/indycam.c @@ -0,0 +1,471 @@ +/* + * indycam.c - Silicon Graphics IndyCam digital camera driver + * + * Copyright (C) 2003 Ladislav Michl + * Copyright (C) 2004,2005 Mikael Nousiainen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +/* IndyCam decodes stream of photons into digital image representation ;-) */ +#include +#include + +#include "indycam.h" + +#define INDYCAM_MODULE_VERSION "0.0.5" + +MODULE_DESCRIPTION("SGI IndyCam driver"); +MODULE_VERSION(INDYCAM_MODULE_VERSION); +MODULE_AUTHOR("Mikael Nousiainen "); +MODULE_LICENSE("GPL"); + +// #define INDYCAM_DEBUG + +#ifdef INDYCAM_DEBUG +#define dprintk(x...) printk("IndyCam: " x); +#define indycam_regdump(client) indycam_regdump_debug(client) +#else +#define dprintk(x...) +#define indycam_regdump(client) +#endif + +struct indycam { + struct i2c_client *client; + u8 version; +}; + +static struct i2c_driver i2c_driver_indycam; + +static const u8 initseq[] = { + INDYCAM_CONTROL_AGCENA, /* INDYCAM_CONTROL */ + INDYCAM_SHUTTER_60, /* INDYCAM_SHUTTER */ + INDYCAM_GAIN_DEFAULT, /* INDYCAM_GAIN */ + 0x00, /* INDYCAM_BRIGHTNESS (read-only) */ + INDYCAM_RED_BALANCE_DEFAULT, /* INDYCAM_RED_BALANCE */ + INDYCAM_BLUE_BALANCE_DEFAULT, /* INDYCAM_BLUE_BALANCE */ + INDYCAM_RED_SATURATION_DEFAULT, /* INDYCAM_RED_SATURATION */ + INDYCAM_BLUE_SATURATION_DEFAULT,/* INDYCAM_BLUE_SATURATION */ +}; + +/* IndyCam register handling */ + +static int indycam_read_reg(struct i2c_client *client, u8 reg, u8 *value) +{ + int ret; + + if (reg == INDYCAM_REG_RESET) { + dprintk("indycam_read_reg(): " + "skipping write-only register %d\n", reg); + *value = 0; + return 0; + } + + ret = i2c_smbus_read_byte_data(client, reg); + + if (ret < 0) { + printk(KERN_ERR "IndyCam: indycam_read_reg(): read failed, " + "register = 0x%02x\n", reg); + return ret; + } + + *value = (u8)ret; + + return 0; +} + +static int indycam_write_reg(struct i2c_client *client, u8 reg, u8 value) +{ + int err; + + if ((reg == INDYCAM_REG_BRIGHTNESS) + || (reg == INDYCAM_REG_VERSION)) { + dprintk("indycam_write_reg(): " + "skipping read-only register %d\n", reg); + return 0; + } + + dprintk("Writing Reg %d = 0x%02x\n", reg, value); + err = i2c_smbus_write_byte_data(client, reg, value); + + if (err) { + printk(KERN_ERR "IndyCam: indycam_write_reg(): write failed, " + "register = 0x%02x, value = 0x%02x\n", reg, value); + } + return err; +} + +static int indycam_write_block(struct i2c_client *client, u8 reg, + u8 length, u8 *data) +{ + int i, err; + + for (i = 0; i < length; i++) { + err = indycam_write_reg(client, reg + i, data[i]); + if (err) + return err; + } + + return 0; +} + +/* Helper functions */ + +#ifdef INDYCAM_DEBUG +static void indycam_regdump_debug(struct i2c_client *client) +{ + int i; + u8 val; + + for (i = 0; i < 9; i++) { + indycam_read_reg(client, i, &val); + dprintk("Reg %d = 0x%02x\n", i, val); + } +} +#endif + +static int indycam_get_control(struct i2c_client *client, + struct indycam_control *ctrl) +{ + struct indycam *camera = i2c_get_clientdata(client); + u8 reg; + int ret = 0; + + switch (ctrl->type) { + case INDYCAM_CONTROL_AGC: + case INDYCAM_CONTROL_AWB: + ret = indycam_read_reg(client, INDYCAM_REG_CONTROL, ®); + if (ret) + return -EIO; + if (ctrl->type == INDYCAM_CONTROL_AGC) + ctrl->value = (reg & INDYCAM_CONTROL_AGCENA) + ? 1 : 0; + else + ctrl->value = (reg & INDYCAM_CONTROL_AWBCTL) + ? 1 : 0; + break; + case INDYCAM_CONTROL_SHUTTER: + ret = indycam_read_reg(client, INDYCAM_REG_SHUTTER, ®); + if (ret) + return -EIO; + ctrl->value = ((s32)reg == 0x00) ? 0xff : ((s32)reg - 1); + break; + case INDYCAM_CONTROL_GAIN: + ret = indycam_read_reg(client, INDYCAM_REG_GAIN, ®); + if (ret) + return -EIO; + ctrl->value = (s32)reg; + break; + case INDYCAM_CONTROL_RED_BALANCE: + ret = indycam_read_reg(client, INDYCAM_REG_RED_BALANCE, ®); + if (ret) + return -EIO; + ctrl->value = (s32)reg; + break; + case INDYCAM_CONTROL_BLUE_BALANCE: + ret = indycam_read_reg(client, INDYCAM_REG_BLUE_BALANCE, ®); + if (ret) + return -EIO; + ctrl->value = (s32)reg; + break; + case INDYCAM_CONTROL_RED_SATURATION: + ret = indycam_read_reg(client, + INDYCAM_REG_RED_SATURATION, ®); + if (ret) + return -EIO; + ctrl->value = (s32)reg; + break; + case INDYCAM_CONTROL_BLUE_SATURATION: + ret = indycam_read_reg(client, + INDYCAM_REG_BLUE_SATURATION, ®); + if (ret) + return -EIO; + ctrl->value = (s32)reg; + break; + case INDYCAM_CONTROL_GAMMA: + if (camera->version == CAMERA_VERSION_MOOSE) { + ret = indycam_read_reg(client, + INDYCAM_REG_GAMMA, ®); + if (ret) + return -EIO; + ctrl->value = (s32)reg; + } else { + ctrl->value = INDYCAM_GAMMA_DEFAULT; + } + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int indycam_set_control(struct i2c_client *client, + struct indycam_control *ctrl) +{ + struct indycam *camera = i2c_get_clientdata(client); + u8 reg; + int ret = 0; + + switch (ctrl->type) { + case INDYCAM_CONTROL_AGC: + case INDYCAM_CONTROL_AWB: + ret = indycam_read_reg(client, INDYCAM_REG_CONTROL, ®); + if (ret) + break; + + if (ctrl->type == INDYCAM_CONTROL_AGC) { + if (ctrl->value) + reg |= INDYCAM_CONTROL_AGCENA; + else + reg &= ~INDYCAM_CONTROL_AGCENA; + } else { + if (ctrl->value) + reg |= INDYCAM_CONTROL_AWBCTL; + else + reg &= ~INDYCAM_CONTROL_AWBCTL; + } + + ret = indycam_write_reg(client, INDYCAM_REG_CONTROL, reg); + break; + case INDYCAM_CONTROL_SHUTTER: + reg = (ctrl->value == 0xff) ? 0x00 : (ctrl->value + 1); + ret = indycam_write_reg(client, INDYCAM_REG_SHUTTER, reg); + break; + case INDYCAM_CONTROL_GAIN: + ret = indycam_write_reg(client, INDYCAM_REG_GAIN, ctrl->value); + break; + case INDYCAM_CONTROL_RED_BALANCE: + ret = indycam_write_reg(client, INDYCAM_REG_RED_BALANCE, + ctrl->value); + break; + case INDYCAM_CONTROL_BLUE_BALANCE: + ret = indycam_write_reg(client, INDYCAM_REG_BLUE_BALANCE, + ctrl->value); + break; + case INDYCAM_CONTROL_RED_SATURATION: + ret = indycam_write_reg(client, INDYCAM_REG_RED_SATURATION, + ctrl->value); + break; + case INDYCAM_CONTROL_BLUE_SATURATION: + ret = indycam_write_reg(client, INDYCAM_REG_BLUE_SATURATION, + ctrl->value); + break; + case INDYCAM_CONTROL_GAMMA: + if (camera->version == CAMERA_VERSION_MOOSE) { + ret = indycam_write_reg(client, INDYCAM_REG_GAMMA, + ctrl->value); + } + break; + default: + ret = -EINVAL; + } + + return ret; +} + +/* I2C-interface */ + +static int indycam_attach(struct i2c_adapter *adap, int addr, int kind) +{ + int err = 0; + struct indycam *camera; + struct i2c_client *client; + + printk(KERN_INFO "SGI IndyCam driver version %s\n", + INDYCAM_MODULE_VERSION); + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (!client) + return -ENOMEM; + camera = kzalloc(sizeof(struct indycam), GFP_KERNEL); + if (!camera) { + err = -ENOMEM; + goto out_free_client; + } + + client->addr = addr; + client->adapter = adap; + client->driver = &i2c_driver_indycam; + client->flags = 0; + strcpy(client->name, "IndyCam client"); + i2c_set_clientdata(client, camera); + + camera->client = client; + + err = i2c_attach_client(client); + if (err) + goto out_free_camera; + + camera->version = i2c_smbus_read_byte_data(client, + INDYCAM_REG_VERSION); + if (camera->version != CAMERA_VERSION_INDY && + camera->version != CAMERA_VERSION_MOOSE) { + err = -ENODEV; + goto out_detach_client; + } + printk(KERN_INFO "IndyCam v%d.%d detected\n", + INDYCAM_VERSION_MAJOR(camera->version), + INDYCAM_VERSION_MINOR(camera->version)); + + indycam_regdump(client); + + // initialize + err = indycam_write_block(client, 0, sizeof(initseq), (u8 *)&initseq); + if (err) { + printk(KERN_ERR "IndyCam initalization failed\n"); + err = -EIO; + goto out_detach_client; + } + + indycam_regdump(client); + + // white balance + err = indycam_write_reg(client, INDYCAM_REG_CONTROL, + INDYCAM_CONTROL_AGCENA | INDYCAM_CONTROL_AWBCTL); + if (err) { + printk(KERN_ERR "IndyCam: White balancing camera failed\n"); + err = -EIO; + goto out_detach_client; + } + + indycam_regdump(client); + + printk(KERN_INFO "IndyCam initialized\n"); + + return 0; + +out_detach_client: + i2c_detach_client(client); +out_free_camera: + kfree(camera); +out_free_client: + kfree(client); + return err; +} + +static int indycam_probe(struct i2c_adapter *adap) +{ + /* Indy specific crap */ + if (adap->id == I2C_HW_SGI_VINO) + return indycam_attach(adap, INDYCAM_ADDR, 0); + /* Feel free to add probe here :-) */ + return -ENODEV; +} + +static int indycam_detach(struct i2c_client *client) +{ + struct indycam *camera = i2c_get_clientdata(client); + + i2c_detach_client(client); + kfree(camera); + kfree(client); + return 0; +} + +static int indycam_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + // struct indycam *camera = i2c_get_clientdata(client); + + /* The old video_decoder interface just isn't enough, + * so we'll use some custom commands. */ + switch (cmd) { + case DECODER_GET_CAPABILITIES: { + struct video_decoder_capability *cap = arg; + + cap->flags = VIDEO_DECODER_NTSC; + cap->inputs = 1; + cap->outputs = 1; + break; + } + case DECODER_GET_STATUS: { + int *iarg = arg; + + *iarg = DECODER_STATUS_GOOD | DECODER_STATUS_NTSC | + DECODER_STATUS_COLOR; + break; + } + case DECODER_SET_NORM: { + int *iarg = arg; + + switch (*iarg) { + case VIDEO_MODE_NTSC: + break; + default: + return -EINVAL; + } + break; + } + case DECODER_SET_INPUT: { + int *iarg = arg; + + if (*iarg != 0) + return -EINVAL; + break; + } + case DECODER_SET_OUTPUT: { + int *iarg = arg; + + if (*iarg != 0) + return -EINVAL; + break; + } + case DECODER_ENABLE_OUTPUT: { + /* Always enabled */ + break; + } + case DECODER_SET_PICTURE: { + // struct video_picture *pic = arg; + /* TODO: convert values for indycam_set_controls() */ + break; + } + case DECODER_INDYCAM_GET_CONTROL: { + return indycam_get_control(client, arg); + } + case DECODER_INDYCAM_SET_CONTROL: { + return indycam_set_control(client, arg); + } + default: + return -EINVAL; + } + + return 0; +} + +static struct i2c_driver i2c_driver_indycam = { + .driver = { + .name = "indycam", + }, + .id = I2C_DRIVERID_INDYCAM, + .attach_adapter = indycam_probe, + .detach_client = indycam_detach, + .command = indycam_command, +}; + +static int __init indycam_init(void) +{ + return i2c_add_driver(&i2c_driver_indycam); +} + +static void __exit indycam_exit(void) +{ + i2c_del_driver(&i2c_driver_indycam); +} + +module_init(indycam_init); +module_exit(indycam_exit); diff --git a/linux/drivers/media/video/indycam.h b/linux/drivers/media/video/indycam.h new file mode 100644 index 000000000..e6ee82063 --- /dev/null +++ b/linux/drivers/media/video/indycam.h @@ -0,0 +1,108 @@ +/* + * indycam.h - Silicon Graphics IndyCam digital camera driver + * + * Copyright (C) 2003 Ladislav Michl + * Copyright (C) 2004,2005 Mikael Nousiainen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _INDYCAM_H_ +#define _INDYCAM_H_ + +/* I2C address for the Guinness Camera */ +#define INDYCAM_ADDR 0x56 + +/* Camera version */ +#define CAMERA_VERSION_INDY 0x10 /* v1.0 */ +#define CAMERA_VERSION_MOOSE 0x12 /* v1.2 */ +#define INDYCAM_VERSION_MAJOR(x) (((x) & 0xf0) >> 4) +#define INDYCAM_VERSION_MINOR(x) ((x) & 0x0f) + +/* Register bus addresses */ +#define INDYCAM_REG_CONTROL 0x00 +#define INDYCAM_REG_SHUTTER 0x01 +#define INDYCAM_REG_GAIN 0x02 +#define INDYCAM_REG_BRIGHTNESS 0x03 /* read-only */ +#define INDYCAM_REG_RED_BALANCE 0x04 +#define INDYCAM_REG_BLUE_BALANCE 0x05 +#define INDYCAM_REG_RED_SATURATION 0x06 +#define INDYCAM_REG_BLUE_SATURATION 0x07 +#define INDYCAM_REG_GAMMA 0x08 +#define INDYCAM_REG_VERSION 0x0e /* read-only */ +#define INDYCAM_REG_RESET 0x0f /* write-only */ + +#define INDYCAM_REG_LED 0x46 +#define INDYCAM_REG_ORIENTATION 0x47 +#define INDYCAM_REG_BUTTON 0x48 + +/* Field definitions of registers */ +#define INDYCAM_CONTROL_AGCENA (1<<0) /* automatic gain control */ +#define INDYCAM_CONTROL_AWBCTL (1<<1) /* automatic white balance */ + /* 2-3 are reserved */ +#define INDYCAM_CONTROL_EVNFLD (1<<4) /* read-only */ + +#define INDYCAM_SHUTTER_10000 0x02 /* 1/10000 second */ +#define INDYCAM_SHUTTER_4000 0x04 /* 1/4000 second */ +#define INDYCAM_SHUTTER_2000 0x08 /* 1/2000 second */ +#define INDYCAM_SHUTTER_1000 0x10 /* 1/1000 second */ +#define INDYCAM_SHUTTER_500 0x20 /* 1/500 second */ +#define INDYCAM_SHUTTER_250 0x3f /* 1/250 second */ +#define INDYCAM_SHUTTER_125 0x7e /* 1/125 second */ +#define INDYCAM_SHUTTER_100 0x9e /* 1/100 second */ +#define INDYCAM_SHUTTER_60 0x00 /* 1/60 second */ + +#define INDYCAM_LED_ACTIVE 0x10 +#define INDYCAM_LED_INACTIVE 0x30 +#define INDYCAM_ORIENTATION_BOTTOM_TO_TOP 0x40 +#define INDYCAM_BUTTON_RELEASED 0x10 + +/* Values for controls */ +#define INDYCAM_SHUTTER_MIN 0x00 +#define INDYCAM_SHUTTER_MAX 0xff +#define INDYCAM_GAIN_MIN 0x00 +#define INDYCAM_GAIN_MAX 0xff +#define INDYCAM_RED_BALANCE_MIN 0x00 +#define INDYCAM_RED_BALANCE_MAX 0xff +#define INDYCAM_BLUE_BALANCE_MIN 0x00 +#define INDYCAM_BLUE_BALANCE_MAX 0xff +#define INDYCAM_RED_SATURATION_MIN 0x00 +#define INDYCAM_RED_SATURATION_MAX 0xff +#define INDYCAM_BLUE_SATURATION_MIN 0x00 +#define INDYCAM_BLUE_SATURATION_MAX 0xff +#define INDYCAM_GAMMA_MIN 0x00 +#define INDYCAM_GAMMA_MAX 0xff + +#define INDYCAM_AGC_DEFAULT 1 +#define INDYCAM_AWB_DEFAULT 0 +#define INDYCAM_SHUTTER_DEFAULT 0xff +#define INDYCAM_GAIN_DEFAULT 0x80 +#define INDYCAM_RED_BALANCE_DEFAULT 0x18 +#define INDYCAM_BLUE_BALANCE_DEFAULT 0xa4 +#define INDYCAM_RED_SATURATION_DEFAULT 0x80 +#define INDYCAM_BLUE_SATURATION_DEFAULT 0xc0 +#define INDYCAM_GAMMA_DEFAULT 0x80 + +/* Driver interface definitions */ + +#define INDYCAM_CONTROL_AGC 0 /* boolean */ +#define INDYCAM_CONTROL_AWB 1 /* boolean */ +#define INDYCAM_CONTROL_SHUTTER 2 +#define INDYCAM_CONTROL_GAIN 3 +#define INDYCAM_CONTROL_RED_BALANCE 4 +#define INDYCAM_CONTROL_BLUE_BALANCE 5 +#define INDYCAM_CONTROL_RED_SATURATION 6 +#define INDYCAM_CONTROL_BLUE_SATURATION 7 +#define INDYCAM_CONTROL_GAMMA 8 + +struct indycam_control { + u8 type; + s32 value; +}; + +#define DECODER_INDYCAM_GET_CONTROL _IOR('d', 193, struct indycam_control) +#define DECODER_INDYCAM_SET_CONTROL _IOW('d', 194, struct indycam_control) + +#endif diff --git a/linux/drivers/media/video/meye.c b/linux/drivers/media/video/meye.c new file mode 100644 index 000000000..dcd90f56b --- /dev/null +++ b/linux/drivers/media/video/meye.c @@ -0,0 +1,2044 @@ +/* + * Motion Eye video4linux driver for Sony Vaio PictureBook + * + * Copyright (C) 2001-2004 Stelian Pop + * + * Copyright (C) 2001-2002 Alcôve + * + * Copyright (C) 2000 Andrew Tridgell + * + * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras. + * + * Some parts borrowed from various video4linux drivers, especially + * bttv-driver.c and zoran.c, see original files for credits. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include "compat.h" +#include +#include +#include +#include +#include +#include +#include + +#include "meye.h" +#include + +MODULE_AUTHOR("Stelian Pop "); +MODULE_DESCRIPTION("v4l/v4l2 driver for the MotionEye camera"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(MEYE_DRIVER_VERSION); + +/* force usage of V4L1 API */ +static int forcev4l1; /* = 0 */ +module_param(forcev4l1, int, 0644); +MODULE_PARM_DESC(forcev4l1, "force use of V4L1 instead of V4L2"); + +/* number of grab buffers */ +static unsigned int gbuffers = 2; +module_param(gbuffers, int, 0444); +MODULE_PARM_DESC(gbuffers, "number of capture buffers, default is 2 (32 max)"); + +/* size of a grab buffer */ +static unsigned int gbufsize = MEYE_MAX_BUFSIZE; +module_param(gbufsize, int, 0444); +MODULE_PARM_DESC(gbufsize, "size of the capture buffers, default is 614400" + " (will be rounded up to a page multiple)"); + +/* /dev/videoX registration number */ +static int video_nr = -1; +module_param(video_nr, int, 0444); +MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)"); + +/* driver structure - only one possible */ +static struct meye meye; + +/****************************************************************************/ +/* Memory allocation routines (stolen from bttv-driver.c) */ +/****************************************************************************/ +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (mem) { + memset(mem, 0, size); + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + } + return mem; +} + +static void rvfree(void * mem, unsigned long size) +{ + unsigned long adr; + + if (mem) { + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); + } +} + +/* + * return a page table pointing to N pages of locked memory + * + * NOTE: The meye device expects DMA addresses on 32 bits, we build + * a table of 1024 entries = 4 bytes * 1024 = 4096 bytes. + */ +static int ptable_alloc(void) +{ + u32 *pt; + int i; + + memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable)); + + /* give only 32 bit DMA addresses */ + if (dma_set_mask(&meye.mchip_dev->dev, DMA_32BIT_MASK)) + return -1; + + meye.mchip_ptable_toc = dma_alloc_coherent(&meye.mchip_dev->dev, + PAGE_SIZE, + &meye.mchip_dmahandle, + GFP_KERNEL); + if (!meye.mchip_ptable_toc) { + meye.mchip_dmahandle = 0; + return -1; + } + + pt = meye.mchip_ptable_toc; + for (i = 0; i < MCHIP_NB_PAGES; i++) { + dma_addr_t dma; + meye.mchip_ptable[i] = dma_alloc_coherent(&meye.mchip_dev->dev, + PAGE_SIZE, + &dma, + GFP_KERNEL); + if (!meye.mchip_ptable[i]) { + int j; + pt = meye.mchip_ptable_toc; + for (j = 0; j < i; ++j) { + dma = (dma_addr_t) *pt; + dma_free_coherent(&meye.mchip_dev->dev, + PAGE_SIZE, + meye.mchip_ptable[j], dma); + pt++; + } + dma_free_coherent(&meye.mchip_dev->dev, + PAGE_SIZE, + meye.mchip_ptable_toc, + meye.mchip_dmahandle); + meye.mchip_ptable_toc = NULL; + meye.mchip_dmahandle = 0; + return -1; + } + *pt = (u32) dma; + pt++; + } + return 0; +} + +static void ptable_free(void) +{ + u32 *pt; + int i; + + pt = meye.mchip_ptable_toc; + for (i = 0; i < MCHIP_NB_PAGES; i++) { + dma_addr_t dma = (dma_addr_t) *pt; + if (meye.mchip_ptable[i]) + dma_free_coherent(&meye.mchip_dev->dev, + PAGE_SIZE, + meye.mchip_ptable[i], dma); + pt++; + } + + if (meye.mchip_ptable_toc) + dma_free_coherent(&meye.mchip_dev->dev, + PAGE_SIZE, + meye.mchip_ptable_toc, + meye.mchip_dmahandle); + + memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable)); + meye.mchip_ptable_toc = NULL; + meye.mchip_dmahandle = 0; +} + +/* copy data from ptable into buf */ +static void ptable_copy(u8 *buf, int start, int size, int pt_pages) +{ + int i; + + for (i = 0; i < (size / PAGE_SIZE) * PAGE_SIZE; i += PAGE_SIZE) { + memcpy(buf + i, meye.mchip_ptable[start++], PAGE_SIZE); + if (start >= pt_pages) + start = 0; + } + memcpy(buf + i, meye.mchip_ptable[start], size % PAGE_SIZE); +} + +/****************************************************************************/ +/* JPEG tables at different qualities to load into the VRJ chip */ +/****************************************************************************/ + +/* return a set of quantisation tables based on a quality from 1 to 10 */ +static u16 *jpeg_quantisation_tables(int *length, int quality) +{ + static u16 jpeg_tables[][70] = { { + 0xdbff, 0x4300, 0xff00, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, + 0xdbff, 0x4300, 0xff01, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, + }, + { + 0xdbff, 0x4300, 0x5000, 0x3c37, 0x3c46, 0x5032, 0x4146, 0x5a46, + 0x5055, 0x785f, 0x82c8, 0x6e78, 0x786e, 0xaff5, 0x91b9, 0xffc8, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, + 0xdbff, 0x4300, 0x5501, 0x5a5a, 0x6978, 0xeb78, 0x8282, 0xffeb, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, + }, + { + 0xdbff, 0x4300, 0x2800, 0x1e1c, 0x1e23, 0x2819, 0x2123, 0x2d23, + 0x282b, 0x3c30, 0x4164, 0x373c, 0x3c37, 0x587b, 0x495d, 0x9164, + 0x9980, 0x8f96, 0x8c80, 0xa08a, 0xe6b4, 0xa0c3, 0xdaaa, 0x8aad, + 0xc88c, 0xcbff, 0xeeda, 0xfff5, 0xffff, 0xc19b, 0xffff, 0xfaff, + 0xe6ff, 0xfffd, 0xfff8, + 0xdbff, 0x4300, 0x2b01, 0x2d2d, 0x353c, 0x763c, 0x4141, 0xf876, + 0x8ca5, 0xf8a5, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, + 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, + 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, + 0xf8f8, 0xf8f8, 0xfff8, + }, + { + 0xdbff, 0x4300, 0x1b00, 0x1412, 0x1417, 0x1b11, 0x1617, 0x1e17, + 0x1b1c, 0x2820, 0x2b42, 0x2528, 0x2825, 0x3a51, 0x303d, 0x6042, + 0x6555, 0x5f64, 0x5d55, 0x6a5b, 0x9978, 0x6a81, 0x9071, 0x5b73, + 0x855d, 0x86b5, 0x9e90, 0xaba3, 0xabad, 0x8067, 0xc9bc, 0xa6ba, + 0x99c7, 0xaba8, 0xffa4, + 0xdbff, 0x4300, 0x1c01, 0x1e1e, 0x2328, 0x4e28, 0x2b2b, 0xa44e, + 0x5d6e, 0xa46e, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, + 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, + 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, + 0xa4a4, 0xa4a4, 0xffa4, + }, + { + 0xdbff, 0x4300, 0x1400, 0x0f0e, 0x0f12, 0x140d, 0x1012, 0x1712, + 0x1415, 0x1e18, 0x2132, 0x1c1e, 0x1e1c, 0x2c3d, 0x242e, 0x4932, + 0x4c40, 0x474b, 0x4640, 0x5045, 0x735a, 0x5062, 0x6d55, 0x4556, + 0x6446, 0x6588, 0x776d, 0x817b, 0x8182, 0x604e, 0x978d, 0x7d8c, + 0x7396, 0x817e, 0xff7c, + 0xdbff, 0x4300, 0x1501, 0x1717, 0x1a1e, 0x3b1e, 0x2121, 0x7c3b, + 0x4653, 0x7c53, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, + 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, + 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, + 0x7c7c, 0x7c7c, 0xff7c, + }, + { + 0xdbff, 0x4300, 0x1000, 0x0c0b, 0x0c0e, 0x100a, 0x0d0e, 0x120e, + 0x1011, 0x1813, 0x1a28, 0x1618, 0x1816, 0x2331, 0x1d25, 0x3a28, + 0x3d33, 0x393c, 0x3833, 0x4037, 0x5c48, 0x404e, 0x5744, 0x3745, + 0x5038, 0x516d, 0x5f57, 0x6762, 0x6768, 0x4d3e, 0x7971, 0x6470, + 0x5c78, 0x6765, 0xff63, + 0xdbff, 0x4300, 0x1101, 0x1212, 0x1518, 0x2f18, 0x1a1a, 0x632f, + 0x3842, 0x6342, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, + 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, + 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, + 0x6363, 0x6363, 0xff63, + }, + { + 0xdbff, 0x4300, 0x0d00, 0x0a09, 0x0a0b, 0x0d08, 0x0a0b, 0x0e0b, + 0x0d0e, 0x130f, 0x1520, 0x1213, 0x1312, 0x1c27, 0x171e, 0x2e20, + 0x3129, 0x2e30, 0x2d29, 0x332c, 0x4a3a, 0x333e, 0x4636, 0x2c37, + 0x402d, 0x4157, 0x4c46, 0x524e, 0x5253, 0x3e32, 0x615a, 0x505a, + 0x4a60, 0x5251, 0xff4f, + 0xdbff, 0x4300, 0x0e01, 0x0e0e, 0x1113, 0x2613, 0x1515, 0x4f26, + 0x2d35, 0x4f35, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, + 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, + 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, + 0x4f4f, 0x4f4f, 0xff4f, + }, + { + 0xdbff, 0x4300, 0x0a00, 0x0707, 0x0708, 0x0a06, 0x0808, 0x0b08, + 0x0a0a, 0x0e0b, 0x1018, 0x0d0e, 0x0e0d, 0x151d, 0x1116, 0x2318, + 0x251f, 0x2224, 0x221f, 0x2621, 0x372b, 0x262f, 0x3429, 0x2129, + 0x3022, 0x3141, 0x3934, 0x3e3b, 0x3e3e, 0x2e25, 0x4944, 0x3c43, + 0x3748, 0x3e3d, 0xff3b, + 0xdbff, 0x4300, 0x0a01, 0x0b0b, 0x0d0e, 0x1c0e, 0x1010, 0x3b1c, + 0x2228, 0x3b28, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, + 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, + 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, + 0x3b3b, 0x3b3b, 0xff3b, + }, + { + 0xdbff, 0x4300, 0x0600, 0x0504, 0x0506, 0x0604, 0x0506, 0x0706, + 0x0607, 0x0a08, 0x0a10, 0x090a, 0x0a09, 0x0e14, 0x0c0f, 0x1710, + 0x1814, 0x1718, 0x1614, 0x1a16, 0x251d, 0x1a1f, 0x231b, 0x161c, + 0x2016, 0x202c, 0x2623, 0x2927, 0x292a, 0x1f19, 0x302d, 0x282d, + 0x2530, 0x2928, 0xff28, + 0xdbff, 0x4300, 0x0701, 0x0707, 0x080a, 0x130a, 0x0a0a, 0x2813, + 0x161a, 0x281a, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, + 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, + 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, + 0x2828, 0x2828, 0xff28, + }, + { + 0xdbff, 0x4300, 0x0300, 0x0202, 0x0203, 0x0302, 0x0303, 0x0403, + 0x0303, 0x0504, 0x0508, 0x0405, 0x0504, 0x070a, 0x0607, 0x0c08, + 0x0c0a, 0x0b0c, 0x0b0a, 0x0d0b, 0x120e, 0x0d10, 0x110e, 0x0b0e, + 0x100b, 0x1016, 0x1311, 0x1514, 0x1515, 0x0f0c, 0x1817, 0x1416, + 0x1218, 0x1514, 0xff14, + 0xdbff, 0x4300, 0x0301, 0x0404, 0x0405, 0x0905, 0x0505, 0x1409, + 0x0b0d, 0x140d, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, + 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, + 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, + 0x1414, 0x1414, 0xff14, + }, + { + 0xdbff, 0x4300, 0x0100, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0xff01, + 0xdbff, 0x4300, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0xff01, + } }; + + if (quality < 0 || quality > 10) { + printk(KERN_WARNING + "meye: invalid quality level %d - using 8\n", quality); + quality = 8; + } + + *length = ARRAY_SIZE(jpeg_tables[quality]); + return jpeg_tables[quality]; +} + +/* return a generic set of huffman tables */ +static u16 *jpeg_huffman_tables(int *length) +{ + static u16 tables[] = { + 0xC4FF, 0xB500, 0x0010, 0x0102, 0x0303, 0x0402, 0x0503, 0x0405, + 0x0004, 0x0100, 0x017D, 0x0302, 0x0400, 0x0511, 0x2112, 0x4131, + 0x1306, 0x6151, 0x2207, 0x1471, 0x8132, 0xA191, 0x2308, 0xB142, + 0x15C1, 0xD152, 0x24F0, 0x6233, 0x8272, 0x0A09, 0x1716, 0x1918, + 0x251A, 0x2726, 0x2928, 0x342A, 0x3635, 0x3837, 0x3A39, 0x4443, + 0x4645, 0x4847, 0x4A49, 0x5453, 0x5655, 0x5857, 0x5A59, 0x6463, + 0x6665, 0x6867, 0x6A69, 0x7473, 0x7675, 0x7877, 0x7A79, 0x8483, + 0x8685, 0x8887, 0x8A89, 0x9392, 0x9594, 0x9796, 0x9998, 0xA29A, + 0xA4A3, 0xA6A5, 0xA8A7, 0xAAA9, 0xB3B2, 0xB5B4, 0xB7B6, 0xB9B8, + 0xC2BA, 0xC4C3, 0xC6C5, 0xC8C7, 0xCAC9, 0xD3D2, 0xD5D4, 0xD7D6, + 0xD9D8, 0xE1DA, 0xE3E2, 0xE5E4, 0xE7E6, 0xE9E8, 0xF1EA, 0xF3F2, + 0xF5F4, 0xF7F6, 0xF9F8, 0xFFFA, + 0xC4FF, 0xB500, 0x0011, 0x0102, 0x0402, 0x0304, 0x0704, 0x0405, + 0x0004, 0x0201, 0x0077, 0x0201, 0x1103, 0x0504, 0x3121, 0x1206, + 0x5141, 0x6107, 0x1371, 0x3222, 0x0881, 0x4214, 0xA191, 0xC1B1, + 0x2309, 0x5233, 0x15F0, 0x7262, 0x0AD1, 0x2416, 0xE134, 0xF125, + 0x1817, 0x1A19, 0x2726, 0x2928, 0x352A, 0x3736, 0x3938, 0x433A, + 0x4544, 0x4746, 0x4948, 0x534A, 0x5554, 0x5756, 0x5958, 0x635A, + 0x6564, 0x6766, 0x6968, 0x736A, 0x7574, 0x7776, 0x7978, 0x827A, + 0x8483, 0x8685, 0x8887, 0x8A89, 0x9392, 0x9594, 0x9796, 0x9998, + 0xA29A, 0xA4A3, 0xA6A5, 0xA8A7, 0xAAA9, 0xB3B2, 0xB5B4, 0xB7B6, + 0xB9B8, 0xC2BA, 0xC4C3, 0xC6C5, 0xC8C7, 0xCAC9, 0xD3D2, 0xD5D4, + 0xD7D6, 0xD9D8, 0xE2DA, 0xE4E3, 0xE6E5, 0xE8E7, 0xEAE9, 0xF3F2, + 0xF5F4, 0xF7F6, 0xF9F8, 0xFFFA, + 0xC4FF, 0x1F00, 0x0000, 0x0501, 0x0101, 0x0101, 0x0101, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0A09, + 0xFF0B, + 0xC4FF, 0x1F00, 0x0001, 0x0103, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0A09, + 0xFF0B + }; + + *length = ARRAY_SIZE(tables); + return tables; +} + +/****************************************************************************/ +/* MCHIP low-level functions */ +/****************************************************************************/ + +/* returns the horizontal capture size */ +static inline int mchip_hsize(void) +{ + return meye.params.subsample ? 320 : 640; +} + +/* returns the vertical capture size */ +static inline int mchip_vsize(void) +{ + return meye.params.subsample ? 240 : 480; +} + +/* waits for a register to be available */ +static void mchip_sync(int reg) +{ + u32 status; + int i; + + if (reg == MCHIP_MM_FIFO_DATA) { + for (i = 0; i < MCHIP_REG_TIMEOUT; i++) { + status = readl(meye.mchip_mmregs + + MCHIP_MM_FIFO_STATUS); + if (!(status & MCHIP_MM_FIFO_WAIT)) { + printk(KERN_WARNING "meye: fifo not ready\n"); + return; + } + if (status & MCHIP_MM_FIFO_READY) + return; + udelay(1); + } + } else if (reg > 0x80) { + u32 mask = (reg < 0x100) ? MCHIP_HIC_STATUS_MCC_RDY + : MCHIP_HIC_STATUS_VRJ_RDY; + for (i = 0; i < MCHIP_REG_TIMEOUT; i++) { + status = readl(meye.mchip_mmregs + MCHIP_HIC_STATUS); + if (status & mask) + return; + udelay(1); + } + } else + return; + printk(KERN_WARNING + "meye: mchip_sync() timeout on reg 0x%x status=0x%x\n", + reg, status); +} + +/* sets a value into the register */ +static inline void mchip_set(int reg, u32 v) +{ + mchip_sync(reg); + writel(v, meye.mchip_mmregs + reg); +} + +/* get the register value */ +static inline u32 mchip_read(int reg) +{ + mchip_sync(reg); + return readl(meye.mchip_mmregs + reg); +} + +/* wait for a register to become a particular value */ +static inline int mchip_delay(u32 reg, u32 v) +{ + int n = 10; + while (--n && mchip_read(reg) != v) + udelay(1); + return n; +} + +/* setup subsampling */ +static void mchip_subsample(void) +{ + mchip_set(MCHIP_MCC_R_SAMPLING, meye.params.subsample); + mchip_set(MCHIP_MCC_R_XRANGE, mchip_hsize()); + mchip_set(MCHIP_MCC_R_YRANGE, mchip_vsize()); + mchip_set(MCHIP_MCC_B_XRANGE, mchip_hsize()); + mchip_set(MCHIP_MCC_B_YRANGE, mchip_vsize()); + mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE); +} + +/* set the framerate into the mchip */ +static void mchip_set_framerate(void) +{ + mchip_set(MCHIP_HIC_S_RATE, meye.params.framerate); +} + +/* load some huffman and quantisation tables into the VRJ chip ready + for JPEG compression */ +static void mchip_load_tables(void) +{ + int i; + int length; + u16 *tables; + + tables = jpeg_huffman_tables(&length); + for (i = 0; i < length; i++) + writel(tables[i], meye.mchip_mmregs + MCHIP_VRJ_TABLE_DATA); + + tables = jpeg_quantisation_tables(&length, meye.params.quality); + for (i = 0; i < length; i++) + writel(tables[i], meye.mchip_mmregs + MCHIP_VRJ_TABLE_DATA); +} + +/* setup the VRJ parameters in the chip */ +static void mchip_vrj_setup(u8 mode) +{ + mchip_set(MCHIP_VRJ_BUS_MODE, 5); + mchip_set(MCHIP_VRJ_SIGNAL_ACTIVE_LEVEL, 0x1f); + mchip_set(MCHIP_VRJ_PDAT_USE, 1); + mchip_set(MCHIP_VRJ_IRQ_FLAG, 0xa0); + mchip_set(MCHIP_VRJ_MODE_SPECIFY, mode); + mchip_set(MCHIP_VRJ_NUM_LINES, mchip_vsize()); + mchip_set(MCHIP_VRJ_NUM_PIXELS, mchip_hsize()); + mchip_set(MCHIP_VRJ_NUM_COMPONENTS, 0x1b); + mchip_set(MCHIP_VRJ_LIMIT_COMPRESSED_LO, 0xFFFF); + mchip_set(MCHIP_VRJ_LIMIT_COMPRESSED_HI, 0xFFFF); + mchip_set(MCHIP_VRJ_COMP_DATA_FORMAT, 0xC); + mchip_set(MCHIP_VRJ_RESTART_INTERVAL, 0); + mchip_set(MCHIP_VRJ_SOF1, 0x601); + mchip_set(MCHIP_VRJ_SOF2, 0x1502); + mchip_set(MCHIP_VRJ_SOF3, 0x1503); + mchip_set(MCHIP_VRJ_SOF4, 0x1596); + mchip_set(MCHIP_VRJ_SOS, 0x0ed0); + + mchip_load_tables(); +} + +/* sets the DMA parameters into the chip */ +static void mchip_dma_setup(dma_addr_t dma_addr) +{ + int i; + + mchip_set(MCHIP_MM_PT_ADDR, (u32)dma_addr); + for (i = 0; i < 4; i++) + mchip_set(MCHIP_MM_FIR(i), 0); + meye.mchip_fnum = 0; +} + +/* setup for DMA transfers - also zeros the framebuffer */ +static int mchip_dma_alloc(void) +{ + if (!meye.mchip_dmahandle) + if (ptable_alloc()) + return -1; + return 0; +} + +/* frees the DMA buffer */ +static void mchip_dma_free(void) +{ + if (meye.mchip_dmahandle) { + mchip_dma_setup(0); + ptable_free(); + } +} + +/* stop any existing HIC action and wait for any dma to complete then + reset the dma engine */ +static void mchip_hic_stop(void) +{ + int i, j; + + meye.mchip_mode = MCHIP_HIC_MODE_NOOP; + if (!(mchip_read(MCHIP_HIC_STATUS) & MCHIP_HIC_STATUS_BUSY)) + return; + for (i = 0; i < 20; ++i) { + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_STOP); + mchip_delay(MCHIP_HIC_CMD, 0); + for (j = 0; j < 100; ++j) { + if (mchip_delay(MCHIP_HIC_STATUS, + MCHIP_HIC_STATUS_IDLE)) + return; + msleep(1); + } + printk(KERN_ERR "meye: need to reset HIC!\n"); + + mchip_set(MCHIP_HIC_CTL, MCHIP_HIC_CTL_SOFT_RESET); + msleep(250); + } + printk(KERN_ERR "meye: resetting HIC hanged!\n"); +} + +/****************************************************************************/ +/* MCHIP frame processing functions */ +/****************************************************************************/ + +/* get the next ready frame from the dma engine */ +static u32 mchip_get_frame(void) +{ + u32 v; + + v = mchip_read(MCHIP_MM_FIR(meye.mchip_fnum)); + return v; +} + +/* frees the current frame from the dma engine */ +static void mchip_free_frame(void) +{ + mchip_set(MCHIP_MM_FIR(meye.mchip_fnum), 0); + meye.mchip_fnum++; + meye.mchip_fnum %= 4; +} + +/* read one frame from the framebuffer assuming it was captured using + a uncompressed transfer */ +static void mchip_cont_read_frame(u32 v, u8 *buf, int size) +{ + int pt_id; + + pt_id = (v >> 17) & 0x3FF; + + ptable_copy(buf, pt_id, size, MCHIP_NB_PAGES); +} + +/* read a compressed frame from the framebuffer */ +static int mchip_comp_read_frame(u32 v, u8 *buf, int size) +{ + int pt_start, pt_end, trailer; + int fsize; + int i; + + pt_start = (v >> 19) & 0xFF; + pt_end = (v >> 11) & 0xFF; + trailer = (v >> 1) & 0x3FF; + + if (pt_end < pt_start) + fsize = (MCHIP_NB_PAGES_MJPEG - pt_start) * PAGE_SIZE + + pt_end * PAGE_SIZE + trailer * 4; + else + fsize = (pt_end - pt_start) * PAGE_SIZE + trailer * 4; + + if (fsize > size) { + printk(KERN_WARNING "meye: oversized compressed frame %d\n", + fsize); + return -1; + } + + ptable_copy(buf, pt_start, fsize, MCHIP_NB_PAGES_MJPEG); + +#ifdef MEYE_JPEG_CORRECTION + + /* Some mchip generated jpeg frames are incorrect. In most + * (all ?) of those cases, the final EOI (0xff 0xd9) marker + * is not present at the end of the frame. + * + * Since adding the final marker is not enough to restore + * the jpeg integrity, we drop the frame. + */ + + for (i = fsize - 1; i > 0 && buf[i] == 0xff; i--) ; + + if (i < 2 || buf[i - 1] != 0xff || buf[i] != 0xd9) + return -1; + +#endif + + return fsize; +} + +/* take a picture into SDRAM */ +static void mchip_take_picture(void) +{ + int i; + + mchip_hic_stop(); + mchip_subsample(); + mchip_dma_setup(meye.mchip_dmahandle); + + mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_CAP); + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); + + mchip_delay(MCHIP_HIC_CMD, 0); + + for (i = 0; i < 100; ++i) { + if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE)) + break; + msleep(1); + } +} + +/* dma a previously taken picture into a buffer */ +static void mchip_get_picture(u8 *buf, int bufsize) +{ + u32 v; + int i; + + mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_OUT); + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); + + mchip_delay(MCHIP_HIC_CMD, 0); + for (i = 0; i < 100; ++i) { + if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE)) + break; + msleep(1); + } + for (i = 0; i < 4; ++i) { + v = mchip_get_frame(); + if (v & MCHIP_MM_FIR_RDY) { + mchip_cont_read_frame(v, buf, bufsize); + break; + } + mchip_free_frame(); + } +} + +/* start continuous dma capture */ +static void mchip_continuous_start(void) +{ + mchip_hic_stop(); + mchip_subsample(); + mchip_set_framerate(); + mchip_dma_setup(meye.mchip_dmahandle); + + meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT; + + mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_CONT_OUT); + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); + + mchip_delay(MCHIP_HIC_CMD, 0); +} + +/* compress one frame into a buffer */ +static int mchip_compress_frame(u8 *buf, int bufsize) +{ + u32 v; + int len = -1, i; + + mchip_vrj_setup(0x3f); + udelay(50); + + mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_COMP); + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); + + mchip_delay(MCHIP_HIC_CMD, 0); + for (i = 0; i < 100; ++i) { + if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE)) + break; + msleep(1); + } + + for (i = 0; i < 4; ++i) { + v = mchip_get_frame(); + if (v & MCHIP_MM_FIR_RDY) { + len = mchip_comp_read_frame(v, buf, bufsize); + break; + } + mchip_free_frame(); + } + return len; +} + +#if 0 +/* uncompress one image into a buffer */ +static int mchip_uncompress_frame(u8 *img, int imgsize, u8 *buf, int bufsize) +{ + mchip_vrj_setup(0x3f); + udelay(50); + + mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_DECOMP); + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); + + mchip_delay(MCHIP_HIC_CMD, 0); + + return mchip_comp_read_frame(buf, bufsize); +} +#endif + +/* start continuous compressed capture */ +static void mchip_cont_compression_start(void) +{ + mchip_hic_stop(); + mchip_vrj_setup(0x3f); + mchip_subsample(); + mchip_set_framerate(); + mchip_dma_setup(meye.mchip_dmahandle); + + meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP; + + mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_CONT_COMP); + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); + + mchip_delay(MCHIP_HIC_CMD, 0); +} + +/****************************************************************************/ +/* Interrupt handling */ +/****************************************************************************/ + +static irqreturn_t meye_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 v; + int reqnr; + static int sequence = 0; + + v = mchip_read(MCHIP_MM_INTA); + + if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_OUT && + meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP) + return IRQ_NONE; + +again: + v = mchip_get_frame(); + if (!(v & MCHIP_MM_FIR_RDY)) + return IRQ_HANDLED; + + if (meye.mchip_mode == MCHIP_HIC_MODE_CONT_OUT) { + if (kfifo_get(meye.grabq, (unsigned char *)&reqnr, + sizeof(int)) != sizeof(int)) { + mchip_free_frame(); + return IRQ_HANDLED; + } + mchip_cont_read_frame(v, meye.grab_fbuffer + gbufsize * reqnr, + mchip_hsize() * mchip_vsize() * 2); + meye.grab_buffer[reqnr].size = mchip_hsize() * mchip_vsize() * 2; + meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; + do_gettimeofday(&meye.grab_buffer[reqnr].timestamp); + meye.grab_buffer[reqnr].sequence = sequence++; + kfifo_put(meye.doneq, (unsigned char *)&reqnr, sizeof(int)); + wake_up_interruptible(&meye.proc_list); + } else { + int size; + size = mchip_comp_read_frame(v, meye.grab_temp, gbufsize); + if (size == -1) { + mchip_free_frame(); + goto again; + } + if (kfifo_get(meye.grabq, (unsigned char *)&reqnr, + sizeof(int)) != sizeof(int)) { + mchip_free_frame(); + goto again; + } + memcpy(meye.grab_fbuffer + gbufsize * reqnr, meye.grab_temp, + size); + meye.grab_buffer[reqnr].size = size; + meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; + do_gettimeofday(&meye.grab_buffer[reqnr].timestamp); + meye.grab_buffer[reqnr].sequence = sequence++; + kfifo_put(meye.doneq, (unsigned char *)&reqnr, sizeof(int)); + wake_up_interruptible(&meye.proc_list); + } + mchip_free_frame(); + goto again; +} + +/****************************************************************************/ +/* video4linux integration */ +/****************************************************************************/ + +static int meye_open(struct inode *inode, struct file *file) +{ + int i, err; + + err = video_exclusive_open(inode, file); + if (err < 0) + return err; + + mchip_hic_stop(); + + if (mchip_dma_alloc()) { + printk(KERN_ERR "meye: mchip framebuffer allocation failed\n"); + video_exclusive_release(inode, file); + return -ENOBUFS; + } + + for (i = 0; i < MEYE_MAX_BUFNBRS; i++) + meye.grab_buffer[i].state = MEYE_BUF_UNUSED; + kfifo_reset(meye.grabq); + kfifo_reset(meye.doneq); + return 0; +} + +static int meye_release(struct inode *inode, struct file *file) +{ + mchip_hic_stop(); + mchip_dma_free(); + video_exclusive_release(inode, file); + return 0; +} + +static int meye_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + switch (cmd) { + + case VIDIOCGCAP: { + struct video_capability *b = arg; + strcpy(b->name,meye.video_dev->name); + b->type = VID_TYPE_CAPTURE; + b->channels = 1; + b->audios = 0; + b->maxwidth = 640; + b->maxheight = 480; + b->minwidth = 320; + b->minheight = 240; + break; + } + + case VIDIOCGCHAN: { + struct video_channel *v = arg; + v->flags = 0; + v->tuners = 0; + v->type = VIDEO_TYPE_CAMERA; + if (v->channel != 0) + return -EINVAL; + strcpy(v->name,"Camera"); + break; + } + + case VIDIOCSCHAN: { + struct video_channel *v = arg; + if (v->channel != 0) + return -EINVAL; + break; + } + + case VIDIOCGPICT: { + struct video_picture *p = arg; + *p = meye.picture; + break; + } + + case VIDIOCSPICT: { + struct video_picture *p = arg; + if (p->depth != 16) + return -EINVAL; + if (p->palette != VIDEO_PALETTE_YUV422) + return -EINVAL; + mutex_lock(&meye.lock); + sonypi_camera_command(SONYPI_COMMAND_SETCAMERABRIGHTNESS, + p->brightness >> 10); + sonypi_camera_command(SONYPI_COMMAND_SETCAMERAHUE, + p->hue >> 10); + sonypi_camera_command(SONYPI_COMMAND_SETCAMERACOLOR, + p->colour >> 10); + sonypi_camera_command(SONYPI_COMMAND_SETCAMERACONTRAST, + p->contrast >> 10); + meye.picture = *p; + mutex_unlock(&meye.lock); + break; + } + + case VIDIOCSYNC: { + int *i = arg; + int unused; + + if (*i < 0 || *i >= gbuffers) + return -EINVAL; + + mutex_lock(&meye.lock); + + switch (meye.grab_buffer[*i].state) { + + case MEYE_BUF_UNUSED: + mutex_unlock(&meye.lock); + return -EINVAL; + case MEYE_BUF_USING: + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&meye.lock); + return -EAGAIN; + } + if (wait_event_interruptible(meye.proc_list, + (meye.grab_buffer[*i].state != MEYE_BUF_USING))) { + mutex_unlock(&meye.lock); + return -EINTR; + } + /* fall through */ + case MEYE_BUF_DONE: + meye.grab_buffer[*i].state = MEYE_BUF_UNUSED; + kfifo_get(meye.doneq, (unsigned char *)&unused, sizeof(int)); + } + mutex_unlock(&meye.lock); + break; + } + + case VIDIOCMCAPTURE: { + struct video_mmap *vm = arg; + int restart = 0; + + if (vm->frame >= gbuffers || vm->frame < 0) + return -EINVAL; + if (vm->format != VIDEO_PALETTE_YUV422) + return -EINVAL; + if (vm->height * vm->width * 2 > gbufsize) + return -EINVAL; + if (!meye.grab_fbuffer) + return -EINVAL; + if (meye.grab_buffer[vm->frame].state != MEYE_BUF_UNUSED) + return -EBUSY; + + mutex_lock(&meye.lock); + if (vm->width == 640 && vm->height == 480) { + if (meye.params.subsample) { + meye.params.subsample = 0; + restart = 1; + } + } else if (vm->width == 320 && vm->height == 240) { + if (!meye.params.subsample) { + meye.params.subsample = 1; + restart = 1; + } + } else { + mutex_unlock(&meye.lock); + return -EINVAL; + } + + if (restart || meye.mchip_mode != MCHIP_HIC_MODE_CONT_OUT) + mchip_continuous_start(); + meye.grab_buffer[vm->frame].state = MEYE_BUF_USING; + kfifo_put(meye.grabq, (unsigned char *)&vm->frame, sizeof(int)); + mutex_unlock(&meye.lock); + break; + } + + case VIDIOCGMBUF: { + struct video_mbuf *vm = arg; + int i; + + memset(vm, 0 , sizeof(*vm)); + vm->size = gbufsize * gbuffers; + vm->frames = gbuffers; + for (i = 0; i < gbuffers; i++) + vm->offsets[i] = i * gbufsize; + break; + } + + case MEYEIOC_G_PARAMS: { + struct meye_params *p = arg; + *p = meye.params; + break; + } + + case MEYEIOC_S_PARAMS: { + struct meye_params *jp = arg; + if (jp->subsample > 1) + return -EINVAL; + if (jp->quality > 10) + return -EINVAL; + if (jp->sharpness > 63 || jp->agc > 63 || jp->picture > 63) + return -EINVAL; + if (jp->framerate > 31) + return -EINVAL; + mutex_lock(&meye.lock); + if (meye.params.subsample != jp->subsample || + meye.params.quality != jp->quality) + mchip_hic_stop(); /* need restart */ + meye.params = *jp; + sonypi_camera_command(SONYPI_COMMAND_SETCAMERASHARPNESS, + meye.params.sharpness); + sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC, + meye.params.agc); + sonypi_camera_command(SONYPI_COMMAND_SETCAMERAPICTURE, + meye.params.picture); + mutex_unlock(&meye.lock); + break; + } + + case MEYEIOC_QBUF_CAPT: { + int *nb = arg; + + if (!meye.grab_fbuffer) + return -EINVAL; + if (*nb >= gbuffers) + return -EINVAL; + if (*nb < 0) { + /* stop capture */ + mchip_hic_stop(); + return 0; + } + if (meye.grab_buffer[*nb].state != MEYE_BUF_UNUSED) + return -EBUSY; + mutex_lock(&meye.lock); + if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP) + mchip_cont_compression_start(); + meye.grab_buffer[*nb].state = MEYE_BUF_USING; + kfifo_put(meye.grabq, (unsigned char *)nb, sizeof(int)); + mutex_unlock(&meye.lock); + break; + } + + case MEYEIOC_SYNC: { + int *i = arg; + int unused; + + if (*i < 0 || *i >= gbuffers) + return -EINVAL; + + mutex_lock(&meye.lock); + switch (meye.grab_buffer[*i].state) { + + case MEYE_BUF_UNUSED: + mutex_unlock(&meye.lock); + return -EINVAL; + case MEYE_BUF_USING: + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&meye.lock); + return -EAGAIN; + } + if (wait_event_interruptible(meye.proc_list, + (meye.grab_buffer[*i].state != MEYE_BUF_USING))) { + mutex_unlock(&meye.lock); + return -EINTR; + } + /* fall through */ + case MEYE_BUF_DONE: + meye.grab_buffer[*i].state = MEYE_BUF_UNUSED; + kfifo_get(meye.doneq, (unsigned char *)&unused, sizeof(int)); + } + *i = meye.grab_buffer[*i].size; + mutex_unlock(&meye.lock); + break; + } + + case MEYEIOC_STILLCAPT: { + + if (!meye.grab_fbuffer) + return -EINVAL; + if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED) + return -EBUSY; + mutex_lock(&meye.lock); + meye.grab_buffer[0].state = MEYE_BUF_USING; + mchip_take_picture(); + mchip_get_picture( + meye.grab_fbuffer, + mchip_hsize() * mchip_vsize() * 2); + meye.grab_buffer[0].state = MEYE_BUF_DONE; + mutex_unlock(&meye.lock); + break; + } + + case MEYEIOC_STILLJCAPT: { + int *len = arg; + + if (!meye.grab_fbuffer) + return -EINVAL; + if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED) + return -EBUSY; + mutex_lock(&meye.lock); + meye.grab_buffer[0].state = MEYE_BUF_USING; + *len = -1; + while (*len == -1) { + mchip_take_picture(); + *len = mchip_compress_frame(meye.grab_fbuffer, gbufsize); + } + meye.grab_buffer[0].state = MEYE_BUF_DONE; + mutex_unlock(&meye.lock); + break; + } + + case VIDIOC_QUERYCAP: { + struct v4l2_capability *cap = arg; + + if (forcev4l1) + return -EINVAL; + + memset(cap, 0, sizeof(*cap)); + strcpy(cap->driver, "meye"); + strcpy(cap->card, "meye"); + sprintf(cap->bus_info, "PCI:%s", pci_name(meye.mchip_dev)); + cap->version = (MEYE_DRIVER_MAJORVERSION << 8) + + MEYE_DRIVER_MINORVERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING; + break; + } + + case VIDIOC_ENUMINPUT: { + struct v4l2_input *i = arg; + + if (i->index != 0) + return -EINVAL; + memset(i, 0, sizeof(*i)); + i->index = 0; + strcpy(i->name, "Camera"); + i->type = V4L2_INPUT_TYPE_CAMERA; + break; + } + + case VIDIOC_G_INPUT: { + int *i = arg; + + *i = 0; + break; + } + + case VIDIOC_S_INPUT: { + int *i = arg; + + if (*i != 0) + return -EINVAL; + break; + } + + case VIDIOC_QUERYCTRL: { + struct v4l2_queryctrl *c = arg; + + switch (c->id) { + + case V4L2_CID_BRIGHTNESS: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Brightness"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 32; + c->flags = 0; + break; + case V4L2_CID_HUE: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Hue"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 32; + c->flags = 0; + break; + case V4L2_CID_CONTRAST: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Contrast"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 32; + c->flags = 0; + break; + case V4L2_CID_SATURATION: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Saturation"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 32; + c->flags = 0; + break; + case V4L2_CID_AGC: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Agc"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 48; + c->flags = 0; + break; + case V4L2_CID_SHARPNESS: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Sharpness"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 32; + c->flags = 0; + break; + case V4L2_CID_PICTURE: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Picture"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 0; + c->flags = 0; + break; + case V4L2_CID_JPEGQUAL: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "JPEG quality"); + c->minimum = 0; + c->maximum = 10; + c->step = 1; + c->default_value = 8; + c->flags = 0; + break; + case V4L2_CID_FRAMERATE: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Framerate"); + c->minimum = 0; + c->maximum = 31; + c->step = 1; + c->default_value = 0; + c->flags = 0; + break; + default: + return -EINVAL; + } + break; + } + + case VIDIOC_S_CTRL: { + struct v4l2_control *c = arg; + + mutex_lock(&meye.lock); + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + sonypi_camera_command( + SONYPI_COMMAND_SETCAMERABRIGHTNESS, c->value); + meye.picture.brightness = c->value << 10; + break; + case V4L2_CID_HUE: + sonypi_camera_command( + SONYPI_COMMAND_SETCAMERAHUE, c->value); + meye.picture.hue = c->value << 10; + break; + case V4L2_CID_CONTRAST: + sonypi_camera_command( + SONYPI_COMMAND_SETCAMERACONTRAST, c->value); + meye.picture.contrast = c->value << 10; + break; + case V4L2_CID_SATURATION: + sonypi_camera_command( + SONYPI_COMMAND_SETCAMERACOLOR, c->value); + meye.picture.colour = c->value << 10; + break; + case V4L2_CID_AGC: + sonypi_camera_command( + SONYPI_COMMAND_SETCAMERAAGC, c->value); + meye.params.agc = c->value; + break; + case V4L2_CID_SHARPNESS: + sonypi_camera_command( + SONYPI_COMMAND_SETCAMERASHARPNESS, c->value); + meye.params.sharpness = c->value; + break; + case V4L2_CID_PICTURE: + sonypi_camera_command( + SONYPI_COMMAND_SETCAMERAPICTURE, c->value); + meye.params.picture = c->value; + break; + case V4L2_CID_JPEGQUAL: + meye.params.quality = c->value; + break; + case V4L2_CID_FRAMERATE: + meye.params.framerate = c->value; + break; + default: + mutex_unlock(&meye.lock); + return -EINVAL; + } + mutex_unlock(&meye.lock); + break; + } + + case VIDIOC_G_CTRL: { + struct v4l2_control *c = arg; + + mutex_lock(&meye.lock); + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + c->value = meye.picture.brightness >> 10; + break; + case V4L2_CID_HUE: + c->value = meye.picture.hue >> 10; + break; + case V4L2_CID_CONTRAST: + c->value = meye.picture.contrast >> 10; + break; + case V4L2_CID_SATURATION: + c->value = meye.picture.colour >> 10; + break; + case V4L2_CID_AGC: + c->value = meye.params.agc; + break; + case V4L2_CID_SHARPNESS: + c->value = meye.params.sharpness; + break; + case V4L2_CID_PICTURE: + c->value = meye.params.picture; + break; + case V4L2_CID_JPEGQUAL: + c->value = meye.params.quality; + break; + case V4L2_CID_FRAMERATE: + c->value = meye.params.framerate; + break; + default: + mutex_unlock(&meye.lock); + return -EINVAL; + } + mutex_unlock(&meye.lock); + break; + } + + case VIDIOC_ENUM_FMT: { + struct v4l2_fmtdesc *f = arg; + + if (f->index > 1) + return -EINVAL; + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (f->index == 0) { + /* standard YUV 422 capture */ + memset(f, 0, sizeof(*f)); + f->index = 0; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->flags = 0; + strcpy(f->description, "YUV422"); + f->pixelformat = V4L2_PIX_FMT_YUYV; + } else { + /* compressed MJPEG capture */ + memset(f, 0, sizeof(*f)); + f->index = 1; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->flags = V4L2_FMT_FLAG_COMPRESSED; + strcpy(f->description, "MJPEG"); + f->pixelformat = V4L2_PIX_FMT_MJPEG; + } + break; + } + + case VIDIOC_TRY_FMT: { + struct v4l2_format *f = arg; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && + f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) + return -EINVAL; + if (f->fmt.pix.field != V4L2_FIELD_ANY && + f->fmt.pix.field != V4L2_FIELD_NONE) + return -EINVAL; + f->fmt.pix.field = V4L2_FIELD_NONE; + if (f->fmt.pix.width <= 320) { + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + } else { + f->fmt.pix.width = 640; + f->fmt.pix.height = 480; + } + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; + break; + } + + case VIDIOC_G_FMT: { + struct v4l2_format *f = arg; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + switch (meye.mchip_mode) { + case MCHIP_HIC_MODE_CONT_OUT: + default: + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + break; + case MCHIP_HIC_MODE_CONT_COMP: + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; + break; + } + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = mchip_hsize(); + f->fmt.pix.height = mchip_vsize(); + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; + break; + } + + case VIDIOC_S_FMT: { + struct v4l2_format *f = arg; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && + f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) + return -EINVAL; + if (f->fmt.pix.field != V4L2_FIELD_ANY && + f->fmt.pix.field != V4L2_FIELD_NONE) + return -EINVAL; + f->fmt.pix.field = V4L2_FIELD_NONE; + mutex_lock(&meye.lock); + if (f->fmt.pix.width <= 320) { + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + meye.params.subsample = 1; + } else { + f->fmt.pix.width = 640; + f->fmt.pix.height = 480; + meye.params.subsample = 0; + } + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YUYV: + meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT; + break; + case V4L2_PIX_FMT_MJPEG: + meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP; + break; + } + mutex_unlock(&meye.lock); + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; + + break; + } + + case VIDIOC_REQBUFS: { + struct v4l2_requestbuffers *req = arg; + int i; + + if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (req->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + if (meye.grab_fbuffer && req->count == gbuffers) { + /* already allocated, no modifications */ + break; + } + mutex_lock(&meye.lock); + if (meye.grab_fbuffer) { + for (i = 0; i < gbuffers; i++) + if (meye.vma_use_count[i]) { + mutex_unlock(&meye.lock); + return -EINVAL; + } + rvfree(meye.grab_fbuffer, gbuffers * gbufsize); + meye.grab_fbuffer = NULL; + } + gbuffers = max(2, min((int)req->count, MEYE_MAX_BUFNBRS)); + req->count = gbuffers; + meye.grab_fbuffer = rvmalloc(gbuffers * gbufsize); + if (!meye.grab_fbuffer) { + printk(KERN_ERR "meye: v4l framebuffer allocation" + " failed\n"); + mutex_unlock(&meye.lock); + return -ENOMEM; + } + for (i = 0; i < gbuffers; i++) + meye.vma_use_count[i] = 0; + mutex_unlock(&meye.lock); + break; + } + + case VIDIOC_QUERYBUF: { + struct v4l2_buffer *buf = arg; + int index = buf->index; + + if (index < 0 || index >= gbuffers) + return -EINVAL; + memset(buf, 0, sizeof(*buf)); + buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->index = index; + buf->bytesused = meye.grab_buffer[index].size; + buf->flags = V4L2_BUF_FLAG_MAPPED; + if (meye.grab_buffer[index].state == MEYE_BUF_USING) + buf->flags |= V4L2_BUF_FLAG_QUEUED; + if (meye.grab_buffer[index].state == MEYE_BUF_DONE) + buf->flags |= V4L2_BUF_FLAG_DONE; + buf->field = V4L2_FIELD_NONE; + buf->timestamp = meye.grab_buffer[index].timestamp; + buf->sequence = meye.grab_buffer[index].sequence; + buf->memory = V4L2_MEMORY_MMAP; + buf->m.offset = index * gbufsize; + buf->length = gbufsize; + break; + } + + case VIDIOC_QBUF: { + struct v4l2_buffer *buf = arg; + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + if (buf->index < 0 || buf->index >= gbuffers) + return -EINVAL; + if (meye.grab_buffer[buf->index].state != MEYE_BUF_UNUSED) + return -EINVAL; + mutex_lock(&meye.lock); + buf->flags |= V4L2_BUF_FLAG_QUEUED; + buf->flags &= ~V4L2_BUF_FLAG_DONE; + meye.grab_buffer[buf->index].state = MEYE_BUF_USING; + kfifo_put(meye.grabq, (unsigned char *)&buf->index, sizeof(int)); + mutex_unlock(&meye.lock); + break; + } + + case VIDIOC_DQBUF: { + struct v4l2_buffer *buf = arg; + int reqnr; + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + mutex_lock(&meye.lock); + if (kfifo_len(meye.doneq) == 0 && file->f_flags & O_NONBLOCK) { + mutex_unlock(&meye.lock); + return -EAGAIN; + } + if (wait_event_interruptible(meye.proc_list, + kfifo_len(meye.doneq) != 0) < 0) { + mutex_unlock(&meye.lock); + return -EINTR; + } + if (!kfifo_get(meye.doneq, (unsigned char *)&reqnr, + sizeof(int))) { + mutex_unlock(&meye.lock); + return -EBUSY; + } + if (meye.grab_buffer[reqnr].state != MEYE_BUF_DONE) { + mutex_unlock(&meye.lock); + return -EINVAL; + } + buf->index = reqnr; + buf->bytesused = meye.grab_buffer[reqnr].size; + buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->field = V4L2_FIELD_NONE; + buf->timestamp = meye.grab_buffer[reqnr].timestamp; + buf->sequence = meye.grab_buffer[reqnr].sequence; + buf->memory = V4L2_MEMORY_MMAP; + buf->m.offset = reqnr * gbufsize; + buf->length = gbufsize; + meye.grab_buffer[reqnr].state = MEYE_BUF_UNUSED; + mutex_unlock(&meye.lock); + break; + } + + case VIDIOC_STREAMON: { + mutex_lock(&meye.lock); + switch (meye.mchip_mode) { + case MCHIP_HIC_MODE_CONT_OUT: + mchip_continuous_start(); + break; + case MCHIP_HIC_MODE_CONT_COMP: + mchip_cont_compression_start(); + break; + default: + mutex_unlock(&meye.lock); + return -EINVAL; + } + mutex_unlock(&meye.lock); + break; + } + + case VIDIOC_STREAMOFF: { + int i; + + mutex_lock(&meye.lock); + mchip_hic_stop(); + kfifo_reset(meye.grabq); + kfifo_reset(meye.doneq); + for (i = 0; i < MEYE_MAX_BUFNBRS; i++) + meye.grab_buffer[i].state = MEYE_BUF_UNUSED; + mutex_unlock(&meye.lock); + break; + } + + /* + * XXX what about private snapshot ioctls ? + * Do they need to be converted to V4L2 ? + */ + + default: + return -ENOIOCTLCMD; + } + + return 0; +} + +static int meye_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, meye_do_ioctl); +} + +static unsigned int meye_poll(struct file *file, poll_table *wait) +{ + unsigned int res = 0; + + mutex_lock(&meye.lock); + poll_wait(file, &meye.proc_list, wait); + if (kfifo_len(meye.doneq)) + res = POLLIN | POLLRDNORM; + mutex_unlock(&meye.lock); + return res; +} + +static void meye_vm_open(struct vm_area_struct *vma) +{ + int idx = (int)vma->vm_private_data; + meye.vma_use_count[idx]++; +} + +static void meye_vm_close(struct vm_area_struct *vma) +{ + int idx = (int)vma->vm_private_data; + meye.vma_use_count[idx]--; +} + +static struct vm_operations_struct meye_vm_ops = { + .open = meye_vm_open, + .close = meye_vm_close, +}; + +static int meye_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long page, pos; + + mutex_lock(&meye.lock); + if (size > gbuffers * gbufsize) { + mutex_unlock(&meye.lock); + return -EINVAL; + } + if (!meye.grab_fbuffer) { + int i; + + /* lazy allocation */ + meye.grab_fbuffer = rvmalloc(gbuffers*gbufsize); + if (!meye.grab_fbuffer) { + printk(KERN_ERR "meye: v4l framebuffer allocation failed\n"); + mutex_unlock(&meye.lock); + return -ENOMEM; + } + for (i = 0; i < gbuffers; i++) + meye.vma_use_count[i] = 0; + } + pos = (unsigned long)meye.grab_fbuffer + offset; + + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { + mutex_unlock(&meye.lock); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + vma->vm_ops = &meye_vm_ops; + vma->vm_flags &= ~VM_IO; /* not I/O memory */ + vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */ + vma->vm_private_data = (void *) (offset / gbufsize); + meye_vm_open(vma); + + mutex_unlock(&meye.lock); + return 0; +} + +static struct file_operations meye_fops = { + .owner = THIS_MODULE, + .open = meye_open, + .release = meye_release, + .mmap = meye_mmap, + .ioctl = meye_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .poll = meye_poll, + .llseek = no_llseek, +}; + +static struct video_device meye_template = { + .owner = THIS_MODULE, + .name = "meye", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_MEYE, + .fops = &meye_fops, + .release = video_device_release, + .minor = -1, +}; + +#ifdef CONFIG_PM +static int meye_suspend(struct pci_dev *pdev, pm_message_t state) +{ + pci_save_state(pdev); + meye.pm_mchip_mode = meye.mchip_mode; + mchip_hic_stop(); + mchip_set(MCHIP_MM_INTA, 0x0); + return 0; +} + +static int meye_resume(struct pci_dev *pdev) +{ + pci_restore_state(pdev); + pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1); + + mchip_delay(MCHIP_HIC_CMD, 0); + mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE); + msleep(1); + mchip_set(MCHIP_VRJ_SOFT_RESET, 1); + msleep(1); + mchip_set(MCHIP_MM_PCI_MODE, 5); + msleep(1); + mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK); + + switch (meye.pm_mchip_mode) { + case MCHIP_HIC_MODE_CONT_OUT: + mchip_continuous_start(); + break; + case MCHIP_HIC_MODE_CONT_COMP: + mchip_cont_compression_start(); + break; + } + return 0; +} +#endif + +static int __devinit meye_probe(struct pci_dev *pcidev, + const struct pci_device_id *ent) +{ + int ret = -EBUSY; + unsigned long mchip_adr; + u8 revision; + + if (meye.mchip_dev != NULL) { + printk(KERN_ERR "meye: only one device allowed!\n"); + goto outnotdev; + } + + meye.mchip_dev = pcidev; + meye.video_dev = video_device_alloc(); + if (!meye.video_dev) { + printk(KERN_ERR "meye: video_device_alloc() failed!\n"); + goto outnotdev; + } + + ret = -ENOMEM; + meye.grab_temp = vmalloc(MCHIP_NB_PAGES_MJPEG * PAGE_SIZE); + if (!meye.grab_temp) { + printk(KERN_ERR "meye: grab buffer allocation failed\n"); + goto outvmalloc; + } + + spin_lock_init(&meye.grabq_lock); + meye.grabq = kfifo_alloc(sizeof(int) * MEYE_MAX_BUFNBRS, GFP_KERNEL, + &meye.grabq_lock); + if (IS_ERR(meye.grabq)) { + printk(KERN_ERR "meye: fifo allocation failed\n"); + goto outkfifoalloc1; + } + spin_lock_init(&meye.doneq_lock); + meye.doneq = kfifo_alloc(sizeof(int) * MEYE_MAX_BUFNBRS, GFP_KERNEL, + &meye.doneq_lock); + if (IS_ERR(meye.doneq)) { + printk(KERN_ERR "meye: fifo allocation failed\n"); + goto outkfifoalloc2; + } + + memcpy(meye.video_dev, &meye_template, sizeof(meye_template)); + meye.video_dev->dev = &meye.mchip_dev->dev; + + if ((ret = sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 1))) { + printk(KERN_ERR "meye: unable to power on the camera\n"); + printk(KERN_ERR "meye: did you enable the camera in " + "sonypi using the module options ?\n"); + goto outsonypienable; + } + + ret = -EIO; + if ((ret = pci_enable_device(meye.mchip_dev))) { + printk(KERN_ERR "meye: pci_enable_device failed\n"); + goto outenabledev; + } + + mchip_adr = pci_resource_start(meye.mchip_dev,0); + if (!mchip_adr) { + printk(KERN_ERR "meye: mchip has no device base address\n"); + goto outregions; + } + if (!request_mem_region(pci_resource_start(meye.mchip_dev, 0), + pci_resource_len(meye.mchip_dev, 0), + "meye")) { + printk(KERN_ERR "meye: request_mem_region failed\n"); + goto outregions; + } + meye.mchip_mmregs = ioremap(mchip_adr, MCHIP_MM_REGS); + if (!meye.mchip_mmregs) { + printk(KERN_ERR "meye: ioremap failed\n"); + goto outremap; + } + + meye.mchip_irq = pcidev->irq; + if (request_irq(meye.mchip_irq, meye_irq, + SA_INTERRUPT | SA_SHIRQ, "meye", meye_irq)) { + printk(KERN_ERR "meye: request_irq failed\n"); + goto outreqirq; + } + + pci_read_config_byte(meye.mchip_dev, PCI_REVISION_ID, &revision); + pci_write_config_byte(meye.mchip_dev, PCI_CACHE_LINE_SIZE, 8); + pci_write_config_byte(meye.mchip_dev, PCI_LATENCY_TIMER, 64); + + pci_set_master(meye.mchip_dev); + + /* Ask the camera to perform a soft reset. */ + pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1); + + mchip_delay(MCHIP_HIC_CMD, 0); + mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE); + + msleep(1); + mchip_set(MCHIP_VRJ_SOFT_RESET, 1); + + msleep(1); + mchip_set(MCHIP_MM_PCI_MODE, 5); + + msleep(1); + mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK); + + if (video_register_device(meye.video_dev, VFL_TYPE_GRABBER, + video_nr) < 0) { + printk(KERN_ERR "meye: video_register_device failed\n"); + goto outvideoreg; + } + + mutex_init(&meye.lock); + init_waitqueue_head(&meye.proc_list); + meye.picture.depth = 16; + meye.picture.palette = VIDEO_PALETTE_YUV422; + meye.picture.brightness = 32 << 10; + meye.picture.hue = 32 << 10; + meye.picture.colour = 32 << 10; + meye.picture.contrast = 32 << 10; + meye.picture.whiteness = 0; + meye.params.subsample = 0; + meye.params.quality = 8; + meye.params.sharpness = 32; + meye.params.agc = 48; + meye.params.picture = 0; + meye.params.framerate = 0; + + sonypi_camera_command(SONYPI_COMMAND_SETCAMERABRIGHTNESS, 32); + sonypi_camera_command(SONYPI_COMMAND_SETCAMERAHUE, 32); + sonypi_camera_command(SONYPI_COMMAND_SETCAMERACOLOR, 32); + sonypi_camera_command(SONYPI_COMMAND_SETCAMERACONTRAST, 32); + sonypi_camera_command(SONYPI_COMMAND_SETCAMERASHARPNESS, 32); + sonypi_camera_command(SONYPI_COMMAND_SETCAMERAPICTURE, 0); + sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC, 48); + + printk(KERN_INFO "meye: Motion Eye Camera Driver v%s.\n", + MEYE_DRIVER_VERSION); + printk(KERN_INFO "meye: mchip KL5A72002 rev. %d, base %lx, irq %d\n", + revision, mchip_adr, meye.mchip_irq); + + return 0; + +outvideoreg: + free_irq(meye.mchip_irq, meye_irq); +outreqirq: + iounmap(meye.mchip_mmregs); +outremap: + release_mem_region(pci_resource_start(meye.mchip_dev, 0), + pci_resource_len(meye.mchip_dev, 0)); +outregions: + pci_disable_device(meye.mchip_dev); +outenabledev: + sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0); +outsonypienable: + kfifo_free(meye.doneq); +outkfifoalloc2: + kfifo_free(meye.grabq); +outkfifoalloc1: + vfree(meye.grab_temp); +outvmalloc: + video_device_release(meye.video_dev); +outnotdev: + return ret; +} + +static void __devexit meye_remove(struct pci_dev *pcidev) +{ + video_unregister_device(meye.video_dev); + + mchip_hic_stop(); + + mchip_dma_free(); + + /* disable interrupts */ + mchip_set(MCHIP_MM_INTA, 0x0); + + free_irq(meye.mchip_irq, meye_irq); + + iounmap(meye.mchip_mmregs); + + release_mem_region(pci_resource_start(meye.mchip_dev, 0), + pci_resource_len(meye.mchip_dev, 0)); + + pci_disable_device(meye.mchip_dev); + + sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0); + + kfifo_free(meye.doneq); + kfifo_free(meye.grabq); + + vfree(meye.grab_temp); + + if (meye.grab_fbuffer) { + rvfree(meye.grab_fbuffer, gbuffers*gbufsize); + meye.grab_fbuffer = NULL; + } + + printk(KERN_INFO "meye: removed\n"); +} + +static struct pci_device_id meye_pci_tbl[] = { + { PCI_VENDOR_ID_KAWASAKI, PCI_DEVICE_ID_MCHIP_KL5A72002, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(pci, meye_pci_tbl); + +static struct pci_driver meye_driver = { + .name = "meye", + .id_table = meye_pci_tbl, + .probe = meye_probe, + .remove = __devexit_p(meye_remove), +#ifdef CONFIG_PM + .suspend = meye_suspend, + .resume = meye_resume, +#endif +}; + +static int __init meye_init(void) +{ + gbuffers = max(2, min((int)gbuffers, MEYE_MAX_BUFNBRS)); + if (gbufsize < 0 || gbufsize > MEYE_MAX_BUFSIZE) + gbufsize = MEYE_MAX_BUFSIZE; + gbufsize = PAGE_ALIGN(gbufsize); + printk(KERN_INFO "meye: using %d buffers with %dk (%dk total)" + "for capture\n", + gbuffers, + gbufsize / 1024, gbuffers * gbufsize / 1024); + return pci_register_driver(&meye_driver); +} + +static void __exit meye_exit(void) +{ + pci_unregister_driver(&meye_driver); +} + +module_init(meye_init); +module_exit(meye_exit); diff --git a/linux/drivers/media/video/meye.h b/linux/drivers/media/video/meye.h new file mode 100644 index 000000000..0d09a0e38 --- /dev/null +++ b/linux/drivers/media/video/meye.h @@ -0,0 +1,320 @@ +/* + * Motion Eye video4linux driver for Sony Vaio PictureBook + * + * Copyright (C) 2001-2004 Stelian Pop + * + * Copyright (C) 2001-2002 Alcôve + * + * Copyright (C) 2000 Andrew Tridgell + * + * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras. + * + * Some parts borrowed from various video4linux drivers, especially + * bttv-driver.c and zoran.c, see original files for credits. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _MEYE_PRIV_H_ +#define _MEYE_PRIV_H_ + +#define MEYE_DRIVER_MAJORVERSION 1 +#define MEYE_DRIVER_MINORVERSION 13 + +#define MEYE_DRIVER_VERSION __stringify(MEYE_DRIVER_MAJORVERSION) "." \ + __stringify(MEYE_DRIVER_MINORVERSION) + +#include +#include +#include +#include + +/****************************************************************************/ +/* Motion JPEG chip registers */ +/****************************************************************************/ + +/* Motion JPEG chip PCI configuration registers */ +#define MCHIP_PCI_POWER_CSR 0x54 +#define MCHIP_PCI_MCORE_STATUS 0x60 /* see HIC_STATUS */ +#define MCHIP_PCI_HOSTUSEREQ_SET 0x64 +#define MCHIP_PCI_HOSTUSEREQ_CLR 0x68 +#define MCHIP_PCI_LOWPOWER_SET 0x6c +#define MCHIP_PCI_LOWPOWER_CLR 0x70 +#define MCHIP_PCI_SOFTRESET_SET 0x74 + +/* Motion JPEG chip memory mapped registers */ +#define MCHIP_MM_REGS 0x200 /* 512 bytes */ +#define MCHIP_REG_TIMEOUT 1000 /* reg access, ~us */ +#define MCHIP_MCC_VRJ_TIMEOUT 1000 /* MCC & VRJ access */ + +#define MCHIP_MM_PCI_MODE 0x00 /* PCI access mode */ +#define MCHIP_MM_PCI_MODE_RETRY 0x00000001 /* retry mode */ +#define MCHIP_MM_PCI_MODE_MASTER 0x00000002 /* master access */ +#define MCHIP_MM_PCI_MODE_READ_LINE 0x00000004 /* read line */ + +#define MCHIP_MM_INTA 0x04 /* Int status/mask */ +#define MCHIP_MM_INTA_MCC 0x00000001 /* MCC interrupt */ +#define MCHIP_MM_INTA_VRJ 0x00000002 /* VRJ interrupt */ +#define MCHIP_MM_INTA_HIC_1 0x00000004 /* one frame done */ +#define MCHIP_MM_INTA_HIC_1_MASK 0x00000400 /* 1: enable */ +#define MCHIP_MM_INTA_HIC_END 0x00000008 /* all frames done */ +#define MCHIP_MM_INTA_HIC_END_MASK 0x00000800 +#define MCHIP_MM_INTA_JPEG 0x00000010 /* decompress. error */ +#define MCHIP_MM_INTA_JPEG_MASK 0x00001000 +#define MCHIP_MM_INTA_CAPTURE 0x00000020 /* capture end */ +#define MCHIP_MM_INTA_PCI_ERR 0x00000040 /* PCI error */ +#define MCHIP_MM_INTA_PCI_ERR_MASK 0x00004000 + +#define MCHIP_MM_PT_ADDR 0x08 /* page table address*/ + /* n*4kB */ +#define MCHIP_NB_PAGES 1024 /* pages for display */ +#define MCHIP_NB_PAGES_MJPEG 256 /* pages for mjpeg */ + +#define MCHIP_MM_FIR(n) (0x0c+(n)*4) /* Frame info 0-3 */ +#define MCHIP_MM_FIR_RDY 0x00000001 /* frame ready */ +#define MCHIP_MM_FIR_FAILFR_MASK 0xf8000000 /* # of failed frames */ +#define MCHIP_MM_FIR_FAILFR_SHIFT 27 + + /* continuous comp/decomp mode */ +#define MCHIP_MM_FIR_C_ENDL_MASK 0x000007fe /* end DW [10] */ +#define MCHIP_MM_FIR_C_ENDL_SHIFT 1 +#define MCHIP_MM_FIR_C_ENDP_MASK 0x0007f800 /* end page [8] */ +#define MCHIP_MM_FIR_C_ENDP_SHIFT 11 +#define MCHIP_MM_FIR_C_STARTP_MASK 0x07f80000 /* start page [8] */ +#define MCHIP_MM_FIR_C_STARTP_SHIFT 19 + + /* continuous picture output mode */ +#define MCHIP_MM_FIR_O_STARTP_MASK 0x7ffe0000 /* start page [10] */ +#define MCHIP_MM_FIR_O_STARTP_SHIFT 17 + +#define MCHIP_MM_FIFO_DATA 0x1c /* PCI TGT FIFO data */ +#define MCHIP_MM_FIFO_STATUS 0x20 /* PCI TGT FIFO stat */ +#define MCHIP_MM_FIFO_MASK 0x00000003 +#define MCHIP_MM_FIFO_WAIT_OR_READY 0x00000002 /* Bits common to WAIT & READY*/ +#define MCHIP_MM_FIFO_IDLE 0x0 /* HIC idle */ +#define MCHIP_MM_FIFO_IDLE1 0x1 /* idem ??? */ +#define MCHIP_MM_FIFO_WAIT 0x2 /* wait request */ +#define MCHIP_MM_FIFO_READY 0x3 /* data ready */ + +#define MCHIP_HIC_HOST_USEREQ 0x40 /* host uses MCORE */ + +#define MCHIP_HIC_TP_BUSY 0x44 /* taking picture */ + +#define MCHIP_HIC_PIC_SAVED 0x48 /* pic in SDRAM */ + +#define MCHIP_HIC_LOWPOWER 0x4c /* clock stopped */ + +#define MCHIP_HIC_CTL 0x50 /* HIC control */ +#define MCHIP_HIC_CTL_SOFT_RESET 0x00000001 /* MCORE reset */ +#define MCHIP_HIC_CTL_MCORE_RDY 0x00000002 /* MCORE ready */ + +#define MCHIP_HIC_CMD 0x54 /* HIC command */ +#define MCHIP_HIC_CMD_BITS 0x00000003 /* cmd width=[1:0]*/ +#define MCHIP_HIC_CMD_NOOP 0x0 +#define MCHIP_HIC_CMD_START 0x1 +#define MCHIP_HIC_CMD_STOP 0x2 + +#define MCHIP_HIC_MODE 0x58 +#define MCHIP_HIC_MODE_NOOP 0x0 +#define MCHIP_HIC_MODE_STILL_CAP 0x1 /* still pic capt */ +#define MCHIP_HIC_MODE_DISPLAY 0x2 /* display */ +#define MCHIP_HIC_MODE_STILL_COMP 0x3 /* still pic comp. */ +#define MCHIP_HIC_MODE_STILL_DECOMP 0x4 /* still pic decomp. */ +#define MCHIP_HIC_MODE_CONT_COMP 0x5 /* cont capt+comp */ +#define MCHIP_HIC_MODE_CONT_DECOMP 0x6 /* cont decomp+disp */ +#define MCHIP_HIC_MODE_STILL_OUT 0x7 /* still pic output */ +#define MCHIP_HIC_MODE_CONT_OUT 0x8 /* cont output */ + +#define MCHIP_HIC_STATUS 0x5c +#define MCHIP_HIC_STATUS_MCC_RDY 0x00000001 /* MCC reg acc ok */ +#define MCHIP_HIC_STATUS_VRJ_RDY 0x00000002 /* VRJ reg acc ok */ +#define MCHIP_HIC_STATUS_IDLE 0x00000003 +#define MCHIP_HIC_STATUS_CAPDIS 0x00000004 /* cap/disp in prog */ +#define MCHIP_HIC_STATUS_COMPDEC 0x00000008 /* (de)comp in prog */ +#define MCHIP_HIC_STATUS_BUSY 0x00000010 /* HIC busy */ + +#define MCHIP_HIC_S_RATE 0x60 /* MJPEG # frames */ + +#define MCHIP_HIC_PCI_VFMT 0x64 /* video format */ +#define MCHIP_HIC_PCI_VFMT_YVYU 0x00000001 /* 0: V Y' U Y */ + /* 1: Y' V Y U */ + +#define MCHIP_MCC_CMD 0x80 /* MCC commands */ +#define MCHIP_MCC_CMD_INITIAL 0x0 /* idle ? */ +#define MCHIP_MCC_CMD_IIC_START_SET 0x1 +#define MCHIP_MCC_CMD_IIC_END_SET 0x2 +#define MCHIP_MCC_CMD_FM_WRITE 0x3 /* frame memory */ +#define MCHIP_MCC_CMD_FM_READ 0x4 +#define MCHIP_MCC_CMD_FM_STOP 0x5 +#define MCHIP_MCC_CMD_CAPTURE 0x6 +#define MCHIP_MCC_CMD_DISPLAY 0x7 +#define MCHIP_MCC_CMD_END_DISP 0x8 +#define MCHIP_MCC_CMD_STILL_COMP 0x9 +#define MCHIP_MCC_CMD_STILL_DECOMP 0xa +#define MCHIP_MCC_CMD_STILL_OUTPUT 0xb +#define MCHIP_MCC_CMD_CONT_OUTPUT 0xc +#define MCHIP_MCC_CMD_CONT_COMP 0xd +#define MCHIP_MCC_CMD_CONT_DECOMP 0xe +#define MCHIP_MCC_CMD_RESET 0xf /* MCC reset */ + +#define MCHIP_MCC_IIC_WR 0x84 + +#define MCHIP_MCC_MCC_WR 0x88 + +#define MCHIP_MCC_MCC_RD 0x8c + +#define MCHIP_MCC_STATUS 0x90 +#define MCHIP_MCC_STATUS_CAPT 0x00000001 /* capturing */ +#define MCHIP_MCC_STATUS_DISP 0x00000002 /* displaying */ +#define MCHIP_MCC_STATUS_COMP 0x00000004 /* compressing */ +#define MCHIP_MCC_STATUS_DECOMP 0x00000008 /* decompressing */ +#define MCHIP_MCC_STATUS_MCC_WR 0x00000010 /* register ready */ +#define MCHIP_MCC_STATUS_MCC_RD 0x00000020 /* register ready */ +#define MCHIP_MCC_STATUS_IIC_WR 0x00000040 /* register ready */ +#define MCHIP_MCC_STATUS_OUTPUT 0x00000080 /* output in prog */ + +#define MCHIP_MCC_SIG_POLARITY 0x94 +#define MCHIP_MCC_SIG_POL_VS_H 0x00000001 /* VS active-high */ +#define MCHIP_MCC_SIG_POL_HS_H 0x00000002 /* HS active-high */ +#define MCHIP_MCC_SIG_POL_DOE_H 0x00000004 /* DOE active-high */ + +#define MCHIP_MCC_IRQ 0x98 +#define MCHIP_MCC_IRQ_CAPDIS_STRT 0x00000001 /* cap/disp started */ +#define MCHIP_MCC_IRQ_CAPDIS_STRT_MASK 0x00000010 +#define MCHIP_MCC_IRQ_CAPDIS_END 0x00000002 /* cap/disp ended */ +#define MCHIP_MCC_IRQ_CAPDIS_END_MASK 0x00000020 +#define MCHIP_MCC_IRQ_COMPDEC_STRT 0x00000004 /* (de)comp started */ +#define MCHIP_MCC_IRQ_COMPDEC_STRT_MASK 0x00000040 +#define MCHIP_MCC_IRQ_COMPDEC_END 0x00000008 /* (de)comp ended */ +#define MCHIP_MCC_IRQ_COMPDEC_END_MASK 0x00000080 + +#define MCHIP_MCC_HSTART 0x9c /* video in */ +#define MCHIP_MCC_VSTART 0xa0 +#define MCHIP_MCC_HCOUNT 0xa4 +#define MCHIP_MCC_VCOUNT 0xa8 +#define MCHIP_MCC_R_XBASE 0xac /* capt/disp */ +#define MCHIP_MCC_R_YBASE 0xb0 +#define MCHIP_MCC_R_XRANGE 0xb4 +#define MCHIP_MCC_R_YRANGE 0xb8 +#define MCHIP_MCC_B_XBASE 0xbc /* comp/decomp */ +#define MCHIP_MCC_B_YBASE 0xc0 +#define MCHIP_MCC_B_XRANGE 0xc4 +#define MCHIP_MCC_B_YRANGE 0xc8 + +#define MCHIP_MCC_R_SAMPLING 0xcc /* 1: 1:4 */ + +#define MCHIP_VRJ_CMD 0x100 /* VRJ commands */ + +/* VRJ registers (see table 12.2.4) */ +#define MCHIP_VRJ_COMPRESSED_DATA 0x1b0 +#define MCHIP_VRJ_PIXEL_DATA 0x1b8 + +#define MCHIP_VRJ_BUS_MODE 0x100 +#define MCHIP_VRJ_SIGNAL_ACTIVE_LEVEL 0x108 +#define MCHIP_VRJ_PDAT_USE 0x110 +#define MCHIP_VRJ_MODE_SPECIFY 0x118 +#define MCHIP_VRJ_LIMIT_COMPRESSED_LO 0x120 +#define MCHIP_VRJ_LIMIT_COMPRESSED_HI 0x124 +#define MCHIP_VRJ_COMP_DATA_FORMAT 0x128 +#define MCHIP_VRJ_TABLE_DATA 0x140 +#define MCHIP_VRJ_RESTART_INTERVAL 0x148 +#define MCHIP_VRJ_NUM_LINES 0x150 +#define MCHIP_VRJ_NUM_PIXELS 0x158 +#define MCHIP_VRJ_NUM_COMPONENTS 0x160 +#define MCHIP_VRJ_SOF1 0x168 +#define MCHIP_VRJ_SOF2 0x170 +#define MCHIP_VRJ_SOF3 0x178 +#define MCHIP_VRJ_SOF4 0x180 +#define MCHIP_VRJ_SOS 0x188 +#define MCHIP_VRJ_SOFT_RESET 0x190 + +#define MCHIP_VRJ_STATUS 0x1c0 +#define MCHIP_VRJ_STATUS_BUSY 0x00001 +#define MCHIP_VRJ_STATUS_COMP_ACCESS 0x00002 +#define MCHIP_VRJ_STATUS_PIXEL_ACCESS 0x00004 +#define MCHIP_VRJ_STATUS_ERROR 0x00008 + +#define MCHIP_VRJ_IRQ_FLAG 0x1c8 +#define MCHIP_VRJ_ERROR_REPORT 0x1d8 + +#define MCHIP_VRJ_START_COMMAND 0x1a0 + +/****************************************************************************/ +/* Driver definitions. */ +/****************************************************************************/ + +/* Sony Programmable I/O Controller for accessing the camera commands */ +#include + +/* private API definitions */ +#include +#include + + +/* Enable jpg software correction */ +#define MEYE_JPEG_CORRECTION 1 + +/* Maximum size of a buffer */ +#define MEYE_MAX_BUFSIZE 614400 /* 640 * 480 * 2 */ + +/* Maximum number of buffers */ +#define MEYE_MAX_BUFNBRS 32 + +/* State of a buffer */ +#define MEYE_BUF_UNUSED 0 /* not used */ +#define MEYE_BUF_USING 1 /* currently grabbing / playing */ +#define MEYE_BUF_DONE 2 /* done */ + +/* grab buffer */ +struct meye_grab_buffer { + int state; /* state of buffer */ + unsigned long size; /* size of jpg frame */ + struct timeval timestamp; /* timestamp */ + unsigned long sequence; /* sequence number */ +}; + +/* size of kfifos containings buffer indices */ +#define MEYE_QUEUE_SIZE MEYE_MAX_BUFNBRS + +/* Motion Eye device structure */ +struct meye { + struct pci_dev *mchip_dev; /* pci device */ + u8 mchip_irq; /* irq */ + u8 mchip_mode; /* actual mchip mode: HIC_MODE... */ + u8 mchip_fnum; /* current mchip frame number */ + unsigned char __iomem *mchip_mmregs;/* mchip: memory mapped registers */ + u8 *mchip_ptable[MCHIP_NB_PAGES];/* mchip: ptable */ + void *mchip_ptable_toc; /* mchip: ptable toc */ + dma_addr_t mchip_dmahandle; /* mchip: dma handle to ptable toc */ + unsigned char *grab_fbuffer; /* capture framebuffer */ + unsigned char *grab_temp; /* temporary buffer */ + /* list of buffers */ + struct meye_grab_buffer grab_buffer[MEYE_MAX_BUFNBRS]; + int vma_use_count[MEYE_MAX_BUFNBRS]; /* mmap count */ + struct mutex lock; /* mutex for open/mmap... */ + struct kfifo *grabq; /* queue for buffers to be grabbed */ + spinlock_t grabq_lock; /* lock protecting the queue */ + struct kfifo *doneq; /* queue for grabbed buffers */ + spinlock_t doneq_lock; /* lock protecting the queue */ + wait_queue_head_t proc_list; /* wait queue */ + struct video_device *video_dev; /* video device parameters */ + struct video_picture picture; /* video picture parameters */ + struct meye_params params; /* additional parameters */ +#ifdef CONFIG_PM + u8 pm_mchip_mode; /* old mchip mode */ +#endif +}; + +#endif diff --git a/linux/drivers/media/video/ov511.c b/linux/drivers/media/video/ov511.c new file mode 100644 index 000000000..fdc8e3f13 --- /dev/null +++ b/linux/drivers/media/video/ov511.c @@ -0,0 +1,5932 @@ +/* + * OmniVision OV511 Camera-to-USB Bridge Driver + * + * Copyright (c) 1999-2003 Mark W. McClelland + * Original decompression code Copyright 1998-2000 OmniVision Technologies + * Many improvements by Bret Wallach + * Color fixes by by Orion Sky Lawlor (2/26/2000) + * Snapshot code by Kevin Moore + * OV7620 fixes by Charl P. Botha + * Changes by Claudio Matsuoka + * Original SAA7111A code by Dave Perks + * URB error messages from pwc driver by Nemosoft + * generic_ioctl() code from videodev.c by Gerd Knorr and Alan Cox + * Memory management (rvmalloc) code from bttv driver, by Gerd Knorr and others + * + * Based on the Linux CPiA driver written by Peter Pregler, + * Scott J. Bertin and Johannes Erdfelt. + * + * Please see the file: Documentation/usb/ov511.txt + * and the website at: http://alpha.dyndns.org/ov511 + * for more info. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined (__i386__) + #include +#endif + +#include "ov511.h" + +/* + * Version Information + */ +#define DRIVER_VERSION "v1.64 for Linux 2.5" +#define EMAIL "mark@alpha.dyndns.org" +#define DRIVER_AUTHOR "Mark McClelland & Bret Wallach \ + & Orion Sky Lawlor & Kevin Moore & Charl P. Botha \ + & Claudio Matsuoka " +#define DRIVER_DESC "ov511 USB Camera Driver" + +#define OV511_I2C_RETRIES 3 +#define ENABLE_Y_QUANTABLE 1 +#define ENABLE_UV_QUANTABLE 1 + +#define OV511_MAX_UNIT_VIDEO 16 + +/* Pixel count * bytes per YUV420 pixel (1.5) */ +#define MAX_FRAME_SIZE(w, h) ((w) * (h) * 3 / 2) + +#define MAX_DATA_SIZE(w, h) (MAX_FRAME_SIZE(w, h) + sizeof(struct timeval)) + +/* Max size * bytes per YUV420 pixel (1.5) + one extra isoc frame for safety */ +#define MAX_RAW_DATA_SIZE(w, h) ((w) * (h) * 3 / 2 + 1024) + +#define FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM) + +/********************************************************************** + * Module Parameters + * (See ov511.txt for detailed descriptions of these) + **********************************************************************/ + +/* These variables (and all static globals) default to zero */ +static int autobright = 1; +static int autogain = 1; +static int autoexp = 1; +static int debug; +static int snapshot; +static int cams = 1; +static int compress; +static int testpat; +static int dumppix; +static int led = 1; +static int dump_bridge; +static int dump_sensor; +static int printph; +static int phy = 0x1f; +static int phuv = 0x05; +static int pvy = 0x06; +static int pvuv = 0x06; +static int qhy = 0x14; +static int qhuv = 0x03; +static int qvy = 0x04; +static int qvuv = 0x04; +static int lightfreq; +static int bandingfilter; +static int clockdiv = -1; +static int packetsize = -1; +static int framedrop = -1; +static int fastset; +static int force_palette; +static int backlight; +static int unit_video[OV511_MAX_UNIT_VIDEO]; +static int remove_zeros; +static int mirror; +static int ov518_color; + +module_param(autobright, int, 0); +MODULE_PARM_DESC(autobright, "Sensor automatically changes brightness"); +module_param(autogain, int, 0); +MODULE_PARM_DESC(autogain, "Sensor automatically changes gain"); +module_param(autoexp, int, 0); +MODULE_PARM_DESC(autoexp, "Sensor automatically changes exposure"); +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, + "Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=max"); +module_param(snapshot, int, 0); +MODULE_PARM_DESC(snapshot, "Enable snapshot mode"); +module_param(cams, int, 0); +MODULE_PARM_DESC(cams, "Number of simultaneous cameras"); +module_param(compress, int, 0); +MODULE_PARM_DESC(compress, "Turn on compression"); +module_param(testpat, int, 0); +MODULE_PARM_DESC(testpat, + "Replace image with vertical bar testpattern (only partially working)"); +module_param(dumppix, int, 0); +MODULE_PARM_DESC(dumppix, "Dump raw pixel data"); +module_param(led, int, 0); +MODULE_PARM_DESC(led, + "LED policy (OV511+ or later). 0=off, 1=on (default), 2=auto (on when open)"); +module_param(dump_bridge, int, 0); +MODULE_PARM_DESC(dump_bridge, "Dump the bridge registers"); +module_param(dump_sensor, int, 0); +MODULE_PARM_DESC(dump_sensor, "Dump the sensor registers"); +module_param(printph, int, 0); +MODULE_PARM_DESC(printph, "Print frame start/end headers"); +module_param(phy, int, 0); +MODULE_PARM_DESC(phy, "Prediction range (horiz. Y)"); +module_param(phuv, int, 0); +MODULE_PARM_DESC(phuv, "Prediction range (horiz. UV)"); +module_param(pvy, int, 0); +MODULE_PARM_DESC(pvy, "Prediction range (vert. Y)"); +module_param(pvuv, int, 0); +MODULE_PARM_DESC(pvuv, "Prediction range (vert. UV)"); +module_param(qhy, int, 0); +MODULE_PARM_DESC(qhy, "Quantization threshold (horiz. Y)"); +module_param(qhuv, int, 0); +MODULE_PARM_DESC(qhuv, "Quantization threshold (horiz. UV)"); +module_param(qvy, int, 0); +MODULE_PARM_DESC(qvy, "Quantization threshold (vert. Y)"); +module_param(qvuv, int, 0); +MODULE_PARM_DESC(qvuv, "Quantization threshold (vert. UV)"); +module_param(lightfreq, int, 0); +MODULE_PARM_DESC(lightfreq, + "Light frequency. Set to 50 or 60 Hz, or zero for default settings"); +module_param(bandingfilter, int, 0); +MODULE_PARM_DESC(bandingfilter, + "Enable banding filter (to reduce effects of fluorescent lighting)"); +module_param(clockdiv, int, 0); +MODULE_PARM_DESC(clockdiv, "Force pixel clock divisor to a specific value"); +module_param(packetsize, int, 0); +MODULE_PARM_DESC(packetsize, "Force a specific isoc packet size"); +module_param(framedrop, int, 0); +MODULE_PARM_DESC(framedrop, "Force a specific frame drop register setting"); +module_param(fastset, int, 0); +MODULE_PARM_DESC(fastset, "Allows picture settings to take effect immediately"); +module_param(force_palette, int, 0); +MODULE_PARM_DESC(force_palette, "Force the palette to a specific value"); +module_param(backlight, int, 0); +MODULE_PARM_DESC(backlight, "For objects that are lit from behind"); +static int num_uv; +module_param_array(unit_video, int, &num_uv, 0); +MODULE_PARM_DESC(unit_video, + "Force use of specific minor number(s). 0 is not allowed."); +module_param(remove_zeros, int, 0); +MODULE_PARM_DESC(remove_zeros, + "Remove zero-padding from uncompressed incoming data"); +module_param(mirror, int, 0); +MODULE_PARM_DESC(mirror, "Reverse image horizontally"); +module_param(ov518_color, int, 0); +MODULE_PARM_DESC(ov518_color, "Enable OV518 color (experimental)"); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/********************************************************************** + * Miscellaneous Globals + **********************************************************************/ + +static struct usb_driver ov511_driver; + +/* Number of times to retry a failed I2C transaction. Increase this if you + * are getting "Failed to read sensor ID..." */ +static const int i2c_detect_tries = 5; + +static struct usb_device_id device_table [] = { + { USB_DEVICE(VEND_OMNIVISION, PROD_OV511) }, + { USB_DEVICE(VEND_OMNIVISION, PROD_OV511PLUS) }, + { USB_DEVICE(VEND_OMNIVISION, PROD_OV518) }, + { USB_DEVICE(VEND_OMNIVISION, PROD_OV518PLUS) }, + { USB_DEVICE(VEND_MATTEL, PROD_ME2CAM) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, device_table); + +static unsigned char yQuanTable511[] = OV511_YQUANTABLE; +static unsigned char uvQuanTable511[] = OV511_UVQUANTABLE; +static unsigned char yQuanTable518[] = OV518_YQUANTABLE; +static unsigned char uvQuanTable518[] = OV518_UVQUANTABLE; + +/********************************************************************** + * Symbolic Names + **********************************************************************/ + +/* Known OV511-based cameras */ +static struct symbolic_list camlist[] = { + { 0, "Generic Camera (no ID)" }, + { 1, "Mustek WCam 3X" }, + { 3, "D-Link DSB-C300" }, + { 4, "Generic OV511/OV7610" }, + { 5, "Puretek PT-6007" }, + { 6, "Lifeview USB Life TV (NTSC)" }, + { 21, "Creative Labs WebCam 3" }, + { 22, "Lifeview USB Life TV (PAL D/K+B/G)" }, + { 36, "Koala-Cam" }, + { 38, "Lifeview USB Life TV (PAL)" }, + { 41, "Samsung Anycam MPC-M10" }, + { 43, "Mtekvision Zeca MV402" }, + { 46, "Suma eON" }, + { 70, "Lifeview USB Life TV (PAL/SECAM)" }, + { 100, "Lifeview RoboCam" }, + { 102, "AverMedia InterCam Elite" }, + { 112, "MediaForte MV300" }, /* or OV7110 evaluation kit */ + { 134, "Ezonics EZCam II" }, + { 192, "Webeye 2000B" }, + { 253, "Alpha Vision Tech. AlphaCam SE" }, + { -1, NULL } +}; + +/* Video4Linux1 Palettes */ +static struct symbolic_list v4l1_plist[] = { + { VIDEO_PALETTE_GREY, "GREY" }, + { VIDEO_PALETTE_HI240, "HI240" }, + { VIDEO_PALETTE_RGB565, "RGB565" }, + { VIDEO_PALETTE_RGB24, "RGB24" }, + { VIDEO_PALETTE_RGB32, "RGB32" }, + { VIDEO_PALETTE_RGB555, "RGB555" }, + { VIDEO_PALETTE_YUV422, "YUV422" }, + { VIDEO_PALETTE_YUYV, "YUYV" }, + { VIDEO_PALETTE_UYVY, "UYVY" }, + { VIDEO_PALETTE_YUV420, "YUV420" }, + { VIDEO_PALETTE_YUV411, "YUV411" }, + { VIDEO_PALETTE_RAW, "RAW" }, + { VIDEO_PALETTE_YUV422P,"YUV422P" }, + { VIDEO_PALETTE_YUV411P,"YUV411P" }, + { VIDEO_PALETTE_YUV420P,"YUV420P" }, + { VIDEO_PALETTE_YUV410P,"YUV410P" }, + { -1, NULL } +}; + +static struct symbolic_list brglist[] = { + { BRG_OV511, "OV511" }, + { BRG_OV511PLUS, "OV511+" }, + { BRG_OV518, "OV518" }, + { BRG_OV518PLUS, "OV518+" }, + { -1, NULL } +}; + +static struct symbolic_list senlist[] = { + { SEN_OV76BE, "OV76BE" }, + { SEN_OV7610, "OV7610" }, + { SEN_OV7620, "OV7620" }, + { SEN_OV7620AE, "OV7620AE" }, + { SEN_OV6620, "OV6620" }, + { SEN_OV6630, "OV6630" }, + { SEN_OV6630AE, "OV6630AE" }, + { SEN_OV6630AF, "OV6630AF" }, + { SEN_OV8600, "OV8600" }, + { SEN_KS0127, "KS0127" }, + { SEN_KS0127B, "KS0127B" }, + { SEN_SAA7111A, "SAA7111A" }, + { -1, NULL } +}; + +/* URB error codes: */ +static struct symbolic_list urb_errlist[] = { + { -ENOSR, "Buffer error (overrun)" }, + { -EPIPE, "Stalled (device not responding)" }, + { -EOVERFLOW, "Babble (bad cable?)" }, + { -EPROTO, "Bit-stuff error (bad cable?)" }, + { -EILSEQ, "CRC/Timeout" }, + { -ETIMEDOUT, "NAK (device does not respond)" }, + { -1, NULL } +}; + +/********************************************************************** + * Memory management + **********************************************************************/ +static void * +rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + +static void +rvfree(void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + +/********************************************************************** + * + * Register I/O + * + **********************************************************************/ + +/* Write an OV51x register */ +static int +reg_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value) +{ + int rc; + + PDEBUG(5, "0x%02X:0x%02X", reg, value); + + mutex_lock(&ov->cbuf_lock); + ov->cbuf[0] = value; + rc = usb_control_msg(ov->dev, + usb_sndctrlpipe(ov->dev, 0), + (ov->bclass == BCL_OV518)?1:2 /* REG_IO */, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, (__u16)reg, &ov->cbuf[0], 1, 1000); + mutex_unlock(&ov->cbuf_lock); + + if (rc < 0) + err("reg write: error %d: %s", rc, symbolic(urb_errlist, rc)); + + return rc; +} + +/* Read from an OV51x register */ +/* returns: negative is error, pos or zero is data */ +static int +reg_r(struct usb_ov511 *ov, unsigned char reg) +{ + int rc; + + mutex_lock(&ov->cbuf_lock); + rc = usb_control_msg(ov->dev, + usb_rcvctrlpipe(ov->dev, 0), + (ov->bclass == BCL_OV518)?1:3 /* REG_IO */, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, (__u16)reg, &ov->cbuf[0], 1, 1000); + + if (rc < 0) { + err("reg read: error %d: %s", rc, symbolic(urb_errlist, rc)); + } else { + rc = ov->cbuf[0]; + PDEBUG(5, "0x%02X:0x%02X", reg, ov->cbuf[0]); + } + + mutex_unlock(&ov->cbuf_lock); + + return rc; +} + +/* + * Writes bits at positions specified by mask to an OV51x reg. Bits that are in + * the same position as 1's in "mask" are cleared and set to "value". Bits + * that are in the same position as 0's in "mask" are preserved, regardless + * of their respective state in "value". + */ +static int +reg_w_mask(struct usb_ov511 *ov, + unsigned char reg, + unsigned char value, + unsigned char mask) +{ + int ret; + unsigned char oldval, newval; + + ret = reg_r(ov, reg); + if (ret < 0) + return ret; + + oldval = (unsigned char) ret; + oldval &= (~mask); /* Clear the masked bits */ + value &= mask; /* Enforce mask on value */ + newval = oldval | value; /* Set the desired bits */ + + return (reg_w(ov, reg, newval)); +} + +/* + * Writes multiple (n) byte value to a single register. Only valid with certain + * registers (0x30 and 0xc4 - 0xce). + */ +static int +ov518_reg_w32(struct usb_ov511 *ov, unsigned char reg, u32 val, int n) +{ + int rc; + + PDEBUG(5, "0x%02X:%7d, n=%d", reg, val, n); + + mutex_lock(&ov->cbuf_lock); + + *((__le32 *)ov->cbuf) = __cpu_to_le32(val); + + rc = usb_control_msg(ov->dev, + usb_sndctrlpipe(ov->dev, 0), + 1 /* REG_IO */, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, (__u16)reg, ov->cbuf, n, 1000); + mutex_unlock(&ov->cbuf_lock); + + if (rc < 0) + err("reg write multiple: error %d: %s", rc, + symbolic(urb_errlist, rc)); + + return rc; +} + +static int +ov511_upload_quan_tables(struct usb_ov511 *ov) +{ + unsigned char *pYTable = yQuanTable511; + unsigned char *pUVTable = uvQuanTable511; + unsigned char val0, val1; + int i, rc, reg = R511_COMP_LUT_BEGIN; + + PDEBUG(4, "Uploading quantization tables"); + + for (i = 0; i < OV511_QUANTABLESIZE / 2; i++) { + if (ENABLE_Y_QUANTABLE) { + val0 = *pYTable++; + val1 = *pYTable++; + val0 &= 0x0f; + val1 &= 0x0f; + val0 |= val1 << 4; + rc = reg_w(ov, reg, val0); + if (rc < 0) + return rc; + } + + if (ENABLE_UV_QUANTABLE) { + val0 = *pUVTable++; + val1 = *pUVTable++; + val0 &= 0x0f; + val1 &= 0x0f; + val0 |= val1 << 4; + rc = reg_w(ov, reg + OV511_QUANTABLESIZE/2, val0); + if (rc < 0) + return rc; + } + + reg++; + } + + return 0; +} + +/* OV518 quantization tables are 8x4 (instead of 8x8) */ +static int +ov518_upload_quan_tables(struct usb_ov511 *ov) +{ + unsigned char *pYTable = yQuanTable518; + unsigned char *pUVTable = uvQuanTable518; + unsigned char val0, val1; + int i, rc, reg = R511_COMP_LUT_BEGIN; + + PDEBUG(4, "Uploading quantization tables"); + + for (i = 0; i < OV518_QUANTABLESIZE / 2; i++) { + if (ENABLE_Y_QUANTABLE) { + val0 = *pYTable++; + val1 = *pYTable++; + val0 &= 0x0f; + val1 &= 0x0f; + val0 |= val1 << 4; + rc = reg_w(ov, reg, val0); + if (rc < 0) + return rc; + } + + if (ENABLE_UV_QUANTABLE) { + val0 = *pUVTable++; + val1 = *pUVTable++; + val0 &= 0x0f; + val1 &= 0x0f; + val0 |= val1 << 4; + rc = reg_w(ov, reg + OV518_QUANTABLESIZE/2, val0); + if (rc < 0) + return rc; + } + + reg++; + } + + return 0; +} + +static int +ov51x_reset(struct usb_ov511 *ov, unsigned char reset_type) +{ + int rc; + + /* Setting bit 0 not allowed on 518/518Plus */ + if (ov->bclass == BCL_OV518) + reset_type &= 0xfe; + + PDEBUG(4, "Reset: type=0x%02X", reset_type); + + rc = reg_w(ov, R51x_SYS_RESET, reset_type); + rc = reg_w(ov, R51x_SYS_RESET, 0); + + if (rc < 0) + err("reset: command failed"); + + return rc; +} + +/********************************************************************** + * + * Low-level I2C I/O functions + * + **********************************************************************/ + +/* NOTE: Do not call this function directly! + * The OV518 I2C I/O procedure is different, hence, this function. + * This is normally only called from i2c_w(). Note that this function + * always succeeds regardless of whether the sensor is present and working. + */ +static int +ov518_i2c_write_internal(struct usb_ov511 *ov, + unsigned char reg, + unsigned char value) +{ + int rc; + + PDEBUG(5, "0x%02X:0x%02X", reg, value); + + /* Select camera register */ + rc = reg_w(ov, R51x_I2C_SADDR_3, reg); + if (rc < 0) + return rc; + + /* Write "value" to I2C data port of OV511 */ + rc = reg_w(ov, R51x_I2C_DATA, value); + if (rc < 0) + return rc; + + /* Initiate 3-byte write cycle */ + rc = reg_w(ov, R518_I2C_CTL, 0x01); + if (rc < 0) + return rc; + + return 0; +} + +/* NOTE: Do not call this function directly! */ +static int +ov511_i2c_write_internal(struct usb_ov511 *ov, + unsigned char reg, + unsigned char value) +{ + int rc, retries; + + PDEBUG(5, "0x%02X:0x%02X", reg, value); + + /* Three byte write cycle */ + for (retries = OV511_I2C_RETRIES; ; ) { + /* Select camera register */ + rc = reg_w(ov, R51x_I2C_SADDR_3, reg); + if (rc < 0) + break; + + /* Write "value" to I2C data port of OV511 */ + rc = reg_w(ov, R51x_I2C_DATA, value); + if (rc < 0) + break; + + /* Initiate 3-byte write cycle */ + rc = reg_w(ov, R511_I2C_CTL, 0x01); + if (rc < 0) + break; + + /* Retry until idle */ + do + rc = reg_r(ov, R511_I2C_CTL); + while (rc > 0 && ((rc&1) == 0)); + if (rc < 0) + break; + + /* Ack? */ + if ((rc&2) == 0) { + rc = 0; + break; + } +#if 0 + /* I2C abort */ + reg_w(ov, R511_I2C_CTL, 0x10); +#endif + if (--retries < 0) { + err("i2c write retries exhausted"); + rc = -1; + break; + } + } + + return rc; +} + +/* NOTE: Do not call this function directly! + * The OV518 I2C I/O procedure is different, hence, this function. + * This is normally only called from i2c_r(). Note that this function + * always succeeds regardless of whether the sensor is present and working. + */ +static int +ov518_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg) +{ + int rc, value; + + /* Select camera register */ + rc = reg_w(ov, R51x_I2C_SADDR_2, reg); + if (rc < 0) + return rc; + + /* Initiate 2-byte write cycle */ + rc = reg_w(ov, R518_I2C_CTL, 0x03); + if (rc < 0) + return rc; + + /* Initiate 2-byte read cycle */ + rc = reg_w(ov, R518_I2C_CTL, 0x05); + if (rc < 0) + return rc; + + value = reg_r(ov, R51x_I2C_DATA); + + PDEBUG(5, "0x%02X:0x%02X", reg, value); + + return value; +} + +/* NOTE: Do not call this function directly! + * returns: negative is error, pos or zero is data */ +static int +ov511_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg) +{ + int rc, value, retries; + + /* Two byte write cycle */ + for (retries = OV511_I2C_RETRIES; ; ) { + /* Select camera register */ + rc = reg_w(ov, R51x_I2C_SADDR_2, reg); + if (rc < 0) + return rc; + + /* Initiate 2-byte write cycle */ + rc = reg_w(ov, R511_I2C_CTL, 0x03); + if (rc < 0) + return rc; + + /* Retry until idle */ + do + rc = reg_r(ov, R511_I2C_CTL); + while (rc > 0 && ((rc&1) == 0)); + if (rc < 0) + return rc; + + if ((rc&2) == 0) /* Ack? */ + break; + + /* I2C abort */ + reg_w(ov, R511_I2C_CTL, 0x10); + + if (--retries < 0) { + err("i2c write retries exhausted"); + return -1; + } + } + + /* Two byte read cycle */ + for (retries = OV511_I2C_RETRIES; ; ) { + /* Initiate 2-byte read cycle */ + rc = reg_w(ov, R511_I2C_CTL, 0x05); + if (rc < 0) + return rc; + + /* Retry until idle */ + do + rc = reg_r(ov, R511_I2C_CTL); + while (rc > 0 && ((rc&1) == 0)); + if (rc < 0) + return rc; + + if ((rc&2) == 0) /* Ack? */ + break; + + /* I2C abort */ + rc = reg_w(ov, R511_I2C_CTL, 0x10); + if (rc < 0) + return rc; + + if (--retries < 0) { + err("i2c read retries exhausted"); + return -1; + } + } + + value = reg_r(ov, R51x_I2C_DATA); + + PDEBUG(5, "0x%02X:0x%02X", reg, value); + + /* This is needed to make i2c_w() work */ + rc = reg_w(ov, R511_I2C_CTL, 0x05); + if (rc < 0) + return rc; + + return value; +} + +/* returns: negative is error, pos or zero is data */ +static int +i2c_r(struct usb_ov511 *ov, unsigned char reg) +{ + int rc; + + mutex_lock(&ov->i2c_lock); + + if (ov->bclass == BCL_OV518) + rc = ov518_i2c_read_internal(ov, reg); + else + rc = ov511_i2c_read_internal(ov, reg); + + mutex_unlock(&ov->i2c_lock); + + return rc; +} + +static int +i2c_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value) +{ + int rc; + + mutex_lock(&ov->i2c_lock); + + if (ov->bclass == BCL_OV518) + rc = ov518_i2c_write_internal(ov, reg, value); + else + rc = ov511_i2c_write_internal(ov, reg, value); + + mutex_unlock(&ov->i2c_lock); + + return rc; +} + +/* Do not call this function directly! */ +static int +ov51x_i2c_write_mask_internal(struct usb_ov511 *ov, + unsigned char reg, + unsigned char value, + unsigned char mask) +{ + int rc; + unsigned char oldval, newval; + + if (mask == 0xff) { + newval = value; + } else { + if (ov->bclass == BCL_OV518) + rc = ov518_i2c_read_internal(ov, reg); + else + rc = ov511_i2c_read_internal(ov, reg); + if (rc < 0) + return rc; + + oldval = (unsigned char) rc; + oldval &= (~mask); /* Clear the masked bits */ + value &= mask; /* Enforce mask on value */ + newval = oldval | value; /* Set the desired bits */ + } + + if (ov->bclass == BCL_OV518) + return (ov518_i2c_write_internal(ov, reg, newval)); + else + return (ov511_i2c_write_internal(ov, reg, newval)); +} + +/* Writes bits at positions specified by mask to an I2C reg. Bits that are in + * the same position as 1's in "mask" are cleared and set to "value". Bits + * that are in the same position as 0's in "mask" are preserved, regardless + * of their respective state in "value". + */ +static int +i2c_w_mask(struct usb_ov511 *ov, + unsigned char reg, + unsigned char value, + unsigned char mask) +{ + int rc; + + mutex_lock(&ov->i2c_lock); + rc = ov51x_i2c_write_mask_internal(ov, reg, value, mask); + mutex_unlock(&ov->i2c_lock); + + return rc; +} + +/* Set the read and write slave IDs. The "slave" argument is the write slave, + * and the read slave will be set to (slave + 1). ov->i2c_lock should be held + * when calling this. This should not be called from outside the i2c I/O + * functions. + */ +static int +i2c_set_slave_internal(struct usb_ov511 *ov, unsigned char slave) +{ + int rc; + + rc = reg_w(ov, R51x_I2C_W_SID, slave); + if (rc < 0) + return rc; + + rc = reg_w(ov, R51x_I2C_R_SID, slave + 1); + if (rc < 0) + return rc; + + return 0; +} + +/* Write to a specific I2C slave ID and register, using the specified mask */ +static int +i2c_w_slave(struct usb_ov511 *ov, + unsigned char slave, + unsigned char reg, + unsigned char value, + unsigned char mask) +{ + int rc = 0; + + mutex_lock(&ov->i2c_lock); + + /* Set new slave IDs */ + rc = i2c_set_slave_internal(ov, slave); + if (rc < 0) + goto out; + + rc = ov51x_i2c_write_mask_internal(ov, reg, value, mask); + +out: + /* Restore primary IDs */ + if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0) + err("Couldn't restore primary I2C slave"); + + mutex_unlock(&ov->i2c_lock); + return rc; +} + +/* Read from a specific I2C slave ID and register */ +static int +i2c_r_slave(struct usb_ov511 *ov, + unsigned char slave, + unsigned char reg) +{ + int rc; + + mutex_lock(&ov->i2c_lock); + + /* Set new slave IDs */ + rc = i2c_set_slave_internal(ov, slave); + if (rc < 0) + goto out; + + if (ov->bclass == BCL_OV518) + rc = ov518_i2c_read_internal(ov, reg); + else + rc = ov511_i2c_read_internal(ov, reg); + +out: + /* Restore primary IDs */ + if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0) + err("Couldn't restore primary I2C slave"); + + mutex_unlock(&ov->i2c_lock); + return rc; +} + +/* Sets I2C read and write slave IDs. Returns <0 for error */ +static int +ov51x_set_slave_ids(struct usb_ov511 *ov, unsigned char sid) +{ + int rc; + + mutex_lock(&ov->i2c_lock); + + rc = i2c_set_slave_internal(ov, sid); + if (rc < 0) + goto out; + + // FIXME: Is this actually necessary? + rc = ov51x_reset(ov, OV511_RESET_NOREGS); +out: + mutex_unlock(&ov->i2c_lock); + return rc; +} + +static int +write_regvals(struct usb_ov511 *ov, struct ov511_regvals * pRegvals) +{ + int rc; + + while (pRegvals->bus != OV511_DONE_BUS) { + if (pRegvals->bus == OV511_REG_BUS) { + if ((rc = reg_w(ov, pRegvals->reg, pRegvals->val)) < 0) + return rc; + } else if (pRegvals->bus == OV511_I2C_BUS) { + if ((rc = i2c_w(ov, pRegvals->reg, pRegvals->val)) < 0) + return rc; + } else { + err("Bad regval array"); + return -1; + } + pRegvals++; + } + return 0; +} + +#ifdef OV511_DEBUG +static void +dump_i2c_range(struct usb_ov511 *ov, int reg1, int regn) +{ + int i, rc; + + for (i = reg1; i <= regn; i++) { + rc = i2c_r(ov, i); + info("Sensor[0x%02X] = 0x%02X", i, rc); + } +} + +static void +dump_i2c_regs(struct usb_ov511 *ov) +{ + info("I2C REGS"); + dump_i2c_range(ov, 0x00, 0x7C); +} + +static void +dump_reg_range(struct usb_ov511 *ov, int reg1, int regn) +{ + int i, rc; + + for (i = reg1; i <= regn; i++) { + rc = reg_r(ov, i); + info("OV511[0x%02X] = 0x%02X", i, rc); + } +} + +static void +ov511_dump_regs(struct usb_ov511 *ov) +{ + info("CAMERA INTERFACE REGS"); + dump_reg_range(ov, 0x10, 0x1f); + info("DRAM INTERFACE REGS"); + dump_reg_range(ov, 0x20, 0x23); + info("ISO FIFO REGS"); + dump_reg_range(ov, 0x30, 0x31); + info("PIO REGS"); + dump_reg_range(ov, 0x38, 0x39); + dump_reg_range(ov, 0x3e, 0x3e); + info("I2C REGS"); + dump_reg_range(ov, 0x40, 0x49); + info("SYSTEM CONTROL REGS"); + dump_reg_range(ov, 0x50, 0x55); + dump_reg_range(ov, 0x5e, 0x5f); + info("OmniCE REGS"); + dump_reg_range(ov, 0x70, 0x79); + /* NOTE: Quantization tables are not readable. You will get the value + * in reg. 0x79 for every table register */ + dump_reg_range(ov, 0x80, 0x9f); + dump_reg_range(ov, 0xa0, 0xbf); + +} + +static void +ov518_dump_regs(struct usb_ov511 *ov) +{ + info("VIDEO MODE REGS"); + dump_reg_range(ov, 0x20, 0x2f); + info("DATA PUMP AND SNAPSHOT REGS"); + dump_reg_range(ov, 0x30, 0x3f); + info("I2C REGS"); + dump_reg_range(ov, 0x40, 0x4f); + info("SYSTEM CONTROL AND VENDOR REGS"); + dump_reg_range(ov, 0x50, 0x5f); + info("60 - 6F"); + dump_reg_range(ov, 0x60, 0x6f); + info("70 - 7F"); + dump_reg_range(ov, 0x70, 0x7f); + info("Y QUANTIZATION TABLE"); + dump_reg_range(ov, 0x80, 0x8f); + info("UV QUANTIZATION TABLE"); + dump_reg_range(ov, 0x90, 0x9f); + info("A0 - BF"); + dump_reg_range(ov, 0xa0, 0xbf); + info("CBR"); + dump_reg_range(ov, 0xc0, 0xcf); +} +#endif + +/*****************************************************************************/ + +/* Temporarily stops OV511 from functioning. Must do this before changing + * registers while the camera is streaming */ +static inline int +ov51x_stop(struct usb_ov511 *ov) +{ + PDEBUG(4, "stopping"); + ov->stopped = 1; + if (ov->bclass == BCL_OV518) + return (reg_w_mask(ov, R51x_SYS_RESET, 0x3a, 0x3a)); + else + return (reg_w(ov, R51x_SYS_RESET, 0x3d)); +} + +/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not + * actually stopped (for performance). */ +static inline int +ov51x_restart(struct usb_ov511 *ov) +{ + if (ov->stopped) { + PDEBUG(4, "restarting"); + ov->stopped = 0; + + /* Reinitialize the stream */ + if (ov->bclass == BCL_OV518) + reg_w(ov, 0x2f, 0x80); + + return (reg_w(ov, R51x_SYS_RESET, 0x00)); + } + + return 0; +} + +/* Sleeps until no frames are active. Returns !0 if got signal */ +static int +ov51x_wait_frames_inactive(struct usb_ov511 *ov) +{ + return wait_event_interruptible(ov->wq, ov->curframe < 0); +} + +/* Resets the hardware snapshot button */ +static void +ov51x_clear_snapshot(struct usb_ov511 *ov) +{ + if (ov->bclass == BCL_OV511) { + reg_w(ov, R51x_SYS_SNAP, 0x00); + reg_w(ov, R51x_SYS_SNAP, 0x02); + reg_w(ov, R51x_SYS_SNAP, 0x00); + } else if (ov->bclass == BCL_OV518) { + warn("snapshot reset not supported yet on OV518(+)"); + } else { + err("clear snap: invalid bridge type"); + } +} + +#if 0 +/* Checks the status of the snapshot button. Returns 1 if it was pressed since + * it was last cleared, and zero in all other cases (including errors) */ +static int +ov51x_check_snapshot(struct usb_ov511 *ov) +{ + int ret, status = 0; + + if (ov->bclass == BCL_OV511) { + ret = reg_r(ov, R51x_SYS_SNAP); + if (ret < 0) { + err("Error checking snspshot status (%d)", ret); + } else if (ret & 0x08) { + status = 1; + } + } else if (ov->bclass == BCL_OV518) { + warn("snapshot check not supported yet on OV518(+)"); + } else { + err("check snap: invalid bridge type"); + } + + return status; +} +#endif + +/* This does an initial reset of an OmniVision sensor and ensures that I2C + * is synchronized. Returns <0 for failure. + */ +static int +init_ov_sensor(struct usb_ov511 *ov) +{ + int i, success; + + /* Reset the sensor */ + if (i2c_w(ov, 0x12, 0x80) < 0) + return -EIO; + + /* Wait for it to initialize */ + msleep(150); + + for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) { + if ((i2c_r(ov, OV7610_REG_ID_HIGH) == 0x7F) && + (i2c_r(ov, OV7610_REG_ID_LOW) == 0xA2)) { + success = 1; + continue; + } + + /* Reset the sensor */ + if (i2c_w(ov, 0x12, 0x80) < 0) + return -EIO; + /* Wait for it to initialize */ + msleep(150); + /* Dummy read to sync I2C */ + if (i2c_r(ov, 0x00) < 0) + return -EIO; + } + + if (!success) + return -EIO; + + PDEBUG(1, "I2C synced in %d attempt(s)", i); + + return 0; +} + +static int +ov511_set_packet_size(struct usb_ov511 *ov, int size) +{ + int alt, mult; + + if (ov51x_stop(ov) < 0) + return -EIO; + + mult = size >> 5; + + if (ov->bridge == BRG_OV511) { + if (size == 0) + alt = OV511_ALT_SIZE_0; + else if (size == 257) + alt = OV511_ALT_SIZE_257; + else if (size == 513) + alt = OV511_ALT_SIZE_513; + else if (size == 769) + alt = OV511_ALT_SIZE_769; + else if (size == 993) + alt = OV511_ALT_SIZE_993; + else { + err("Set packet size: invalid size (%d)", size); + return -EINVAL; + } + } else if (ov->bridge == BRG_OV511PLUS) { + if (size == 0) + alt = OV511PLUS_ALT_SIZE_0; + else if (size == 33) + alt = OV511PLUS_ALT_SIZE_33; + else if (size == 129) + alt = OV511PLUS_ALT_SIZE_129; + else if (size == 257) + alt = OV511PLUS_ALT_SIZE_257; + else if (size == 385) + alt = OV511PLUS_ALT_SIZE_385; + else if (size == 513) + alt = OV511PLUS_ALT_SIZE_513; + else if (size == 769) + alt = OV511PLUS_ALT_SIZE_769; + else if (size == 961) + alt = OV511PLUS_ALT_SIZE_961; + else { + err("Set packet size: invalid size (%d)", size); + return -EINVAL; + } + } else { + err("Set packet size: Invalid bridge type"); + return -EINVAL; + } + + PDEBUG(3, "%d, mult=%d, alt=%d", size, mult, alt); + + if (reg_w(ov, R51x_FIFO_PSIZE, mult) < 0) + return -EIO; + + if (usb_set_interface(ov->dev, ov->iface, alt) < 0) { + err("Set packet size: set interface error"); + return -EBUSY; + } + + if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0) + return -EIO; + + ov->packet_size = size; + + if (ov51x_restart(ov) < 0) + return -EIO; + + return 0; +} + +/* Note: Unlike the OV511/OV511+, the size argument does NOT include the + * optional packet number byte. The actual size *is* stored in ov->packet_size, + * though. */ +static int +ov518_set_packet_size(struct usb_ov511 *ov, int size) +{ + int alt; + + if (ov51x_stop(ov) < 0) + return -EIO; + + if (ov->bclass == BCL_OV518) { + if (size == 0) + alt = OV518_ALT_SIZE_0; + else if (size == 128) + alt = OV518_ALT_SIZE_128; + else if (size == 256) + alt = OV518_ALT_SIZE_256; + else if (size == 384) + alt = OV518_ALT_SIZE_384; + else if (size == 512) + alt = OV518_ALT_SIZE_512; + else if (size == 640) + alt = OV518_ALT_SIZE_640; + else if (size == 768) + alt = OV518_ALT_SIZE_768; + else if (size == 896) + alt = OV518_ALT_SIZE_896; + else { + err("Set packet size: invalid size (%d)", size); + return -EINVAL; + } + } else { + err("Set packet size: Invalid bridge type"); + return -EINVAL; + } + + PDEBUG(3, "%d, alt=%d", size, alt); + + ov->packet_size = size; + if (size > 0) { + /* Program ISO FIFO size reg (packet number isn't included) */ + ov518_reg_w32(ov, 0x30, size, 2); + + if (ov->packet_numbering) + ++ov->packet_size; + } + + if (usb_set_interface(ov->dev, ov->iface, alt) < 0) { + err("Set packet size: set interface error"); + return -EBUSY; + } + + /* Initialize the stream */ + if (reg_w(ov, 0x2f, 0x80) < 0) + return -EIO; + + if (ov51x_restart(ov) < 0) + return -EIO; + + if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0) + return -EIO; + + return 0; +} + +/* Upload compression params and quantization tables. Returns 0 for success. */ +static int +ov511_init_compression(struct usb_ov511 *ov) +{ + int rc = 0; + + if (!ov->compress_inited) { + reg_w(ov, 0x70, phy); + reg_w(ov, 0x71, phuv); + reg_w(ov, 0x72, pvy); + reg_w(ov, 0x73, pvuv); + reg_w(ov, 0x74, qhy); + reg_w(ov, 0x75, qhuv); + reg_w(ov, 0x76, qvy); + reg_w(ov, 0x77, qvuv); + + if (ov511_upload_quan_tables(ov) < 0) { + err("Error uploading quantization tables"); + rc = -EIO; + goto out; + } + } + + ov->compress_inited = 1; +out: + return rc; +} + +/* Upload compression params and quantization tables. Returns 0 for success. */ +static int +ov518_init_compression(struct usb_ov511 *ov) +{ + int rc = 0; + + if (!ov->compress_inited) { + if (ov518_upload_quan_tables(ov) < 0) { + err("Error uploading quantization tables"); + rc = -EIO; + goto out; + } + } + + ov->compress_inited = 1; +out: + return rc; +} + +/* -------------------------------------------------------------------------- */ + +/* Sets sensor's contrast setting to "val" */ +static int +sensor_set_contrast(struct usb_ov511 *ov, unsigned short val) +{ + int rc; + + PDEBUG(3, "%d", val); + + if (ov->stop_during_set) + if (ov51x_stop(ov) < 0) + return -EIO; + + switch (ov->sensor) { + case SEN_OV7610: + case SEN_OV6620: + { + rc = i2c_w(ov, OV7610_REG_CNT, val >> 8); + if (rc < 0) + goto out; + break; + } + case SEN_OV6630: + { + rc = i2c_w_mask(ov, OV7610_REG_CNT, val >> 12, 0x0f); + if (rc < 0) + goto out; + break; + } + case SEN_OV7620: + { + unsigned char ctab[] = { + 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57, + 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff + }; + + /* Use Y gamma control instead. Bit 0 enables it. */ + rc = i2c_w(ov, 0x64, ctab[val>>12]); + if (rc < 0) + goto out; + break; + } + case SEN_SAA7111A: + { + rc = i2c_w(ov, 0x0b, val >> 9); + if (rc < 0) + goto out; + break; + } + default: + { + PDEBUG(3, "Unsupported with this sensor"); + rc = -EPERM; + goto out; + } + } + + rc = 0; /* Success */ + ov->contrast = val; +out: + if (ov51x_restart(ov) < 0) + return -EIO; + + return rc; +} + +/* Gets sensor's contrast setting */ +static int +sensor_get_contrast(struct usb_ov511 *ov, unsigned short *val) +{ + int rc; + + switch (ov->sensor) { + case SEN_OV7610: + case SEN_OV6620: + rc = i2c_r(ov, OV7610_REG_CNT); + if (rc < 0) + return rc; + else + *val = rc << 8; + break; + case SEN_OV6630: + rc = i2c_r(ov, OV7610_REG_CNT); + if (rc < 0) + return rc; + else + *val = rc << 12; + break; + case SEN_OV7620: + /* Use Y gamma reg instead. Bit 0 is the enable bit. */ + rc = i2c_r(ov, 0x64); + if (rc < 0) + return rc; + else + *val = (rc & 0xfe) << 8; + break; + case SEN_SAA7111A: + *val = ov->contrast; + break; + default: + PDEBUG(3, "Unsupported with this sensor"); + return -EPERM; + } + + PDEBUG(3, "%d", *val); + ov->contrast = *val; + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +/* Sets sensor's brightness setting to "val" */ +static int +sensor_set_brightness(struct usb_ov511 *ov, unsigned short val) +{ + int rc; + + PDEBUG(4, "%d", val); + + if (ov->stop_during_set) + if (ov51x_stop(ov) < 0) + return -EIO; + + switch (ov->sensor) { + case SEN_OV7610: + case SEN_OV76BE: + case SEN_OV6620: + case SEN_OV6630: + rc = i2c_w(ov, OV7610_REG_BRT, val >> 8); + if (rc < 0) + goto out; + break; + case SEN_OV7620: + /* 7620 doesn't like manual changes when in auto mode */ + if (!ov->auto_brt) { + rc = i2c_w(ov, OV7610_REG_BRT, val >> 8); + if (rc < 0) + goto out; + } + break; + case SEN_SAA7111A: + rc = i2c_w(ov, 0x0a, val >> 8); + if (rc < 0) + goto out; + break; + default: + PDEBUG(3, "Unsupported with this sensor"); + rc = -EPERM; + goto out; + } + + rc = 0; /* Success */ + ov->brightness = val; +out: + if (ov51x_restart(ov) < 0) + return -EIO; + + return rc; +} + +/* Gets sensor's brightness setting */ +static int +sensor_get_brightness(struct usb_ov511 *ov, unsigned short *val) +{ + int rc; + + switch (ov->sensor) { + case SEN_OV7610: + case SEN_OV76BE: + case SEN_OV7620: + case SEN_OV6620: + case SEN_OV6630: + rc = i2c_r(ov, OV7610_REG_BRT); + if (rc < 0) + return rc; + else + *val = rc << 8; + break; + case SEN_SAA7111A: + *val = ov->brightness; + break; + default: + PDEBUG(3, "Unsupported with this sensor"); + return -EPERM; + } + + PDEBUG(3, "%d", *val); + ov->brightness = *val; + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +/* Sets sensor's saturation (color intensity) setting to "val" */ +static int +sensor_set_saturation(struct usb_ov511 *ov, unsigned short val) +{ + int rc; + + PDEBUG(3, "%d", val); + + if (ov->stop_during_set) + if (ov51x_stop(ov) < 0) + return -EIO; + + switch (ov->sensor) { + case SEN_OV7610: + case SEN_OV76BE: + case SEN_OV6620: + case SEN_OV6630: + rc = i2c_w(ov, OV7610_REG_SAT, val >> 8); + if (rc < 0) + goto out; + break; + case SEN_OV7620: +// /* Use UV gamma control instead. Bits 0 & 7 are reserved. */ +// rc = ov_i2c_write(ov->dev, 0x62, (val >> 9) & 0x7e); +// if (rc < 0) +// goto out; + rc = i2c_w(ov, OV7610_REG_SAT, val >> 8); + if (rc < 0) + goto out; + break; + case SEN_SAA7111A: + rc = i2c_w(ov, 0x0c, val >> 9); + if (rc < 0) + goto out; + break; + default: + PDEBUG(3, "Unsupported with this sensor"); + rc = -EPERM; + goto out; + } + + rc = 0; /* Success */ + ov->colour = val; +out: + if (ov51x_restart(ov) < 0) + return -EIO; + + return rc; +} + +/* Gets sensor's saturation (color intensity) setting */ +static int +sensor_get_saturation(struct usb_ov511 *ov, unsigned short *val) +{ + int rc; + + switch (ov->sensor) { + case SEN_OV7610: + case SEN_OV76BE: + case SEN_OV6620: + case SEN_OV6630: + rc = i2c_r(ov, OV7610_REG_SAT); + if (rc < 0) + return rc; + else + *val = rc << 8; + break; + case SEN_OV7620: +// /* Use UV gamma reg instead. Bits 0 & 7 are reserved. */ +// rc = i2c_r(ov, 0x62); +// if (rc < 0) +// return rc; +// else +// *val = (rc & 0x7e) << 9; + rc = i2c_r(ov, OV7610_REG_SAT); + if (rc < 0) + return rc; + else + *val = rc << 8; + break; + case SEN_SAA7111A: + *val = ov->colour; + break; + default: + PDEBUG(3, "Unsupported with this sensor"); + return -EPERM; + } + + PDEBUG(3, "%d", *val); + ov->colour = *val; + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +/* Sets sensor's hue (red/blue balance) setting to "val" */ +static int +sensor_set_hue(struct usb_ov511 *ov, unsigned short val) +{ + int rc; + + PDEBUG(3, "%d", val); + + if (ov->stop_during_set) + if (ov51x_stop(ov) < 0) + return -EIO; + + switch (ov->sensor) { + case SEN_OV7610: + case SEN_OV6620: + case SEN_OV6630: + rc = i2c_w(ov, OV7610_REG_RED, 0xFF - (val >> 8)); + if (rc < 0) + goto out; + + rc = i2c_w(ov, OV7610_REG_BLUE, val >> 8); + if (rc < 0) + goto out; + break; + case SEN_OV7620: +// Hue control is causing problems. I will enable it once it's fixed. +#if 0 + rc = i2c_w(ov, 0x7a, (unsigned char)(val >> 8) + 0xb); + if (rc < 0) + goto out; + + rc = i2c_w(ov, 0x79, (unsigned char)(val >> 8) + 0xb); + if (rc < 0) + goto out; +#endif + break; + case SEN_SAA7111A: + rc = i2c_w(ov, 0x0d, (val + 32768) >> 8); + if (rc < 0) + goto out; + break; + default: + PDEBUG(3, "Unsupported with this sensor"); + rc = -EPERM; + goto out; + } + + rc = 0; /* Success */ + ov->hue = val; +out: + if (ov51x_restart(ov) < 0) + return -EIO; + + return rc; +} + +/* Gets sensor's hue (red/blue balance) setting */ +static int +sensor_get_hue(struct usb_ov511 *ov, unsigned short *val) +{ + int rc; + + switch (ov->sensor) { + case SEN_OV7610: + case SEN_OV6620: + case SEN_OV6630: + rc = i2c_r(ov, OV7610_REG_BLUE); + if (rc < 0) + return rc; + else + *val = rc << 8; + break; + case SEN_OV7620: + rc = i2c_r(ov, 0x7a); + if (rc < 0) + return rc; + else + *val = rc << 8; + break; + case SEN_SAA7111A: + *val = ov->hue; + break; + default: + PDEBUG(3, "Unsupported with this sensor"); + return -EPERM; + } + + PDEBUG(3, "%d", *val); + ov->hue = *val; + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +static int +sensor_set_picture(struct usb_ov511 *ov, struct video_picture *p) +{ + int rc; + + PDEBUG(4, "sensor_set_picture"); + + ov->whiteness = p->whiteness; + + /* Don't return error if a setting is unsupported, or rest of settings + * will not be performed */ + + rc = sensor_set_contrast(ov, p->contrast); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_set_brightness(ov, p->brightness); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_set_saturation(ov, p->colour); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_set_hue(ov, p->hue); + if (FATAL_ERROR(rc)) + return rc; + + return 0; +} + +static int +sensor_get_picture(struct usb_ov511 *ov, struct video_picture *p) +{ + int rc; + + PDEBUG(4, "sensor_get_picture"); + + /* Don't return error if a setting is unsupported, or rest of settings + * will not be performed */ + + rc = sensor_get_contrast(ov, &(p->contrast)); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_get_brightness(ov, &(p->brightness)); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_get_saturation(ov, &(p->colour)); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_get_hue(ov, &(p->hue)); + if (FATAL_ERROR(rc)) + return rc; + + p->whiteness = 105 << 8; + + return 0; +} + +#if 0 +// FIXME: Exposure range is only 0x00-0x7f in interlace mode +/* Sets current exposure for sensor. This only has an effect if auto-exposure + * is off */ +static inline int +sensor_set_exposure(struct usb_ov511 *ov, unsigned char val) +{ + int rc; + + PDEBUG(3, "%d", val); + + if (ov->stop_during_set) + if (ov51x_stop(ov) < 0) + return -EIO; + + switch (ov->sensor) { + case SEN_OV6620: + case SEN_OV6630: + case SEN_OV7610: + case SEN_OV7620: + case SEN_OV76BE: + case SEN_OV8600: + rc = i2c_w(ov, 0x10, val); + if (rc < 0) + goto out; + + break; + case SEN_KS0127: + case SEN_KS0127B: + case SEN_SAA7111A: + PDEBUG(3, "Unsupported with this sensor"); + return -EPERM; + default: + err("Sensor not supported for set_exposure"); + return -EINVAL; + } + + rc = 0; /* Success */ + ov->exposure = val; +out: + if (ov51x_restart(ov) < 0) + return -EIO; + + return rc; +} +#endif + +/* Gets current exposure level from sensor, regardless of whether it is under + * manual control. */ +static int +sensor_get_exposure(struct usb_ov511 *ov, unsigned char *val) +{ + int rc; + + switch (ov->sensor) { + case SEN_OV7610: + case SEN_OV6620: + case SEN_OV6630: + case SEN_OV7620: + case SEN_OV76BE: + case SEN_OV8600: + rc = i2c_r(ov, 0x10); + if (rc < 0) + return rc; + else + *val = rc; + break; + case SEN_KS0127: + case SEN_KS0127B: + case SEN_SAA7111A: + val = NULL; + PDEBUG(3, "Unsupported with this sensor"); + return -EPERM; + default: + err("Sensor not supported for get_exposure"); + return -EINVAL; + } + + PDEBUG(3, "%d", *val); + ov->exposure = *val; + + return 0; +} + +/* Turns on or off the LED. Only has an effect with OV511+/OV518(+) */ +static void +ov51x_led_control(struct usb_ov511 *ov, int enable) +{ + PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); + + if (ov->bridge == BRG_OV511PLUS) + reg_w(ov, R511_SYS_LED_CTL, enable ? 1 : 0); + else if (ov->bclass == BCL_OV518) + reg_w_mask(ov, R518_GPIO_OUT, enable ? 0x02 : 0x00, 0x02); + + return; +} + +/* Matches the sensor's internal frame rate to the lighting frequency. + * Valid frequencies are: + * 50 - 50Hz, for European and Asian lighting + * 60 - 60Hz, for American lighting + * + * Tested with: OV7610, OV7620, OV76BE, OV6620 + * Unsupported: KS0127, KS0127B, SAA7111A + * Returns: 0 for success + */ +static int +sensor_set_light_freq(struct usb_ov511 *ov, int freq) +{ + int sixty; + + PDEBUG(4, "%d Hz", freq); + + if (freq == 60) + sixty = 1; + else if (freq == 50) + sixty = 0; + else { + err("Invalid light freq (%d Hz)", freq); + return -EINVAL; + } + + switch (ov->sensor) { + case SEN_OV7610: + i2c_w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80); + i2c_w(ov, 0x2b, sixty?0x00:0xac); + i2c_w_mask(ov, 0x13, 0x10, 0x10); + i2c_w_mask(ov, 0x13, 0x00, 0x10); + break; + case SEN_OV7620: + case SEN_OV76BE: + case SEN_OV8600: + i2c_w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80); + i2c_w(ov, 0x2b, sixty?0x00:0xac); + i2c_w_mask(ov, 0x76, 0x01, 0x01); + break; + case SEN_OV6620: + case SEN_OV6630: + i2c_w(ov, 0x2b, sixty?0xa8:0x28); + i2c_w(ov, 0x2a, sixty?0x84:0xa4); + break; + case SEN_KS0127: + case SEN_KS0127B: + case SEN_SAA7111A: + PDEBUG(5, "Unsupported with this sensor"); + return -EPERM; + default: + err("Sensor not supported for set_light_freq"); + return -EINVAL; + } + + ov->lightfreq = freq; + + return 0; +} + +/* If enable is true, turn on the sensor's banding filter, otherwise turn it + * off. This filter tries to reduce the pattern of horizontal light/dark bands + * caused by some (usually fluorescent) lighting. The light frequency must be + * set either before or after enabling it with ov51x_set_light_freq(). + * + * Tested with: OV7610, OV7620, OV76BE, OV6620. + * Unsupported: KS0127, KS0127B, SAA7111A + * Returns: 0 for success + */ +static int +sensor_set_banding_filter(struct usb_ov511 *ov, int enable) +{ + int rc; + + PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); + + if (ov->sensor == SEN_KS0127 || ov->sensor == SEN_KS0127B + || ov->sensor == SEN_SAA7111A) { + PDEBUG(5, "Unsupported with this sensor"); + return -EPERM; + } + + rc = i2c_w_mask(ov, 0x2d, enable?0x04:0x00, 0x04); + if (rc < 0) + return rc; + + ov->bandfilt = enable; + + return 0; +} + +/* If enable is true, turn on the sensor's auto brightness control, otherwise + * turn it off. + * + * Unsupported: KS0127, KS0127B, SAA7111A + * Returns: 0 for success + */ +static int +sensor_set_auto_brightness(struct usb_ov511 *ov, int enable) +{ + int rc; + + PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); + + if (ov->sensor == SEN_KS0127 || ov->sensor == SEN_KS0127B + || ov->sensor == SEN_SAA7111A) { + PDEBUG(5, "Unsupported with this sensor"); + return -EPERM; + } + + rc = i2c_w_mask(ov, 0x2d, enable?0x10:0x00, 0x10); + if (rc < 0) + return rc; + + ov->auto_brt = enable; + + return 0; +} + +/* If enable is true, turn on the sensor's auto exposure control, otherwise + * turn it off. + * + * Unsupported: KS0127, KS0127B, SAA7111A + * Returns: 0 for success + */ +static int +sensor_set_auto_exposure(struct usb_ov511 *ov, int enable) +{ + PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); + + switch (ov->sensor) { + case SEN_OV7610: + i2c_w_mask(ov, 0x29, enable?0x00:0x80, 0x80); + break; + case SEN_OV6620: + case SEN_OV7620: + case SEN_OV76BE: + case SEN_OV8600: + i2c_w_mask(ov, 0x13, enable?0x01:0x00, 0x01); + break; + case SEN_OV6630: + i2c_w_mask(ov, 0x28, enable?0x00:0x10, 0x10); + break; + case SEN_KS0127: + case SEN_KS0127B: + case SEN_SAA7111A: + PDEBUG(5, "Unsupported with this sensor"); + return -EPERM; + default: + err("Sensor not supported for set_auto_exposure"); + return -EINVAL; + } + + ov->auto_exp = enable; + + return 0; +} + +/* Modifies the sensor's exposure algorithm to allow proper exposure of objects + * that are illuminated from behind. + * + * Tested with: OV6620, OV7620 + * Unsupported: OV7610, OV76BE, KS0127, KS0127B, SAA7111A + * Returns: 0 for success + */ +static int +sensor_set_backlight(struct usb_ov511 *ov, int enable) +{ + PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); + + switch (ov->sensor) { + case SEN_OV7620: + case SEN_OV8600: + i2c_w_mask(ov, 0x68, enable?0xe0:0xc0, 0xe0); + i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08); + i2c_w_mask(ov, 0x28, enable?0x02:0x00, 0x02); + break; + case SEN_OV6620: + i2c_w_mask(ov, 0x4e, enable?0xe0:0xc0, 0xe0); + i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08); + i2c_w_mask(ov, 0x0e, enable?0x80:0x00, 0x80); + break; + case SEN_OV6630: + i2c_w_mask(ov, 0x4e, enable?0x80:0x60, 0xe0); + i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08); + i2c_w_mask(ov, 0x28, enable?0x02:0x00, 0x02); + break; + case SEN_OV7610: + case SEN_OV76BE: + case SEN_KS0127: + case SEN_KS0127B: + case SEN_SAA7111A: + PDEBUG(5, "Unsupported with this sensor"); + return -EPERM; + default: + err("Sensor not supported for set_backlight"); + return -EINVAL; + } + + ov->backlight = enable; + + return 0; +} + +static int +sensor_set_mirror(struct usb_ov511 *ov, int enable) +{ + PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); + + switch (ov->sensor) { + case SEN_OV6620: + case SEN_OV6630: + case SEN_OV7610: + case SEN_OV7620: + case SEN_OV76BE: + case SEN_OV8600: + i2c_w_mask(ov, 0x12, enable?0x40:0x00, 0x40); + break; + case SEN_KS0127: + case SEN_KS0127B: + case SEN_SAA7111A: + PDEBUG(5, "Unsupported with this sensor"); + return -EPERM; + default: + err("Sensor not supported for set_mirror"); + return -EINVAL; + } + + ov->mirror = enable; + + return 0; +} + +/* Returns number of bits per pixel (regardless of where they are located; + * planar or not), or zero for unsupported format. + */ +static inline int +get_depth(int palette) +{ + switch (palette) { + case VIDEO_PALETTE_GREY: return 8; + case VIDEO_PALETTE_YUV420: return 12; + case VIDEO_PALETTE_YUV420P: return 12; /* Planar */ + default: return 0; /* Invalid format */ + } +} + +/* Bytes per frame. Used by read(). Return of 0 indicates error */ +static inline long int +get_frame_length(struct ov511_frame *frame) +{ + if (!frame) + return 0; + else + return ((frame->width * frame->height + * get_depth(frame->format)) >> 3); +} + +static int +mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height, + int mode, int sub_flag, int qvga) +{ + int clock; + + /******** Mode (VGA/QVGA) and sensor specific regs ********/ + + switch (ov->sensor) { + case SEN_OV7610: + i2c_w(ov, 0x14, qvga?0x24:0x04); +// FIXME: Does this improve the image quality or frame rate? +#if 0 + i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); + i2c_w(ov, 0x24, 0x10); + i2c_w(ov, 0x25, qvga?0x40:0x8a); + i2c_w(ov, 0x2f, qvga?0x30:0xb0); + i2c_w(ov, 0x35, qvga?0x1c:0x9c); +#endif + break; + case SEN_OV7620: +// i2c_w(ov, 0x2b, 0x00); + i2c_w(ov, 0x14, qvga?0xa4:0x84); + i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); + i2c_w(ov, 0x24, qvga?0x20:0x3a); + i2c_w(ov, 0x25, qvga?0x30:0x60); + i2c_w_mask(ov, 0x2d, qvga?0x40:0x00, 0x40); + i2c_w_mask(ov, 0x67, qvga?0xf0:0x90, 0xf0); + i2c_w_mask(ov, 0x74, qvga?0x20:0x00, 0x20); + break; + case SEN_OV76BE: +// i2c_w(ov, 0x2b, 0x00); + i2c_w(ov, 0x14, qvga?0xa4:0x84); +// FIXME: Enable this once 7620AE uses 7620 initial settings +#if 0 + i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); + i2c_w(ov, 0x24, qvga?0x20:0x3a); + i2c_w(ov, 0x25, qvga?0x30:0x60); + i2c_w_mask(ov, 0x2d, qvga?0x40:0x00, 0x40); + i2c_w_mask(ov, 0x67, qvga?0xb0:0x90, 0xf0); + i2c_w_mask(ov, 0x74, qvga?0x20:0x00, 0x20); +#endif + break; + case SEN_OV6620: + i2c_w(ov, 0x14, qvga?0x24:0x04); + break; + case SEN_OV6630: + i2c_w(ov, 0x14, qvga?0xa0:0x80); + break; + default: + err("Invalid sensor"); + return -EINVAL; + } + + /******** Palette-specific regs ********/ + + if (mode == VIDEO_PALETTE_GREY) { + if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) { + /* these aren't valid on the OV6620/OV7620/6630? */ + i2c_w_mask(ov, 0x0e, 0x40, 0x40); + } + + if (ov->sensor == SEN_OV6630 && ov->bridge == BRG_OV518 + && ov518_color) { + i2c_w_mask(ov, 0x12, 0x00, 0x10); + i2c_w_mask(ov, 0x13, 0x00, 0x20); + } else { + i2c_w_mask(ov, 0x13, 0x20, 0x20); + } + } else { + if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) { + /* not valid on the OV6620/OV7620/6630? */ + i2c_w_mask(ov, 0x0e, 0x00, 0x40); + } + + /* The OV518 needs special treatment. Although both the OV518 + * and the OV6630 support a 16-bit video bus, only the 8 bit Y + * bus is actually used. The UV bus is tied to ground. + * Therefore, the OV6630 needs to be in 8-bit multiplexed + * output mode */ + + if (ov->sensor == SEN_OV6630 && ov->bridge == BRG_OV518 + && ov518_color) { + i2c_w_mask(ov, 0x12, 0x10, 0x10); + i2c_w_mask(ov, 0x13, 0x20, 0x20); + } else { + i2c_w_mask(ov, 0x13, 0x00, 0x20); + } + } + + /******** Clock programming ********/ + + /* The OV6620 needs special handling. This prevents the + * severe banding that normally occurs */ + if (ov->sensor == SEN_OV6620 || ov->sensor == SEN_OV6630) + { + /* Clock down */ + + i2c_w(ov, 0x2a, 0x04); + + if (ov->compress) { +// clock = 0; /* This ensures the highest frame rate */ + clock = 3; + } else if (clockdiv == -1) { /* If user didn't override it */ + clock = 3; /* Gives better exposure time */ + } else { + clock = clockdiv; + } + + PDEBUG(4, "Setting clock divisor to %d", clock); + + i2c_w(ov, 0x11, clock); + + i2c_w(ov, 0x2a, 0x84); + /* This next setting is critical. It seems to improve + * the gain or the contrast. The "reserved" bits seem + * to have some effect in this case. */ + i2c_w(ov, 0x2d, 0x85); + } + else + { + if (ov->compress) { + clock = 1; /* This ensures the highest frame rate */ + } else if (clockdiv == -1) { /* If user didn't override it */ + /* Calculate and set the clock divisor */ + clock = ((sub_flag ? ov->subw * ov->subh + : width * height) + * (mode == VIDEO_PALETTE_GREY ? 2 : 3) / 2) + / 66000; + } else { + clock = clockdiv; + } + + PDEBUG(4, "Setting clock divisor to %d", clock); + + i2c_w(ov, 0x11, clock); + } + + /******** Special Features ********/ + + if (framedrop >= 0) + i2c_w(ov, 0x16, framedrop); + + /* Test Pattern */ + i2c_w_mask(ov, 0x12, (testpat?0x02:0x00), 0x02); + + /* Enable auto white balance */ + i2c_w_mask(ov, 0x12, 0x04, 0x04); + + // This will go away as soon as ov51x_mode_init_sensor_regs() + // is fully tested. + /* 7620/6620/6630? don't have register 0x35, so play it safe */ + if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) { + if (width == 640 && height == 480) + i2c_w(ov, 0x35, 0x9e); + else + i2c_w(ov, 0x35, 0x1e); + } + + return 0; +} + +static int +set_ov_sensor_window(struct usb_ov511 *ov, int width, int height, int mode, + int sub_flag) +{ + int ret; + int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize; + int hoffset, voffset, hwscale = 0, vwscale = 0; + + /* The different sensor ICs handle setting up of window differently. + * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!!! */ + switch (ov->sensor) { + case SEN_OV7610: + case SEN_OV76BE: + hwsbase = 0x38; + hwebase = 0x3a; + vwsbase = vwebase = 0x05; + break; + case SEN_OV6620: + case SEN_OV6630: + hwsbase = 0x38; + hwebase = 0x3a; + vwsbase = 0x05; + vwebase = 0x06; + break; + case SEN_OV7620: + hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */ + hwebase = 0x2f; + vwsbase = vwebase = 0x05; + break; + default: + err("Invalid sensor"); + return -EINVAL; + } + + if (ov->sensor == SEN_OV6620 || ov->sensor == SEN_OV6630) { + /* Note: OV518(+) does downsample on its own) */ + if ((width > 176 && height > 144) + || ov->bclass == BCL_OV518) { /* CIF */ + ret = mode_init_ov_sensor_regs(ov, width, height, + mode, sub_flag, 0); + if (ret < 0) + return ret; + hwscale = 1; + vwscale = 1; /* The datasheet says 0; it's wrong */ + hwsize = 352; + vwsize = 288; + } else if (width > 176 || height > 144) { + err("Illegal dimensions"); + return -EINVAL; + } else { /* QCIF */ + ret = mode_init_ov_sensor_regs(ov, width, height, + mode, sub_flag, 1); + if (ret < 0) + return ret; + hwsize = 176; + vwsize = 144; + } + } else { + if (width > 320 && height > 240) { /* VGA */ + ret = mode_init_ov_sensor_regs(ov, width, height, + mode, sub_flag, 0); + if (ret < 0) + return ret; + hwscale = 2; + vwscale = 1; + hwsize = 640; + vwsize = 480; + } else if (width > 320 || height > 240) { + err("Illegal dimensions"); + return -EINVAL; + } else { /* QVGA */ + ret = mode_init_ov_sensor_regs(ov, width, height, + mode, sub_flag, 1); + if (ret < 0) + return ret; + hwscale = 1; + hwsize = 320; + vwsize = 240; + } + } + + /* Center the window */ + hoffset = ((hwsize - width) / 2) >> hwscale; + voffset = ((vwsize - height) / 2) >> vwscale; + + /* FIXME! - This needs to be changed to support 160x120 and 6620!!! */ + if (sub_flag) { + i2c_w(ov, 0x17, hwsbase+(ov->subx>>hwscale)); + i2c_w(ov, 0x18, hwebase+((ov->subx+ov->subw)>>hwscale)); + i2c_w(ov, 0x19, vwsbase+(ov->suby>>vwscale)); + i2c_w(ov, 0x1a, vwebase+((ov->suby+ov->subh)>>vwscale)); + } else { + i2c_w(ov, 0x17, hwsbase + hoffset); + i2c_w(ov, 0x18, hwebase + hoffset + (hwsize>>hwscale)); + i2c_w(ov, 0x19, vwsbase + voffset); + i2c_w(ov, 0x1a, vwebase + voffset + (vwsize>>vwscale)); + } + +#ifdef OV511_DEBUG + if (dump_sensor) + dump_i2c_regs(ov); +#endif + + return 0; +} + +/* Set up the OV511/OV511+ with the given image parameters. + * + * Do not put any sensor-specific code in here (including I2C I/O functions) + */ +static int +ov511_mode_init_regs(struct usb_ov511 *ov, + int width, int height, int mode, int sub_flag) +{ + int hsegs, vsegs; + + if (sub_flag) { + width = ov->subw; + height = ov->subh; + } + + PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", + width, height, mode, sub_flag); + + // FIXME: This should be moved to a 7111a-specific function once + // subcapture is dealt with properly + if (ov->sensor == SEN_SAA7111A) { + if (width == 320 && height == 240) { + /* No need to do anything special */ + } else if (width == 640 && height == 480) { + /* Set the OV511 up as 320x480, but keep the + * V4L resolution as 640x480 */ + width = 320; + } else { + err("SAA7111A only allows 320x240 or 640x480"); + return -EINVAL; + } + } + + /* Make sure width and height are a multiple of 8 */ + if (width % 8 || height % 8) { + err("Invalid size (%d, %d) (mode = %d)", width, height, mode); + return -EINVAL; + } + + if (width < ov->minwidth || height < ov->minheight) { + err("Requested dimensions are too small"); + return -EINVAL; + } + + if (ov51x_stop(ov) < 0) + return -EIO; + + if (mode == VIDEO_PALETTE_GREY) { + reg_w(ov, R511_CAM_UV_EN, 0x00); + reg_w(ov, R511_SNAP_UV_EN, 0x00); + reg_w(ov, R511_SNAP_OPTS, 0x01); + } else { + reg_w(ov, R511_CAM_UV_EN, 0x01); + reg_w(ov, R511_SNAP_UV_EN, 0x01); + reg_w(ov, R511_SNAP_OPTS, 0x03); + } + + /* Here I'm assuming that snapshot size == image size. + * I hope that's always true. --claudio + */ + hsegs = (width >> 3) - 1; + vsegs = (height >> 3) - 1; + + reg_w(ov, R511_CAM_PXCNT, hsegs); + reg_w(ov, R511_CAM_LNCNT, vsegs); + reg_w(ov, R511_CAM_PXDIV, 0x00); + reg_w(ov, R511_CAM_LNDIV, 0x00); + + /* YUV420, low pass filter on */ + reg_w(ov, R511_CAM_OPTS, 0x03); + + /* Snapshot additions */ + reg_w(ov, R511_SNAP_PXCNT, hsegs); + reg_w(ov, R511_SNAP_LNCNT, vsegs); + reg_w(ov, R511_SNAP_PXDIV, 0x00); + reg_w(ov, R511_SNAP_LNDIV, 0x00); + + if (ov->compress) { + /* Enable Y and UV quantization and compression */ + reg_w(ov, R511_COMP_EN, 0x07); + reg_w(ov, R511_COMP_LUT_EN, 0x03); + ov51x_reset(ov, OV511_RESET_OMNICE); + } + + if (ov51x_restart(ov) < 0) + return -EIO; + + return 0; +} + +/* Sets up the OV518/OV518+ with the given image parameters + * + * OV518 needs a completely different approach, until we can figure out what + * the individual registers do. Also, only 15 FPS is supported now. + * + * Do not put any sensor-specific code in here (including I2C I/O functions) + */ +static int +ov518_mode_init_regs(struct usb_ov511 *ov, + int width, int height, int mode, int sub_flag) +{ + int hsegs, vsegs, hi_res; + + if (sub_flag) { + width = ov->subw; + height = ov->subh; + } + + PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", + width, height, mode, sub_flag); + + if (width % 16 || height % 8) { + err("Invalid size (%d, %d)", width, height); + return -EINVAL; + } + + if (width < ov->minwidth || height < ov->minheight) { + err("Requested dimensions are too small"); + return -EINVAL; + } + + if (width >= 320 && height >= 240) { + hi_res = 1; + } else if (width >= 320 || height >= 240) { + err("Invalid width/height combination (%d, %d)", width, height); + return -EINVAL; + } else { + hi_res = 0; + } + + if (ov51x_stop(ov) < 0) + return -EIO; + + /******** Set the mode ********/ + + reg_w(ov, 0x2b, 0); + reg_w(ov, 0x2c, 0); + reg_w(ov, 0x2d, 0); + reg_w(ov, 0x2e, 0); + reg_w(ov, 0x3b, 0); + reg_w(ov, 0x3c, 0); + reg_w(ov, 0x3d, 0); + reg_w(ov, 0x3e, 0); + + if (ov->bridge == BRG_OV518 && ov518_color) { + /* OV518 needs U and V swapped */ + i2c_w_mask(ov, 0x15, 0x00, 0x01); + + if (mode == VIDEO_PALETTE_GREY) { + /* Set 16-bit input format (UV data are ignored) */ + reg_w_mask(ov, 0x20, 0x00, 0x08); + + /* Set 8-bit (4:0:0) output format */ + reg_w_mask(ov, 0x28, 0x00, 0xf0); + reg_w_mask(ov, 0x38, 0x00, 0xf0); + } else { + /* Set 8-bit (YVYU) input format */ + reg_w_mask(ov, 0x20, 0x08, 0x08); + + /* Set 12-bit (4:2:0) output format */ + reg_w_mask(ov, 0x28, 0x80, 0xf0); + reg_w_mask(ov, 0x38, 0x80, 0xf0); + } + } else { + reg_w(ov, 0x28, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80); + reg_w(ov, 0x38, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80); + } + + hsegs = width / 16; + vsegs = height / 4; + + reg_w(ov, 0x29, hsegs); + reg_w(ov, 0x2a, vsegs); + + reg_w(ov, 0x39, hsegs); + reg_w(ov, 0x3a, vsegs); + + /* Windows driver does this here; who knows why */ + reg_w(ov, 0x2f, 0x80); + + /******** Set the framerate (to 15 FPS) ********/ + + /* Mode independent, but framerate dependent, regs */ + reg_w(ov, 0x51, 0x02); /* Clock divider; lower==faster */ + reg_w(ov, 0x22, 0x18); + reg_w(ov, 0x23, 0xff); + + if (ov->bridge == BRG_OV518PLUS) + reg_w(ov, 0x21, 0x19); + else + reg_w(ov, 0x71, 0x19); /* Compression-related? */ + + // FIXME: Sensor-specific + /* Bit 5 is what matters here. Of course, it is "reserved" */ + i2c_w(ov, 0x54, 0x23); + + reg_w(ov, 0x2f, 0x80); + + if (ov->bridge == BRG_OV518PLUS) { + reg_w(ov, 0x24, 0x94); + reg_w(ov, 0x25, 0x90); + ov518_reg_w32(ov, 0xc4, 400, 2); /* 190h */ + ov518_reg_w32(ov, 0xc6, 540, 2); /* 21ch */ + ov518_reg_w32(ov, 0xc7, 540, 2); /* 21ch */ + ov518_reg_w32(ov, 0xc8, 108, 2); /* 6ch */ + ov518_reg_w32(ov, 0xca, 131098, 3); /* 2001ah */ + ov518_reg_w32(ov, 0xcb, 532, 2); /* 214h */ + ov518_reg_w32(ov, 0xcc, 2400, 2); /* 960h */ + ov518_reg_w32(ov, 0xcd, 32, 2); /* 20h */ + ov518_reg_w32(ov, 0xce, 608, 2); /* 260h */ + } else { + reg_w(ov, 0x24, 0x9f); + reg_w(ov, 0x25, 0x90); + ov518_reg_w32(ov, 0xc4, 400, 2); /* 190h */ + ov518_reg_w32(ov, 0xc6, 500, 2); /* 1f4h */ + ov518_reg_w32(ov, 0xc7, 500, 2); /* 1f4h */ + ov518_reg_w32(ov, 0xc8, 142, 2); /* 8eh */ + ov518_reg_w32(ov, 0xca, 131098, 3); /* 2001ah */ + ov518_reg_w32(ov, 0xcb, 532, 2); /* 214h */ + ov518_reg_w32(ov, 0xcc, 2000, 2); /* 7d0h */ + ov518_reg_w32(ov, 0xcd, 32, 2); /* 20h */ + ov518_reg_w32(ov, 0xce, 608, 2); /* 260h */ + } + + reg_w(ov, 0x2f, 0x80); + + if (ov51x_restart(ov) < 0) + return -EIO; + + /* Reset it just for good measure */ + if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0) + return -EIO; + + return 0; +} + +/* This is a wrapper around the OV511, OV518, and sensor specific functions */ +static int +mode_init_regs(struct usb_ov511 *ov, + int width, int height, int mode, int sub_flag) +{ + int rc = 0; + + if (!ov || !ov->dev) + return -EFAULT; + + if (ov->bclass == BCL_OV518) { + rc = ov518_mode_init_regs(ov, width, height, mode, sub_flag); + } else { + rc = ov511_mode_init_regs(ov, width, height, mode, sub_flag); + } + + if (FATAL_ERROR(rc)) + return rc; + + switch (ov->sensor) { + case SEN_OV7610: + case SEN_OV7620: + case SEN_OV76BE: + case SEN_OV8600: + case SEN_OV6620: + case SEN_OV6630: + rc = set_ov_sensor_window(ov, width, height, mode, sub_flag); + break; + case SEN_KS0127: + case SEN_KS0127B: + err("KS0127-series decoders not supported yet"); + rc = -EINVAL; + break; + case SEN_SAA7111A: +// rc = mode_init_saa_sensor_regs(ov, width, height, mode, +// sub_flag); + + PDEBUG(1, "SAA status = 0x%02X", i2c_r(ov, 0x1f)); + break; + default: + err("Unknown sensor"); + rc = -EINVAL; + } + + if (FATAL_ERROR(rc)) + return rc; + + /* Sensor-independent settings */ + rc = sensor_set_auto_brightness(ov, ov->auto_brt); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_set_auto_exposure(ov, ov->auto_exp); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_set_banding_filter(ov, bandingfilter); + if (FATAL_ERROR(rc)) + return rc; + + if (ov->lightfreq) { + rc = sensor_set_light_freq(ov, lightfreq); + if (FATAL_ERROR(rc)) + return rc; + } + + rc = sensor_set_backlight(ov, ov->backlight); + if (FATAL_ERROR(rc)) + return rc; + + rc = sensor_set_mirror(ov, ov->mirror); + if (FATAL_ERROR(rc)) + return rc; + + return 0; +} + +/* This sets the default image parameters. This is useful for apps that use + * read() and do not set these. + */ +static int +ov51x_set_default_params(struct usb_ov511 *ov) +{ + int i; + + /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used + * (using read() instead). */ + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov->frame[i].width = ov->maxwidth; + ov->frame[i].height = ov->maxheight; + ov->frame[i].bytes_read = 0; + if (force_palette) + ov->frame[i].format = force_palette; + else + ov->frame[i].format = VIDEO_PALETTE_YUV420; + + ov->frame[i].depth = get_depth(ov->frame[i].format); + } + + PDEBUG(3, "%dx%d, %s", ov->maxwidth, ov->maxheight, + symbolic(v4l1_plist, ov->frame[0].format)); + + /* Initialize to max width/height, YUV420 or RGB24 (if supported) */ + if (mode_init_regs(ov, ov->maxwidth, ov->maxheight, + ov->frame[0].format, 0) < 0) + return -EINVAL; + + return 0; +} + +/********************************************************************** + * + * Video decoder stuff + * + **********************************************************************/ + +/* Set analog input port of decoder */ +static int +decoder_set_input(struct usb_ov511 *ov, int input) +{ + PDEBUG(4, "port %d", input); + + switch (ov->sensor) { + case SEN_SAA7111A: + { + /* Select mode */ + i2c_w_mask(ov, 0x02, input, 0x07); + /* Bypass chrominance trap for modes 4..7 */ + i2c_w_mask(ov, 0x09, (input > 3) ? 0x80:0x00, 0x80); + break; + } + default: + return -EINVAL; + } + + return 0; +} + +/* Get ASCII name of video input */ +static int +decoder_get_input_name(struct usb_ov511 *ov, int input, char *name) +{ + switch (ov->sensor) { + case SEN_SAA7111A: + { + if (input < 0 || input > 7) + return -EINVAL; + else if (input < 4) + sprintf(name, "CVBS-%d", input); + else // if (input < 8) + sprintf(name, "S-Video-%d", input - 4); + break; + } + default: + sprintf(name, "%s", "Camera"); + } + + return 0; +} + +/* Set norm (NTSC, PAL, SECAM, AUTO) */ +static int +decoder_set_norm(struct usb_ov511 *ov, int norm) +{ + PDEBUG(4, "%d", norm); + + switch (ov->sensor) { + case SEN_SAA7111A: + { + int reg_8, reg_e; + + if (norm == VIDEO_MODE_NTSC) { + reg_8 = 0x40; /* 60 Hz */ + reg_e = 0x00; /* NTSC M / PAL BGHI */ + } else if (norm == VIDEO_MODE_PAL) { + reg_8 = 0x00; /* 50 Hz */ + reg_e = 0x00; /* NTSC M / PAL BGHI */ + } else if (norm == VIDEO_MODE_AUTO) { + reg_8 = 0x80; /* Auto field detect */ + reg_e = 0x00; /* NTSC M / PAL BGHI */ + } else if (norm == VIDEO_MODE_SECAM) { + reg_8 = 0x00; /* 50 Hz */ + reg_e = 0x50; /* SECAM / PAL 4.43 */ + } else { + return -EINVAL; + } + + i2c_w_mask(ov, 0x08, reg_8, 0xc0); + i2c_w_mask(ov, 0x0e, reg_e, 0x70); + break; + } + default: + return -EINVAL; + } + + return 0; +} + +/********************************************************************** + * + * Raw data parsing + * + **********************************************************************/ + +/* Copies a 64-byte segment at pIn to an 8x8 block at pOut. The width of the + * image at pOut is specified by w. + */ +static inline void +make_8x8(unsigned char *pIn, unsigned char *pOut, int w) +{ + unsigned char *pOut1 = pOut; + int x, y; + + for (y = 0; y < 8; y++) { + pOut1 = pOut; + for (x = 0; x < 8; x++) { + *pOut1++ = *pIn++; + } + pOut += w; + } +} + +/* + * For RAW BW (YUV 4:0:0) images, data show up in 256 byte segments. + * The segments represent 4 squares of 8x8 pixels as follows: + * + * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 + * 8 9 ... 15 72 73 ... 79 200 201 ... 207 + * ... ... ... + * 56 57 ... 63 120 121 ... 127 248 249 ... 255 + * + */ +static void +yuv400raw_to_yuv400p(struct ov511_frame *frame, + unsigned char *pIn0, unsigned char *pOut0) +{ + int x, y; + unsigned char *pIn, *pOut, *pOutLine; + + /* Copy Y */ + pIn = pIn0; + pOutLine = pOut0; + for (y = 0; y < frame->rawheight - 1; y += 8) { + pOut = pOutLine; + for (x = 0; x < frame->rawwidth - 1; x += 8) { + make_8x8(pIn, pOut, frame->rawwidth); + pIn += 64; + pOut += 8; + } + pOutLine += 8 * frame->rawwidth; + } +} + +/* + * For YUV 4:2:0 images, the data show up in 384 byte segments. + * The first 64 bytes of each segment are U, the next 64 are V. The U and + * V are arranged as follows: + * + * 0 1 ... 7 + * 8 9 ... 15 + * ... + * 56 57 ... 63 + * + * U and V are shipped at half resolution (1 U,V sample -> one 2x2 block). + * + * The next 256 bytes are full resolution Y data and represent 4 squares + * of 8x8 pixels as follows: + * + * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 + * 8 9 ... 15 72 73 ... 79 200 201 ... 207 + * ... ... ... + * 56 57 ... 63 120 121 ... 127 ... 248 249 ... 255 + * + * Note that the U and V data in one segment represent a 16 x 16 pixel + * area, but the Y data represent a 32 x 8 pixel area. If the width is not an + * even multiple of 32, the extra 8x8 blocks within a 32x8 block belong to the + * next horizontal stripe. + * + * If dumppix module param is set, _parse_data just dumps the incoming segments, + * verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480 + * this puts the data on the standard output and can be analyzed with the + * parseppm.c utility I wrote. That's a much faster way for figuring out how + * these data are scrambled. + */ + +/* Converts from raw, uncompressed segments at pIn0 to a YUV420P frame at pOut0. + * + * FIXME: Currently only handles width and height that are multiples of 16 + */ +static void +yuv420raw_to_yuv420p(struct ov511_frame *frame, + unsigned char *pIn0, unsigned char *pOut0) +{ + int k, x, y; + unsigned char *pIn, *pOut, *pOutLine; + const unsigned int a = frame->rawwidth * frame->rawheight; + const unsigned int w = frame->rawwidth / 2; + + /* Copy U and V */ + pIn = pIn0; + pOutLine = pOut0 + a; + for (y = 0; y < frame->rawheight - 1; y += 16) { + pOut = pOutLine; + for (x = 0; x < frame->rawwidth - 1; x += 16) { + make_8x8(pIn, pOut, w); + make_8x8(pIn + 64, pOut + a/4, w); + pIn += 384; + pOut += 8; + } + pOutLine += 8 * w; + } + + /* Copy Y */ + pIn = pIn0 + 128; + pOutLine = pOut0; + k = 0; + for (y = 0; y < frame->rawheight - 1; y += 8) { + pOut = pOutLine; + for (x = 0; x < frame->rawwidth - 1; x += 8) { + make_8x8(pIn, pOut, frame->rawwidth); + pIn += 64; + pOut += 8; + if ((++k) > 3) { + k = 0; + pIn += 128; + } + } + pOutLine += 8 * frame->rawwidth; + } +} + +/********************************************************************** + * + * Decompression + * + **********************************************************************/ + +static int +request_decompressor(struct usb_ov511 *ov) +{ + if (ov->bclass == BCL_OV511 || ov->bclass == BCL_OV518) { + err("No decompressor available"); + } else { + err("Unknown bridge"); + } + + return -ENOSYS; +} + +static void +decompress(struct usb_ov511 *ov, struct ov511_frame *frame, + unsigned char *pIn0, unsigned char *pOut0) +{ + if (!ov->decomp_ops) + if (request_decompressor(ov)) + return; + +} + +/********************************************************************** + * + * Format conversion + * + **********************************************************************/ + +/* Fuses even and odd fields together, and doubles width. + * INPUT: an odd field followed by an even field at pIn0, in YUV planar format + * OUTPUT: a normal YUV planar image, with correct aspect ratio + */ +static void +deinterlace(struct ov511_frame *frame, int rawformat, + unsigned char *pIn0, unsigned char *pOut0) +{ + const int fieldheight = frame->rawheight / 2; + const int fieldpix = fieldheight * frame->rawwidth; + const int w = frame->width; + int x, y; + unsigned char *pInEven, *pInOdd, *pOut; + + PDEBUG(5, "fieldheight=%d", fieldheight); + + if (frame->rawheight != frame->height) { + err("invalid height"); + return; + } + + if ((frame->rawwidth * 2) != frame->width) { + err("invalid width"); + return; + } + + /* Y */ + pInOdd = pIn0; + pInEven = pInOdd + fieldpix; + pOut = pOut0; + for (y = 0; y < fieldheight; y++) { + for (x = 0; x < frame->rawwidth; x++) { + *pOut = *pInEven; + *(pOut+1) = *pInEven++; + *(pOut+w) = *pInOdd; + *(pOut+w+1) = *pInOdd++; + pOut += 2; + } + pOut += w; + } + + if (rawformat == RAWFMT_YUV420) { + /* U */ + pInOdd = pIn0 + fieldpix * 2; + pInEven = pInOdd + fieldpix / 4; + for (y = 0; y < fieldheight / 2; y++) { + for (x = 0; x < frame->rawwidth / 2; x++) { + *pOut = *pInEven; + *(pOut+1) = *pInEven++; + *(pOut+w/2) = *pInOdd; + *(pOut+w/2+1) = *pInOdd++; + pOut += 2; + } + pOut += w/2; + } + /* V */ + pInOdd = pIn0 + fieldpix * 2 + fieldpix / 2; + pInEven = pInOdd + fieldpix / 4; + for (y = 0; y < fieldheight / 2; y++) { + for (x = 0; x < frame->rawwidth / 2; x++) { + *pOut = *pInEven; + *(pOut+1) = *pInEven++; + *(pOut+w/2) = *pInOdd; + *(pOut+w/2+1) = *pInOdd++; + pOut += 2; + } + pOut += w/2; + } + } +} + +static void +ov51x_postprocess_grey(struct usb_ov511 *ov, struct ov511_frame *frame) +{ + /* Deinterlace frame, if necessary */ + if (ov->sensor == SEN_SAA7111A && frame->rawheight >= 480) { + if (frame->compressed) + decompress(ov, frame, frame->rawdata, + frame->tempdata); + else + yuv400raw_to_yuv400p(frame, frame->rawdata, + frame->tempdata); + + deinterlace(frame, RAWFMT_YUV400, frame->tempdata, + frame->data); + } else { + if (frame->compressed) + decompress(ov, frame, frame->rawdata, + frame->data); + else + yuv400raw_to_yuv400p(frame, frame->rawdata, + frame->data); + } +} + +/* Process raw YUV420 data into standard YUV420P */ +static void +ov51x_postprocess_yuv420(struct usb_ov511 *ov, struct ov511_frame *frame) +{ + /* Deinterlace frame, if necessary */ + if (ov->sensor == SEN_SAA7111A && frame->rawheight >= 480) { + if (frame->compressed) + decompress(ov, frame, frame->rawdata, frame->tempdata); + else + yuv420raw_to_yuv420p(frame, frame->rawdata, + frame->tempdata); + + deinterlace(frame, RAWFMT_YUV420, frame->tempdata, + frame->data); + } else { + if (frame->compressed) + decompress(ov, frame, frame->rawdata, frame->data); + else + yuv420raw_to_yuv420p(frame, frame->rawdata, + frame->data); + } +} + +/* Post-processes the specified frame. This consists of: + * 1. Decompress frame, if necessary + * 2. Deinterlace frame and scale to proper size, if necessary + * 3. Convert from YUV planar to destination format, if necessary + * 4. Fix the RGB offset, if necessary + */ +static void +ov51x_postprocess(struct usb_ov511 *ov, struct ov511_frame *frame) +{ + if (dumppix) { + memset(frame->data, 0, + MAX_DATA_SIZE(ov->maxwidth, ov->maxheight)); + PDEBUG(4, "Dumping %d bytes", frame->bytes_recvd); + memcpy(frame->data, frame->rawdata, frame->bytes_recvd); + } else { + switch (frame->format) { + case VIDEO_PALETTE_GREY: + ov51x_postprocess_grey(ov, frame); + break; + case VIDEO_PALETTE_YUV420: + case VIDEO_PALETTE_YUV420P: + ov51x_postprocess_yuv420(ov, frame); + break; + default: + err("Cannot convert data to %s", + symbolic(v4l1_plist, frame->format)); + } + } +} + +/********************************************************************** + * + * OV51x data transfer, IRQ handler + * + **********************************************************************/ + +static inline void +ov511_move_data(struct usb_ov511 *ov, unsigned char *in, int n) +{ + int num, offset; + int pnum = in[ov->packet_size - 1]; /* Get packet number */ + int max_raw = MAX_RAW_DATA_SIZE(ov->maxwidth, ov->maxheight); + struct ov511_frame *frame = &ov->frame[ov->curframe]; + struct timeval *ts; + + /* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th + * byte non-zero. The EOF packet has image width/height in the + * 10th and 11th bytes. The 9th byte is given as follows: + * + * bit 7: EOF + * 6: compression enabled + * 5: 422/420/400 modes + * 4: 422/420/400 modes + * 3: 1 + * 2: snapshot button on + * 1: snapshot frame + * 0: even/odd field + */ + + if (printph) { + info("ph(%3d): %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x", + pnum, in[0], in[1], in[2], in[3], in[4], in[5], in[6], + in[7], in[8], in[9], in[10], in[11]); + } + + /* Check for SOF/EOF packet */ + if ((in[0] | in[1] | in[2] | in[3] | in[4] | in[5] | in[6] | in[7]) || + (~in[8] & 0x08)) + goto check_middle; + + /* Frame end */ + if (in[8] & 0x80) { + ts = (struct timeval *)(frame->data + + MAX_FRAME_SIZE(ov->maxwidth, ov->maxheight)); + do_gettimeofday(ts); + + /* Get the actual frame size from the EOF header */ + frame->rawwidth = ((int)(in[9]) + 1) * 8; + frame->rawheight = ((int)(in[10]) + 1) * 8; + + PDEBUG(4, "Frame end, frame=%d, pnum=%d, w=%d, h=%d, recvd=%d", + ov->curframe, pnum, frame->rawwidth, frame->rawheight, + frame->bytes_recvd); + + /* Validate the header data */ + RESTRICT_TO_RANGE(frame->rawwidth, ov->minwidth, ov->maxwidth); + RESTRICT_TO_RANGE(frame->rawheight, ov->minheight, + ov->maxheight); + + /* Don't allow byte count to exceed buffer size */ + RESTRICT_TO_RANGE(frame->bytes_recvd, 8, max_raw); + + if (frame->scanstate == STATE_LINES) { + int nextf; + + frame->grabstate = FRAME_DONE; + wake_up_interruptible(&frame->wq); + + /* If next frame is ready or grabbing, + * point to it */ + nextf = (ov->curframe + 1) % OV511_NUMFRAMES; + if (ov->frame[nextf].grabstate == FRAME_READY + || ov->frame[nextf].grabstate == FRAME_GRABBING) { + ov->curframe = nextf; + ov->frame[nextf].scanstate = STATE_SCANNING; + } else { + if (frame->grabstate == FRAME_DONE) { + PDEBUG(4, "** Frame done **"); + } else { + PDEBUG(4, "Frame not ready? state = %d", + ov->frame[nextf].grabstate); + } + + ov->curframe = -1; + } + } else { + PDEBUG(5, "Frame done, but not scanning"); + } + /* Image corruption caused by misplaced frame->segment = 0 + * fixed by carlosf@conectiva.com.br + */ + } else { + /* Frame start */ + PDEBUG(4, "Frame start, framenum = %d", ov->curframe); + + /* Check to see if it's a snapshot frame */ + /* FIXME?? Should the snapshot reset go here? Performance? */ + if (in[8] & 0x02) { + frame->snapshot = 1; + PDEBUG(3, "snapshot detected"); + } + + frame->scanstate = STATE_LINES; + frame->bytes_recvd = 0; + frame->compressed = in[8] & 0x40; + } + +check_middle: + /* Are we in a frame? */ + if (frame->scanstate != STATE_LINES) { + PDEBUG(5, "Not in a frame; packet skipped"); + return; + } + + /* If frame start, skip header */ + if (frame->bytes_recvd == 0) + offset = 9; + else + offset = 0; + + num = n - offset - 1; + + /* Dump all data exactly as received */ + if (dumppix == 2) { + frame->bytes_recvd += n - 1; + if (frame->bytes_recvd <= max_raw) + memcpy(frame->rawdata + frame->bytes_recvd - (n - 1), + in, n - 1); + else + PDEBUG(3, "Raw data buffer overrun!! (%d)", + frame->bytes_recvd - max_raw); + } else if (!frame->compressed && !remove_zeros) { + frame->bytes_recvd += num; + if (frame->bytes_recvd <= max_raw) + memcpy(frame->rawdata + frame->bytes_recvd - num, + in + offset, num); + else + PDEBUG(3, "Raw data buffer overrun!! (%d)", + frame->bytes_recvd - max_raw); + } else { /* Remove all-zero FIFO lines (aligned 32-byte blocks) */ + int b, read = 0, allzero, copied = 0; + if (offset) { + frame->bytes_recvd += 32 - offset; // Bytes out + memcpy(frame->rawdata, in + offset, 32 - offset); + read += 32; + } + + while (read < n - 1) { + allzero = 1; + for (b = 0; b < 32; b++) { + if (in[read + b]) { + allzero = 0; + break; + } + } + + if (allzero) { + /* Don't copy it */ + } else { + if (frame->bytes_recvd + copied + 32 <= max_raw) + { + memcpy(frame->rawdata + + frame->bytes_recvd + copied, + in + read, 32); + copied += 32; + } else { + PDEBUG(3, "Raw data buffer overrun!!"); + } + } + read += 32; + } + + frame->bytes_recvd += copied; + } +} + +static inline void +ov518_move_data(struct usb_ov511 *ov, unsigned char *in, int n) +{ + int max_raw = MAX_RAW_DATA_SIZE(ov->maxwidth, ov->maxheight); + struct ov511_frame *frame = &ov->frame[ov->curframe]; + struct timeval *ts; + + /* Don't copy the packet number byte */ + if (ov->packet_numbering) + --n; + + /* A false positive here is likely, until OVT gives me + * the definitive SOF/EOF format */ + if ((!(in[0] | in[1] | in[2] | in[3] | in[5])) && in[6]) { + if (printph) { + info("ph: %2x %2x %2x %2x %2x %2x %2x %2x", in[0], + in[1], in[2], in[3], in[4], in[5], in[6], in[7]); + } + + if (frame->scanstate == STATE_LINES) { + PDEBUG(4, "Detected frame end/start"); + goto eof; + } else { //scanstate == STATE_SCANNING + /* Frame start */ + PDEBUG(4, "Frame start, framenum = %d", ov->curframe); + goto sof; + } + } else { + goto check_middle; + } + +eof: + ts = (struct timeval *)(frame->data + + MAX_FRAME_SIZE(ov->maxwidth, ov->maxheight)); + do_gettimeofday(ts); + + PDEBUG(4, "Frame end, curframe = %d, hw=%d, vw=%d, recvd=%d", + ov->curframe, + (int)(in[9]), (int)(in[10]), frame->bytes_recvd); + + // FIXME: Since we don't know the header formats yet, + // there is no way to know what the actual image size is + frame->rawwidth = frame->width; + frame->rawheight = frame->height; + + /* Validate the header data */ + RESTRICT_TO_RANGE(frame->rawwidth, ov->minwidth, ov->maxwidth); + RESTRICT_TO_RANGE(frame->rawheight, ov->minheight, ov->maxheight); + + /* Don't allow byte count to exceed buffer size */ + RESTRICT_TO_RANGE(frame->bytes_recvd, 8, max_raw); + + if (frame->scanstate == STATE_LINES) { + int nextf; + + frame->grabstate = FRAME_DONE; + wake_up_interruptible(&frame->wq); + + /* If next frame is ready or grabbing, + * point to it */ + nextf = (ov->curframe + 1) % OV511_NUMFRAMES; + if (ov->frame[nextf].grabstate == FRAME_READY + || ov->frame[nextf].grabstate == FRAME_GRABBING) { + ov->curframe = nextf; + ov->frame[nextf].scanstate = STATE_SCANNING; + frame = &ov->frame[nextf]; + } else { + if (frame->grabstate == FRAME_DONE) { + PDEBUG(4, "** Frame done **"); + } else { + PDEBUG(4, "Frame not ready? state = %d", + ov->frame[nextf].grabstate); + } + + ov->curframe = -1; + PDEBUG(4, "SOF dropped (no active frame)"); + return; /* Nowhere to store this frame */ + } + } +sof: + PDEBUG(4, "Starting capture on frame %d", frame->framenum); + +// Snapshot not reverse-engineered yet. +#if 0 + /* Check to see if it's a snapshot frame */ + /* FIXME?? Should the snapshot reset go here? Performance? */ + if (in[8] & 0x02) { + frame->snapshot = 1; + PDEBUG(3, "snapshot detected"); + } +#endif + frame->scanstate = STATE_LINES; + frame->bytes_recvd = 0; + frame->compressed = 1; + +check_middle: + /* Are we in a frame? */ + if (frame->scanstate != STATE_LINES) { + PDEBUG(4, "scanstate: no SOF yet"); + return; + } + + /* Dump all data exactly as received */ + if (dumppix == 2) { + frame->bytes_recvd += n; + if (frame->bytes_recvd <= max_raw) + memcpy(frame->rawdata + frame->bytes_recvd - n, in, n); + else + PDEBUG(3, "Raw data buffer overrun!! (%d)", + frame->bytes_recvd - max_raw); + } else { + /* All incoming data are divided into 8-byte segments. If the + * segment contains all zero bytes, it must be skipped. These + * zero-segments allow the OV518 to mainain a constant data rate + * regardless of the effectiveness of the compression. Segments + * are aligned relative to the beginning of each isochronous + * packet. The first segment in each image is a header (the + * decompressor skips it later). + */ + + int b, read = 0, allzero, copied = 0; + + while (read < n) { + allzero = 1; + for (b = 0; b < 8; b++) { + if (in[read + b]) { + allzero = 0; + break; + } + } + + if (allzero) { + /* Don't copy it */ + } else { + if (frame->bytes_recvd + copied + 8 <= max_raw) + { + memcpy(frame->rawdata + + frame->bytes_recvd + copied, + in + read, 8); + copied += 8; + } else { + PDEBUG(3, "Raw data buffer overrun!!"); + } + } + read += 8; + } + frame->bytes_recvd += copied; + } +} + +static void +ov51x_isoc_irq(struct urb *urb, struct pt_regs *regs) +{ + int i; + struct usb_ov511 *ov; + struct ov511_sbuf *sbuf; + + if (!urb->context) { + PDEBUG(4, "no context"); + return; + } + + sbuf = urb->context; + ov = sbuf->ov; + + if (!ov || !ov->dev || !ov->user) { + PDEBUG(4, "no device, or not open"); + return; + } + + if (!ov->streaming) { + PDEBUG(4, "hmmm... not streaming, but got interrupt"); + return; + } + + if (urb->status == -ENOENT || urb->status == -ECONNRESET) { + PDEBUG(4, "URB unlinked"); + return; + } + + if (urb->status != -EINPROGRESS && urb->status != 0) { + err("ERROR: urb->status=%d: %s", urb->status, + symbolic(urb_errlist, urb->status)); + } + + /* Copy the data received into our frame buffer */ + PDEBUG(5, "sbuf[%d]: Moving %d packets", sbuf->n, + urb->number_of_packets); + for (i = 0; i < urb->number_of_packets; i++) { + /* Warning: Don't call *_move_data() if no frame active! */ + if (ov->curframe >= 0) { + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + unsigned char *cdata; + + urb->iso_frame_desc[i].actual_length = 0; + urb->iso_frame_desc[i].status = 0; + + cdata = urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + + if (!n) { + PDEBUG(4, "Zero-length packet"); + continue; + } + + if (st) + PDEBUG(2, "data error: [%d] len=%d, status=%d", + i, n, st); + + if (ov->bclass == BCL_OV511) + ov511_move_data(ov, cdata, n); + else if (ov->bclass == BCL_OV518) + ov518_move_data(ov, cdata, n); + else + err("Unknown bridge device (%d)", ov->bridge); + + } else if (waitqueue_active(&ov->wq)) { + wake_up_interruptible(&ov->wq); + } + } + + /* Resubmit this URB */ + urb->dev = ov->dev; + if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) + err("usb_submit_urb() ret %d", i); + + return; +} + +/**************************************************************************** + * + * Stream initialization and termination + * + ***************************************************************************/ + +static int +ov51x_init_isoc(struct usb_ov511 *ov) +{ + struct urb *urb; + int fx, err, n, size; + + PDEBUG(3, "*** Initializing capture ***"); + + ov->curframe = -1; + + if (ov->bridge == BRG_OV511) { + if (cams == 1) + size = 993; + else if (cams == 2) + size = 513; + else if (cams == 3 || cams == 4) + size = 257; + else { + err("\"cams\" parameter too high!"); + return -1; + } + } else if (ov->bridge == BRG_OV511PLUS) { + if (cams == 1) + size = 961; + else if (cams == 2) + size = 513; + else if (cams == 3 || cams == 4) + size = 257; + else if (cams >= 5 && cams <= 8) + size = 129; + else if (cams >= 9 && cams <= 31) + size = 33; + else { + err("\"cams\" parameter too high!"); + return -1; + } + } else if (ov->bclass == BCL_OV518) { + if (cams == 1) + size = 896; + else if (cams == 2) + size = 512; + else if (cams == 3 || cams == 4) + size = 256; + else if (cams >= 5 && cams <= 8) + size = 128; + else { + err("\"cams\" parameter too high!"); + return -1; + } + } else { + err("invalid bridge type"); + return -1; + } + + // FIXME: OV518 is hardcoded to 15 FPS (alternate 5) for now + if (ov->bclass == BCL_OV518) { + if (packetsize == -1) { + ov518_set_packet_size(ov, 640); + } else { + info("Forcing packet size to %d", packetsize); + ov518_set_packet_size(ov, packetsize); + } + } else { + if (packetsize == -1) { + ov511_set_packet_size(ov, size); + } else { + info("Forcing packet size to %d", packetsize); + ov511_set_packet_size(ov, packetsize); + } + } + + for (n = 0; n < OV511_NUMSBUF; n++) { + urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); + if (!urb) { + err("init isoc: usb_alloc_urb ret. NULL"); + return -ENOMEM; + } + ov->sbuf[n].urb = urb; + urb->dev = ov->dev; + urb->context = &ov->sbuf[n]; + urb->pipe = usb_rcvisocpipe(ov->dev, OV511_ENDPOINT_ADDRESS); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = ov->sbuf[n].data; + urb->complete = ov51x_isoc_irq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = ov->packet_size * FRAMES_PER_DESC; + urb->interval = 1; + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = ov->packet_size * fx; + urb->iso_frame_desc[fx].length = ov->packet_size; + } + } + + ov->streaming = 1; + + for (n = 0; n < OV511_NUMSBUF; n++) { + ov->sbuf[n].urb->dev = ov->dev; + err = usb_submit_urb(ov->sbuf[n].urb, GFP_KERNEL); + if (err) { + err("init isoc: usb_submit_urb(%d) ret %d", n, err); + return err; + } + } + + return 0; +} + +static void +ov51x_unlink_isoc(struct usb_ov511 *ov) +{ + int n; + + /* Unschedule all of the iso td's */ + for (n = OV511_NUMSBUF - 1; n >= 0; n--) { + if (ov->sbuf[n].urb) { + usb_kill_urb(ov->sbuf[n].urb); + usb_free_urb(ov->sbuf[n].urb); + ov->sbuf[n].urb = NULL; + } + } +} + +static void +ov51x_stop_isoc(struct usb_ov511 *ov) +{ + if (!ov->streaming || !ov->dev) + return; + + PDEBUG(3, "*** Stopping capture ***"); + + if (ov->bclass == BCL_OV518) + ov518_set_packet_size(ov, 0); + else + ov511_set_packet_size(ov, 0); + + ov->streaming = 0; + + ov51x_unlink_isoc(ov); +} + +static int +ov51x_new_frame(struct usb_ov511 *ov, int framenum) +{ + struct ov511_frame *frame; + int newnum; + + PDEBUG(4, "ov->curframe = %d, framenum = %d", ov->curframe, framenum); + + if (!ov->dev) + return -1; + + /* If we're not grabbing a frame right now and the other frame is */ + /* ready to be grabbed into, then use it instead */ + if (ov->curframe == -1) { + newnum = (framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES; + if (ov->frame[newnum].grabstate == FRAME_READY) + framenum = newnum; + } else + return 0; + + frame = &ov->frame[framenum]; + + PDEBUG(4, "framenum = %d, width = %d, height = %d", framenum, + frame->width, frame->height); + + frame->grabstate = FRAME_GRABBING; + frame->scanstate = STATE_SCANNING; + frame->snapshot = 0; + + ov->curframe = framenum; + + /* Make sure it's not too big */ + if (frame->width > ov->maxwidth) + frame->width = ov->maxwidth; + + frame->width &= ~7L; /* Multiple of 8 */ + + if (frame->height > ov->maxheight) + frame->height = ov->maxheight; + + frame->height &= ~3L; /* Multiple of 4 */ + + return 0; +} + +/**************************************************************************** + * + * Buffer management + * + ***************************************************************************/ + +/* + * - You must acquire buf_lock before entering this function. + * - Because this code will free any non-null pointer, you must be sure to null + * them if you explicitly free them somewhere else! + */ +static void +ov51x_do_dealloc(struct usb_ov511 *ov) +{ + int i; + PDEBUG(4, "entered"); + + if (ov->fbuf) { + rvfree(ov->fbuf, OV511_NUMFRAMES + * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight)); + ov->fbuf = NULL; + } + + vfree(ov->rawfbuf); + ov->rawfbuf = NULL; + + vfree(ov->tempfbuf); + ov->tempfbuf = NULL; + + for (i = 0; i < OV511_NUMSBUF; i++) { + kfree(ov->sbuf[i].data); + ov->sbuf[i].data = NULL; + } + + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov->frame[i].data = NULL; + ov->frame[i].rawdata = NULL; + ov->frame[i].tempdata = NULL; + if (ov->frame[i].compbuf) { + free_page((unsigned long) ov->frame[i].compbuf); + ov->frame[i].compbuf = NULL; + } + } + + PDEBUG(4, "buffer memory deallocated"); + ov->buf_state = BUF_NOT_ALLOCATED; + PDEBUG(4, "leaving"); +} + +static int +ov51x_alloc(struct usb_ov511 *ov) +{ + int i; + const int w = ov->maxwidth; + const int h = ov->maxheight; + const int data_bufsize = OV511_NUMFRAMES * MAX_DATA_SIZE(w, h); + const int raw_bufsize = OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h); + + PDEBUG(4, "entered"); + mutex_lock(&ov->buf_lock); + + if (ov->buf_state == BUF_ALLOCATED) + goto out; + + ov->fbuf = rvmalloc(data_bufsize); + if (!ov->fbuf) + goto error; + + ov->rawfbuf = vmalloc(raw_bufsize); + if (!ov->rawfbuf) + goto error; + + memset(ov->rawfbuf, 0, raw_bufsize); + + ov->tempfbuf = vmalloc(raw_bufsize); + if (!ov->tempfbuf) + goto error; + + memset(ov->tempfbuf, 0, raw_bufsize); + + for (i = 0; i < OV511_NUMSBUF; i++) { + ov->sbuf[i].data = kmalloc(FRAMES_PER_DESC * + MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!ov->sbuf[i].data) + goto error; + + PDEBUG(4, "sbuf[%d] @ %p", i, ov->sbuf[i].data); + } + + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov->frame[i].data = ov->fbuf + i * MAX_DATA_SIZE(w, h); + ov->frame[i].rawdata = ov->rawfbuf + + i * MAX_RAW_DATA_SIZE(w, h); + ov->frame[i].tempdata = ov->tempfbuf + + i * MAX_RAW_DATA_SIZE(w, h); + + ov->frame[i].compbuf = + (unsigned char *) __get_free_page(GFP_KERNEL); + if (!ov->frame[i].compbuf) + goto error; + + PDEBUG(4, "frame[%d] @ %p", i, ov->frame[i].data); + } + + ov->buf_state = BUF_ALLOCATED; +out: + mutex_unlock(&ov->buf_lock); + PDEBUG(4, "leaving"); + return 0; +error: + ov51x_do_dealloc(ov); + mutex_unlock(&ov->buf_lock); + PDEBUG(4, "errored"); + return -ENOMEM; +} + +static void +ov51x_dealloc(struct usb_ov511 *ov) +{ + PDEBUG(4, "entered"); + mutex_lock(&ov->buf_lock); + ov51x_do_dealloc(ov); + mutex_unlock(&ov->buf_lock); + PDEBUG(4, "leaving"); +} + +/**************************************************************************** + * + * V4L 1 API + * + ***************************************************************************/ + +static int +ov51x_v4l1_open(struct inode *inode, struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct usb_ov511 *ov = video_get_drvdata(vdev); + int err, i; + + PDEBUG(4, "opening"); + + mutex_lock(&ov->lock); + + err = -EBUSY; + if (ov->user) + goto out; + + ov->sub_flag = 0; + + /* In case app doesn't set them... */ + err = ov51x_set_default_params(ov); + if (err < 0) + goto out; + + /* Make sure frames are reset */ + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov->frame[i].grabstate = FRAME_UNUSED; + ov->frame[i].bytes_read = 0; + } + + /* If compression is on, make sure now that a + * decompressor can be loaded */ + if (ov->compress && !ov->decomp_ops) { + err = request_decompressor(ov); + if (err && !dumppix) + goto out; + } + + err = ov51x_alloc(ov); + if (err < 0) + goto out; + + err = ov51x_init_isoc(ov); + if (err) { + ov51x_dealloc(ov); + goto out; + } + + ov->user++; + file->private_data = vdev; + + if (ov->led_policy == LED_AUTO) + ov51x_led_control(ov, 1); + +out: + mutex_unlock(&ov->lock); + return err; +} + +static int +ov51x_v4l1_close(struct inode *inode, struct file *file) +{ + struct video_device *vdev = file->private_data; + struct usb_ov511 *ov = video_get_drvdata(vdev); + + PDEBUG(4, "ov511_close"); + + mutex_lock(&ov->lock); + + ov->user--; + ov51x_stop_isoc(ov); + + if (ov->led_policy == LED_AUTO) + ov51x_led_control(ov, 0); + + if (ov->dev) + ov51x_dealloc(ov); + + mutex_unlock(&ov->lock); + + /* Device unplugged while open. Only a minimum of unregistration is done + * here; the disconnect callback already did the rest. */ + if (!ov->dev) { + mutex_lock(&ov->cbuf_lock); + kfree(ov->cbuf); + ov->cbuf = NULL; + mutex_unlock(&ov->cbuf_lock); + + ov51x_dealloc(ov); + kfree(ov); + ov = NULL; + } + + file->private_data = NULL; + return 0; +} + +/* Do not call this function directly! */ +static int +ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vdev = file->private_data; + struct usb_ov511 *ov = video_get_drvdata(vdev); + PDEBUG(5, "IOCtl: 0x%X", cmd); + + if (!ov->dev) + return -EIO; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability *b = arg; + + PDEBUG(4, "VIDIOCGCAP"); + + memset(b, 0, sizeof(struct video_capability)); + sprintf(b->name, "%s USB Camera", + symbolic(brglist, ov->bridge)); + b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; + b->channels = ov->num_inputs; + b->audios = 0; + b->maxwidth = ov->maxwidth; + b->maxheight = ov->maxheight; + b->minwidth = ov->minwidth; + b->minheight = ov->minheight; + + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + + PDEBUG(4, "VIDIOCGCHAN"); + + if ((unsigned)(v->channel) >= ov->num_inputs) { + err("Invalid channel (%d)", v->channel); + return -EINVAL; + } + + v->norm = ov->norm; + v->type = VIDEO_TYPE_CAMERA; + v->flags = 0; +// v->flags |= (ov->has_decoder) ? VIDEO_VC_NORM : 0; + v->tuners = 0; + decoder_get_input_name(ov, v->channel, v->name); + + return 0; + } + case VIDIOCSCHAN: + { + struct video_channel *v = arg; + int err; + + PDEBUG(4, "VIDIOCSCHAN"); + + /* Make sure it's not a camera */ + if (!ov->has_decoder) { + if (v->channel == 0) + return 0; + else + return -EINVAL; + } + + if (v->norm != VIDEO_MODE_PAL && + v->norm != VIDEO_MODE_NTSC && + v->norm != VIDEO_MODE_SECAM && + v->norm != VIDEO_MODE_AUTO) { + err("Invalid norm (%d)", v->norm); + return -EINVAL; + } + + if ((unsigned)(v->channel) >= ov->num_inputs) { + err("Invalid channel (%d)", v->channel); + return -EINVAL; + } + + err = decoder_set_input(ov, v->channel); + if (err) + return err; + + err = decoder_set_norm(ov, v->norm); + if (err) + return err; + + return 0; + } + case VIDIOCGPICT: + { + struct video_picture *p = arg; + + PDEBUG(4, "VIDIOCGPICT"); + + memset(p, 0, sizeof(struct video_picture)); + if (sensor_get_picture(ov, p)) + return -EIO; + + /* Can we get these from frame[0]? -claudio? */ + p->depth = ov->frame[0].depth; + p->palette = ov->frame[0].format; + + return 0; + } + case VIDIOCSPICT: + { + struct video_picture *p = arg; + int i, rc; + + PDEBUG(4, "VIDIOCSPICT"); + + if (!get_depth(p->palette)) + return -EINVAL; + + if (sensor_set_picture(ov, p)) + return -EIO; + + if (force_palette && p->palette != force_palette) { + info("Palette rejected (%s)", + symbolic(v4l1_plist, p->palette)); + return -EINVAL; + } + + // FIXME: Format should be independent of frames + if (p->palette != ov->frame[0].format) { + PDEBUG(4, "Detected format change"); + + rc = ov51x_wait_frames_inactive(ov); + if (rc) + return rc; + + mode_init_regs(ov, ov->frame[0].width, + ov->frame[0].height, p->palette, ov->sub_flag); + } + + PDEBUG(4, "Setting depth=%d, palette=%s", + p->depth, symbolic(v4l1_plist, p->palette)); + + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov->frame[i].depth = p->depth; + ov->frame[i].format = p->palette; + } + + return 0; + } + case VIDIOCGCAPTURE: + { + int *vf = arg; + + PDEBUG(4, "VIDIOCGCAPTURE"); + + ov->sub_flag = *vf; + return 0; + } + case VIDIOCSCAPTURE: + { + struct video_capture *vc = arg; + + PDEBUG(4, "VIDIOCSCAPTURE"); + + if (vc->flags) + return -EINVAL; + if (vc->decimation) + return -EINVAL; + + vc->x &= ~3L; + vc->y &= ~1L; + vc->y &= ~31L; + + if (vc->width == 0) + vc->width = 32; + + vc->height /= 16; + vc->height *= 16; + if (vc->height == 0) + vc->height = 16; + + ov->subx = vc->x; + ov->suby = vc->y; + ov->subw = vc->width; + ov->subh = vc->height; + + return 0; + } + case VIDIOCSWIN: + { + struct video_window *vw = arg; + int i, rc; + + PDEBUG(4, "VIDIOCSWIN: %dx%d", vw->width, vw->height); + +#if 0 + if (vw->flags) + return -EINVAL; + if (vw->clipcount) + return -EINVAL; + if (vw->height != ov->maxheight) + return -EINVAL; + if (vw->width != ov->maxwidth) + return -EINVAL; +#endif + + rc = ov51x_wait_frames_inactive(ov); + if (rc) + return rc; + + rc = mode_init_regs(ov, vw->width, vw->height, + ov->frame[0].format, ov->sub_flag); + if (rc < 0) + return rc; + + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov->frame[i].width = vw->width; + ov->frame[i].height = vw->height; + } + + return 0; + } + case VIDIOCGWIN: + { + struct video_window *vw = arg; + + memset(vw, 0, sizeof(struct video_window)); + vw->x = 0; /* FIXME */ + vw->y = 0; + vw->width = ov->frame[0].width; + vw->height = ov->frame[0].height; + vw->flags = 30; + + PDEBUG(4, "VIDIOCGWIN: %dx%d", vw->width, vw->height); + + return 0; + } + case VIDIOCGMBUF: + { + struct video_mbuf *vm = arg; + int i; + + PDEBUG(4, "VIDIOCGMBUF"); + + memset(vm, 0, sizeof(struct video_mbuf)); + vm->size = OV511_NUMFRAMES + * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight); + vm->frames = OV511_NUMFRAMES; + + vm->offsets[0] = 0; + for (i = 1; i < OV511_NUMFRAMES; i++) { + vm->offsets[i] = vm->offsets[i-1] + + MAX_DATA_SIZE(ov->maxwidth, ov->maxheight); + } + + return 0; + } + case VIDIOCMCAPTURE: + { + struct video_mmap *vm = arg; + int rc, depth; + unsigned int f = vm->frame; + + PDEBUG(4, "VIDIOCMCAPTURE: frame: %d, %dx%d, %s", f, vm->width, + vm->height, symbolic(v4l1_plist, vm->format)); + + depth = get_depth(vm->format); + if (!depth) { + PDEBUG(2, "VIDIOCMCAPTURE: invalid format (%s)", + symbolic(v4l1_plist, vm->format)); + return -EINVAL; + } + + if (f >= OV511_NUMFRAMES) { + err("VIDIOCMCAPTURE: invalid frame (%d)", f); + return -EINVAL; + } + + if (vm->width > ov->maxwidth + || vm->height > ov->maxheight) { + err("VIDIOCMCAPTURE: requested dimensions too big"); + return -EINVAL; + } + + if (ov->frame[f].grabstate == FRAME_GRABBING) { + PDEBUG(4, "VIDIOCMCAPTURE: already grabbing"); + return -EBUSY; + } + + if (force_palette && (vm->format != force_palette)) { + PDEBUG(2, "palette rejected (%s)", + symbolic(v4l1_plist, vm->format)); + return -EINVAL; + } + + if ((ov->frame[f].width != vm->width) || + (ov->frame[f].height != vm->height) || + (ov->frame[f].format != vm->format) || + (ov->frame[f].sub_flag != ov->sub_flag) || + (ov->frame[f].depth != depth)) { + PDEBUG(4, "VIDIOCMCAPTURE: change in image parameters"); + + rc = ov51x_wait_frames_inactive(ov); + if (rc) + return rc; + + rc = mode_init_regs(ov, vm->width, vm->height, + vm->format, ov->sub_flag); +#if 0 + if (rc < 0) { + PDEBUG(1, "Got error while initializing regs "); + return ret; + } +#endif + ov->frame[f].width = vm->width; + ov->frame[f].height = vm->height; + ov->frame[f].format = vm->format; + ov->frame[f].sub_flag = ov->sub_flag; + ov->frame[f].depth = depth; + } + + /* Mark it as ready */ + ov->frame[f].grabstate = FRAME_READY; + + PDEBUG(4, "VIDIOCMCAPTURE: renewing frame %d", f); + + return ov51x_new_frame(ov, f); + } + case VIDIOCSYNC: + { + unsigned int fnum = *((unsigned int *) arg); + struct ov511_frame *frame; + int rc; + + if (fnum >= OV511_NUMFRAMES) { + err("VIDIOCSYNC: invalid frame (%d)", fnum); + return -EINVAL; + } + + frame = &ov->frame[fnum]; + + PDEBUG(4, "syncing to frame %d, grabstate = %d", fnum, + frame->grabstate); + + switch (frame->grabstate) { + case FRAME_UNUSED: + return -EINVAL; + case FRAME_READY: + case FRAME_GRABBING: + case FRAME_ERROR: +redo: + if (!ov->dev) + return -EIO; + + rc = wait_event_interruptible(frame->wq, + (frame->grabstate == FRAME_DONE) + || (frame->grabstate == FRAME_ERROR)); + + if (rc) + return rc; + + if (frame->grabstate == FRAME_ERROR) { + if ((rc = ov51x_new_frame(ov, fnum)) < 0) + return rc; + goto redo; + } + /* Fall through */ + case FRAME_DONE: + if (ov->snap_enabled && !frame->snapshot) { + if ((rc = ov51x_new_frame(ov, fnum)) < 0) + return rc; + goto redo; + } + + frame->grabstate = FRAME_UNUSED; + + /* Reset the hardware snapshot button */ + /* FIXME - Is this the best place for this? */ + if ((ov->snap_enabled) && (frame->snapshot)) { + frame->snapshot = 0; + ov51x_clear_snapshot(ov); + } + + /* Decompression, format conversion, etc... */ + ov51x_postprocess(ov, frame); + + break; + } /* end switch */ + + return 0; + } + case VIDIOCGFBUF: + { + struct video_buffer *vb = arg; + + PDEBUG(4, "VIDIOCGFBUF"); + + memset(vb, 0, sizeof(struct video_buffer)); + + return 0; + } + case VIDIOCGUNIT: + { + struct video_unit *vu = arg; + + PDEBUG(4, "VIDIOCGUNIT"); + + memset(vu, 0, sizeof(struct video_unit)); + + vu->video = ov->vdev->minor; + vu->vbi = VIDEO_NO_UNIT; + vu->radio = VIDEO_NO_UNIT; + vu->audio = VIDEO_NO_UNIT; + vu->teletext = VIDEO_NO_UNIT; + + return 0; + } + case OV511IOC_WI2C: + { + struct ov511_i2c_struct *w = arg; + + return i2c_w_slave(ov, w->slave, w->reg, w->value, w->mask); + } + case OV511IOC_RI2C: + { + struct ov511_i2c_struct *r = arg; + int rc; + + rc = i2c_r_slave(ov, r->slave, r->reg); + if (rc < 0) + return rc; + + r->value = rc; + return 0; + } + default: + PDEBUG(3, "Unsupported IOCtl: 0x%X", cmd); + return -ENOIOCTLCMD; + } /* end switch */ + + return 0; +} + +static int +ov51x_v4l1_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vdev = file->private_data; + struct usb_ov511 *ov = video_get_drvdata(vdev); + int rc; + + if (mutex_lock_interruptible(&ov->lock)) + return -EINTR; + + rc = video_usercopy(inode, file, cmd, arg, ov51x_v4l1_ioctl_internal); + + mutex_unlock(&ov->lock); + return rc; +} + +static ssize_t +ov51x_v4l1_read(struct file *file, char __user *buf, size_t cnt, loff_t *ppos) +{ + struct video_device *vdev = file->private_data; + int noblock = file->f_flags&O_NONBLOCK; + unsigned long count = cnt; + struct usb_ov511 *ov = video_get_drvdata(vdev); + int i, rc = 0, frmx = -1; + struct ov511_frame *frame; + + if (mutex_lock_interruptible(&ov->lock)) + return -EINTR; + + PDEBUG(4, "%ld bytes, noblock=%d", count, noblock); + + if (!vdev || !buf) { + rc = -EFAULT; + goto error; + } + + if (!ov->dev) { + rc = -EIO; + goto error; + } + +// FIXME: Only supports two frames + /* See if a frame is completed, then use it. */ + if (ov->frame[0].grabstate >= FRAME_DONE) /* _DONE or _ERROR */ + frmx = 0; + else if (ov->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */ + frmx = 1; + + /* If nonblocking we return immediately */ + if (noblock && (frmx == -1)) { + rc = -EAGAIN; + goto error; + } + + /* If no FRAME_DONE, look for a FRAME_GRABBING state. */ + /* See if a frame is in process (grabbing), then use it. */ + if (frmx == -1) { + if (ov->frame[0].grabstate == FRAME_GRABBING) + frmx = 0; + else if (ov->frame[1].grabstate == FRAME_GRABBING) + frmx = 1; + } + + /* If no frame is active, start one. */ + if (frmx == -1) { + if ((rc = ov51x_new_frame(ov, frmx = 0))) { + err("read: ov51x_new_frame error"); + goto error; + } + } + + frame = &ov->frame[frmx]; + +restart: + if (!ov->dev) { + rc = -EIO; + goto error; + } + + /* Wait while we're grabbing the image */ + PDEBUG(4, "Waiting image grabbing"); + rc = wait_event_interruptible(frame->wq, + (frame->grabstate == FRAME_DONE) + || (frame->grabstate == FRAME_ERROR)); + + if (rc) + goto error; + + PDEBUG(4, "Got image, frame->grabstate = %d", frame->grabstate); + PDEBUG(4, "bytes_recvd = %d", frame->bytes_recvd); + + if (frame->grabstate == FRAME_ERROR) { + frame->bytes_read = 0; + err("** ick! ** Errored frame %d", ov->curframe); + if (ov51x_new_frame(ov, frmx)) { + err("read: ov51x_new_frame error"); + goto error; + } + goto restart; + } + + + /* Repeat until we get a snapshot frame */ + if (ov->snap_enabled) + PDEBUG(4, "Waiting snapshot frame"); + if (ov->snap_enabled && !frame->snapshot) { + frame->bytes_read = 0; + if ((rc = ov51x_new_frame(ov, frmx))) { + err("read: ov51x_new_frame error"); + goto error; + } + goto restart; + } + + /* Clear the snapshot */ + if (ov->snap_enabled && frame->snapshot) { + frame->snapshot = 0; + ov51x_clear_snapshot(ov); + } + + /* Decompression, format conversion, etc... */ + ov51x_postprocess(ov, frame); + + PDEBUG(4, "frmx=%d, bytes_read=%ld, length=%ld", frmx, + frame->bytes_read, + get_frame_length(frame)); + + /* copy bytes to user space; we allow for partials reads */ +// if ((count + frame->bytes_read) +// > get_frame_length((struct ov511_frame *)frame)) +// count = frame->scanlength - frame->bytes_read; + + /* FIXME - count hardwired to be one frame... */ + count = get_frame_length(frame); + + PDEBUG(4, "Copy to user space: %ld bytes", count); + if ((i = copy_to_user(buf, frame->data + frame->bytes_read, count))) { + PDEBUG(4, "Copy failed! %d bytes not copied", i); + rc = -EFAULT; + goto error; + } + + frame->bytes_read += count; + PDEBUG(4, "{copy} count used=%ld, new bytes_read=%ld", + count, frame->bytes_read); + + /* If all data have been read... */ + if (frame->bytes_read + >= get_frame_length(frame)) { + frame->bytes_read = 0; + +// FIXME: Only supports two frames + /* Mark it as available to be used again. */ + ov->frame[frmx].grabstate = FRAME_UNUSED; + if ((rc = ov51x_new_frame(ov, !frmx))) { + err("ov51x_new_frame returned error"); + goto error; + } + } + + PDEBUG(4, "read finished, returning %ld (sweet)", count); + + mutex_unlock(&ov->lock); + return count; + +error: + mutex_unlock(&ov->lock); + return rc; +} + +static int +ov51x_v4l1_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vdev = file->private_data; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; + struct usb_ov511 *ov = video_get_drvdata(vdev); + unsigned long page, pos; + + if (ov->dev == NULL) + return -EIO; + + PDEBUG(4, "mmap: %ld (%lX) bytes", size, size); + + if (size > (((OV511_NUMFRAMES + * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight) + + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))) + return -EINVAL; + + if (mutex_lock_interruptible(&ov->lock)) + return -EINTR; + + pos = (unsigned long)ov->fbuf; + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { + mutex_unlock(&ov->lock); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + mutex_unlock(&ov->lock); + return 0; +} + +static struct file_operations ov511_fops = { + .owner = THIS_MODULE, + .open = ov51x_v4l1_open, + .release = ov51x_v4l1_close, + .read = ov51x_v4l1_read, + .mmap = ov51x_v4l1_mmap, + .ioctl = ov51x_v4l1_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device vdev_template = { + .owner = THIS_MODULE, + .name = "OV511 USB Camera", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_OV511, + .fops = &ov511_fops, + .release = video_device_release, + .minor = -1, +}; + +/**************************************************************************** + * + * OV511 and sensor configuration + * + ***************************************************************************/ + +/* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses + * the same register settings as the OV7610, since they are very similar. + */ +static int +ov7xx0_configure(struct usb_ov511 *ov) +{ + int i, success; + int rc; + + /* Lawrence Glaister reports: + * + * Register 0x0f in the 7610 has the following effects: + * + * 0x85 (AEC method 1): Best overall, good contrast range + * 0x45 (AEC method 2): Very overexposed + * 0xa5 (spec sheet default): Ok, but the black level is + * shifted resulting in loss of contrast + * 0x05 (old driver setting): very overexposed, too much + * contrast + */ + static struct ov511_regvals aRegvalsNorm7610[] = { + { OV511_I2C_BUS, 0x10, 0xff }, + { OV511_I2C_BUS, 0x16, 0x06 }, + { OV511_I2C_BUS, 0x28, 0x24 }, + { OV511_I2C_BUS, 0x2b, 0xac }, + { OV511_I2C_BUS, 0x12, 0x00 }, + { OV511_I2C_BUS, 0x38, 0x81 }, + { OV511_I2C_BUS, 0x28, 0x24 }, /* 0c */ + { OV511_I2C_BUS, 0x0f, 0x85 }, /* lg's setting */ + { OV511_I2C_BUS, 0x15, 0x01 }, + { OV511_I2C_BUS, 0x20, 0x1c }, + { OV511_I2C_BUS, 0x23, 0x2a }, + { OV511_I2C_BUS, 0x24, 0x10 }, + { OV511_I2C_BUS, 0x25, 0x8a }, + { OV511_I2C_BUS, 0x26, 0xa2 }, + { OV511_I2C_BUS, 0x27, 0xc2 }, + { OV511_I2C_BUS, 0x2a, 0x04 }, + { OV511_I2C_BUS, 0x2c, 0xfe }, + { OV511_I2C_BUS, 0x2d, 0x93 }, + { OV511_I2C_BUS, 0x30, 0x71 }, + { OV511_I2C_BUS, 0x31, 0x60 }, + { OV511_I2C_BUS, 0x32, 0x26 }, + { OV511_I2C_BUS, 0x33, 0x20 }, + { OV511_I2C_BUS, 0x34, 0x48 }, + { OV511_I2C_BUS, 0x12, 0x24 }, + { OV511_I2C_BUS, 0x11, 0x01 }, + { OV511_I2C_BUS, 0x0c, 0x24 }, + { OV511_I2C_BUS, 0x0d, 0x24 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + static struct ov511_regvals aRegvalsNorm7620[] = { + { OV511_I2C_BUS, 0x00, 0x00 }, + { OV511_I2C_BUS, 0x01, 0x80 }, + { OV511_I2C_BUS, 0x02, 0x80 }, + { OV511_I2C_BUS, 0x03, 0xc0 }, + { OV511_I2C_BUS, 0x06, 0x60 }, + { OV511_I2C_BUS, 0x07, 0x00 }, + { OV511_I2C_BUS, 0x0c, 0x24 }, + { OV511_I2C_BUS, 0x0c, 0x24 }, + { OV511_I2C_BUS, 0x0d, 0x24 }, + { OV511_I2C_BUS, 0x11, 0x01 }, + { OV511_I2C_BUS, 0x12, 0x24 }, + { OV511_I2C_BUS, 0x13, 0x01 }, + { OV511_I2C_BUS, 0x14, 0x84 }, + { OV511_I2C_BUS, 0x15, 0x01 }, + { OV511_I2C_BUS, 0x16, 0x03 }, + { OV511_I2C_BUS, 0x17, 0x2f }, + { OV511_I2C_BUS, 0x18, 0xcf }, + { OV511_I2C_BUS, 0x19, 0x06 }, + { OV511_I2C_BUS, 0x1a, 0xf5 }, + { OV511_I2C_BUS, 0x1b, 0x00 }, + { OV511_I2C_BUS, 0x20, 0x18 }, + { OV511_I2C_BUS, 0x21, 0x80 }, + { OV511_I2C_BUS, 0x22, 0x80 }, + { OV511_I2C_BUS, 0x23, 0x00 }, + { OV511_I2C_BUS, 0x26, 0xa2 }, + { OV511_I2C_BUS, 0x27, 0xea }, + { OV511_I2C_BUS, 0x28, 0x20 }, + { OV511_I2C_BUS, 0x29, 0x00 }, + { OV511_I2C_BUS, 0x2a, 0x10 }, + { OV511_I2C_BUS, 0x2b, 0x00 }, + { OV511_I2C_BUS, 0x2c, 0x88 }, + { OV511_I2C_BUS, 0x2d, 0x91 }, + { OV511_I2C_BUS, 0x2e, 0x80 }, + { OV511_I2C_BUS, 0x2f, 0x44 }, + { OV511_I2C_BUS, 0x60, 0x27 }, + { OV511_I2C_BUS, 0x61, 0x02 }, + { OV511_I2C_BUS, 0x62, 0x5f }, + { OV511_I2C_BUS, 0x63, 0xd5 }, + { OV511_I2C_BUS, 0x64, 0x57 }, + { OV511_I2C_BUS, 0x65, 0x83 }, + { OV511_I2C_BUS, 0x66, 0x55 }, + { OV511_I2C_BUS, 0x67, 0x92 }, + { OV511_I2C_BUS, 0x68, 0xcf }, + { OV511_I2C_BUS, 0x69, 0x76 }, + { OV511_I2C_BUS, 0x6a, 0x22 }, + { OV511_I2C_BUS, 0x6b, 0x00 }, + { OV511_I2C_BUS, 0x6c, 0x02 }, + { OV511_I2C_BUS, 0x6d, 0x44 }, + { OV511_I2C_BUS, 0x6e, 0x80 }, + { OV511_I2C_BUS, 0x6f, 0x1d }, + { OV511_I2C_BUS, 0x70, 0x8b }, + { OV511_I2C_BUS, 0x71, 0x00 }, + { OV511_I2C_BUS, 0x72, 0x14 }, + { OV511_I2C_BUS, 0x73, 0x54 }, + { OV511_I2C_BUS, 0x74, 0x00 }, + { OV511_I2C_BUS, 0x75, 0x8e }, + { OV511_I2C_BUS, 0x76, 0x00 }, + { OV511_I2C_BUS, 0x77, 0xff }, + { OV511_I2C_BUS, 0x78, 0x80 }, + { OV511_I2C_BUS, 0x79, 0x80 }, + { OV511_I2C_BUS, 0x7a, 0x80 }, + { OV511_I2C_BUS, 0x7b, 0xe2 }, + { OV511_I2C_BUS, 0x7c, 0x00 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + PDEBUG(4, "starting configuration"); + + /* This looks redundant, but is necessary for WebCam 3 */ + ov->primary_i2c_slave = OV7xx0_SID; + if (ov51x_set_slave_ids(ov, OV7xx0_SID) < 0) + return -1; + + if (init_ov_sensor(ov) >= 0) { + PDEBUG(1, "OV7xx0 sensor initalized (method 1)"); + } else { + /* Reset the 76xx */ + if (i2c_w(ov, 0x12, 0x80) < 0) + return -1; + + /* Wait for it to initialize */ + msleep(150); + + i = 0; + success = 0; + while (i <= i2c_detect_tries) { + if ((i2c_r(ov, OV7610_REG_ID_HIGH) == 0x7F) && + (i2c_r(ov, OV7610_REG_ID_LOW) == 0xA2)) { + success = 1; + break; + } else { + i++; + } + } + +// Was (i == i2c_detect_tries) previously. This obviously used to always report +// success. Whether anyone actually depended on that bug is unknown + if ((i >= i2c_detect_tries) && (success == 0)) { + err("Failed to read sensor ID. You might not have an"); + err("OV7610/20, or it may be not responding. Report"); + err("this to " EMAIL); + err("This is only a warning. You can attempt to use"); + err("your camera anyway"); +// Only issue a warning for now +// return -1; + } else { + PDEBUG(1, "OV7xx0 initialized (method 2, %dx)", i+1); + } + } + + /* Detect sensor (sub)type */ + rc = i2c_r(ov, OV7610_REG_COM_I); + + if (rc < 0) { + err("Error detecting sensor type"); + return -1; + } else if ((rc & 3) == 3) { + info("Sensor is an OV7610"); + ov->sensor = SEN_OV7610; + } else if ((rc & 3) == 1) { + /* I don't know what's different about the 76BE yet. */ + if (i2c_r(ov, 0x15) & 1) + info("Sensor is an OV7620AE"); + else + info("Sensor is an OV76BE"); + + /* OV511+ will return all zero isoc data unless we + * configure the sensor as a 7620. Someone needs to + * find the exact reg. setting that causes this. */ + if (ov->bridge == BRG_OV511PLUS) { + info("Enabling 511+/7620AE workaround"); + ov->sensor = SEN_OV7620; + } else { + ov->sensor = SEN_OV76BE; + } + } else if ((rc & 3) == 0) { + info("Sensor is an OV7620"); + ov->sensor = SEN_OV7620; + } else { + err("Unknown image sensor version: %d", rc & 3); + return -1; + } + + if (ov->sensor == SEN_OV7620) { + PDEBUG(4, "Writing 7620 registers"); + if (write_regvals(ov, aRegvalsNorm7620)) + return -1; + } else { + PDEBUG(4, "Writing 7610 registers"); + if (write_regvals(ov, aRegvalsNorm7610)) + return -1; + } + + /* Set sensor-specific vars */ + ov->maxwidth = 640; + ov->maxheight = 480; + ov->minwidth = 64; + ov->minheight = 48; + + // FIXME: These do not match the actual settings yet + ov->brightness = 0x80 << 8; + ov->contrast = 0x80 << 8; + ov->colour = 0x80 << 8; + ov->hue = 0x80 << 8; + + return 0; +} + +/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */ +static int +ov6xx0_configure(struct usb_ov511 *ov) +{ + int rc; + + static struct ov511_regvals aRegvalsNorm6x20[] = { + { OV511_I2C_BUS, 0x12, 0x80 }, /* reset */ + { OV511_I2C_BUS, 0x11, 0x01 }, + { OV511_I2C_BUS, 0x03, 0x60 }, + { OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */ + { OV511_I2C_BUS, 0x07, 0xa8 }, + /* The ratio of 0x0c and 0x0d controls the white point */ + { OV511_I2C_BUS, 0x0c, 0x24 }, + { OV511_I2C_BUS, 0x0d, 0x24 }, + { OV511_I2C_BUS, 0x0f, 0x15 }, /* COMS */ + { OV511_I2C_BUS, 0x10, 0x75 }, /* AEC Exposure time */ + { OV511_I2C_BUS, 0x12, 0x24 }, /* Enable AGC */ + { OV511_I2C_BUS, 0x14, 0x04 }, + /* 0x16: 0x06 helps frame stability with moving objects */ + { OV511_I2C_BUS, 0x16, 0x06 }, +// { OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */ + { OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */ + /* 0x28: 0x05 Selects RGB format if RGB on */ + { OV511_I2C_BUS, 0x28, 0x05 }, + { OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */ +// { OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */ + { OV511_I2C_BUS, 0x2d, 0x99 }, + { OV511_I2C_BUS, 0x33, 0xa0 }, /* Color Processing Parameter */ + { OV511_I2C_BUS, 0x34, 0xd2 }, /* Max A/D range */ + { OV511_I2C_BUS, 0x38, 0x8b }, + { OV511_I2C_BUS, 0x39, 0x40 }, + + { OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */ + { OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */ + { OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */ + + { OV511_I2C_BUS, 0x3d, 0x80 }, + /* These next two registers (0x4a, 0x4b) are undocumented. They + * control the color balance */ + { OV511_I2C_BUS, 0x4a, 0x80 }, + { OV511_I2C_BUS, 0x4b, 0x80 }, + { OV511_I2C_BUS, 0x4d, 0xd2 }, /* This reduces noise a bit */ + { OV511_I2C_BUS, 0x4e, 0xc1 }, + { OV511_I2C_BUS, 0x4f, 0x04 }, +// Do 50-53 have any effect? +// Toggle 0x12[2] off and on here? + { OV511_DONE_BUS, 0x0, 0x00 }, /* END MARKER */ + }; + + static struct ov511_regvals aRegvalsNorm6x30[] = { + /*OK*/ { OV511_I2C_BUS, 0x12, 0x80 }, /* reset */ + { OV511_I2C_BUS, 0x11, 0x00 }, + /*OK*/ { OV511_I2C_BUS, 0x03, 0x60 }, + /*0A?*/ { OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */ + { OV511_I2C_BUS, 0x07, 0xa8 }, + /* The ratio of 0x0c and 0x0d controls the white point */ + /*OK*/ { OV511_I2C_BUS, 0x0c, 0x24 }, + /*OK*/ { OV511_I2C_BUS, 0x0d, 0x24 }, + /*A*/ { OV511_I2C_BUS, 0x0e, 0x20 }, +// /*04?*/ { OV511_I2C_BUS, 0x14, 0x80 }, + { OV511_I2C_BUS, 0x16, 0x03 }, +// /*OK*/ { OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */ + // 21 & 22? The suggested values look wrong. Go with default + /*A*/ { OV511_I2C_BUS, 0x23, 0xc0 }, + /*A*/ { OV511_I2C_BUS, 0x25, 0x9a }, // Check this against default +// /*OK*/ { OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */ + + /* 0x28: 0x05 Selects RGB format if RGB on */ +// /*04?*/ { OV511_I2C_BUS, 0x28, 0x05 }, +// /*04?*/ { OV511_I2C_BUS, 0x28, 0x45 }, // DEBUG: Tristate UV bus + + /*OK*/ { OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */ +// /*OK*/ { OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */ + { OV511_I2C_BUS, 0x2d, 0x99 }, +// /*A*/ { OV511_I2C_BUS, 0x33, 0x26 }, // Reserved bits on 6620 +// /*d2?*/ { OV511_I2C_BUS, 0x34, 0x03 }, /* Max A/D range */ +// /*8b?*/ { OV511_I2C_BUS, 0x38, 0x83 }, +// /*40?*/ { OV511_I2C_BUS, 0x39, 0xc0 }, // 6630 adds bit 7 +// { OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */ +// { OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */ +// { OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */ + { OV511_I2C_BUS, 0x3d, 0x80 }, +// /*A*/ { OV511_I2C_BUS, 0x3f, 0x0e }, + + /* These next two registers (0x4a, 0x4b) are undocumented. They + * control the color balance */ +// /*OK?*/ { OV511_I2C_BUS, 0x4a, 0x80 }, // Check these +// /*OK?*/ { OV511_I2C_BUS, 0x4b, 0x80 }, + { OV511_I2C_BUS, 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */ + /*c1?*/ { OV511_I2C_BUS, 0x4e, 0x40 }, + + /* UV average mode, color killer: strongest */ + { OV511_I2C_BUS, 0x4f, 0x07 }, + + { OV511_I2C_BUS, 0x54, 0x23 }, /* Max AGC gain: 18dB */ + { OV511_I2C_BUS, 0x57, 0x81 }, /* (default) */ + { OV511_I2C_BUS, 0x59, 0x01 }, /* AGC dark current comp: +1 */ + { OV511_I2C_BUS, 0x5a, 0x2c }, /* (undocumented) */ + { OV511_I2C_BUS, 0x5b, 0x0f }, /* AWB chrominance levels */ +// { OV511_I2C_BUS, 0x5c, 0x10 }, + { OV511_DONE_BUS, 0x0, 0x00 }, /* END MARKER */ + }; + + PDEBUG(4, "starting sensor configuration"); + + if (init_ov_sensor(ov) < 0) { + err("Failed to read sensor ID. You might not have an OV6xx0,"); + err("or it may be not responding. Report this to " EMAIL); + return -1; + } else { + PDEBUG(1, "OV6xx0 sensor detected"); + } + + /* Detect sensor (sub)type */ + rc = i2c_r(ov, OV7610_REG_COM_I); + + if (rc < 0) { + err("Error detecting sensor type"); + return -1; + } + + if ((rc & 3) == 0) { + ov->sensor = SEN_OV6630; + info("Sensor is an OV6630"); + } else if ((rc & 3) == 1) { + ov->sensor = SEN_OV6620; + info("Sensor is an OV6620"); + } else if ((rc & 3) == 2) { + ov->sensor = SEN_OV6630; + info("Sensor is an OV6630AE"); + } else if ((rc & 3) == 3) { + ov->sensor = SEN_OV6630; + info("Sensor is an OV6630AF"); + } + + /* Set sensor-specific vars */ + ov->maxwidth = 352; + ov->maxheight = 288; + ov->minwidth = 64; + ov->minheight = 48; + + // FIXME: These do not match the actual settings yet + ov->brightness = 0x80 << 8; + ov->contrast = 0x80 << 8; + ov->colour = 0x80 << 8; + ov->hue = 0x80 << 8; + + if (ov->sensor == SEN_OV6620) { + PDEBUG(4, "Writing 6x20 registers"); + if (write_regvals(ov, aRegvalsNorm6x20)) + return -1; + } else { + PDEBUG(4, "Writing 6x30 registers"); + if (write_regvals(ov, aRegvalsNorm6x30)) + return -1; + } + + return 0; +} + +/* This initializes the KS0127 and KS0127B video decoders. */ +static int +ks0127_configure(struct usb_ov511 *ov) +{ + int rc; + +// FIXME: I don't know how to sync or reset it yet +#if 0 + if (ov51x_init_ks_sensor(ov) < 0) { + err("Failed to initialize the KS0127"); + return -1; + } else { + PDEBUG(1, "KS012x(B) sensor detected"); + } +#endif + + /* Detect decoder subtype */ + rc = i2c_r(ov, 0x00); + if (rc < 0) { + err("Error detecting sensor type"); + return -1; + } else if (rc & 0x08) { + rc = i2c_r(ov, 0x3d); + if (rc < 0) { + err("Error detecting sensor type"); + return -1; + } else if ((rc & 0x0f) == 0) { + info("Sensor is a KS0127"); + ov->sensor = SEN_KS0127; + } else if ((rc & 0x0f) == 9) { + info("Sensor is a KS0127B Rev. A"); + ov->sensor = SEN_KS0127B; + } + } else { + err("Error: Sensor is an unsupported KS0122"); + return -1; + } + + /* Set sensor-specific vars */ + ov->maxwidth = 640; + ov->maxheight = 480; + ov->minwidth = 64; + ov->minheight = 48; + + // FIXME: These do not match the actual settings yet + ov->brightness = 0x80 << 8; + ov->contrast = 0x80 << 8; + ov->colour = 0x80 << 8; + ov->hue = 0x80 << 8; + + /* This device is not supported yet. Bail out now... */ + err("This sensor is not supported yet."); + return -1; + + return 0; +} + +/* This initializes the SAA7111A video decoder. */ +static int +saa7111a_configure(struct usb_ov511 *ov) +{ + int rc; + + /* Since there is no register reset command, all registers must be + * written, otherwise gives erratic results */ + static struct ov511_regvals aRegvalsNormSAA7111A[] = { + { OV511_I2C_BUS, 0x06, 0xce }, + { OV511_I2C_BUS, 0x07, 0x00 }, + { OV511_I2C_BUS, 0x10, 0x44 }, /* YUV422, 240/286 lines */ + { OV511_I2C_BUS, 0x0e, 0x01 }, /* NTSC M or PAL BGHI */ + { OV511_I2C_BUS, 0x00, 0x00 }, + { OV511_I2C_BUS, 0x01, 0x00 }, + { OV511_I2C_BUS, 0x03, 0x23 }, + { OV511_I2C_BUS, 0x04, 0x00 }, + { OV511_I2C_BUS, 0x05, 0x00 }, + { OV511_I2C_BUS, 0x08, 0xc8 }, /* Auto field freq */ + { OV511_I2C_BUS, 0x09, 0x01 }, /* Chrom. trap off, APER=0.25 */ + { OV511_I2C_BUS, 0x0a, 0x80 }, /* BRIG=128 */ + { OV511_I2C_BUS, 0x0b, 0x40 }, /* CONT=1.0 */ + { OV511_I2C_BUS, 0x0c, 0x40 }, /* SATN=1.0 */ + { OV511_I2C_BUS, 0x0d, 0x00 }, /* HUE=0 */ + { OV511_I2C_BUS, 0x0f, 0x00 }, + { OV511_I2C_BUS, 0x11, 0x0c }, + { OV511_I2C_BUS, 0x12, 0x00 }, + { OV511_I2C_BUS, 0x13, 0x00 }, + { OV511_I2C_BUS, 0x14, 0x00 }, + { OV511_I2C_BUS, 0x15, 0x00 }, + { OV511_I2C_BUS, 0x16, 0x00 }, + { OV511_I2C_BUS, 0x17, 0x00 }, + { OV511_I2C_BUS, 0x02, 0xc0 }, /* Composite input 0 */ + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + +// FIXME: I don't know how to sync or reset it yet +#if 0 + if (ov51x_init_saa_sensor(ov) < 0) { + err("Failed to initialize the SAA7111A"); + return -1; + } else { + PDEBUG(1, "SAA7111A sensor detected"); + } +#endif + + /* 640x480 not supported with PAL */ + if (ov->pal) { + ov->maxwidth = 320; + ov->maxheight = 240; /* Even field only */ + } else { + ov->maxwidth = 640; + ov->maxheight = 480; /* Even/Odd fields */ + } + + ov->minwidth = 320; + ov->minheight = 240; /* Even field only */ + + ov->has_decoder = 1; + ov->num_inputs = 8; + ov->norm = VIDEO_MODE_AUTO; + ov->stop_during_set = 0; /* Decoder guarantees stable image */ + + /* Decoder doesn't change these values, so we use these instead of + * acutally reading the registers (which doesn't work) */ + ov->brightness = 0x80 << 8; + ov->contrast = 0x40 << 9; + ov->colour = 0x40 << 9; + ov->hue = 32768; + + PDEBUG(4, "Writing SAA7111A registers"); + if (write_regvals(ov, aRegvalsNormSAA7111A)) + return -1; + + /* Detect version of decoder. This must be done after writing the + * initial regs or the decoder will lock up. */ + rc = i2c_r(ov, 0x00); + + if (rc < 0) { + err("Error detecting sensor version"); + return -1; + } else { + info("Sensor is an SAA7111A (version 0x%x)", rc); + ov->sensor = SEN_SAA7111A; + } + + // FIXME: Fix this for OV518(+) + /* Latch to negative edge of clock. Otherwise, we get incorrect + * colors and jitter in the digital signal. */ + if (ov->bclass == BCL_OV511) + reg_w(ov, 0x11, 0x00); + else + warn("SAA7111A not yet supported with OV518/OV518+"); + + return 0; +} + +/* This initializes the OV511/OV511+ and the sensor */ +static int +ov511_configure(struct usb_ov511 *ov) +{ + static struct ov511_regvals aRegvalsInit511[] = { + { OV511_REG_BUS, R51x_SYS_RESET, 0x7f }, + { OV511_REG_BUS, R51x_SYS_INIT, 0x01 }, + { OV511_REG_BUS, R51x_SYS_RESET, 0x7f }, + { OV511_REG_BUS, R51x_SYS_INIT, 0x01 }, + { OV511_REG_BUS, R51x_SYS_RESET, 0x3f }, + { OV511_REG_BUS, R51x_SYS_INIT, 0x01 }, + { OV511_REG_BUS, R51x_SYS_RESET, 0x3d }, + { OV511_DONE_BUS, 0x0, 0x00}, + }; + + static struct ov511_regvals aRegvalsNorm511[] = { + { OV511_REG_BUS, R511_DRAM_FLOW_CTL, 0x01 }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 }, + { OV511_REG_BUS, R511_FIFO_OPTS, 0x1f }, + { OV511_REG_BUS, R511_COMP_EN, 0x00 }, + { OV511_REG_BUS, R511_COMP_LUT_EN, 0x03 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + static struct ov511_regvals aRegvalsNorm511Plus[] = { + { OV511_REG_BUS, R511_DRAM_FLOW_CTL, 0xff }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, + { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 }, + { OV511_REG_BUS, R511_FIFO_OPTS, 0xff }, + { OV511_REG_BUS, R511_COMP_EN, 0x00 }, + { OV511_REG_BUS, R511_COMP_LUT_EN, 0x03 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + PDEBUG(4, ""); + + ov->customid = reg_r(ov, R511_SYS_CUST_ID); + if (ov->customid < 0) { + err("Unable to read camera bridge registers"); + goto error; + } + + PDEBUG (1, "CustomID = %d", ov->customid); + ov->desc = symbolic(camlist, ov->customid); + info("model: %s", ov->desc); + + if (0 == strcmp(ov->desc, NOT_DEFINED_STR)) { + err("Camera type (%d) not recognized", ov->customid); + err("Please notify " EMAIL " of the name,"); + err("manufacturer, model, and this number of your camera."); + err("Also include the output of the detection process."); + } + + if (ov->customid == 70) /* USB Life TV (PAL/SECAM) */ + ov->pal = 1; + + if (write_regvals(ov, aRegvalsInit511)) + goto error; + + if (ov->led_policy == LED_OFF || ov->led_policy == LED_AUTO) + ov51x_led_control(ov, 0); + + /* The OV511+ has undocumented bits in the flow control register. + * Setting it to 0xff fixes the corruption with moving objects. */ + if (ov->bridge == BRG_OV511) { + if (write_regvals(ov, aRegvalsNorm511)) + goto error; + } else if (ov->bridge == BRG_OV511PLUS) { + if (write_regvals(ov, aRegvalsNorm511Plus)) + goto error; + } else { + err("Invalid bridge"); + } + + if (ov511_init_compression(ov)) + goto error; + + ov->packet_numbering = 1; + ov511_set_packet_size(ov, 0); + + ov->snap_enabled = snapshot; + + /* Test for 7xx0 */ + PDEBUG(3, "Testing for 0V7xx0"); + ov->primary_i2c_slave = OV7xx0_SID; + if (ov51x_set_slave_ids(ov, OV7xx0_SID) < 0) + goto error; + + if (i2c_w(ov, 0x12, 0x80) < 0) { + /* Test for 6xx0 */ + PDEBUG(3, "Testing for 0V6xx0"); + ov->primary_i2c_slave = OV6xx0_SID; + if (ov51x_set_slave_ids(ov, OV6xx0_SID) < 0) + goto error; + + if (i2c_w(ov, 0x12, 0x80) < 0) { + /* Test for 8xx0 */ + PDEBUG(3, "Testing for 0V8xx0"); + ov->primary_i2c_slave = OV8xx0_SID; + if (ov51x_set_slave_ids(ov, OV8xx0_SID) < 0) + goto error; + + if (i2c_w(ov, 0x12, 0x80) < 0) { + /* Test for SAA7111A */ + PDEBUG(3, "Testing for SAA7111A"); + ov->primary_i2c_slave = SAA7111A_SID; + if (ov51x_set_slave_ids(ov, SAA7111A_SID) < 0) + goto error; + + if (i2c_w(ov, 0x0d, 0x00) < 0) { + /* Test for KS0127 */ + PDEBUG(3, "Testing for KS0127"); + ov->primary_i2c_slave = KS0127_SID; + if (ov51x_set_slave_ids(ov, KS0127_SID) < 0) + goto error; + + if (i2c_w(ov, 0x10, 0x00) < 0) { + err("Can't determine sensor slave IDs"); + goto error; + } else { + if (ks0127_configure(ov) < 0) { + err("Failed to configure KS0127"); + goto error; + } + } + } else { + if (saa7111a_configure(ov) < 0) { + err("Failed to configure SAA7111A"); + goto error; + } + } + } else { + err("Detected unsupported OV8xx0 sensor"); + goto error; + } + } else { + if (ov6xx0_configure(ov) < 0) { + err("Failed to configure OV6xx0"); + goto error; + } + } + } else { + if (ov7xx0_configure(ov) < 0) { + err("Failed to configure OV7xx0"); + goto error; + } + } + + return 0; + +error: + err("OV511 Config failed"); + + return -EBUSY; +} + +/* This initializes the OV518/OV518+ and the sensor */ +static int +ov518_configure(struct usb_ov511 *ov) +{ + /* For 518 and 518+ */ + static struct ov511_regvals aRegvalsInit518[] = { + { OV511_REG_BUS, R51x_SYS_RESET, 0x40 }, + { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 }, + { OV511_REG_BUS, R51x_SYS_RESET, 0x3e }, + { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 }, + { OV511_REG_BUS, R51x_SYS_RESET, 0x00 }, + { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 }, + { OV511_REG_BUS, 0x46, 0x00 }, + { OV511_REG_BUS, 0x5d, 0x03 }, + { OV511_DONE_BUS, 0x0, 0x00}, + }; + + static struct ov511_regvals aRegvalsNorm518[] = { + { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, /* Reset */ + { OV511_REG_BUS, R51x_SYS_SNAP, 0x01 }, /* Enable */ + { OV511_REG_BUS, 0x31, 0x0f }, + { OV511_REG_BUS, 0x5d, 0x03 }, + { OV511_REG_BUS, 0x24, 0x9f }, + { OV511_REG_BUS, 0x25, 0x90 }, + { OV511_REG_BUS, 0x20, 0x00 }, + { OV511_REG_BUS, 0x51, 0x04 }, + { OV511_REG_BUS, 0x71, 0x19 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + static struct ov511_regvals aRegvalsNorm518Plus[] = { + { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, /* Reset */ + { OV511_REG_BUS, R51x_SYS_SNAP, 0x01 }, /* Enable */ + { OV511_REG_BUS, 0x31, 0x0f }, + { OV511_REG_BUS, 0x5d, 0x03 }, + { OV511_REG_BUS, 0x24, 0x9f }, + { OV511_REG_BUS, 0x25, 0x90 }, + { OV511_REG_BUS, 0x20, 0x60 }, + { OV511_REG_BUS, 0x51, 0x02 }, + { OV511_REG_BUS, 0x71, 0x19 }, + { OV511_REG_BUS, 0x40, 0xff }, + { OV511_REG_BUS, 0x41, 0x42 }, + { OV511_REG_BUS, 0x46, 0x00 }, + { OV511_REG_BUS, 0x33, 0x04 }, + { OV511_REG_BUS, 0x21, 0x19 }, + { OV511_REG_BUS, 0x3f, 0x10 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + PDEBUG(4, ""); + + /* First 5 bits of custom ID reg are a revision ID on OV518 */ + info("Device revision %d", 0x1F & reg_r(ov, R511_SYS_CUST_ID)); + + /* Give it the default description */ + ov->desc = symbolic(camlist, 0); + + if (write_regvals(ov, aRegvalsInit518)) + goto error; + + /* Set LED GPIO pin to output mode */ + if (reg_w_mask(ov, 0x57, 0x00, 0x02) < 0) + goto error; + + /* LED is off by default with OV518; have to explicitly turn it on */ + if (ov->led_policy == LED_OFF || ov->led_policy == LED_AUTO) + ov51x_led_control(ov, 0); + else + ov51x_led_control(ov, 1); + + /* Don't require compression if dumppix is enabled; otherwise it's + * required. OV518 has no uncompressed mode, to save RAM. */ + if (!dumppix && !ov->compress) { + ov->compress = 1; + warn("Compression required with OV518...enabling"); + } + + if (ov->bridge == BRG_OV518) { + if (write_regvals(ov, aRegvalsNorm518)) + goto error; + } else if (ov->bridge == BRG_OV518PLUS) { + if (write_regvals(ov, aRegvalsNorm518Plus)) + goto error; + } else { + err("Invalid bridge"); + } + + if (reg_w(ov, 0x2f, 0x80) < 0) + goto error; + + if (ov518_init_compression(ov)) + goto error; + + if (ov->bridge == BRG_OV518) + { + struct usb_interface *ifp; + struct usb_host_interface *alt; + __u16 mxps = 0; + + ifp = usb_ifnum_to_if(ov->dev, 0); + if (ifp) { + alt = usb_altnum_to_altsetting(ifp, 7); + if (alt) + mxps = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); + } + + /* Some OV518s have packet numbering by default, some don't */ + if (mxps == 897) + ov->packet_numbering = 1; + else + ov->packet_numbering = 0; + } else { + /* OV518+ has packet numbering turned on by default */ + ov->packet_numbering = 1; + } + + ov518_set_packet_size(ov, 0); + + ov->snap_enabled = snapshot; + + /* Test for 76xx */ + ov->primary_i2c_slave = OV7xx0_SID; + if (ov51x_set_slave_ids(ov, OV7xx0_SID) < 0) + goto error; + + /* The OV518 must be more aggressive about sensor detection since + * I2C write will never fail if the sensor is not present. We have + * to try to initialize the sensor to detect its presence */ + + if (init_ov_sensor(ov) < 0) { + /* Test for 6xx0 */ + ov->primary_i2c_slave = OV6xx0_SID; + if (ov51x_set_slave_ids(ov, OV6xx0_SID) < 0) + goto error; + + if (init_ov_sensor(ov) < 0) { + /* Test for 8xx0 */ + ov->primary_i2c_slave = OV8xx0_SID; + if (ov51x_set_slave_ids(ov, OV8xx0_SID) < 0) + goto error; + + if (init_ov_sensor(ov) < 0) { + err("Can't determine sensor slave IDs"); + goto error; + } else { + err("Detected unsupported OV8xx0 sensor"); + goto error; + } + } else { + if (ov6xx0_configure(ov) < 0) { + err("Failed to configure OV6xx0"); + goto error; + } + } + } else { + if (ov7xx0_configure(ov) < 0) { + err("Failed to configure OV7xx0"); + goto error; + } + } + + ov->maxwidth = 352; + ov->maxheight = 288; + + // The OV518 cannot go as low as the sensor can + ov->minwidth = 160; + ov->minheight = 120; + + return 0; + +error: + err("OV518 Config failed"); + + return -EBUSY; +} + +/**************************************************************************** + * sysfs + ***************************************************************************/ + +static inline struct usb_ov511 *cd_to_ov(struct class_device *cd) +{ + struct video_device *vdev = to_video_device(cd); + return video_get_drvdata(vdev); +} + +static ssize_t show_custom_id(struct class_device *cd, char *buf) +{ + struct usb_ov511 *ov = cd_to_ov(cd); + return sprintf(buf, "%d\n", ov->customid); +} +static CLASS_DEVICE_ATTR(custom_id, S_IRUGO, show_custom_id, NULL); + +static ssize_t show_model(struct class_device *cd, char *buf) +{ + struct usb_ov511 *ov = cd_to_ov(cd); + return sprintf(buf, "%s\n", ov->desc); +} +static CLASS_DEVICE_ATTR(model, S_IRUGO, show_model, NULL); + +static ssize_t show_bridge(struct class_device *cd, char *buf) +{ + struct usb_ov511 *ov = cd_to_ov(cd); + return sprintf(buf, "%s\n", symbolic(brglist, ov->bridge)); +} +static CLASS_DEVICE_ATTR(bridge, S_IRUGO, show_bridge, NULL); + +static ssize_t show_sensor(struct class_device *cd, char *buf) +{ + struct usb_ov511 *ov = cd_to_ov(cd); + return sprintf(buf, "%s\n", symbolic(senlist, ov->sensor)); +} +static CLASS_DEVICE_ATTR(sensor, S_IRUGO, show_sensor, NULL); + +static ssize_t show_brightness(struct class_device *cd, char *buf) +{ + struct usb_ov511 *ov = cd_to_ov(cd); + unsigned short x; + + if (!ov->dev) + return -ENODEV; + sensor_get_brightness(ov, &x); + return sprintf(buf, "%d\n", x >> 8); +} +static CLASS_DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL); + +static ssize_t show_saturation(struct class_device *cd, char *buf) +{ + struct usb_ov511 *ov = cd_to_ov(cd); + unsigned short x; + + if (!ov->dev) + return -ENODEV; + sensor_get_saturation(ov, &x); + return sprintf(buf, "%d\n", x >> 8); +} +static CLASS_DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL); + +static ssize_t show_contrast(struct class_device *cd, char *buf) +{ + struct usb_ov511 *ov = cd_to_ov(cd); + unsigned short x; + + if (!ov->dev) + return -ENODEV; + sensor_get_contrast(ov, &x); + return sprintf(buf, "%d\n", x >> 8); +} +static CLASS_DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL); + +static ssize_t show_hue(struct class_device *cd, char *buf) +{ + struct usb_ov511 *ov = cd_to_ov(cd); + unsigned short x; + + if (!ov->dev) + return -ENODEV; + sensor_get_hue(ov, &x); + return sprintf(buf, "%d\n", x >> 8); +} +static CLASS_DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL); + +static ssize_t show_exposure(struct class_device *cd, char *buf) +{ + struct usb_ov511 *ov = cd_to_ov(cd); + unsigned char exp = 0; + + if (!ov->dev) + return -ENODEV; + sensor_get_exposure(ov, &exp); + return sprintf(buf, "%d\n", exp >> 8); +} +static CLASS_DEVICE_ATTR(exposure, S_IRUGO, show_exposure, NULL); + +static void ov_create_sysfs(struct video_device *vdev) +{ + video_device_create_file(vdev, &class_device_attr_custom_id); + video_device_create_file(vdev, &class_device_attr_model); + video_device_create_file(vdev, &class_device_attr_bridge); + video_device_create_file(vdev, &class_device_attr_sensor); + video_device_create_file(vdev, &class_device_attr_brightness); + video_device_create_file(vdev, &class_device_attr_saturation); + video_device_create_file(vdev, &class_device_attr_contrast); + video_device_create_file(vdev, &class_device_attr_hue); + video_device_create_file(vdev, &class_device_attr_exposure); +} + +/**************************************************************************** + * USB routines + ***************************************************************************/ + +static int +ov51x_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_interface_descriptor *idesc; + struct usb_ov511 *ov; + int i; + + PDEBUG(1, "probing for device..."); + + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return -ENODEV; + + idesc = &intf->cur_altsetting->desc; + + if (idesc->bInterfaceClass != 0xFF) + return -ENODEV; + if (idesc->bInterfaceSubClass != 0x00) + return -ENODEV; + + if ((ov = kzalloc(sizeof(*ov), GFP_KERNEL)) == NULL) { + err("couldn't kmalloc ov struct"); + goto error_out; + } + + ov->dev = dev; + ov->iface = idesc->bInterfaceNumber; + ov->led_policy = led; + ov->compress = compress; + ov->lightfreq = lightfreq; + ov->num_inputs = 1; /* Video decoder init functs. change this */ + ov->stop_during_set = !fastset; + ov->backlight = backlight; + ov->mirror = mirror; + ov->auto_brt = autobright; + ov->auto_gain = autogain; + ov->auto_exp = autoexp; + + switch (le16_to_cpu(dev->descriptor.idProduct)) { + case PROD_OV511: + ov->bridge = BRG_OV511; + ov->bclass = BCL_OV511; + break; + case PROD_OV511PLUS: + ov->bridge = BRG_OV511PLUS; + ov->bclass = BCL_OV511; + break; + case PROD_OV518: + ov->bridge = BRG_OV518; + ov->bclass = BCL_OV518; + break; + case PROD_OV518PLUS: + ov->bridge = BRG_OV518PLUS; + ov->bclass = BCL_OV518; + break; + case PROD_ME2CAM: + if (le16_to_cpu(dev->descriptor.idVendor) != VEND_MATTEL) + goto error; + ov->bridge = BRG_OV511PLUS; + ov->bclass = BCL_OV511; + break; + default: + err("Unknown product ID 0x%04x", le16_to_cpu(dev->descriptor.idProduct)); + goto error; + } + + info("USB %s video device found", symbolic(brglist, ov->bridge)); + + init_waitqueue_head(&ov->wq); + + mutex_init(&ov->lock); /* to 1 == available */ + mutex_init(&ov->buf_lock); + mutex_init(&ov->i2c_lock); + mutex_init(&ov->cbuf_lock); + + ov->buf_state = BUF_NOT_ALLOCATED; + + if (usb_make_path(dev, ov->usb_path, OV511_USB_PATH_LEN) < 0) { + err("usb_make_path error"); + goto error; + } + + /* Allocate control transfer buffer. */ + /* Must be kmalloc()'ed, for DMA compatibility */ + ov->cbuf = kmalloc(OV511_CBUF_SIZE, GFP_KERNEL); + if (!ov->cbuf) + goto error; + + if (ov->bclass == BCL_OV518) { + if (ov518_configure(ov) < 0) + goto error; + } else { + if (ov511_configure(ov) < 0) + goto error; + } + + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov->frame[i].framenum = i; + init_waitqueue_head(&ov->frame[i].wq); + } + + for (i = 0; i < OV511_NUMSBUF; i++) { + ov->sbuf[i].ov = ov; + spin_lock_init(&ov->sbuf[i].lock); + ov->sbuf[i].n = i; + } + + /* Unnecessary? (This is done on open(). Need to make sure variables + * are properly initialized without this before removing it, though). */ + if (ov51x_set_default_params(ov) < 0) + goto error; + +#ifdef OV511_DEBUG + if (dump_bridge) { + if (ov->bclass == BCL_OV511) + ov511_dump_regs(ov); + else + ov518_dump_regs(ov); + } +#endif + + ov->vdev = video_device_alloc(); + if (!ov->vdev) + goto error; + + memcpy(ov->vdev, &vdev_template, sizeof(*ov->vdev)); + ov->vdev->dev = &dev->dev; + video_set_drvdata(ov->vdev, ov); + + for (i = 0; i < OV511_MAX_UNIT_VIDEO; i++) { + /* Minor 0 cannot be specified; assume user wants autodetect */ + if (unit_video[i] == 0) + break; + + if (video_register_device(ov->vdev, VFL_TYPE_GRABBER, + unit_video[i]) >= 0) { + break; + } + } + + /* Use the next available one */ + if ((ov->vdev->minor == -1) && + video_register_device(ov->vdev, VFL_TYPE_GRABBER, -1) < 0) { + err("video_register_device failed"); + goto error; + } + + info("Device at %s registered to minor %d", ov->usb_path, + ov->vdev->minor); + + usb_set_intfdata(intf, ov); + ov_create_sysfs(ov->vdev); + return 0; + +error: + if (ov->vdev) { + if (-1 == ov->vdev->minor) + video_device_release(ov->vdev); + else + video_unregister_device(ov->vdev); + ov->vdev = NULL; + } + + if (ov->cbuf) { + mutex_lock(&ov->cbuf_lock); + kfree(ov->cbuf); + ov->cbuf = NULL; + mutex_unlock(&ov->cbuf_lock); + } + + kfree(ov); + ov = NULL; + +error_out: + err("Camera initialization failed"); + return -EIO; +} + +static void +ov51x_disconnect(struct usb_interface *intf) +{ + struct usb_ov511 *ov = usb_get_intfdata(intf); + int n; + + PDEBUG(3, ""); + + usb_set_intfdata (intf, NULL); + + if (!ov) + return; + + if (ov->vdev) + video_unregister_device(ov->vdev); + + for (n = 0; n < OV511_NUMFRAMES; n++) + ov->frame[n].grabstate = FRAME_ERROR; + + ov->curframe = -1; + + /* This will cause the process to request another frame */ + for (n = 0; n < OV511_NUMFRAMES; n++) + wake_up_interruptible(&ov->frame[n].wq); + + wake_up_interruptible(&ov->wq); + + ov->streaming = 0; + ov51x_unlink_isoc(ov); + + ov->dev = NULL; + + /* Free the memory */ + if (ov && !ov->user) { + mutex_lock(&ov->cbuf_lock); + kfree(ov->cbuf); + ov->cbuf = NULL; + mutex_unlock(&ov->cbuf_lock); + + ov51x_dealloc(ov); + kfree(ov); + ov = NULL; + } + + PDEBUG(3, "Disconnect complete"); +} + +static struct usb_driver ov511_driver = { + .name = "ov511", + .id_table = device_table, + .probe = ov51x_probe, + .disconnect = ov51x_disconnect +}; + +/**************************************************************************** + * + * Module routines + * + ***************************************************************************/ + +static int __init +usb_ov511_init(void) +{ + int retval; + + retval = usb_register(&ov511_driver); + if (retval) + goto out; + + info(DRIVER_VERSION " : " DRIVER_DESC); + +out: + return retval; +} + +static void __exit +usb_ov511_exit(void) +{ + usb_deregister(&ov511_driver); + info("driver deregistered"); + +} + +module_init(usb_ov511_init); +module_exit(usb_ov511_exit); + diff --git a/linux/drivers/media/video/ov511.h b/linux/drivers/media/video/ov511.h new file mode 100644 index 000000000..63da4e8d8 --- /dev/null +++ b/linux/drivers/media/video/ov511.h @@ -0,0 +1,569 @@ +#ifndef __LINUX_OV511_H +#define __LINUX_OV511_H + +#include +#include "compat.h" +#include +#include +#include +#include + +#define OV511_DEBUG /* Turn on debug messages */ + +#ifdef OV511_DEBUG + #define PDEBUG(level, fmt, args...) \ + if (debug >= (level)) info("[%s:%d] " fmt, \ + __FUNCTION__, __LINE__ , ## args) +#else + #define PDEBUG(level, fmt, args...) do {} while(0) +#endif + +/* This macro restricts an int variable to an inclusive range */ +#define RESTRICT_TO_RANGE(v,mi,ma) { \ + if ((v) < (mi)) (v) = (mi); \ + else if ((v) > (ma)) (v) = (ma); \ +} + +/* --------------------------------- */ +/* DEFINES FOR OV511 AND OTHER CHIPS */ +/* --------------------------------- */ + +/* USB IDs */ +#define VEND_OMNIVISION 0x05A9 +#define PROD_OV511 0x0511 +#define PROD_OV511PLUS 0xA511 +#define PROD_OV518 0x0518 +#define PROD_OV518PLUS 0xA518 + +#define VEND_MATTEL 0x0813 +#define PROD_ME2CAM 0x0002 + +/* --------------------------------- */ +/* OV51x REGISTER MNEMONICS */ +/* --------------------------------- */ + +/* Camera interface register numbers */ +#define R511_CAM_DELAY 0x10 +#define R511_CAM_EDGE 0x11 +#define R511_CAM_PXCNT 0x12 +#define R511_CAM_LNCNT 0x13 +#define R511_CAM_PXDIV 0x14 +#define R511_CAM_LNDIV 0x15 +#define R511_CAM_UV_EN 0x16 +#define R511_CAM_LINE_MODE 0x17 +#define R511_CAM_OPTS 0x18 + +/* Snapshot mode camera interface register numbers */ +#define R511_SNAP_FRAME 0x19 +#define R511_SNAP_PXCNT 0x1A +#define R511_SNAP_LNCNT 0x1B +#define R511_SNAP_PXDIV 0x1C +#define R511_SNAP_LNDIV 0x1D +#define R511_SNAP_UV_EN 0x1E +#define R511_SNAP_OPTS 0x1F + +/* DRAM register numbers */ +#define R511_DRAM_FLOW_CTL 0x20 +#define R511_DRAM_ARCP 0x21 +#define R511_DRAM_MRC 0x22 +#define R511_DRAM_RFC 0x23 + +/* ISO FIFO register numbers */ +#define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */ +#define R511_FIFO_OPTS 0x31 + +/* Parallel IO register numbers */ +#define R511_PIO_OPTS 0x38 +#define R511_PIO_DATA 0x39 +#define R511_PIO_BIST 0x3E +#define R518_GPIO_IN 0x55 /* OV518(+) only */ +#define R518_GPIO_OUT 0x56 /* OV518(+) only */ +#define R518_GPIO_CTL 0x57 /* OV518(+) only */ +#define R518_GPIO_PULSE_IN 0x58 /* OV518(+) only */ +#define R518_GPIO_PULSE_CLEAR 0x59 /* OV518(+) only */ +#define R518_GPIO_PULSE_POL 0x5a /* OV518(+) only */ +#define R518_GPIO_PULSE_EN 0x5b /* OV518(+) only */ +#define R518_GPIO_RESET 0x5c /* OV518(+) only */ + +/* I2C registers */ +#define R511_I2C_CTL 0x40 +#define R518_I2C_CTL 0x47 /* OV518(+) only */ +#define R51x_I2C_W_SID 0x41 +#define R51x_I2C_SADDR_3 0x42 +#define R51x_I2C_SADDR_2 0x43 +#define R51x_I2C_R_SID 0x44 +#define R51x_I2C_DATA 0x45 +#define R51x_I2C_CLOCK 0x46 +#define R51x_I2C_TIMEOUT 0x47 + +/* I2C snapshot registers */ +#define R511_SI2C_SADDR_3 0x48 +#define R511_SI2C_DATA 0x49 + +/* System control registers */ +#define R51x_SYS_RESET 0x50 + /* Reset type definitions */ +#define OV511_RESET_UDC 0x01 +#define OV511_RESET_I2C 0x02 +#define OV511_RESET_FIFO 0x04 +#define OV511_RESET_OMNICE 0x08 +#define OV511_RESET_DRAM 0x10 +#define OV511_RESET_CAM_INT 0x20 +#define OV511_RESET_OV511 0x40 +#define OV511_RESET_NOREGS 0x3F /* All but OV511 & regs */ +#define OV511_RESET_ALL 0x7F + +#define R511_SYS_CLOCK_DIV 0x51 +#define R51x_SYS_SNAP 0x52 +#define R51x_SYS_INIT 0x53 +#define R511_SYS_PWR_CLK 0x54 /* OV511+/OV518(+) only */ +#define R511_SYS_LED_CTL 0x55 /* OV511+ only */ +#define R511_SYS_USER 0x5E +#define R511_SYS_CUST_ID 0x5F + +/* OmniCE (compression) registers */ +#define R511_COMP_PHY 0x70 +#define R511_COMP_PHUV 0x71 +#define R511_COMP_PVY 0x72 +#define R511_COMP_PVUV 0x73 +#define R511_COMP_QHY 0x74 +#define R511_COMP_QHUV 0x75 +#define R511_COMP_QVY 0x76 +#define R511_COMP_QVUV 0x77 +#define R511_COMP_EN 0x78 +#define R511_COMP_LUT_EN 0x79 +#define R511_COMP_LUT_BEGIN 0x80 + +/* --------------------------------- */ +/* ALTERNATE NUMBERS */ +/* --------------------------------- */ + +/* Alternate numbers for various max packet sizes (OV511 only) */ +#define OV511_ALT_SIZE_992 0 +#define OV511_ALT_SIZE_993 1 +#define OV511_ALT_SIZE_768 2 +#define OV511_ALT_SIZE_769 3 +#define OV511_ALT_SIZE_512 4 +#define OV511_ALT_SIZE_513 5 +#define OV511_ALT_SIZE_257 6 +#define OV511_ALT_SIZE_0 7 + +/* Alternate numbers for various max packet sizes (OV511+ only) */ +#define OV511PLUS_ALT_SIZE_0 0 +#define OV511PLUS_ALT_SIZE_33 1 +#define OV511PLUS_ALT_SIZE_129 2 +#define OV511PLUS_ALT_SIZE_257 3 +#define OV511PLUS_ALT_SIZE_385 4 +#define OV511PLUS_ALT_SIZE_513 5 +#define OV511PLUS_ALT_SIZE_769 6 +#define OV511PLUS_ALT_SIZE_961 7 + +/* Alternate numbers for various max packet sizes (OV518(+) only) */ +#define OV518_ALT_SIZE_0 0 +#define OV518_ALT_SIZE_128 1 +#define OV518_ALT_SIZE_256 2 +#define OV518_ALT_SIZE_384 3 +#define OV518_ALT_SIZE_512 4 +#define OV518_ALT_SIZE_640 5 +#define OV518_ALT_SIZE_768 6 +#define OV518_ALT_SIZE_896 7 + +/* --------------------------------- */ +/* OV7610 REGISTER MNEMONICS */ +/* --------------------------------- */ + +/* OV7610 registers */ +#define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */ +#define OV7610_REG_BLUE 0x01 /* blue channel balance */ +#define OV7610_REG_RED 0x02 /* red channel balance */ +#define OV7610_REG_SAT 0x03 /* saturation */ + /* 04 reserved */ +#define OV7610_REG_CNT 0x05 /* Y contrast */ +#define OV7610_REG_BRT 0x06 /* Y brightness */ + /* 08-0b reserved */ +#define OV7610_REG_BLUE_BIAS 0x0C /* blue channel bias (5:0) */ +#define OV7610_REG_RED_BIAS 0x0D /* read channel bias (5:0) */ +#define OV7610_REG_GAMMA_COEFF 0x0E /* gamma settings */ +#define OV7610_REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */ +#define OV7610_REG_EXP 0x10 /* manual exposure setting */ +#define OV7610_REG_CLOCK 0x11 /* polarity/clock prescaler */ +#define OV7610_REG_COM_A 0x12 /* misc common regs */ +#define OV7610_REG_COM_B 0x13 /* misc common regs */ +#define OV7610_REG_COM_C 0x14 /* misc common regs */ +#define OV7610_REG_COM_D 0x15 /* misc common regs */ +#define OV7610_REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */ +#define OV7610_REG_HWIN_START 0x17 /* horizontal window start */ +#define OV7610_REG_HWIN_END 0x18 /* horizontal window end */ +#define OV7610_REG_VWIN_START 0x19 /* vertical window start */ +#define OV7610_REG_VWIN_END 0x1A /* vertical window end */ +#define OV7610_REG_PIXEL_SHIFT 0x1B /* pixel shift */ +#define OV7610_REG_ID_HIGH 0x1C /* manufacturer ID MSB */ +#define OV7610_REG_ID_LOW 0x1D /* manufacturer ID LSB */ + /* 0e-0f reserved */ +#define OV7610_REG_COM_E 0x20 /* misc common regs */ +#define OV7610_REG_YOFFSET 0x21 /* Y channel offset */ +#define OV7610_REG_UOFFSET 0x22 /* U channel offset */ + /* 23 reserved */ +#define OV7610_REG_ECW 0x24 /* Exposure white level for AEC */ +#define OV7610_REG_ECB 0x25 /* Exposure black level for AEC */ +#define OV7610_REG_COM_F 0x26 /* misc settings */ +#define OV7610_REG_COM_G 0x27 /* misc settings */ +#define OV7610_REG_COM_H 0x28 /* misc settings */ +#define OV7610_REG_COM_I 0x29 /* misc settings */ +#define OV7610_REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */ +#define OV7610_REG_FRAMERATE_L 0x2B /* frame rate LSB */ +#define OV7610_REG_ALC 0x2C /* Auto Level Control settings */ +#define OV7610_REG_COM_J 0x2D /* misc settings */ +#define OV7610_REG_VOFFSET 0x2E /* V channel offset adjustment */ +#define OV7610_REG_ARRAY_BIAS 0x2F /* Array bias -- don't change */ + /* 30-32 reserved */ +#define OV7610_REG_YGAMMA 0x33 /* misc gamma settings (7:6) */ +#define OV7610_REG_BIAS_ADJUST 0x34 /* misc bias settings */ +#define OV7610_REG_COM_L 0x35 /* misc settings */ + /* 36-37 reserved */ +#define OV7610_REG_COM_K 0x38 /* misc registers */ + +/* --------------------------------- */ +/* I2C ADDRESSES */ +/* --------------------------------- */ + +#define OV7xx0_SID 0x42 +#define OV6xx0_SID 0xC0 +#define OV8xx0_SID 0xA0 +#define KS0127_SID 0xD8 +#define SAA7111A_SID 0x48 + +/* --------------------------------- */ +/* MISCELLANEOUS DEFINES */ +/* --------------------------------- */ + +#define I2C_CLOCK_PRESCALER 0x03 + +#define FRAMES_PER_DESC 10 /* FIXME - What should this be? */ +#define MAX_FRAME_SIZE_PER_DESC 993 /* For statically allocated stuff */ +#define PIXELS_PER_SEG 256 /* Pixels per segment */ + +#define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */ + +#define OV511_NUMFRAMES 2 +#if OV511_NUMFRAMES > VIDEO_MAX_FRAME + #error "OV511_NUMFRAMES is too high" +#endif + +#define OV511_NUMSBUF 2 + +/* Control transfers use up to 4 bytes */ +#define OV511_CBUF_SIZE 4 + +/* Size of usb_make_path() buffer */ +#define OV511_USB_PATH_LEN 64 + +/* Bridge types */ +enum { + BRG_UNKNOWN, + BRG_OV511, + BRG_OV511PLUS, + BRG_OV518, + BRG_OV518PLUS, +}; + +/* Bridge classes */ +enum { + BCL_UNKNOWN, + BCL_OV511, + BCL_OV518, +}; + +/* Sensor types */ +enum { + SEN_UNKNOWN, + SEN_OV76BE, + SEN_OV7610, + SEN_OV7620, + SEN_OV7620AE, + SEN_OV6620, + SEN_OV6630, + SEN_OV6630AE, + SEN_OV6630AF, + SEN_OV8600, + SEN_KS0127, + SEN_KS0127B, + SEN_SAA7111A, +}; + +enum { + STATE_SCANNING, /* Scanning for start */ + STATE_HEADER, /* Parsing header */ + STATE_LINES, /* Parsing lines */ +}; + +/* Buffer states */ +enum { + BUF_NOT_ALLOCATED, + BUF_ALLOCATED, +}; + +/* --------- Definition of ioctl interface --------- */ + +#define OV511_INTERFACE_VER 101 + +/* LED options */ +enum { + LED_OFF, + LED_ON, + LED_AUTO, +}; + +/* Raw frame formats */ +enum { + RAWFMT_INVALID, + RAWFMT_YUV400, + RAWFMT_YUV420, + RAWFMT_YUV422, + RAWFMT_GBR422, +}; + +struct ov511_i2c_struct { + unsigned char slave; /* Write slave ID (read ID - 1) */ + unsigned char reg; /* Index of register */ + unsigned char value; /* User sets this w/ write, driver does w/ read */ + unsigned char mask; /* Bits to be changed. Not used with read ops */ +}; + +/* ioctls */ +#define OV511IOC_WI2C _IOW('v', BASE_VIDIOCPRIVATE + 5, \ + struct ov511_i2c_struct) +#define OV511IOC_RI2C _IOWR('v', BASE_VIDIOCPRIVATE + 6, \ + struct ov511_i2c_struct) +/* ------------- End IOCTL interface -------------- */ + +struct usb_ov511; /* Forward declaration */ + +struct ov511_sbuf { + struct usb_ov511 *ov; + unsigned char *data; + struct urb *urb; + spinlock_t lock; + int n; +}; + +enum { + FRAME_UNUSED, /* Unused (no MCAPTURE) */ + FRAME_READY, /* Ready to start grabbing */ + FRAME_GRABBING, /* In the process of being grabbed into */ + FRAME_DONE, /* Finished grabbing, but not been synced yet */ + FRAME_ERROR, /* Something bad happened while processing */ +}; + +struct ov511_regvals { + enum { + OV511_DONE_BUS, + OV511_REG_BUS, + OV511_I2C_BUS, + } bus; + unsigned char reg; + unsigned char val; +}; + +struct ov511_frame { + int framenum; /* Index of this frame */ + unsigned char *data; /* Frame buffer */ + unsigned char *tempdata; /* Temp buffer for multi-stage conversions */ + unsigned char *rawdata; /* Raw camera data buffer */ + unsigned char *compbuf; /* Temp buffer for decompressor */ + + int depth; /* Bytes per pixel */ + int width; /* Width application is expecting */ + int height; /* Height application is expecting */ + + int rawwidth; /* Actual width of frame sent from camera */ + int rawheight; /* Actual height of frame sent from camera */ + + int sub_flag; /* Sub-capture mode for this frame? */ + unsigned int format; /* Format for this frame */ + int compressed; /* Is frame compressed? */ + + volatile int grabstate; /* State of grabbing */ + int scanstate; /* State of scanning */ + + int bytes_recvd; /* Number of image bytes received from camera */ + + long bytes_read; /* Amount that has been read() */ + + wait_queue_head_t wq; /* Processes waiting */ + + int snapshot; /* True if frame was a snapshot */ +}; + +#define DECOMP_INTERFACE_VER 4 + +/* Compression module operations */ +struct ov51x_decomp_ops { + int (*decomp_400)(unsigned char *, unsigned char *, unsigned char *, + int, int, int); + int (*decomp_420)(unsigned char *, unsigned char *, unsigned char *, + int, int, int); + int (*decomp_422)(unsigned char *, unsigned char *, unsigned char *, + int, int, int); + struct module *owner; +}; + +struct usb_ov511 { + struct video_device *vdev; + struct usb_device *dev; + + int customid; + char *desc; + unsigned char iface; + char usb_path[OV511_USB_PATH_LEN]; + + /* Determined by sensor type */ + int maxwidth; + int maxheight; + int minwidth; + int minheight; + + int brightness; + int colour; + int contrast; + int hue; + int whiteness; + int exposure; + int auto_brt; /* Auto brightness enabled flag */ + int auto_gain; /* Auto gain control enabled flag */ + int auto_exp; /* Auto exposure enabled flag */ + int backlight; /* Backlight exposure algorithm flag */ + int mirror; /* Image is reversed horizontally */ + + int led_policy; /* LED: off|on|auto; OV511+ only */ + + struct mutex lock; /* Serializes user-accessible operations */ + int user; /* user count for exclusive use */ + + int streaming; /* Are we streaming Isochronous? */ + int grabbing; /* Are we grabbing? */ + + int compress; /* Should the next frame be compressed? */ + int compress_inited; /* Are compression params uploaded? */ + + int lightfreq; /* Power (lighting) frequency */ + int bandfilt; /* Banding filter enabled flag */ + + unsigned char *fbuf; /* Videodev buffer area */ + unsigned char *tempfbuf; /* Temporary (intermediate) buffer area */ + unsigned char *rawfbuf; /* Raw camera data buffer area */ + + int sub_flag; /* Pix Array subcapture on flag */ + int subx; /* Pix Array subcapture x offset */ + int suby; /* Pix Array subcapture y offset */ + int subw; /* Pix Array subcapture width */ + int subh; /* Pix Array subcapture height */ + + int curframe; /* Current receiving sbuf */ + struct ov511_frame frame[OV511_NUMFRAMES]; + + struct ov511_sbuf sbuf[OV511_NUMSBUF]; + + wait_queue_head_t wq; /* Processes waiting */ + + int snap_enabled; /* Snapshot mode enabled */ + + int bridge; /* Type of bridge (BRG_*) */ + int bclass; /* Class of bridge (BCL_*) */ + int sensor; /* Type of image sensor chip (SEN_*) */ + + int packet_size; /* Frame size per isoc desc */ + int packet_numbering; /* Is ISO frame numbering enabled? */ + + /* Framebuffer/sbuf management */ + int buf_state; + struct mutex buf_lock; + + struct ov51x_decomp_ops *decomp_ops; + + /* Stop streaming while changing picture settings */ + int stop_during_set; + + int stopped; /* Streaming is temporarily paused */ + + /* Video decoder stuff */ + int input; /* Composite, S-VIDEO, etc... */ + int num_inputs; /* Number of inputs */ + int norm; /* NTSC / PAL / SECAM */ + int has_decoder; /* Device has a video decoder */ + int pal; /* Device is designed for PAL resolution */ + + /* I2C interface */ + struct mutex i2c_lock; /* Protect I2C controller regs */ + unsigned char primary_i2c_slave; /* I2C write id of sensor */ + + /* Control transaction stuff */ + unsigned char *cbuf; /* Buffer for payload */ + struct mutex cbuf_lock; +}; + +/* Used to represent a list of values and their respective symbolic names */ +struct symbolic_list { + int num; + char *name; +}; + +#define NOT_DEFINED_STR "Unknown" + +/* Returns the name of the matching element in the symbolic_list array. The + * end of the list must be marked with an element that has a NULL name. + */ +static inline char * +symbolic(struct symbolic_list list[], int num) +{ + int i; + + for (i = 0; list[i].name != NULL; i++) + if (list[i].num == num) + return (list[i].name); + + return (NOT_DEFINED_STR); +} + +/* Compression stuff */ + +#define OV511_QUANTABLESIZE 64 +#define OV518_QUANTABLESIZE 32 + +#define OV511_YQUANTABLE { \ + 0, 1, 1, 2, 2, 3, 3, 4, \ + 1, 1, 1, 2, 2, 3, 4, 4, \ + 1, 1, 2, 2, 3, 4, 4, 4, \ + 2, 2, 2, 3, 4, 4, 4, 4, \ + 2, 2, 3, 4, 4, 5, 5, 5, \ + 3, 3, 4, 4, 5, 5, 5, 5, \ + 3, 4, 4, 4, 5, 5, 5, 5, \ + 4, 4, 4, 4, 5, 5, 5, 5 \ +} + +#define OV511_UVQUANTABLE { \ + 0, 2, 2, 3, 4, 4, 4, 4, \ + 2, 2, 2, 4, 4, 4, 4, 4, \ + 2, 2, 3, 4, 4, 4, 4, 4, \ + 3, 4, 4, 4, 4, 4, 4, 4, \ + 4, 4, 4, 4, 4, 4, 4, 4, \ + 4, 4, 4, 4, 4, 4, 4, 4, \ + 4, 4, 4, 4, 4, 4, 4, 4, \ + 4, 4, 4, 4, 4, 4, 4, 4 \ +} + +#define OV518_YQUANTABLE { \ + 5, 4, 5, 6, 6, 7, 7, 7, \ + 5, 5, 5, 5, 6, 7, 7, 7, \ + 6, 6, 6, 6, 7, 7, 7, 8, \ + 7, 7, 6, 7, 7, 7, 8, 8 \ +} + +#define OV518_UVQUANTABLE { \ + 6, 6, 6, 7, 7, 7, 7, 7, \ + 6, 6, 6, 7, 7, 7, 7, 7, \ + 6, 6, 6, 7, 7, 7, 7, 8, \ + 7, 7, 7, 7, 7, 7, 8, 8 \ +} + +#endif diff --git a/linux/drivers/media/video/planb.c b/linux/drivers/media/video/planb.c new file mode 100644 index 000000000..c3026d701 --- /dev/null +++ b/linux/drivers/media/video/planb.c @@ -0,0 +1,2304 @@ +/* + planb - PlanB frame grabber driver + + PlanB is used in the 7x00/8x00 series of PowerMacintosh + Computers as video input DMA controller. + + Copyright (C) 1998 Michel Lanners (mlan@cpu.lu) + + Based largely on the bttv driver by Ralph Metzler (rjkm@thp.uni-koeln.de) + + Additional debugging and coding by Takashi Oe (toe@unlserve.unl.edu) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* $Id: planb.c,v 1.18 1999/05/02 17:36:34 mlan Exp $ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "compat.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "planb.h" +#include "saa7196.h" + +/* Would you mind for some ugly debugging? */ +#if 0 +#define DEBUG(x...) printk(KERN_DEBUG ## x) /* Debug driver */ +#else +#define DEBUG(x...) /* Don't debug driver */ +#endif + +#if 0 +#define IDEBUG(x...) printk(KERN_DEBUG ## x) /* Debug interrupt part */ +#else +#define IDEBUG(x...) /* Don't debug interrupt part */ +#endif + +/* Ever seen a Mac with more than 1 of these? */ +#define PLANB_MAX 1 + +static int planb_num; +static struct planb planbs[PLANB_MAX]; +static volatile struct planb_registers *planb_regs; + +static int def_norm = PLANB_DEF_NORM; /* default norm */ +static int video_nr = -1; + +module_param(def_norm, int, 0); +MODULE_PARM_DESC(def_norm, "Default startup norm (0=PAL, 1=NTSC, 2=SECAM)"); +module_param(video_nr, int, 0); +MODULE_LICENSE("GPL"); + + +/* ------------------ PlanB Exported Functions ------------------ */ +static long planb_write(struct video_device *, const char *, unsigned long, int); +static long planb_read(struct video_device *, char *, unsigned long, int); +static int planb_open(struct video_device *, int); +static void planb_close(struct video_device *); +static int planb_ioctl(struct video_device *, unsigned int, void *); +static int planb_init_done(struct video_device *); +static int planb_mmap(struct video_device *, const char *, unsigned long); +static void planb_irq(int, void *, struct pt_regs *); +static void release_planb(void); +int init_planbs(struct video_init *); + +/* ------------------ PlanB Internal Functions ------------------ */ +static int planb_prepare_open(struct planb *); +static void planb_prepare_close(struct planb *); +static void saa_write_reg(unsigned char, unsigned char); +static unsigned char saa_status(int, struct planb *); +static void saa_set(unsigned char, unsigned char, struct planb *); +static void saa_init_regs(struct planb *); +static int grabbuf_alloc(struct planb *); +static int vgrab(struct planb *, struct video_mmap *); +static void add_clip(struct planb *, struct video_clip *); +static void fill_cmd_buff(struct planb *); +static void cmd_buff(struct planb *); +static volatile struct dbdma_cmd *setup_grab_cmd(int, struct planb *); +static void overlay_start(struct planb *); +static void overlay_stop(struct planb *); +static inline void tab_cmd_dbdma(volatile struct dbdma_cmd *, unsigned short, + unsigned int); +static inline void tab_cmd_store(volatile struct dbdma_cmd *, unsigned int, + unsigned int); +static inline void tab_cmd_gen(volatile struct dbdma_cmd *, unsigned short, + unsigned short, unsigned int, unsigned int); +static int init_planb(struct planb *); +static int find_planb(void); +static void planb_pre_capture(int, int, struct planb *); +static volatile struct dbdma_cmd *cmd_geo_setup(volatile struct dbdma_cmd *, + int, int, int, int, int, struct planb *); +static inline void planb_dbdma_stop(volatile struct dbdma_regs *); +static unsigned int saa_geo_setup(int, int, int, int, struct planb *); +static inline int overlay_is_active(struct planb *); + +/*******************************/ +/* Memory management functions */ +/*******************************/ + +static int grabbuf_alloc(struct planb *pb) +{ + int i, npage; + + npage = MAX_GBUFFERS * ((PLANB_MAX_FBUF / PAGE_SIZE + 1) +#ifndef PLANB_GSCANLINE + + MAX_LNUM +#endif /* PLANB_GSCANLINE */ + ); + if ((pb->rawbuf = (unsigned char**) kmalloc (npage + * sizeof(unsigned long), GFP_KERNEL)) == 0) + return -ENOMEM; + for (i = 0; i < npage; i++) { + pb->rawbuf[i] = (unsigned char *)__get_free_pages(GFP_KERNEL + |GFP_DMA, 0); + if (!pb->rawbuf[i]) + break; + SetPageReserved(virt_to_page(pb->rawbuf[i])); + } + if (i-- < npage) { + printk(KERN_DEBUG "PlanB: init_grab: grab buffer not allocated\n"); + for (; i > 0; i--) { + ClearPageReserved(virt_to_page(pb->rawbuf[i])); + free_pages((unsigned long)pb->rawbuf[i], 0); + } + kfree(pb->rawbuf); + return -ENOBUFS; + } + pb->rawbuf_size = npage; + return 0; +} + +/*****************************/ +/* Hardware access functions */ +/*****************************/ + +static void saa_write_reg(unsigned char addr, unsigned char val) +{ + planb_regs->saa_addr = addr; eieio(); + planb_regs->saa_regval = val; eieio(); + return; +} + +/* return status byte 0 or 1: */ +static unsigned char saa_status(int byte, struct planb *pb) +{ + saa_regs[pb->win.norm][SAA7196_STDC] = + (saa_regs[pb->win.norm][SAA7196_STDC] & ~2) | ((byte & 1) << 1); + saa_write_reg (SAA7196_STDC, saa_regs[pb->win.norm][SAA7196_STDC]); + + /* Let's wait 30msec for this one */ + msleep_interruptible(30); + + return (unsigned char)in_8 (&planb_regs->saa_status); +} + +static void saa_set(unsigned char addr, unsigned char val, struct planb *pb) +{ + if(saa_regs[pb->win.norm][addr] != val) { + saa_regs[pb->win.norm][addr] = val; + saa_write_reg (addr, val); + } + return; +} + +static void saa_init_regs(struct planb *pb) +{ + int i; + + for (i = 0; i < SAA7196_NUMREGS; i++) + saa_write_reg (i, saa_regs[pb->win.norm][i]); +} + +static unsigned int saa_geo_setup(int width, int height, int interlace, int bpp, + struct planb *pb) +{ + int ht, norm = pb->win.norm; + + switch(bpp) { + case 2: + /* RGB555+a 1x16-bit + 16-bit transparent */ + saa_regs[norm][SAA7196_FMTS] &= ~0x3; + break; + case 1: + case 4: + /* RGB888 1x24-bit + 8-bit transparent */ + saa_regs[norm][SAA7196_FMTS] &= ~0x1; + saa_regs[norm][SAA7196_FMTS] |= 0x2; + break; + default: + return -EINVAL; + } + ht = (interlace ? height / 2 : height); + saa_regs[norm][SAA7196_OUTPIX] = (unsigned char) (width & 0x00ff); + saa_regs[norm][SAA7196_HFILT] = (saa_regs[norm][SAA7196_HFILT] & ~0x3) + | (width >> 8 & 0x3); + saa_regs[norm][SAA7196_OUTLINE] = (unsigned char) (ht & 0xff); + saa_regs[norm][SAA7196_VYP] = (saa_regs[norm][SAA7196_VYP] & ~0x3) + | (ht >> 8 & 0x3); + /* feed both fields if interlaced, or else feed only even fields */ + saa_regs[norm][SAA7196_FMTS] = (interlace) ? + (saa_regs[norm][SAA7196_FMTS] & ~0x60) + : (saa_regs[norm][SAA7196_FMTS] | 0x60); + /* transparent mode; extended format enabled */ + saa_regs[norm][SAA7196_DPATH] |= 0x3; + + return 0; +} + +/***************************/ +/* DBDMA support functions */ +/***************************/ + +static inline void planb_dbdma_restart(volatile struct dbdma_regs *ch) +{ + out_le32(&ch->control, PLANB_CLR(RUN)); + out_le32(&ch->control, PLANB_SET(RUN|WAKE) | PLANB_CLR(PAUSE)); +} + +static inline void planb_dbdma_stop(volatile struct dbdma_regs *ch) +{ + int i = 0; + + out_le32(&ch->control, PLANB_CLR(RUN) | PLANB_SET(FLUSH)); + while((in_le32(&ch->status) == (ACTIVE | FLUSH)) && (i < 999)) { + IDEBUG("PlanB: waiting for DMA to stop\n"); + i++; + } +} + +static inline void tab_cmd_dbdma(volatile struct dbdma_cmd *ch, + unsigned short command, unsigned int cmd_dep) +{ + st_le16(&ch->command, command); + st_le32(&ch->cmd_dep, cmd_dep); +} + +static inline void tab_cmd_store(volatile struct dbdma_cmd *ch, + unsigned int phy_addr, unsigned int cmd_dep) +{ + st_le16(&ch->command, STORE_WORD | KEY_SYSTEM); + st_le16(&ch->req_count, 4); + st_le32(&ch->phy_addr, phy_addr); + st_le32(&ch->cmd_dep, cmd_dep); +} + +static inline void tab_cmd_gen(volatile struct dbdma_cmd *ch, + unsigned short command, unsigned short req_count, + unsigned int phy_addr, unsigned int cmd_dep) +{ + st_le16(&ch->command, command); + st_le16(&ch->req_count, req_count); + st_le32(&ch->phy_addr, phy_addr); + st_le32(&ch->cmd_dep, cmd_dep); +} + +static volatile struct dbdma_cmd *cmd_geo_setup( + volatile struct dbdma_cmd *c1, int width, int height, int interlace, + int bpp, int clip, struct planb *pb) +{ + int norm = pb->win.norm; + + if((saa_geo_setup(width, height, interlace, bpp, pb)) != 0) + return (volatile struct dbdma_cmd *)NULL; + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), + SAA7196_FMTS); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), + saa_regs[norm][SAA7196_FMTS]); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), + SAA7196_DPATH); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), + saa_regs[norm][SAA7196_DPATH]); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->even), + bpp | ((clip)? PLANB_CLIPMASK: 0)); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->odd), + bpp | ((clip)? PLANB_CLIPMASK: 0)); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), + SAA7196_OUTPIX); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), + saa_regs[norm][SAA7196_OUTPIX]); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), + SAA7196_HFILT); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), + saa_regs[norm][SAA7196_HFILT]); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), + SAA7196_OUTLINE); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), + saa_regs[norm][SAA7196_OUTLINE]); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), + SAA7196_VYP); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), + saa_regs[norm][SAA7196_VYP]); + return c1; +} + +/******************************/ +/* misc. supporting functions */ +/******************************/ + +static inline void planb_lock(struct planb *pb) +{ + mutex_lock(&pb->lock); +} + +static inline void planb_unlock(struct planb *pb) +{ + mutex_unlock(&pb->lock); +} + +/***************/ +/* Driver Core */ +/***************/ + +static int planb_prepare_open(struct planb *pb) +{ + int i, size; + + /* allocate memory for two plus alpha command buffers (size: max lines, + plus 40 commands handling, plus 1 alignment), plus dummy command buf, + plus clipmask buffer, plus frame grabbing status */ + size = (pb->tab_size*(2+MAX_GBUFFERS*TAB_FACTOR)+1+MAX_GBUFFERS + * PLANB_DUMMY)*sizeof(struct dbdma_cmd) + +(PLANB_MAXLINES*((PLANB_MAXPIXELS+7)& ~7))/8 + +MAX_GBUFFERS*sizeof(unsigned int); + if ((pb->priv_space = kmalloc (size, GFP_KERNEL)) == 0) + return -ENOMEM; + memset ((void *) pb->priv_space, 0, size); + pb->overlay_last1 = pb->ch1_cmd = (volatile struct dbdma_cmd *) + DBDMA_ALIGN (pb->priv_space); + pb->overlay_last2 = pb->ch2_cmd = pb->ch1_cmd + pb->tab_size; + pb->ch1_cmd_phys = virt_to_bus(pb->ch1_cmd); + pb->cap_cmd[0] = pb->ch2_cmd + pb->tab_size; + pb->pre_cmd[0] = pb->cap_cmd[0] + pb->tab_size * TAB_FACTOR; + for (i = 1; i < MAX_GBUFFERS; i++) { + pb->cap_cmd[i] = pb->pre_cmd[i-1] + PLANB_DUMMY; + pb->pre_cmd[i] = pb->cap_cmd[i] + pb->tab_size * TAB_FACTOR; + } + pb->frame_stat=(volatile unsigned int *)(pb->pre_cmd[MAX_GBUFFERS-1] + + PLANB_DUMMY); + pb->mask = (unsigned char *)(pb->frame_stat+MAX_GBUFFERS); + + pb->rawbuf = NULL; + pb->rawbuf_size = 0; + pb->grabbing = 0; + for (i = 0; i < MAX_GBUFFERS; i++) { + pb->frame_stat[i] = GBUFFER_UNUSED; + pb->gwidth[i] = 0; + pb->gheight[i] = 0; + pb->gfmt[i] = 0; + pb->gnorm_switch[i] = 0; +#ifndef PLANB_GSCANLINE + pb->lsize[i] = 0; + pb->lnum[i] = 0; +#endif /* PLANB_GSCANLINE */ + } + pb->gcount = 0; + pb->suspend = 0; + pb->last_fr = -999; + pb->prev_last_fr = -999; + + /* Reset DMA controllers */ + planb_dbdma_stop(&pb->planb_base->ch2); + planb_dbdma_stop(&pb->planb_base->ch1); + + return 0; +} + +static void planb_prepare_close(struct planb *pb) +{ + int i; + + /* make sure the dma's are idle */ + planb_dbdma_stop(&pb->planb_base->ch2); + planb_dbdma_stop(&pb->planb_base->ch1); + /* free kernel memory of command buffers */ + if(pb->priv_space != 0) { + kfree (pb->priv_space); + pb->priv_space = 0; + pb->cmd_buff_inited = 0; + } + if(pb->rawbuf) { + for (i = 0; i < pb->rawbuf_size; i++) { + ClearPageReserved(virt_to_page(pb->rawbuf[i])); + free_pages((unsigned long)pb->rawbuf[i], 0); + } + kfree(pb->rawbuf); + } + pb->rawbuf = NULL; +} + +/*****************************/ +/* overlay support functions */ +/*****************************/ + +static inline int overlay_is_active(struct planb *pb) +{ + unsigned int size = pb->tab_size * sizeof(struct dbdma_cmd); + unsigned int caddr = (unsigned)in_le32(&pb->planb_base->ch1.cmdptr); + + return (in_le32(&pb->overlay_last1->cmd_dep) == pb->ch1_cmd_phys) + && (caddr < (pb->ch1_cmd_phys + size)) + && (caddr >= (unsigned)pb->ch1_cmd_phys); +} + +static void overlay_start(struct planb *pb) +{ + + DEBUG("PlanB: overlay_start()\n"); + + if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) { + + DEBUG("PlanB: presumably, grabbing is in progress...\n"); + + planb_dbdma_stop(&pb->planb_base->ch2); + out_le32 (&pb->planb_base->ch2.cmdptr, + virt_to_bus(pb->ch2_cmd)); + planb_dbdma_restart(&pb->planb_base->ch2); + st_le16 (&pb->ch1_cmd->command, DBDMA_NOP); + tab_cmd_dbdma(pb->last_cmd[pb->last_fr], + DBDMA_NOP | BR_ALWAYS, + virt_to_bus(pb->ch1_cmd)); + eieio(); + pb->prev_last_fr = pb->last_fr; + pb->last_fr = -2; + if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) { + IDEBUG("PlanB: became inactive " + "in the mean time... reactivating\n"); + planb_dbdma_stop(&pb->planb_base->ch1); + out_le32 (&pb->planb_base->ch1.cmdptr, + virt_to_bus(pb->ch1_cmd)); + planb_dbdma_restart(&pb->planb_base->ch1); + } + } else { + + DEBUG("PlanB: currently idle, so can do whatever\n"); + + planb_dbdma_stop(&pb->planb_base->ch2); + planb_dbdma_stop(&pb->planb_base->ch1); + st_le32 (&pb->planb_base->ch2.cmdptr, + virt_to_bus(pb->ch2_cmd)); + st_le32 (&pb->planb_base->ch1.cmdptr, + virt_to_bus(pb->ch1_cmd)); + out_le16 (&pb->ch1_cmd->command, DBDMA_NOP); + planb_dbdma_restart(&pb->planb_base->ch2); + planb_dbdma_restart(&pb->planb_base->ch1); + pb->last_fr = -1; + } + return; +} + +static void overlay_stop(struct planb *pb) +{ + DEBUG("PlanB: overlay_stop()\n"); + + if(pb->last_fr == -1) { + + DEBUG("PlanB: no grabbing, it seems...\n"); + + planb_dbdma_stop(&pb->planb_base->ch2); + planb_dbdma_stop(&pb->planb_base->ch1); + pb->last_fr = -999; + } else if(pb->last_fr == -2) { + unsigned int cmd_dep; + tab_cmd_dbdma(pb->cap_cmd[pb->prev_last_fr], DBDMA_STOP, 0); + eieio(); + cmd_dep = (unsigned int)in_le32(&pb->overlay_last1->cmd_dep); + if(overlay_is_active(pb)) { + + DEBUG("PlanB: overlay is currently active\n"); + + planb_dbdma_stop(&pb->planb_base->ch2); + planb_dbdma_stop(&pb->planb_base->ch1); + if(cmd_dep != pb->ch1_cmd_phys) { + out_le32(&pb->planb_base->ch1.cmdptr, + virt_to_bus(pb->overlay_last1)); + planb_dbdma_restart(&pb->planb_base->ch1); + } + } + pb->last_fr = pb->prev_last_fr; + pb->prev_last_fr = -999; + } + return; +} + +static void suspend_overlay(struct planb *pb) +{ + int fr = -1; + struct dbdma_cmd last; + + DEBUG("PlanB: suspend_overlay: %d\n", pb->suspend); + + if(pb->suspend++) + return; + if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) { + if(pb->last_fr == -2) { + fr = pb->prev_last_fr; + memcpy(&last, (void*)pb->last_cmd[fr], sizeof(last)); + tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0); + } + if(overlay_is_active(pb)) { + planb_dbdma_stop(&pb->planb_base->ch2); + planb_dbdma_stop(&pb->planb_base->ch1); + pb->suspended.overlay = 1; + pb->suspended.frame = fr; + memcpy(&pb->suspended.cmd, &last, sizeof(last)); + return; + } + } + pb->suspended.overlay = 0; + pb->suspended.frame = fr; + memcpy(&pb->suspended.cmd, &last, sizeof(last)); + return; +} + +static void resume_overlay(struct planb *pb) +{ + + DEBUG("PlanB: resume_overlay: %d\n", pb->suspend); + + if(pb->suspend > 1) + return; + if(pb->suspended.frame != -1) { + memcpy((void*)pb->last_cmd[pb->suspended.frame], + &pb->suspended.cmd, sizeof(pb->suspended.cmd)); + } + if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) { + goto finish; + } + if(pb->suspended.overlay) { + + DEBUG("PlanB: overlay being resumed\n"); + + st_le16 (&pb->ch1_cmd->command, DBDMA_NOP); + st_le16 (&pb->ch2_cmd->command, DBDMA_NOP); + /* Set command buffer addresses */ + st_le32(&pb->planb_base->ch1.cmdptr, + virt_to_bus(pb->overlay_last1)); + out_le32(&pb->planb_base->ch2.cmdptr, + virt_to_bus(pb->overlay_last2)); + /* Start the DMA controller */ + out_le32 (&pb->planb_base->ch2.control, + PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE)); + out_le32 (&pb->planb_base->ch1.control, + PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE)); + } else if(pb->suspended.frame != -1) { + out_le32(&pb->planb_base->ch1.cmdptr, + virt_to_bus(pb->last_cmd[pb->suspended.frame])); + out_le32 (&pb->planb_base->ch1.control, + PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE)); + } + +finish: + pb->suspend--; + wake_up_interruptible(&pb->suspendq); +} + +static void add_clip(struct planb *pb, struct video_clip *clip) +{ + volatile unsigned char *base; + int xc = clip->x, yc = clip->y; + int wc = clip->width, hc = clip->height; + int ww = pb->win.width, hw = pb->win.height; + int x, y, xtmp1, xtmp2; + + DEBUG("PlanB: clip %dx%d+%d+%d\n", wc, hc, xc, yc); + + if(xc < 0) { + wc += xc; + xc = 0; + } + if(yc < 0) { + hc += yc; + yc = 0; + } + if(xc + wc > ww) + wc = ww - xc; + if(wc <= 0) /* Nothing to do */ + return; + if(yc + hc > hw) + hc = hw - yc; + + for (y = yc; y < yc+hc; y++) { + xtmp1=xc>>3; + xtmp2=(xc+wc)>>3; + base = pb->mask + y*96; + if(xc != 0 || wc >= 8) + *(base + xtmp1) &= (unsigned char)(0x00ff & + (0xff00 >> (xc&7))); + for (x = xtmp1 + 1; x < xtmp2; x++) { + *(base + x) = 0; + } + if(xc < (ww & ~0x7)) + *(base + xtmp2) &= (unsigned char)(0x00ff >> + ((xc+wc) & 7)); + } + + return; +} + +static void fill_cmd_buff(struct planb *pb) +{ + int restore = 0; + volatile struct dbdma_cmd last; + + DEBUG("PlanB: fill_cmd_buff()\n"); + + if(pb->overlay_last1 != pb->ch1_cmd) { + restore = 1; + last = *(pb->overlay_last1); + } + memset ((void *) pb->ch1_cmd, 0, 2 * pb->tab_size + * sizeof(struct dbdma_cmd)); + cmd_buff (pb); + if(restore) + *(pb->overlay_last1) = last; + if(pb->suspended.overlay) { + unsigned long jump_addr = in_le32(&pb->overlay_last1->cmd_dep); + if(jump_addr != pb->ch1_cmd_phys) { + int i; + + DEBUG("PlanB: adjusting ch1's jump address\n"); + + for(i = 0; i < MAX_GBUFFERS; i++) { + if(pb->need_pre_capture[i]) { + if(jump_addr == virt_to_bus(pb->pre_cmd[i])) + goto found; + } else { + if(jump_addr == virt_to_bus(pb->cap_cmd[i])) + goto found; + } + } + + DEBUG("PlanB: not found...\n"); + + goto out; +found: + if(pb->need_pre_capture[i]) + out_le32(&pb->pre_cmd[i]->phy_addr, + virt_to_bus(pb->overlay_last1)); + else + out_le32(&pb->cap_cmd[i]->phy_addr, + virt_to_bus(pb->overlay_last1)); + } + } +out: + pb->cmd_buff_inited = 1; + + return; +} + +static void cmd_buff(struct planb *pb) +{ + int i, bpp, count, nlines, stepsize, interlace; + unsigned long base, jump, addr_com, addr_dep; + volatile struct dbdma_cmd *c1 = pb->ch1_cmd; + volatile struct dbdma_cmd *c2 = pb->ch2_cmd; + + interlace = pb->win.interlace; + bpp = pb->win.bpp; + count = (bpp * ((pb->win.x + pb->win.width > pb->win.swidth) ? + (pb->win.swidth - pb->win.x) : pb->win.width)); + nlines = ((pb->win.y + pb->win.height > pb->win.sheight) ? + (pb->win.sheight - pb->win.y) : pb->win.height); + + /* Do video in: */ + + /* Preamble commands: */ + addr_com = virt_to_bus(c1); + addr_dep = virt_to_bus(&c1->cmd_dep); + tab_cmd_dbdma(c1++, DBDMA_NOP, 0); + jump = virt_to_bus(c1+16); /* 14 by cmd_geo_setup() and 2 for padding */ + if((c1 = cmd_geo_setup(c1, pb->win.width, pb->win.height, interlace, + bpp, 1, pb)) == NULL) { + printk(KERN_WARNING "PlanB: encountered serious problems\n"); + tab_cmd_dbdma(pb->ch1_cmd + 1, DBDMA_STOP, 0); + tab_cmd_dbdma(pb->ch2_cmd + 1, DBDMA_STOP, 0); + return; + } + tab_cmd_store(c1++, addr_com, (unsigned)(DBDMA_NOP | BR_ALWAYS) << 16); + tab_cmd_store(c1++, addr_dep, jump); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel), + PLANB_SET(FIELD_SYNC)); + /* (1) wait for field sync to be set */ + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(ODD_FIELD)); + /* wait for field sync to be cleared */ + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0); + /* if not odd field, wait until field sync is set again */ + tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++; + /* assert ch_sync to ch2 */ + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch2.control), + PLANB_SET(CH_SYNC)); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(DMA_ABORT)); + + base = (pb->frame_buffer_phys + pb->offset + pb->win.y * (pb->win.bpl + + pb->win.pad) + pb->win.x * bpp); + + if (interlace) { + stepsize = 2; + jump = virt_to_bus(c1 + (nlines + 1) / 2); + } else { + stepsize = 1; + jump = virt_to_bus(c1 + nlines); + } + + /* even field data: */ + for (i=0; i < nlines; i += stepsize, c1++) + tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, + count, base + i * (pb->win.bpl + pb->win.pad), jump); + + /* For non-interlaced, we use even fields only */ + if (!interlace) + goto cmd_tab_data_end; + + /* Resync to odd field */ + /* (2) wait for field sync to be set */ + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(ODD_FIELD)); + /* wait for field sync to be cleared */ + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0); + /* if not odd field, wait until field sync is set again */ + tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++; + /* assert ch_sync to ch2 */ + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch2.control), + PLANB_SET(CH_SYNC)); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(DMA_ABORT)); + + /* odd field data: */ + jump = virt_to_bus(c1 + nlines / 2); + for (i=1; i < nlines; i += stepsize, c1++) + tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count, + base + i * (pb->win.bpl + pb->win.pad), jump); + + /* And jump back to the start */ +cmd_tab_data_end: + pb->overlay_last1 = c1; /* keep a pointer to the last command */ + tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->ch1_cmd)); + + /* Clipmask command buffer */ + + /* Preamble commands: */ + tab_cmd_dbdma(c2++, DBDMA_NOP, 0); + tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.wait_sel), + PLANB_SET(CH_SYNC)); + /* wait until ch1 asserts ch_sync */ + tab_cmd_dbdma(c2++, DBDMA_NOP | WAIT_IFCLR, 0); + /* clear ch_sync asserted by ch1 */ + tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.control), + PLANB_CLR(CH_SYNC)); + tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.wait_sel), + PLANB_SET(FIELD_SYNC)); + tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel), + PLANB_SET(ODD_FIELD)); + + /* jump to end of even field if appropriate */ + /* this points to (interlace)? pos. C: pos. B */ + jump = (interlace) ? virt_to_bus(c2 + (nlines + 1) / 2 + 2): + virt_to_bus(c2 + nlines + 2); + /* if odd field, skip over to odd field clipmasking */ + tab_cmd_dbdma(c2++, DBDMA_NOP | BR_IFSET, jump); + + /* even field mask: */ + tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel), + PLANB_SET(DMA_ABORT)); + /* this points to pos. B */ + jump = (interlace) ? virt_to_bus(c2 + nlines + 1): + virt_to_bus(c2 + nlines); + base = virt_to_bus(pb->mask); + for (i=0; i < nlines; i += stepsize, c2++) + tab_cmd_gen(c2, OUTPUT_MORE | KEY_STREAM0 | BR_IFSET, 96, + base + i * 96, jump); + + /* For non-interlaced, we use only even fields */ + if(!interlace) + goto cmd_tab_mask_end; + + /* odd field mask: */ +/* C */ tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel), + PLANB_SET(DMA_ABORT)); + /* this points to pos. B */ + jump = virt_to_bus(c2 + nlines / 2); + base = virt_to_bus(pb->mask); + for (i=1; i < nlines; i += 2, c2++) /* abort if set */ + tab_cmd_gen(c2, OUTPUT_MORE | KEY_STREAM0 | BR_IFSET, 96, + base + i * 96, jump); + + /* Inform channel 1 and jump back to start */ +cmd_tab_mask_end: + /* ok, I just realized this is kind of flawed. */ + /* this part is reached only after odd field clipmasking. */ + /* wanna clean up? */ + /* wait for field sync to be set */ + /* corresponds to fsync (1) of ch1 */ +/* B */ tab_cmd_dbdma(c2++, DBDMA_NOP | WAIT_IFCLR, 0); + /* restart ch1, meant to clear any dead bit or something */ + tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch1.control), + PLANB_CLR(RUN)); + tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch1.control), + PLANB_SET(RUN)); + pb->overlay_last2 = c2; /* keep a pointer to the last command */ + /* start over even field clipmasking */ + tab_cmd_dbdma(c2, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->ch2_cmd)); + + eieio(); + return; +} + +/*********************************/ +/* grabdisplay support functions */ +/*********************************/ + +static int palette2fmt[] = { + 0, + PLANB_GRAY, + 0, + 0, + 0, + PLANB_COLOUR32, + PLANB_COLOUR15, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; + +#define PLANB_PALETTE_MAX 15 + +static int vgrab(struct planb *pb, struct video_mmap *mp) +{ + unsigned int fr = mp->frame; + unsigned int format; + + if(pb->rawbuf==NULL) { + int err; + if((err=grabbuf_alloc(pb))) + return err; + } + + IDEBUG("PlanB: grab %d: %dx%d(%u)\n", pb->grabbing, + mp->width, mp->height, fr); + + if(pb->grabbing >= MAX_GBUFFERS) + return -ENOBUFS; + if(fr > (MAX_GBUFFERS - 1) || fr < 0) + return -EINVAL; + if(mp->height <= 0 || mp->width <= 0) + return -EINVAL; + if(mp->format < 0 || mp->format >= PLANB_PALETTE_MAX) + return -EINVAL; + if((format = palette2fmt[mp->format]) == 0) + return -EINVAL; + if (mp->height * mp->width * format > PLANB_MAX_FBUF) /* format = bpp */ + return -EINVAL; + + planb_lock(pb); + if(mp->width != pb->gwidth[fr] || mp->height != pb->gheight[fr] || + format != pb->gfmt[fr] || (pb->gnorm_switch[fr])) { + int i; +#ifndef PLANB_GSCANLINE + unsigned int osize = pb->gwidth[fr] * pb->gheight[fr] + * pb->gfmt[fr]; + unsigned int nsize = mp->width * mp->height * format; +#endif + + IDEBUG("PlanB: gwidth = %d, gheight = %d, mp->format = %u\n", + mp->width, mp->height, mp->format); + +#ifndef PLANB_GSCANLINE + if(pb->gnorm_switch[fr]) + nsize = 0; + if (nsize < osize) { + for(i = pb->gbuf_idx[fr]; osize > 0; i++) { + memset((void *)pb->rawbuf[i], 0, PAGE_SIZE); + osize -= PAGE_SIZE; + } + } + for(i = pb->l_fr_addr_idx[fr]; i < pb->l_fr_addr_idx[fr] + + pb->lnum[fr]; i++) + memset((void *)pb->rawbuf[i], 0, PAGE_SIZE); +#else +/* XXX TODO */ +/* + if(pb->gnorm_switch[fr]) + memset((void *)pb->gbuffer[fr], 0, + pb->gbytes_per_line * pb->gheight[fr]); + else { + if(mp-> + for(i = 0; i < pb->gheight[fr]; i++) { + memset((void *)(pb->gbuffer[fr] + + pb->gbytes_per_line * i + } + } +*/ +#endif + pb->gwidth[fr] = mp->width; + pb->gheight[fr] = mp->height; + pb->gfmt[fr] = format; + pb->last_cmd[fr] = setup_grab_cmd(fr, pb); + planb_pre_capture(fr, pb->gfmt[fr], pb); /* gfmt = bpp */ + pb->need_pre_capture[fr] = 1; + pb->gnorm_switch[fr] = 0; + } else + pb->need_pre_capture[fr] = 0; + pb->frame_stat[fr] = GBUFFER_GRABBING; + if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) { + + IDEBUG("PlanB: ch1 inactive, initiating grabbing\n"); + + planb_dbdma_stop(&pb->planb_base->ch1); + if(pb->need_pre_capture[fr]) { + + IDEBUG("PlanB: padding pre-capture sequence\n"); + + out_le32 (&pb->planb_base->ch1.cmdptr, + virt_to_bus(pb->pre_cmd[fr])); + } else { + tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0); + tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0); + /* let's be on the safe side. here is not timing critical. */ + tab_cmd_dbdma((pb->cap_cmd[fr] + 1), DBDMA_NOP, 0); + out_le32 (&pb->planb_base->ch1.cmdptr, + virt_to_bus(pb->cap_cmd[fr])); + } + planb_dbdma_restart(&pb->planb_base->ch1); + pb->last_fr = fr; + } else { + int i; + + IDEBUG("PlanB: ch1 active, grabbing being queued\n"); + + if((pb->last_fr == -1) || ((pb->last_fr == -2) && + overlay_is_active(pb))) { + + IDEBUG("PlanB: overlay is active, grabbing defered\n"); + + tab_cmd_dbdma(pb->last_cmd[fr], + DBDMA_NOP | BR_ALWAYS, + virt_to_bus(pb->ch1_cmd)); + if(pb->need_pre_capture[fr]) { + + IDEBUG("PlanB: padding pre-capture sequence\n"); + + tab_cmd_store(pb->pre_cmd[fr], + virt_to_bus(&pb->overlay_last1->cmd_dep), + virt_to_bus(pb->ch1_cmd)); + eieio(); + out_le32 (&pb->overlay_last1->cmd_dep, + virt_to_bus(pb->pre_cmd[fr])); + } else { + tab_cmd_store(pb->cap_cmd[fr], + virt_to_bus(&pb->overlay_last1->cmd_dep), + virt_to_bus(pb->ch1_cmd)); + tab_cmd_dbdma((pb->cap_cmd[fr] + 1), + DBDMA_NOP, 0); + eieio(); + out_le32 (&pb->overlay_last1->cmd_dep, + virt_to_bus(pb->cap_cmd[fr])); + } + for(i = 0; overlay_is_active(pb) && i < 999; i++) + IDEBUG("PlanB: waiting for overlay done\n"); + tab_cmd_dbdma(pb->ch1_cmd, DBDMA_NOP, 0); + pb->prev_last_fr = fr; + pb->last_fr = -2; + } else if(pb->last_fr == -2) { + + IDEBUG("PlanB: mixed mode detected, grabbing" + " will be done before activating overlay\n"); + + tab_cmd_dbdma(pb->ch1_cmd, DBDMA_NOP, 0); + if(pb->need_pre_capture[fr]) { + + IDEBUG("PlanB: padding pre-capture sequence\n"); + + tab_cmd_dbdma(pb->last_cmd[pb->prev_last_fr], + DBDMA_NOP | BR_ALWAYS, + virt_to_bus(pb->pre_cmd[fr])); + eieio(); + } else { + tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0); + if(pb->gwidth[pb->prev_last_fr] != + pb->gwidth[fr] + || pb->gheight[pb->prev_last_fr] != + pb->gheight[fr] + || pb->gfmt[pb->prev_last_fr] != + pb->gfmt[fr]) + tab_cmd_dbdma((pb->cap_cmd[fr] + 1), + DBDMA_NOP, 0); + else + tab_cmd_dbdma((pb->cap_cmd[fr] + 1), + DBDMA_NOP | BR_ALWAYS, + virt_to_bus(pb->cap_cmd[fr] + 16)); + tab_cmd_dbdma(pb->last_cmd[pb->prev_last_fr], + DBDMA_NOP | BR_ALWAYS, + virt_to_bus(pb->cap_cmd[fr])); + eieio(); + } + tab_cmd_dbdma(pb->last_cmd[fr], + DBDMA_NOP | BR_ALWAYS, + virt_to_bus(pb->ch1_cmd)); + eieio(); + pb->prev_last_fr = fr; + pb->last_fr = -2; + } else { + + IDEBUG("PlanB: active grabbing session detected\n"); + + if(pb->need_pre_capture[fr]) { + + IDEBUG("PlanB: padding pre-capture sequence\n"); + + tab_cmd_dbdma(pb->last_cmd[pb->last_fr], + DBDMA_NOP | BR_ALWAYS, + virt_to_bus(pb->pre_cmd[fr])); + eieio(); + } else { + tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0); + tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0); + if(pb->gwidth[pb->last_fr] != pb->gwidth[fr] + || pb->gheight[pb->last_fr] != + pb->gheight[fr] + || pb->gfmt[pb->last_fr] != + pb->gfmt[fr]) + tab_cmd_dbdma((pb->cap_cmd[fr] + 1), + DBDMA_NOP, 0); + else + tab_cmd_dbdma((pb->cap_cmd[fr] + 1), + DBDMA_NOP | BR_ALWAYS, + virt_to_bus(pb->cap_cmd[fr] + 16)); + tab_cmd_dbdma(pb->last_cmd[pb->last_fr], + DBDMA_NOP | BR_ALWAYS, + virt_to_bus(pb->cap_cmd[fr])); + eieio(); + } + pb->last_fr = fr; + } + if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) { + + IDEBUG("PlanB: became inactive in the mean time..." + "reactivating\n"); + + planb_dbdma_stop(&pb->planb_base->ch1); + out_le32 (&pb->planb_base->ch1.cmdptr, + virt_to_bus(pb->cap_cmd[fr])); + planb_dbdma_restart(&pb->planb_base->ch1); + } + } + pb->grabbing++; + planb_unlock(pb); + + return 0; +} + +static void planb_pre_capture(int fr, int bpp, struct planb *pb) +{ + volatile struct dbdma_cmd *c1 = pb->pre_cmd[fr]; + int interlace = (pb->gheight[fr] > pb->maxlines/2)? 1: 0; + + tab_cmd_dbdma(c1++, DBDMA_NOP, 0); + if((c1 = cmd_geo_setup(c1, pb->gwidth[fr], pb->gheight[fr], interlace, + bpp, 0, pb)) == NULL) { + printk(KERN_WARNING "PlanB: encountered some problems\n"); + tab_cmd_dbdma(pb->pre_cmd[fr] + 1, DBDMA_STOP, 0); + return; + } + /* Sync to even field */ + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel), + PLANB_SET(FIELD_SYNC)); + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(ODD_FIELD)); + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0); + tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++; + tab_cmd_dbdma(c1++, DBDMA_NOP | INTR_ALWAYS, 0); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(DMA_ABORT)); + /* For non-interlaced, we use even fields only */ + if (pb->gheight[fr] <= pb->maxlines/2) + goto cmd_tab_data_end; + /* Sync to odd field */ + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(ODD_FIELD)); + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0); + tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++; + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(DMA_ABORT)); +cmd_tab_data_end: + tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->cap_cmd[fr])); + + eieio(); +} + +static volatile struct dbdma_cmd *setup_grab_cmd(int fr, struct planb *pb) +{ + int i, bpp, count, nlines, stepsize, interlace; +#ifdef PLANB_GSCANLINE + int scanline; +#else + int nlpp, leftover1; + unsigned long base; +#endif + unsigned long jump; + int pagei; + volatile struct dbdma_cmd *c1; + volatile struct dbdma_cmd *jump_addr; + + c1 = pb->cap_cmd[fr]; + interlace = (pb->gheight[fr] > pb->maxlines/2)? 1: 0; + bpp = pb->gfmt[fr]; /* gfmt = bpp */ + count = bpp * pb->gwidth[fr]; + nlines = pb->gheight[fr]; +#ifdef PLANB_GSCANLINE + scanline = pb->gbytes_per_line; +#else + pb->lsize[fr] = count; + pb->lnum[fr] = 0; +#endif + + /* Do video in: */ + + /* Preamble commands: */ + tab_cmd_dbdma(c1++, DBDMA_NOP, 0); + tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(c1 + 16)); c1++; + if((c1 = cmd_geo_setup(c1, pb->gwidth[fr], pb->gheight[fr], interlace, + bpp, 0, pb)) == NULL) { + printk(KERN_WARNING "PlanB: encountered serious problems\n"); + tab_cmd_dbdma(pb->cap_cmd[fr] + 1, DBDMA_STOP, 0); + return (pb->cap_cmd[fr] + 2); + } + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel), + PLANB_SET(FIELD_SYNC)); + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(ODD_FIELD)); + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0); + tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++; + tab_cmd_dbdma(c1++, DBDMA_NOP | INTR_ALWAYS, 0); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(DMA_ABORT)); + + if (interlace) { + stepsize = 2; + jump_addr = c1 + TAB_FACTOR * (nlines + 1) / 2; + } else { + stepsize = 1; + jump_addr = c1 + TAB_FACTOR * nlines; + } + jump = virt_to_bus(jump_addr); + + /* even field data: */ + + pagei = pb->gbuf_idx[fr]; +#ifdef PLANB_GSCANLINE + for (i = 0; i < nlines; i += stepsize) { + tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, + virt_to_bus(pb->rawbuf[pagei + + i * scanline / PAGE_SIZE]), jump); + } +#else + i = 0; + leftover1 = 0; + do { + int j; + + base = virt_to_bus(pb->rawbuf[pagei]); + nlpp = (PAGE_SIZE - leftover1) / count / stepsize; + for(j = 0; j < nlpp && i < nlines; j++, i += stepsize, c1++) + tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, + count, base + count * j * stepsize + leftover1, jump); + if(i < nlines) { + int lov0 = PAGE_SIZE - count * nlpp * stepsize - leftover1; + + if(lov0 == 0) + leftover1 = 0; + else { + if(lov0 >= count) { + tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, base + + count * nlpp * stepsize + leftover1, jump); + } else { + pb->l_to_addr[fr][pb->lnum[fr]] = pb->rawbuf[pagei] + + count * nlpp * stepsize + leftover1; + pb->l_to_next_idx[fr][pb->lnum[fr]] = pagei + 1; + pb->l_to_next_size[fr][pb->lnum[fr]] = count - lov0; + tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, + virt_to_bus(pb->rawbuf[pb->l_fr_addr_idx[fr] + + pb->lnum[fr]]), jump); + if(++pb->lnum[fr] > MAX_LNUM) + pb->lnum[fr]--; + } + leftover1 = count * stepsize - lov0; + i += stepsize; + } + } + pagei++; + } while(i < nlines); + tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, jump); + c1 = jump_addr; +#endif /* PLANB_GSCANLINE */ + + /* For non-interlaced, we use even fields only */ + if (!interlace) + goto cmd_tab_data_end; + + /* Sync to odd field */ + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(ODD_FIELD)); + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0); + tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++; + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(DMA_ABORT)); + + /* odd field data: */ + jump_addr = c1 + TAB_FACTOR * nlines / 2; + jump = virt_to_bus(jump_addr); +#ifdef PLANB_GSCANLINE + for (i = 1; i < nlines; i += stepsize) { + tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, + virt_to_bus(pb->rawbuf[pagei + + i * scanline / PAGE_SIZE]), jump); + } +#else + i = 1; + leftover1 = 0; + pagei = pb->gbuf_idx[fr]; + if(nlines <= 1) + goto skip; + do { + int j; + + base = virt_to_bus(pb->rawbuf[pagei]); + nlpp = (PAGE_SIZE - leftover1) / count / stepsize; + if(leftover1 >= count) { + tab_cmd_gen(c1++, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count, + base + leftover1 - count, jump); + i += stepsize; + } + for(j = 0; j < nlpp && i < nlines; j++, i += stepsize, c1++) + tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count, + base + count * (j * stepsize + 1) + leftover1, jump); + if(i < nlines) { + int lov0 = PAGE_SIZE - count * nlpp * stepsize - leftover1; + + if(lov0 == 0) + leftover1 = 0; + else { + if(lov0 > count) { + pb->l_to_addr[fr][pb->lnum[fr]] = pb->rawbuf[pagei] + + count * (nlpp * stepsize + 1) + leftover1; + pb->l_to_next_idx[fr][pb->lnum[fr]] = pagei + 1; + pb->l_to_next_size[fr][pb->lnum[fr]] = count * stepsize + - lov0; + tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, + virt_to_bus(pb->rawbuf[pb->l_fr_addr_idx[fr] + + pb->lnum[fr]]), jump); + if(++pb->lnum[fr] > MAX_LNUM) + pb->lnum[fr]--; + i += stepsize; + } + leftover1 = count * stepsize - lov0; + } + } + pagei++; + } while(i < nlines); +skip: + tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, jump); + c1 = jump_addr; +#endif /* PLANB_GSCANLINE */ + +cmd_tab_data_end: + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->intr_stat), + (fr << 9) | PLANB_FRM_IRQ | PLANB_GEN_IRQ); + /* stop it */ + tab_cmd_dbdma(c1, DBDMA_STOP, 0); + + eieio(); + return c1; +} + +static void planb_irq(int irq, void *dev_id, struct pt_regs * regs) +{ + unsigned int stat, astat; + struct planb *pb = (struct planb *)dev_id; + + IDEBUG("PlanB: planb_irq()\n"); + + /* get/clear interrupt status bits */ + eieio(); + stat = in_le32(&pb->planb_base->intr_stat); + astat = stat & pb->intr_mask; + out_le32(&pb->planb_base->intr_stat, PLANB_FRM_IRQ + & ~astat & stat & ~PLANB_GEN_IRQ); + IDEBUG("PlanB: stat = %X, astat = %X\n", stat, astat); + + if(astat & PLANB_FRM_IRQ) { + unsigned int fr = stat >> 9; +#ifndef PLANB_GSCANLINE + int i; +#endif + IDEBUG("PlanB: PLANB_FRM_IRQ\n"); + + pb->gcount++; + + IDEBUG("PlanB: grab %d: fr = %d, gcount = %d\n", + pb->grabbing, fr, pb->gcount); +#ifndef PLANB_GSCANLINE + IDEBUG("PlanB: %d * %d bytes are being copied over\n", + pb->lnum[fr], pb->lsize[fr]); + for(i = 0; i < pb->lnum[fr]; i++) { + int first = pb->lsize[fr] - pb->l_to_next_size[fr][i]; + + memcpy(pb->l_to_addr[fr][i], + pb->rawbuf[pb->l_fr_addr_idx[fr] + i], + first); + memcpy(pb->rawbuf[pb->l_to_next_idx[fr][i]], + pb->rawbuf[pb->l_fr_addr_idx[fr] + i] + first, + pb->l_to_next_size[fr][i]); + } +#endif + pb->frame_stat[fr] = GBUFFER_DONE; + pb->grabbing--; + wake_up_interruptible(&pb->capq); + return; + } + /* incorrect interrupts? */ + pb->intr_mask = PLANB_CLR_IRQ; + out_le32(&pb->planb_base->intr_stat, PLANB_CLR_IRQ); + printk(KERN_ERR "PlanB: IRQ lockup, cleared intrrupts" + " unconditionally\n"); +} + +/******************************* + * Device Operations functions * + *******************************/ + +static int planb_open(struct video_device *dev, int mode) +{ + struct planb *pb = (struct planb *)dev; + + if (pb->user == 0) { + int err; + if((err = planb_prepare_open(pb)) != 0) + return err; + } + pb->user++; + + DEBUG("PlanB: device opened\n"); + return 0; +} + +static void planb_close(struct video_device *dev) +{ + struct planb *pb = (struct planb *)dev; + + if(pb->user < 1) /* ??? */ + return; + planb_lock(pb); + if (pb->user == 1) { + if (pb->overlay) { + planb_dbdma_stop(&pb->planb_base->ch2); + planb_dbdma_stop(&pb->planb_base->ch1); + pb->overlay = 0; + } + planb_prepare_close(pb); + } + pb->user--; + planb_unlock(pb); + + DEBUG("PlanB: device closed\n"); +} + +static long planb_read(struct video_device *v, char *buf, unsigned long count, + int nonblock) +{ + DEBUG("planb: read request\n"); + return -EINVAL; +} + +static long planb_write(struct video_device *v, const char *buf, + unsigned long count, int nonblock) +{ + DEBUG("planb: write request\n"); + return -EINVAL; +} + +static int planb_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct planb *pb=(struct planb *)dev; + + switch (cmd) + { + case VIDIOCGCAP: + { + struct video_capability b; + + DEBUG("PlanB: IOCTL VIDIOCGCAP\n"); + + strcpy (b.name, pb->video_dev.name); + b.type = VID_TYPE_OVERLAY | VID_TYPE_CLIPPING | + VID_TYPE_FRAMERAM | VID_TYPE_SCALES | + VID_TYPE_CAPTURE; + b.channels = 2; /* composite & svhs */ + b.audios = 0; + b.maxwidth = PLANB_MAXPIXELS; + b.maxheight = PLANB_MAXLINES; + b.minwidth = 32; /* wild guess */ + b.minheight = 32; + if (copy_to_user(arg,&b,sizeof(b))) + return -EFAULT; + return 0; + } + case VIDIOCSFBUF: + { + struct video_buffer v; + unsigned short bpp; + unsigned int fmt; + + DEBUG("PlanB: IOCTL VIDIOCSFBUF\n"); + + if (!capable(CAP_SYS_ADMIN) + || !capable(CAP_SYS_RAWIO)) + return -EPERM; + if (copy_from_user(&v, arg,sizeof(v))) + return -EFAULT; + planb_lock(pb); + switch(v.depth) { + case 8: + bpp = 1; + fmt = PLANB_GRAY; + break; + case 15: + case 16: + bpp = 2; + fmt = PLANB_COLOUR15; + break; + case 24: + case 32: + bpp = 4; + fmt = PLANB_COLOUR32; + break; + default: + planb_unlock(pb); + return -EINVAL; + } + if (bpp * v.width > v.bytesperline) { + planb_unlock(pb); + return -EINVAL; + } + pb->win.bpp = bpp; + pb->win.color_fmt = fmt; + pb->frame_buffer_phys = (unsigned long) v.base; + pb->win.sheight = v.height; + pb->win.swidth = v.width; + pb->picture.depth = pb->win.depth = v.depth; + pb->win.bpl = pb->win.bpp * pb->win.swidth; + pb->win.pad = v.bytesperline - pb->win.bpl; + + DEBUG("PlanB: Display at %p is %d by %d, bytedepth %d," + " bpl %d (+ %d)\n", v.base, v.width,v.height, + pb->win.bpp, pb->win.bpl, pb->win.pad); + + pb->cmd_buff_inited = 0; + if(pb->overlay) { + suspend_overlay(pb); + fill_cmd_buff(pb); + resume_overlay(pb); + } + planb_unlock(pb); + return 0; + } + case VIDIOCGFBUF: + { + struct video_buffer v; + + DEBUG("PlanB: IOCTL VIDIOCGFBUF\n"); + + v.base = (void *)pb->frame_buffer_phys; + v.height = pb->win.sheight; + v.width = pb->win.swidth; + v.depth = pb->win.depth; + v.bytesperline = pb->win.bpl + pb->win.pad; + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCCAPTURE: + { + int i; + + if(copy_from_user(&i, arg, sizeof(i))) + return -EFAULT; + if(i==0) { + DEBUG("PlanB: IOCTL VIDIOCCAPTURE Stop\n"); + + if (!(pb->overlay)) + return 0; + planb_lock(pb); + pb->overlay = 0; + overlay_stop(pb); + planb_unlock(pb); + } else { + DEBUG("PlanB: IOCTL VIDIOCCAPTURE Start\n"); + + if (pb->frame_buffer_phys == 0 || + pb->win.width == 0 || + pb->win.height == 0) + return -EINVAL; + if (pb->overlay) + return 0; + planb_lock(pb); + pb->overlay = 1; + if(!(pb->cmd_buff_inited)) + fill_cmd_buff(pb); + overlay_start(pb); + planb_unlock(pb); + } + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel v; + + DEBUG("PlanB: IOCTL VIDIOCGCHAN\n"); + + if(copy_from_user(&v, arg,sizeof(v))) + return -EFAULT; + v.flags = 0; + v.tuners = 0; + v.type = VIDEO_TYPE_CAMERA; + v.norm = pb->win.norm; + switch(v.channel) + { + case 0: + strcpy(v.name,"Composite"); + break; + case 1: + strcpy(v.name,"SVHS"); + break; + default: + return -EINVAL; + break; + } + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + + return 0; + } + case VIDIOCSCHAN: + { + struct video_channel v; + + DEBUG("PlanB: IOCTL VIDIOCSCHAN\n"); + + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + + if (v.norm != pb->win.norm) { + int i, maxlines; + + switch (v.norm) + { + case VIDEO_MODE_PAL: + case VIDEO_MODE_SECAM: + maxlines = PLANB_MAXLINES; + break; + case VIDEO_MODE_NTSC: + maxlines = PLANB_NTSC_MAXLINES; + break; + default: + return -EINVAL; + break; + } + planb_lock(pb); + /* empty the grabbing queue */ + wait_event(pb->capq, !pb->grabbing); + pb->maxlines = maxlines; + pb->win.norm = v.norm; + /* Stop overlay if running */ + suspend_overlay(pb); + for(i = 0; i < MAX_GBUFFERS; i++) + pb->gnorm_switch[i] = 1; + /* I know it's an overkill, but.... */ + fill_cmd_buff(pb); + /* ok, now init it accordingly */ + saa_init_regs (pb); + /* restart overlay if it was running */ + resume_overlay(pb); + planb_unlock(pb); + } + + switch(v.channel) + { + case 0: /* Composite */ + saa_set (SAA7196_IOCC, + ((saa_regs[pb->win.norm][SAA7196_IOCC] & + ~7) | 3), pb); + break; + case 1: /* SVHS */ + saa_set (SAA7196_IOCC, + ((saa_regs[pb->win.norm][SAA7196_IOCC] & + ~7) | 4), pb); + break; + default: + return -EINVAL; + break; + } + + return 0; + } + case VIDIOCGPICT: + { + struct video_picture vp = pb->picture; + + DEBUG("PlanB: IOCTL VIDIOCGPICT\n"); + + switch(pb->win.color_fmt) { + case PLANB_GRAY: + vp.palette = VIDEO_PALETTE_GREY; + case PLANB_COLOUR15: + vp.palette = VIDEO_PALETTE_RGB555; + break; + case PLANB_COLOUR32: + vp.palette = VIDEO_PALETTE_RGB32; + break; + default: + vp.palette = 0; + break; + } + + if(copy_to_user(arg,&vp,sizeof(vp))) + return -EFAULT; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture vp; + + DEBUG("PlanB: IOCTL VIDIOCSPICT\n"); + + if(copy_from_user(&vp,arg,sizeof(vp))) + return -EFAULT; + pb->picture = vp; + /* Should we do sanity checks here? */ + saa_set (SAA7196_BRIG, (unsigned char) + ((pb->picture.brightness) >> 8), pb); + saa_set (SAA7196_HUEC, (unsigned char) + ((pb->picture.hue) >> 8) ^ 0x80, pb); + saa_set (SAA7196_CSAT, (unsigned char) + ((pb->picture.colour) >> 9), pb); + saa_set (SAA7196_CONT, (unsigned char) + ((pb->picture.contrast) >> 9), pb); + + return 0; + } + case VIDIOCSWIN: + { + struct video_window vw; + struct video_clip clip; + int i; + + DEBUG("PlanB: IOCTL VIDIOCSWIN\n"); + + if(copy_from_user(&vw,arg,sizeof(vw))) + return -EFAULT; + + planb_lock(pb); + /* Stop overlay if running */ + suspend_overlay(pb); + pb->win.interlace = (vw.height > pb->maxlines/2)? 1: 0; + if (pb->win.x != vw.x || + pb->win.y != vw.y || + pb->win.width != vw.width || + pb->win.height != vw.height || + !pb->cmd_buff_inited) { + pb->win.x = vw.x; + pb->win.y = vw.y; + pb->win.width = vw.width; + pb->win.height = vw.height; + fill_cmd_buff(pb); + } + /* Reset clip mask */ + memset ((void *) pb->mask, 0xff, (pb->maxlines + * ((PLANB_MAXPIXELS + 7) & ~7)) / 8); + /* Add any clip rects */ + for (i = 0; i < vw.clipcount; i++) { + if (copy_from_user(&clip, vw.clips + i, + sizeof(struct video_clip))) + return -EFAULT; + add_clip(pb, &clip); + } + /* restart overlay if it was running */ + resume_overlay(pb); + planb_unlock(pb); + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + + DEBUG("PlanB: IOCTL VIDIOCGWIN\n"); + + vw.x=pb->win.x; + vw.y=pb->win.y; + vw.width=pb->win.width; + vw.height=pb->win.height; + vw.chromakey=0; + vw.flags=0; + if(pb->win.interlace) + vw.flags|=VIDEO_WINDOW_INTERLACE; + if(copy_to_user(arg,&vw,sizeof(vw))) + return -EFAULT; + return 0; + } + case VIDIOCSYNC: { + int i; + + IDEBUG("PlanB: IOCTL VIDIOCSYNC\n"); + + if(copy_from_user((void *)&i,arg,sizeof(int))) + return -EFAULT; + + IDEBUG("PlanB: sync to frame %d\n", i); + + if(i > (MAX_GBUFFERS - 1) || i < 0) + return -EINVAL; +chk_grab: + switch (pb->frame_stat[i]) { + case GBUFFER_UNUSED: + return -EINVAL; + case GBUFFER_GRABBING: + IDEBUG("PlanB: waiting for grab" + " done (%d)\n", i); + interruptible_sleep_on(&pb->capq); + if(signal_pending(current)) + return -EINTR; + goto chk_grab; + case GBUFFER_DONE: + pb->frame_stat[i] = GBUFFER_UNUSED; + break; + } + return 0; + } + + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + volatile unsigned int status; + + IDEBUG("PlanB: IOCTL VIDIOCMCAPTURE\n"); + + if(copy_from_user((void *) &vm,(void *)arg,sizeof(vm))) + return -EFAULT; + status = pb->frame_stat[vm.frame]; + if (status != GBUFFER_UNUSED) + return -EBUSY; + + return vgrab(pb, &vm); + } + + case VIDIOCGMBUF: + { + int i; + struct video_mbuf vm; + + DEBUG("PlanB: IOCTL VIDIOCGMBUF\n"); + + memset(&vm, 0 , sizeof(vm)); + vm.size = PLANB_MAX_FBUF * MAX_GBUFFERS; + vm.frames = MAX_GBUFFERS; + for(i = 0; i= SAA7196_NUMREGS) + return -EINVAL; + preg.val = saa_regs[pb->win.norm][preg.addr]; + if(copy_to_user((void *)arg, (void *)&preg, + sizeof(preg))) + return -EFAULT; + return 0; + } + + case PLANBIOCSSAAREGS: + { + struct planb_saa_regs preg; + + DEBUG("PlanB: IOCTL PLANBIOCSSAAREGS\n"); + + if(copy_from_user(&preg, arg, sizeof(preg))) + return -EFAULT; + if(preg.addr >= SAA7196_NUMREGS) + return -EINVAL; + saa_set (preg.addr, preg.val, pb); + return 0; + } + + case PLANBIOCGSTAT: + { + struct planb_stat_regs pstat; + + DEBUG("PlanB: IOCTL PLANBIOCGSTAT\n"); + + pstat.ch1_stat = in_le32(&pb->planb_base->ch1.status); + pstat.ch2_stat = in_le32(&pb->planb_base->ch2.status); + pstat.saa_stat0 = saa_status(0, pb); + pstat.saa_stat1 = saa_status(1, pb); + + if(copy_to_user((void *)arg, (void *)&pstat, + sizeof(pstat))) + return -EFAULT; + return 0; + } + + case PLANBIOCSMODE: { + int v; + + DEBUG("PlanB: IOCTL PLANBIOCSMODE\n"); + + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + + switch(v) + { + case PLANB_TV_MODE: + saa_set (SAA7196_STDC, + (saa_regs[pb->win.norm][SAA7196_STDC] & + 0x7f), pb); + break; + case PLANB_VTR_MODE: + saa_set (SAA7196_STDC, + (saa_regs[pb->win.norm][SAA7196_STDC] | + 0x80), pb); + break; + default: + return -EINVAL; + break; + } + pb->win.mode = v; + return 0; + } + case PLANBIOCGMODE: { + int v=pb->win.mode; + + DEBUG("PlanB: IOCTL PLANBIOCGMODE\n"); + + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } +#ifdef PLANB_GSCANLINE + case PLANBG_GRAB_BPL: { + int v=pb->gbytes_per_line; + + DEBUG("PlanB: IOCTL PLANBG_GRAB_BPL\n"); + + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } +#endif /* PLANB_GSCANLINE */ + case PLANB_INTR_DEBUG: { + int i; + + DEBUG("PlanB: IOCTL PLANB_INTR_DEBUG\n"); + + if(copy_from_user(&i, arg, sizeof(i))) + return -EFAULT; + + /* avoid hang ups all together */ + for (i = 0; i < MAX_GBUFFERS; i++) { + if(pb->frame_stat[i] == GBUFFER_GRABBING) { + pb->frame_stat[i] = GBUFFER_DONE; + } + } + if(pb->grabbing) + pb->grabbing--; + wake_up_interruptible(&pb->capq); + return 0; + } + case PLANB_INV_REGS: { + int i; + struct planb_any_regs any; + + DEBUG("PlanB: IOCTL PLANB_INV_REGS\n"); + + if(copy_from_user(&any, arg, sizeof(any))) + return -EFAULT; + if(any.offset < 0 || any.offset + any.bytes > 0x400) + return -EINVAL; + if(any.bytes > 128) + return -EINVAL; + for (i = 0; i < any.bytes; i++) { + any.data[i] = + in_8((unsigned char *)pb->planb_base + + any.offset + i); + } + if(copy_to_user(arg,&any,sizeof(any))) + return -EFAULT; + return 0; + } + default: + { + DEBUG("PlanB: Unimplemented IOCTL\n"); + return -ENOIOCTLCMD; + } + /* Some IOCTLs are currently unsupported on PlanB */ + case VIDIOCGTUNER: { + DEBUG("PlanB: IOCTL VIDIOCGTUNER\n"); + goto unimplemented; } + case VIDIOCSTUNER: { + DEBUG("PlanB: IOCTL VIDIOCSTUNER\n"); + goto unimplemented; } + case VIDIOCSFREQ: { + DEBUG("PlanB: IOCTL VIDIOCSFREQ\n"); + goto unimplemented; } + case VIDIOCGFREQ: { + DEBUG("PlanB: IOCTL VIDIOCGFREQ\n"); + goto unimplemented; } + case VIDIOCKEY: { + DEBUG("PlanB: IOCTL VIDIOCKEY\n"); + goto unimplemented; } + case VIDIOCSAUDIO: { + DEBUG("PlanB: IOCTL VIDIOCSAUDIO\n"); + goto unimplemented; } + case VIDIOCGAUDIO: { + DEBUG("PlanB: IOCTL VIDIOCGAUDIO\n"); + goto unimplemented; } +unimplemented: + DEBUG(" Unimplemented\n"); + return -ENOIOCTLCMD; + } + return 0; +} + +static int planb_mmap(struct vm_area_struct *vma, struct video_device *dev, const char *adr, unsigned long size) +{ + int i; + struct planb *pb = (struct planb *)dev; + unsigned long start = (unsigned long)adr; + + if (size > MAX_GBUFFERS * PLANB_MAX_FBUF) + return -EINVAL; + if (!pb->rawbuf) { + int err; + if((err=grabbuf_alloc(pb))) + return err; + } + for (i = 0; i < pb->rawbuf_size; i++) { + unsigned long pfn; + + pfn = virt_to_phys((void *)pb->rawbuf[i]) >> PAGE_SHIFT; + if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + start += PAGE_SIZE; + if (size <= PAGE_SIZE) + break; + size -= PAGE_SIZE; + } + return 0; +} + +static struct video_device planb_template= +{ + .owner = THIS_MODULE, + .name = PLANB_DEVICE_NAME, + .type = VID_TYPE_OVERLAY, + .hardware = VID_HARDWARE_PLANB, + .open = planb_open, + .close = planb_close, + .read = planb_read, + .write = planb_write, + .ioctl = planb_ioctl, + .mmap = planb_mmap, /* mmap? */ +}; + +static int init_planb(struct planb *pb) +{ + unsigned char saa_rev; + int i, result; + + memset ((void *) &pb->win, 0, sizeof (struct planb_window)); + /* Simple sanity check */ + if(def_norm >= NUM_SUPPORTED_NORM || def_norm < 0) { + printk(KERN_ERR "PlanB: Option(s) invalid\n"); + return -2; + } + pb->win.norm = def_norm; + pb->win.mode = PLANB_TV_MODE; /* TV mode */ + pb->win.interlace=1; + pb->win.x=0; + pb->win.y=0; + pb->win.width=768; /* 640 */ + pb->win.height=576; /* 480 */ + pb->maxlines=576; +#if 0 + btv->win.cropwidth=768; /* 640 */ + btv->win.cropheight=576; /* 480 */ + btv->win.cropx=0; + btv->win.cropy=0; +#endif + pb->win.pad=0; + pb->win.bpp=4; + pb->win.depth=32; + pb->win.color_fmt=PLANB_COLOUR32; + pb->win.bpl=1024*pb->win.bpp; + pb->win.swidth=1024; + pb->win.sheight=768; +#ifdef PLANB_GSCANLINE + if((pb->gbytes_per_line = PLANB_MAXPIXELS * 4) > PAGE_SIZE + || (pb->gbytes_per_line <= 0)) + return -3; + else { + /* page align pb->gbytes_per_line for DMA purpose */ + for(i = PAGE_SIZE; pb->gbytes_per_line < (i>>1);) + i>>=1; + pb->gbytes_per_line = i; + } +#endif + pb->tab_size = PLANB_MAXLINES + 40; + pb->suspend = 0; + mutex_init(&pb->lock); + pb->ch1_cmd = 0; + pb->ch2_cmd = 0; + pb->mask = 0; + pb->priv_space = 0; + pb->offset = 0; + pb->user = 0; + pb->overlay = 0; + init_waitqueue_head(&pb->suspendq); + pb->cmd_buff_inited = 0; + pb->frame_buffer_phys = 0; + + /* Reset DMA controllers */ + planb_dbdma_stop(&pb->planb_base->ch2); + planb_dbdma_stop(&pb->planb_base->ch1); + + saa_rev = (saa_status(0, pb) & 0xf0) >> 4; + printk(KERN_INFO "PlanB: SAA7196 video processor rev. %d\n", saa_rev); + /* Initialize the SAA registers in memory and on chip */ + saa_init_regs (pb); + + /* clear interrupt mask */ + pb->intr_mask = PLANB_CLR_IRQ; + + result = request_irq(pb->irq, planb_irq, 0, "PlanB", (void *)pb); + if (result < 0) { + if (result==-EINVAL) + printk(KERN_ERR "PlanB: Bad irq number (%d) " + "or handler\n", (int)pb->irq); + else if (result==-EBUSY) + printk(KERN_ERR "PlanB: I don't know why, " + "but IRQ %d is busy\n", (int)pb->irq); + return result; + } + disable_irq(pb->irq); + + /* Now add the template and register the device unit. */ + memcpy(&pb->video_dev,&planb_template,sizeof(planb_template)); + + pb->picture.brightness=0x90<<8; + pb->picture.contrast = 0x70 << 8; + pb->picture.colour = 0x70<<8; + pb->picture.hue = 0x8000; + pb->picture.whiteness = 0; + pb->picture.depth = pb->win.depth; + + pb->frame_stat=NULL; + init_waitqueue_head(&pb->capq); + for(i=0; igbuf_idx[i] = PLANB_MAX_FBUF * i / PAGE_SIZE; + pb->gwidth[i]=0; + pb->gheight[i]=0; + pb->gfmt[i]=0; + pb->cap_cmd[i]=NULL; +#ifndef PLANB_GSCANLINE + pb->l_fr_addr_idx[i] = MAX_GBUFFERS * (PLANB_MAX_FBUF + / PAGE_SIZE + 1) + MAX_LNUM * i; + pb->lsize[i] = 0; + pb->lnum[i] = 0; +#endif + } + pb->rawbuf=NULL; + pb->grabbing=0; + + /* enable interrupts */ + out_le32(&pb->planb_base->intr_stat, PLANB_CLR_IRQ); + pb->intr_mask = PLANB_FRM_IRQ; + enable_irq(pb->irq); + + if(video_register_device(&pb->video_dev, VFL_TYPE_GRABBER, video_nr)<0) + return -1; + + return 0; +} + +/* + * Scan for a PlanB controller, request the irq and map the io memory + */ + +static int find_planb(void) +{ + struct planb *pb; + struct device_node *planb_devices; + unsigned char dev_fn, confreg, bus; + unsigned int old_base, new_base; + unsigned int irq; + struct pci_dev *pdev; + int rc; + + if (!machine_is(powermac)) + return 0; + + planb_devices = find_devices("planb"); + if (planb_devices == 0) { + planb_num=0; + printk(KERN_WARNING "PlanB: no device found!\n"); + return planb_num; + } + + if (planb_devices->next != NULL) + printk(KERN_ERR "Warning: only using first PlanB device!\n"); + pb = &planbs[0]; + planb_num = 1; + + if (planb_devices->n_addrs != 1) { + printk (KERN_WARNING "PlanB: expecting 1 address for planb " + "(got %d)", planb_devices->n_addrs); + return 0; + } + + if (planb_devices->n_intrs == 0) { + printk(KERN_WARNING "PlanB: no intrs for device %s\n", + planb_devices->full_name); + return 0; + } else { + irq = planb_devices->intrs[0].line; + } + + /* Initialize PlanB's PCI registers */ + + /* There is a bug with the way OF assigns addresses + to the devices behind the chaos bridge. + control needs only 0x1000 of space, but decodes only + the upper 16 bits. It therefore occupies a full 64K. + OF assigns the planb controller memory within this space; + so we need to change that here in order to access planb. */ + + /* We remap to 0xf1000000 in hope that nobody uses it ! */ + + bus = (planb_devices->addrs[0].space >> 16) & 0xff; + dev_fn = (planb_devices->addrs[0].space >> 8) & 0xff; + confreg = planb_devices->addrs[0].space & 0xff; + old_base = planb_devices->addrs[0].address; + new_base = 0xf1000000; + + DEBUG("PlanB: Found on bus %d, dev %d, func %d, " + "membase 0x%x (base reg. 0x%x)\n", + bus, PCI_SLOT(dev_fn), PCI_FUNC(dev_fn), old_base, confreg); + + pdev = pci_find_slot (bus, dev_fn); + if (!pdev) { + printk(KERN_ERR "planb: cannot find slot\n"); + goto err_out; + } + + /* Enable response in memory space, bus mastering, + use memory write and invalidate */ + rc = pci_enable_device(pdev); + if (rc) { + printk(KERN_ERR "planb: cannot enable PCI device %s\n", + pci_name(pdev)); + goto err_out; + } + rc = pci_set_mwi(pdev); + if (rc) { + printk(KERN_ERR "planb: cannot enable MWI on PCI device %s\n", + pci_name(pdev)); + goto err_out_disable; + } + pci_set_master(pdev); + + /* Set the new base address */ + pci_write_config_dword (pdev, confreg, new_base); + + planb_regs = (volatile struct planb_registers *) + ioremap (new_base, 0x400); + pb->planb_base = planb_regs; + pb->planb_base_phys = (struct planb_registers *)new_base; + pb->irq = irq; + + return planb_num; + +err_out_disable: + pci_disable_device(pdev); +err_out: + /* FIXME handle error */ /* comment moved from pci_find_slot, above */ + return 0; +} + +static void release_planb(void) +{ + int i; + struct planb *pb; + + for (i=0;iplanb_base->ch2); + planb_dbdma_stop(&pb->planb_base->ch1); + + /* clear and free interrupts */ + pb->intr_mask = PLANB_CLR_IRQ; + out_le32 (&pb->planb_base->intr_stat, PLANB_CLR_IRQ); + free_irq(pb->irq, pb); + + /* make sure all allocated memory are freed */ + planb_prepare_close(pb); + + printk(KERN_INFO "PlanB: unregistering with v4l\n"); + video_unregister_device(&pb->video_dev); + + /* note that iounmap() does nothing on the PPC right now */ + iounmap ((void *)pb->planb_base); + } +} + +static int __init init_planbs(void) +{ + int i; + + if (find_planb()<=0) + return -EIO; + + for (i=0; i +#include "saa7196.h" +#endif /* __KERNEL__ */ + +#define PLANB_DEVICE_NAME "Apple PlanB Video-In" +#define PLANB_REV "1.0" + +#ifdef __KERNEL__ +//#define PLANB_GSCANLINE /* use this if apps have the notion of */ + /* grab buffer scanline */ +/* This should be safe for both PAL and NTSC */ +#define PLANB_MAXPIXELS 768 +#define PLANB_MAXLINES 576 +#define PLANB_NTSC_MAXLINES 480 + +/* Uncomment your preferred norm ;-) */ +#define PLANB_DEF_NORM VIDEO_MODE_PAL +//#define PLANB_DEF_NORM VIDEO_MODE_NTSC +//#define PLANB_DEF_NORM VIDEO_MODE_SECAM + +/* fields settings */ +#define PLANB_GRAY 0x1 /* 8-bit mono? */ +#define PLANB_COLOUR15 0x2 /* 16-bit mode */ +#define PLANB_COLOUR32 0x4 /* 32-bit mode */ +#define PLANB_CLIPMASK 0x8 /* hardware clipmasking */ + +/* misc. flags for PlanB DMA operation */ +#define CH_SYNC 0x1 /* synchronize channels (set by ch1; + cleared by ch2) */ +#define FIELD_SYNC 0x2 /* used for the start of each field + (0 -> 1 -> 0 for ch1; 0 -> 1 for ch2) */ +#define EVEN_FIELD 0x0 /* even field is detected if unset */ +#define DMA_ABORT 0x2 /* error or just out of sync if set */ +#define ODD_FIELD 0x4 /* odd field is detected if set */ + +/* for capture operations */ +#define MAX_GBUFFERS 2 +/* note PLANB_MAX_FBUF must be divisible by PAGE_SIZE */ +#ifdef PLANB_GSCANLINE +#define PLANB_MAX_FBUF 0x240000 /* 576 * 1024 * 4 */ +#define TAB_FACTOR (1) +#else +#define PLANB_MAX_FBUF 0x1b0000 /* 576 * 768 * 4 */ +#define TAB_FACTOR (2) +#endif +#endif /* __KERNEL__ */ + +struct planb_saa_regs { + unsigned char addr; + unsigned char val; +}; + +struct planb_stat_regs { + unsigned int ch1_stat; + unsigned int ch2_stat; + unsigned char saa_stat0; + unsigned char saa_stat1; +}; + +struct planb_any_regs { + unsigned int offset; + unsigned int bytes; + unsigned char data[128]; +}; + +/* planb private ioctls */ +#define PLANBIOCGSAAREGS _IOWR('v', BASE_VIDIOCPRIVATE, struct planb_saa_regs) /* Read a saa7196 reg value */ +#define PLANBIOCSSAAREGS _IOW('v', BASE_VIDIOCPRIVATE + 1, struct planb_saa_regs) /* Set a saa7196 reg value */ +#define PLANBIOCGSTAT _IOR('v', BASE_VIDIOCPRIVATE + 2, struct planb_stat_regs) /* Read planb status */ +#define PLANB_TV_MODE 1 +#define PLANB_VTR_MODE 2 +#define PLANBIOCGMODE _IOR('v', BASE_VIDIOCPRIVATE + 3, int) /* Get TV/VTR mode */ +#define PLANBIOCSMODE _IOW('v', BASE_VIDIOCPRIVATE + 4, int) /* Set TV/VTR mode */ + +#ifdef PLANB_GSCANLINE +#define PLANBG_GRAB_BPL _IOR('v', BASE_VIDIOCPRIVATE + 5, int) /* # of bytes per scanline in grab buffer */ +#endif + +/* call wake_up_interruptible() with appropriate actions */ +#define PLANB_INTR_DEBUG _IOW('v', BASE_VIDIOCPRIVATE + 20, int) +/* investigate which reg does what */ +#define PLANB_INV_REGS _IOWR('v', BASE_VIDIOCPRIVATE + 21, struct planb_any_regs) + +#ifdef __KERNEL__ + +/* Potentially useful macros */ +#define PLANB_SET(x) ((x) << 16 | (x)) +#define PLANB_CLR(x) ((x) << 16) + +/* This represents the physical register layout */ +struct planb_registers { + volatile struct dbdma_regs ch1; /* 0x00: video in */ + volatile unsigned int even; /* 0x40: even field setting */ + volatile unsigned int odd; /* 0x44; odd field setting */ + unsigned int pad1[14]; /* empty? */ + volatile struct dbdma_regs ch2; /* 0x80: clipmask out */ + unsigned int pad2[16]; /* 0xc0: empty? */ + volatile unsigned int reg3; /* 0x100: ???? */ + volatile unsigned int intr_stat; /* 0x104: irq status */ +#define PLANB_CLR_IRQ 0x00 /* clear Plan B interrupt */ +#define PLANB_GEN_IRQ 0x01 /* assert Plan B interrupt */ +#define PLANB_FRM_IRQ 0x0100 /* end of frame */ + unsigned int pad3[1]; /* empty? */ + volatile unsigned int reg5; /* 0x10c: ??? */ + unsigned int pad4[60]; /* empty? */ + volatile unsigned char saa_addr; /* 0x200: SAA subadr */ + char pad5[3]; + volatile unsigned char saa_regval; /* SAA7196 write reg. val */ + char pad6[3]; + volatile unsigned char saa_status; /* SAA7196 status byte */ + /* There is more unused stuff here */ +}; + +struct planb_window { + int x, y; + ushort width, height; + ushort bpp, bpl, depth, pad; + ushort swidth, sheight; + int norm; + int interlace; + u32 color_fmt; + int chromakey; + int mode; /* used to switch between TV/VTR modes */ +}; + +struct planb_suspend { + int overlay; + int frame; + struct dbdma_cmd cmd; +}; + +struct planb { + struct video_device video_dev; + struct video_picture picture; /* Current picture params */ + struct video_audio audio_dev; /* Current audio params */ + + volatile struct planb_registers *planb_base; /* virt base of planb */ + struct planb_registers *planb_base_phys; /* phys base of planb */ + void *priv_space; /* Org. alloc. mem for kfree */ + int user; + unsigned int tab_size; + int maxlines; + struct mutex lock; + unsigned int irq; /* interrupt number */ + volatile unsigned int intr_mask; + + int overlay; /* overlay running? */ + struct planb_window win; + unsigned long frame_buffer_phys; /* We need phys for DMA */ + int offset; /* offset of pixel 1 */ + volatile struct dbdma_cmd *ch1_cmd; /* Video In DMA cmd buffer */ + volatile struct dbdma_cmd *ch2_cmd; /* Clip Out DMA cmd buffer */ + volatile struct dbdma_cmd *overlay_last1; + volatile struct dbdma_cmd *overlay_last2; + unsigned long ch1_cmd_phys; + volatile unsigned char *mask; /* Clipmask buffer */ + int suspend; + wait_queue_head_t suspendq; + struct planb_suspend suspended; + int cmd_buff_inited; /* cmd buffer inited? */ + + int grabbing; + unsigned int gcount; + wait_queue_head_t capq; + int last_fr; + int prev_last_fr; + unsigned char **rawbuf; + int rawbuf_size; + int gbuf_idx[MAX_GBUFFERS]; + volatile struct dbdma_cmd *cap_cmd[MAX_GBUFFERS]; + volatile struct dbdma_cmd *last_cmd[MAX_GBUFFERS]; + volatile struct dbdma_cmd *pre_cmd[MAX_GBUFFERS]; + int need_pre_capture[MAX_GBUFFERS]; +#define PLANB_DUMMY 40 /* # of command buf's allocated for pre-capture seq. */ + int gwidth[MAX_GBUFFERS], gheight[MAX_GBUFFERS]; + unsigned int gfmt[MAX_GBUFFERS]; + int gnorm_switch[MAX_GBUFFERS]; + volatile unsigned int *frame_stat; +#define GBUFFER_UNUSED 0x00U +#define GBUFFER_GRABBING 0x01U +#define GBUFFER_DONE 0x02U +#ifdef PLANB_GSCANLINE + int gbytes_per_line; +#else +#define MAX_LNUM 431 /* change this if PLANB_MAXLINES or */ + /* PLANB_MAXPIXELS changes */ + int l_fr_addr_idx[MAX_GBUFFERS]; + unsigned char *l_to_addr[MAX_GBUFFERS][MAX_LNUM]; + int l_to_next_idx[MAX_GBUFFERS][MAX_LNUM]; + int l_to_next_size[MAX_GBUFFERS][MAX_LNUM]; + int lsize[MAX_GBUFFERS], lnum[MAX_GBUFFERS]; +#endif +}; + +#endif /* __KERNEL__ */ + +#endif /* _PLANB_H_ */ diff --git a/linux/drivers/media/video/pms.c b/linux/drivers/media/video/pms.c new file mode 100644 index 000000000..7df71e084 --- /dev/null +++ b/linux/drivers/media/video/pms.c @@ -0,0 +1,1064 @@ +/* + * Media Vision Pro Movie Studio + * or + * "all you need is an I2C bus some RAM and a prayer" + * + * This draws heavily on code + * + * (c) Wolfgang Koehler, wolf@first.gmd.de, Dec. 1994 + * Kiefernring 15 + * 14478 Potsdam, Germany + * + * Most of this code is directly derived from his userspace driver. + * His driver works so send any reports to alan@redhat.com unless the + * userspace driver also doesn't work for you... + * + * Changes: + * 08/07/2003 Daniele Bellucci + * - pms_capture: report back -EFAULT + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "compat.h" +#include +#include + +#include + + +#define MOTOROLA 1 +#define PHILIPS2 2 +#define PHILIPS1 3 +#define MVVMEMORYWIDTH 0x40 /* 512 bytes */ + +struct pms_device +{ + struct video_device v; + struct video_picture picture; + int height; + int width; + struct mutex lock; +}; + +struct i2c_info +{ + u8 slave; + u8 sub; + u8 data; + u8 hits; +}; + +static int i2c_count = 0; +static struct i2c_info i2cinfo[64]; + +static int decoder = PHILIPS2; +static int standard = 0; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */ + +/* + * I/O ports and Shared Memory + */ + +static int io_port = 0x250; +static int data_port = 0x251; +static int mem_base = 0xC8000; +static void __iomem *mem; +static int video_nr = -1; + + + +static inline void mvv_write(u8 index, u8 value) +{ + outw(index|(value<<8), io_port); +} + +static inline u8 mvv_read(u8 index) +{ + outb(index, io_port); + return inb(data_port); +} + +static int pms_i2c_stat(u8 slave) +{ + int counter; + int i; + + outb(0x28, io_port); + + counter=0; + while((inb(data_port)&0x01)==0) + if(counter++==256) + break; + + while((inb(data_port)&0x01)!=0) + if(counter++==256) + break; + + outb(slave, io_port); + + counter=0; + while((inb(data_port)&0x01)==0) + if(counter++==256) + break; + + while((inb(data_port)&0x01)!=0) + if(counter++==256) + break; + + for(i=0;i<12;i++) + { + char st=inb(data_port); + if((st&2)!=0) + return -1; + if((st&1)==0) + break; + } + outb(0x29, io_port); + return inb(data_port); +} + +static int pms_i2c_write(u16 slave, u16 sub, u16 data) +{ + int skip=0; + int count; + int i; + + for(i=0;i255) + break; + while((inb(data_port)&1)!=0) + if(count>255) + break; + + count=inb(data_port); + + if(count&2) + return -1; + return count; +} + +static int pms_i2c_read(int slave, int sub) +{ + int i=0; + for(i=0;i>8)&0x01); +} + +#endif + +static void pms_secamcross(short cross) +{ + if(decoder==PHILIPS2) + pms_i2c_andor(0x8A, 0x0F, 0xDF, (cross&1)<<5); + else if(decoder==PHILIPS1) + pms_i2c_andor(0x42, 0x0F, 0xDF, (cross&1)<<5); +} + + +static void pms_swsense(short sense) +{ + if(decoder==PHILIPS2) + { + pms_i2c_write(0x8A, 0x0A, sense); + pms_i2c_write(0x8A, 0x0B, sense); + } + else if(decoder==PHILIPS1) + { + pms_i2c_write(0x42, 0x0A, sense); + pms_i2c_write(0x42, 0x0B, sense); + } +} + + +static void pms_framerate(short frr) +{ + int fps=(standard==1)?30:25; + if(frr==0) + return; + fps=fps/frr; + mvv_write(0x14,0x80|fps); + mvv_write(0x15,1); +} + +static void pms_vert(u8 deciden, u8 decinum) +{ + mvv_write(0x1C, deciden); /* Denominator */ + mvv_write(0x1D, decinum); /* Numerator */ +} + +/* + * Turn 16bit ratios into best small ratio the chipset can grok + */ + +static void pms_vertdeci(unsigned short decinum, unsigned short deciden) +{ + /* Knock it down by /5 once */ + if(decinum%5==0) + { + deciden/=5; + decinum/=5; + } + /* + * 3's + */ + while(decinum%3==0 && deciden%3==0) + { + deciden/=3; + decinum/=3; + } + /* + * 2's + */ + while(decinum%2==0 && deciden%2==0) + { + decinum/=2; + deciden/=2; + } + /* + * Fudgyify + */ + while(deciden>32) + { + deciden/=2; + decinum=(decinum+1)/2; + } + if(deciden==32) + deciden--; + pms_vert(deciden,decinum); +} + +static void pms_horzdeci(short decinum, short deciden) +{ + if(decinum<=512) + { + if(decinum%5==0) + { + decinum/=5; + deciden/=5; + } + } + else + { + decinum=512; + deciden=640; /* 768 would be ideal */ + } + + while(((decinum|deciden)&1)==0) + { + decinum>>=1; + deciden>>=1; + } + while(deciden>32) + { + deciden>>=1; + decinum=(decinum+1)>>1; + } + if(deciden==32) + deciden--; + + mvv_write(0x24, 0x80|deciden); + mvv_write(0x25, decinum); +} + +static void pms_resolution(short width, short height) +{ + int fg_height; + + fg_height=height; + if(fg_height>280) + fg_height=280; + + mvv_write(0x18, fg_height); + mvv_write(0x19, fg_height>>8); + + if(standard==1) + { + mvv_write(0x1A, 0xFC); + mvv_write(0x1B, 0x00); + if(height>fg_height) + pms_vertdeci(240,240); + else + pms_vertdeci(fg_height,240); + } + else + { + mvv_write(0x1A, 0x1A); + mvv_write(0x1B, 0x01); + if(fg_height>256) + pms_vertdeci(270,270); + else + pms_vertdeci(fg_height, 270); + } + mvv_write(0x12,0); + mvv_write(0x13, MVVMEMORYWIDTH); + mvv_write(0x42, 0x00); + mvv_write(0x43, 0x00); + mvv_write(0x44, MVVMEMORYWIDTH); + + mvv_write(0x22, width+8); + mvv_write(0x23, (width+8)>> 8); + + if(standard==1) + pms_horzdeci(width,640); + else + pms_horzdeci(width+8, 768); + + mvv_write(0x30, mvv_read(0x30)&0xFE); + mvv_write(0x08, mvv_read(0x08)|0x01); + mvv_write(0x01, mvv_read(0x01)&0xFD); + mvv_write(0x32, 0x00); + mvv_write(0x33, MVVMEMORYWIDTH); +} + + +/* + * Set Input + */ + +static void pms_vcrinput(short input) +{ + if(decoder==PHILIPS2) + pms_i2c_andor(0x8A,0x0D,0x7F,(input&1)<<7); + else if(decoder==PHILIPS1) + pms_i2c_andor(0x42,0x0D,0x7F,(input&1)<<7); +} + + +static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int count) +{ + int y; + int dw = 2*dev->width; + + char tmp[dw+32]; /* using a temp buffer is faster than direct */ + int cnt = 0; + int len=0; + unsigned char r8 = 0x5; /* value for reg8 */ + + if (rgb555) + r8 |= 0x20; /* else use untranslated rgb = 565 */ + mvv_write(0x08,r8); /* capture rgb555/565, init DRAM, PC enable */ + +/* printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */ + + for (y = 0; y < dev->height; y++ ) + { + writeb(0, mem); /* synchronisiert neue Zeile */ + + /* + * This is in truth a fifo, be very careful as if you + * forgot this odd things will occur 8) + */ + + memcpy_fromio(tmp, mem, dw+32); /* discard 16 word */ + cnt -= dev->height; + while (cnt <= 0) + { + /* + * Don't copy too far + */ + int dt=dw; + if(dt+len>count) + dt=count-len; + cnt += dev->height; + if (copy_to_user(buf, tmp+32, dt)) + return len ? len : -EFAULT; + buf += dt; + len += dt; + } + } + return len; +} + + +/* + * Video4linux interfacing + */ + +static int pms_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct pms_device *pd=(struct pms_device *)dev; + + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability *b = arg; + strcpy(b->name, "Mediavision PMS"); + b->type = VID_TYPE_CAPTURE|VID_TYPE_SCALES; + b->channels = 4; + b->audios = 0; + b->maxwidth = 640; + b->maxheight = 480; + b->minwidth = 16; + b->minheight = 16; + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + if(v->channel<0 || v->channel>3) + return -EINVAL; + v->flags=0; + v->tuners=1; + /* Good question.. its composite or SVHS so.. */ + v->type = VIDEO_TYPE_CAMERA; + switch(v->channel) + { + case 0: + strcpy(v->name, "Composite");break; + case 1: + strcpy(v->name, "SVideo");break; + case 2: + strcpy(v->name, "Composite(VCR)");break; + case 3: + strcpy(v->name, "SVideo(VCR)");break; + } + return 0; + } + case VIDIOCSCHAN: + { + struct video_channel *v = arg; + if(v->channel<0 || v->channel>3) + return -EINVAL; + mutex_lock(&pd->lock); + pms_videosource(v->channel&1); + pms_vcrinput(v->channel>>1); + mutex_unlock(&pd->lock); + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + if(v->tuner) + return -EINVAL; + strcpy(v->name, "Format"); + v->rangelow=0; + v->rangehigh=0; + v->flags= VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM; + switch(standard) + { + case 0: + v->mode = VIDEO_MODE_AUTO; + break; + case 1: + v->mode = VIDEO_MODE_NTSC; + break; + case 2: + v->mode = VIDEO_MODE_PAL; + break; + case 3: + v->mode = VIDEO_MODE_SECAM; + break; + } + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *v = arg; + if(v->tuner) + return -EINVAL; + mutex_lock(&pd->lock); + switch(v->mode) + { + case VIDEO_MODE_AUTO: + pms_framerate(25); + pms_secamcross(0); + pms_format(0); + break; + case VIDEO_MODE_NTSC: + pms_framerate(30); + pms_secamcross(0); + pms_format(1); + break; + case VIDEO_MODE_PAL: + pms_framerate(25); + pms_secamcross(0); + pms_format(2); + break; + case VIDEO_MODE_SECAM: + pms_framerate(25); + pms_secamcross(1); + pms_format(2); + break; + default: + mutex_unlock(&pd->lock); + return -EINVAL; + } + mutex_unlock(&pd->lock); + return 0; + } + case VIDIOCGPICT: + { + struct video_picture *p = arg; + *p = pd->picture; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture *p = arg; + if(!((p->palette==VIDEO_PALETTE_RGB565 && p->depth==16) + ||(p->palette==VIDEO_PALETTE_RGB555 && p->depth==15))) + return -EINVAL; + pd->picture= *p; + + /* + * Now load the card. + */ + + mutex_lock(&pd->lock); + pms_brightness(p->brightness>>8); + pms_hue(p->hue>>8); + pms_colour(p->colour>>8); + pms_contrast(p->contrast>>8); + mutex_unlock(&pd->lock); + return 0; + } + case VIDIOCSWIN: + { + struct video_window *vw = arg; + if(vw->flags) + return -EINVAL; + if(vw->clipcount) + return -EINVAL; + if(vw->height<16||vw->height>480) + return -EINVAL; + if(vw->width<16||vw->width>640) + return -EINVAL; + pd->width=vw->width; + pd->height=vw->height; + mutex_lock(&pd->lock); + pms_resolution(pd->width, pd->height); + mutex_unlock(&pd->lock); /* Ok we figured out what to use from our wide choice */ + return 0; + } + case VIDIOCGWIN: + { + struct video_window *vw = arg; + memset(vw,0,sizeof(*vw)); + vw->width=pd->width; + vw->height=pd->height; + return 0; + } + case VIDIOCKEY: + return 0; + case VIDIOCCAPTURE: + case VIDIOCGFBUF: + case VIDIOCSFBUF: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int pms_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, pms_do_ioctl); +} + +static ssize_t pms_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct video_device *v = video_devdata(file); + struct pms_device *pd=(struct pms_device *)v; + int len; + + mutex_lock(&pd->lock); + len=pms_capture(pd, buf, (pd->picture.depth==16)?0:1,count); + mutex_unlock(&pd->lock); + return len; +} + +static struct file_operations pms_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = pms_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .read = pms_read, + .llseek = no_llseek, +}; + +static struct video_device pms_template= +{ + .owner = THIS_MODULE, + .name = "Mediavision PMS", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_PMS, + .fops = &pms_fops, +}; + +static struct pms_device pms_device; + + +/* + * Probe for and initialise the Mediavision PMS + */ + +static int init_mediavision(void) +{ + int id; + int idec, decst; + int i; + + unsigned char i2c_defs[]={ + 0x4C,0x30,0x00,0xE8, + 0xB6,0xE2,0x00,0x00, + 0xFF,0xFF,0x00,0x00, + 0x00,0x00,0x78,0x98, + 0x00,0x00,0x00,0x00, + 0x34,0x0A,0xF4,0xCE, + 0xE4 + }; + + mem = ioremap(mem_base, 0x800); + if (!mem) + return -ENOMEM; + + if (!request_region(0x9A01, 1, "Mediavision PMS config")) + { + printk(KERN_WARNING "mediavision: unable to detect: 0x9A01 in use.\n"); + iounmap(mem); + return -EBUSY; + } + if (!request_region(io_port, 3, "Mediavision PMS")) + { + printk(KERN_WARNING "mediavision: I/O port %d in use.\n", io_port); + release_region(0x9A01, 1); + iounmap(mem); + return -EBUSY; + } + outb(0xB8, 0x9A01); /* Unlock */ + outb(io_port>>4, 0x9A01); /* Set IO port */ + + + id=mvv_read(3); + decst=pms_i2c_stat(0x43); + + if(decst!=-1) + idec=2; + else if(pms_i2c_stat(0xb9)!=-1) + idec=3; + else if(pms_i2c_stat(0x8b)!=-1) + idec=1; + else + idec=0; + + printk(KERN_INFO "PMS type is %d\n", idec); + if(idec == 0) { + release_region(io_port, 3); + release_region(0x9A01, 1); + iounmap(mem); + return -ENODEV; + } + + /* + * Ok we have a PMS of some sort + */ + + mvv_write(0x04, mem_base>>12); /* Set the memory area */ + + /* Ok now load the defaults */ + + for(i=0;i<0x19;i++) + { + if(i2c_defs[i]==0xFF) + pms_i2c_andor(0x8A, i, 0x07,0x00); + else + pms_i2c_write(0x8A, i, i2c_defs[i]); + } + + pms_i2c_write(0xB8,0x00,0x12); + pms_i2c_write(0xB8,0x04,0x00); + pms_i2c_write(0xB8,0x07,0x00); + pms_i2c_write(0xB8,0x08,0x00); + pms_i2c_write(0xB8,0x09,0xFF); + pms_i2c_write(0xB8,0x0A,0x00); + pms_i2c_write(0xB8,0x0B,0x10); + pms_i2c_write(0xB8,0x10,0x03); + + mvv_write(0x01, 0x00); + mvv_write(0x05, 0xA0); + mvv_write(0x08, 0x25); + mvv_write(0x09, 0x00); + mvv_write(0x0A, 0x20|MVVMEMORYWIDTH); + + mvv_write(0x10, 0x02); + mvv_write(0x1E, 0x0C); + mvv_write(0x1F, 0x03); + mvv_write(0x26, 0x06); + + mvv_write(0x2B, 0x00); + mvv_write(0x2C, 0x20); + mvv_write(0x2D, 0x00); + mvv_write(0x2F, 0x70); + mvv_write(0x32, 0x00); + mvv_write(0x33, MVVMEMORYWIDTH); + mvv_write(0x34, 0x00); + mvv_write(0x35, 0x00); + mvv_write(0x3A, 0x80); + mvv_write(0x3B, 0x10); + mvv_write(0x20, 0x00); + mvv_write(0x21, 0x00); + mvv_write(0x30, 0x22); + return 0; +} + +/* + * Initialization and module stuff + */ + +static int __init init_pms_cards(void) +{ + printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n"); + + data_port = io_port +1; + + if(init_mediavision()) + { + printk(KERN_INFO "Board not found.\n"); + return -ENODEV; + } + memcpy(&pms_device, &pms_template, sizeof(pms_template)); + mutex_init(&pms_device.lock); + pms_device.height=240; + pms_device.width=320; + pms_swsense(75); + pms_resolution(320,240); + return video_register_device((struct video_device *)&pms_device, VFL_TYPE_GRABBER, video_nr); +} + +module_param(io_port, int, 0); +module_param(mem_base, int, 0); +module_param(video_nr, int, 0); +MODULE_LICENSE("GPL"); + + +static void __exit shutdown_mediavision(void) +{ + release_region(io_port,3); + release_region(0x9A01, 1); +} + +static void __exit cleanup_pms_module(void) +{ + shutdown_mediavision(); + video_unregister_device((struct video_device *)&pms_device); + iounmap(mem); +} + +module_init(init_pms_cards); +module_exit(cleanup_pms_module); + diff --git a/linux/drivers/media/video/saa5246a.c b/linux/drivers/media/video/saa5246a.c new file mode 100644 index 000000000..bfc970087 --- /dev/null +++ b/linux/drivers/media/video/saa5246a.c @@ -0,0 +1,837 @@ +/* + * Driver for the SAA5246A or SAA5281 Teletext (=Videotext) decoder chips from + * Philips. + * + * Only capturing of Teletext pages is tested. The videotext chips also have a + * TV output but my hardware doesn't use it. For this reason this driver does + * not support changing any TV display settings. + * + * Copyright (C) 2004 Michael Geng + * + * Derived from + * + * saa5249 driver + * Copyright (C) 1998 Richard Guenther + * + * + * with changes by + * Alan Cox + * + * and + * + * vtx.c + * Copyright (C) 1994-97 Martin Buck + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "compat.h" +#include +#include + +#include "saa5246a.h" + +MODULE_AUTHOR("Michael Geng "); +MODULE_DESCRIPTION("Philips SAA5246A, SAA5281 Teletext decoder driver"); +MODULE_LICENSE("GPL"); + +struct saa5246a_device +{ + u8 pgbuf[NUM_DAUS][VTX_VIRTUALSIZE]; + int is_searching[NUM_DAUS]; + struct i2c_client *client; + struct mutex lock; +}; + +static struct video_device saa_template; /* Declared near bottom */ + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { I2C_ADDRESS, I2C_CLIENT_END }; +I2C_CLIENT_INSMOD; + +static struct i2c_client client_template; + +static int saa5246a_attach(struct i2c_adapter *adap, int addr, int kind) +{ + int pgbuf; + int err; + struct i2c_client *client; + struct video_device *vd; + struct saa5246a_device *t; + + printk(KERN_INFO "saa5246a: teletext chip found.\n"); + client=kmalloc(sizeof(*client), GFP_KERNEL); + if(client==NULL) + return -ENOMEM; + client_template.adapter = adap; + client_template.addr = addr; + memcpy(client, &client_template, sizeof(*client)); + t = kzalloc(sizeof(*t), GFP_KERNEL); + if(t==NULL) + { + kfree(client); + return -ENOMEM; + } + strlcpy(client->name, IF_NAME, I2C_NAME_SIZE); + mutex_init(&t->lock); + + /* + * Now create a video4linux device + */ + + vd = video_device_alloc(); + if(vd==NULL) + { + kfree(t); + kfree(client); + return -ENOMEM; + } + i2c_set_clientdata(client, vd); + memcpy(vd, &saa_template, sizeof(*vd)); + + for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) + { + memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0])); + t->is_searching[pgbuf] = FALSE; + } + vd->priv=t; + + + /* + * Register it + */ + + if((err=video_register_device(vd, VFL_TYPE_VTX,-1))<0) + { + kfree(t); + kfree(client); + video_device_release(vd); + return err; + } + t->client = client; + i2c_attach_client(client); + return 0; +} + +/* + * We do most of the hard work when we become a device on the i2c. + */ +static int saa5246a_probe(struct i2c_adapter *adap) +{ + if (adap->class & I2C_CLASS_TV_ANALOG) + return i2c_probe(adap, &addr_data, saa5246a_attach); + return 0; +} + +static int saa5246a_detach(struct i2c_client *client) +{ + struct video_device *vd = i2c_get_clientdata(client); + i2c_detach_client(client); + video_unregister_device(vd); + kfree(vd->priv); + kfree(client); + return 0; +} + +/* + * I2C interfaces + */ + +static struct i2c_driver i2c_driver_videotext = +{ + .driver = { + .name = IF_NAME, /* name */ + }, + .id = I2C_DRIVERID_SAA5249, /* in i2c.h */ + .attach_adapter = saa5246a_probe, + .detach_client = saa5246a_detach, +}; + +static struct i2c_client client_template = { + .driver = &i2c_driver_videotext, + .name = "(unset)", +}; + +static int i2c_sendbuf(struct saa5246a_device *t, int reg, int count, u8 *data) +{ + char buf[64]; + + buf[0] = reg; + memcpy(buf+1, data, count); + + if(i2c_master_send(t->client, buf, count+1)==count+1) + return 0; + return -1; +} + +static int i2c_senddata(struct saa5246a_device *t, ...) +{ + unsigned char buf[64]; + int v; + int ct=0; + va_list argp; + va_start(argp,t); + + while((v=va_arg(argp,int))!=-1) + buf[ct++]=v; + return i2c_sendbuf(t, buf[0], ct-1, buf+1); +} + +/* Get count number of bytes from I²C-device at address adr, store them in buf. + * Start & stop handshaking is done by this routine, ack will be sent after the + * last byte to inhibit further sending of data. If uaccess is TRUE, data is + * written to user-space with put_user. Returns -1 if I²C-device didn't send + * acknowledge, 0 otherwise + */ +static int i2c_getdata(struct saa5246a_device *t, int count, u8 *buf) +{ + if(i2c_master_recv(t->client, buf, count)!=count) + return -1; + return 0; +} + +/* When a page is found then the not FOUND bit in one of the status registers + * of the SAA5264A chip is cleared. Unfortunately this bit is not set + * automatically when a new page is requested. Instead this function must be + * called after a page has been requested. + * + * Return value: 0 if successful + */ +static int saa5246a_clear_found_bit(struct saa5246a_device *t, + unsigned char dau_no) +{ + unsigned char row_25_column_8; + + if (i2c_senddata(t, SAA5246A_REGISTER_R8, + + dau_no | + R8_DO_NOT_CLEAR_MEMORY, + + R9_CURSER_ROW_25, + + R10_CURSER_COLUMN_8, + + COMMAND_END) || + i2c_getdata(t, 1, &row_25_column_8)) + { + return -EIO; + } + row_25_column_8 |= ROW25_COLUMN8_PAGE_NOT_FOUND; + if (i2c_senddata(t, SAA5246A_REGISTER_R8, + + dau_no | + R8_DO_NOT_CLEAR_MEMORY, + + R9_CURSER_ROW_25, + + R10_CURSER_COLUMN_8, + + row_25_column_8, + + COMMAND_END)) + { + return -EIO; + } + + return 0; +} + +/* Requests one videotext page as described in req. The fields of req are + * checked and an error is returned if something is invalid. + * + * Return value: 0 if successful + */ +static int saa5246a_request_page(struct saa5246a_device *t, + vtx_pagereq_t *req) +{ + if (req->pagemask < 0 || req->pagemask >= PGMASK_MAX) + return -EINVAL; + if (req->pagemask & PGMASK_PAGE) + if (req->page < 0 || req->page > PAGE_MAX) + return -EINVAL; + if (req->pagemask & PGMASK_HOUR) + if (req->hour < 0 || req->hour > HOUR_MAX) + return -EINVAL; + if (req->pagemask & PGMASK_MINUTE) + if (req->minute < 0 || req->minute > MINUTE_MAX) + return -EINVAL; + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + + if (i2c_senddata(t, SAA5246A_REGISTER_R2, + + R2_IN_R3_SELECT_PAGE_HUNDREDS | + req->pgbuf << 4 | + R2_BANK_0 | + R2_HAMMING_CHECK_OFF, + + HUNDREDS_OF_PAGE(req->page) | + R3_HOLD_PAGE | + (req->pagemask & PG_HUND ? + R3_PAGE_HUNDREDS_DO_CARE : + R3_PAGE_HUNDREDS_DO_NOT_CARE), + + TENS_OF_PAGE(req->page) | + (req->pagemask & PG_TEN ? + R3_PAGE_TENS_DO_CARE : + R3_PAGE_TENS_DO_NOT_CARE), + + UNITS_OF_PAGE(req->page) | + (req->pagemask & PG_UNIT ? + R3_PAGE_UNITS_DO_CARE : + R3_PAGE_UNITS_DO_NOT_CARE), + + TENS_OF_HOUR(req->hour) | + (req->pagemask & HR_TEN ? + R3_HOURS_TENS_DO_CARE : + R3_HOURS_TENS_DO_NOT_CARE), + + UNITS_OF_HOUR(req->hour) | + (req->pagemask & HR_UNIT ? + R3_HOURS_UNITS_DO_CARE : + R3_HOURS_UNITS_DO_NOT_CARE), + + TENS_OF_MINUTE(req->minute) | + (req->pagemask & MIN_TEN ? + R3_MINUTES_TENS_DO_CARE : + R3_MINUTES_TENS_DO_NOT_CARE), + + UNITS_OF_MINUTE(req->minute) | + (req->pagemask & MIN_UNIT ? + R3_MINUTES_UNITS_DO_CARE : + R3_MINUTES_UNITS_DO_NOT_CARE), + + COMMAND_END) || i2c_senddata(t, SAA5246A_REGISTER_R2, + + R2_IN_R3_SELECT_PAGE_HUNDREDS | + req->pgbuf << 4 | + R2_BANK_0 | + R2_HAMMING_CHECK_OFF, + + HUNDREDS_OF_PAGE(req->page) | + R3_UPDATE_PAGE | + (req->pagemask & PG_HUND ? + R3_PAGE_HUNDREDS_DO_CARE : + R3_PAGE_HUNDREDS_DO_NOT_CARE), + + COMMAND_END)) + { + return -EIO; + } + + t->is_searching[req->pgbuf] = TRUE; + return 0; +} + +/* This routine decodes the page number from the infobits contained in line 25. + * + * Parameters: + * infobits: must be bits 0 to 9 of column 25 + * + * Return value: page number coded in hexadecimal, i. e. page 123 is coded 0x123 + */ +static inline int saa5246a_extract_pagenum_from_infobits( + unsigned char infobits[10]) +{ + int page_hundreds, page_tens, page_units; + + page_units = infobits[0] & ROW25_COLUMN0_PAGE_UNITS; + page_tens = infobits[1] & ROW25_COLUMN1_PAGE_TENS; + page_hundreds = infobits[8] & ROW25_COLUMN8_PAGE_HUNDREDS; + + /* page 0x.. means page 8.. */ + if (page_hundreds == 0) + page_hundreds = 8; + + return((page_hundreds << 8) | (page_tens << 4) | page_units); +} + +/* Decodes the hour from the infobits contained in line 25. + * + * Parameters: + * infobits: must be bits 0 to 9 of column 25 + * + * Return: hour coded in hexadecimal, i. e. 12h is coded 0x12 + */ +static inline int saa5246a_extract_hour_from_infobits( + unsigned char infobits[10]) +{ + int hour_tens, hour_units; + + hour_units = infobits[4] & ROW25_COLUMN4_HOUR_UNITS; + hour_tens = infobits[5] & ROW25_COLUMN5_HOUR_TENS; + + return((hour_tens << 4) | hour_units); +} + +/* Decodes the minutes from the infobits contained in line 25. + * + * Parameters: + * infobits: must be bits 0 to 9 of column 25 + * + * Return: minutes coded in hexadecimal, i. e. 10min is coded 0x10 + */ +static inline int saa5246a_extract_minutes_from_infobits( + unsigned char infobits[10]) +{ + int minutes_tens, minutes_units; + + minutes_units = infobits[2] & ROW25_COLUMN2_MINUTES_UNITS; + minutes_tens = infobits[3] & ROW25_COLUMN3_MINUTES_TENS; + + return((minutes_tens << 4) | minutes_units); +} + +/* Reads the status bits contained in the first 10 columns of the first line + * and extracts the information into info. + * + * Return value: 0 if successful + */ +static inline int saa5246a_get_status(struct saa5246a_device *t, + vtx_pageinfo_t *info, unsigned char dau_no) +{ + unsigned char infobits[10]; + int column; + + if (dau_no >= NUM_DAUS) + return -EINVAL; + + if (i2c_senddata(t, SAA5246A_REGISTER_R8, + + dau_no | + R8_DO_NOT_CLEAR_MEMORY, + + R9_CURSER_ROW_25, + + R10_CURSER_COLUMN_0, + + COMMAND_END) || + i2c_getdata(t, 10, infobits)) + { + return -EIO; + } + + info->pagenum = saa5246a_extract_pagenum_from_infobits(infobits); + info->hour = saa5246a_extract_hour_from_infobits(infobits); + info->minute = saa5246a_extract_minutes_from_infobits(infobits); + info->charset = ((infobits[7] & ROW25_COLUMN7_CHARACTER_SET) >> 1); + info->delete = !!(infobits[3] & ROW25_COLUMN3_DELETE_PAGE); + info->headline = !!(infobits[5] & ROW25_COLUMN5_INSERT_HEADLINE); + info->subtitle = !!(infobits[5] & ROW25_COLUMN5_INSERT_SUBTITLE); + info->supp_header = !!(infobits[6] & ROW25_COLUMN6_SUPPRESS_HEADER); + info->update = !!(infobits[6] & ROW25_COLUMN6_UPDATE_PAGE); + info->inter_seq = !!(infobits[6] & ROW25_COLUMN6_INTERRUPTED_SEQUENCE); + info->dis_disp = !!(infobits[6] & ROW25_COLUMN6_SUPPRESS_DISPLAY); + info->serial = !!(infobits[7] & ROW25_COLUMN7_SERIAL_MODE); + info->notfound = !!(infobits[8] & ROW25_COLUMN8_PAGE_NOT_FOUND); + info->pblf = !!(infobits[9] & ROW25_COLUMN9_PAGE_BEING_LOOKED_FOR); + info->hamming = 0; + for (column = 0; column <= 7; column++) { + if (infobits[column] & ROW25_COLUMN0_TO_7_HAMMING_ERROR) { + info->hamming = 1; + break; + } + } + if (!info->hamming && !info->notfound) + t->is_searching[dau_no] = FALSE; + return 0; +} + +/* Reads 1 videotext page buffer of the SAA5246A. + * + * req is used both as input and as output. It contains information which part + * must be read. The videotext page is copied into req->buffer. + * + * Return value: 0 if successful + */ +static inline int saa5246a_get_page(struct saa5246a_device *t, + vtx_pagereq_t *req) +{ + int start, end, size; + char *buf; + int err; + + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS || + req->start < 0 || req->start > req->end || req->end >= VTX_PAGESIZE) + return -EINVAL; + + buf = kmalloc(VTX_PAGESIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* Read "normal" part of page */ + err = -EIO; + + end = min(req->end, VTX_PAGESIZE - 1); + if (i2c_senddata(t, SAA5246A_REGISTER_R8, + req->pgbuf | R8_DO_NOT_CLEAR_MEMORY, + ROW(req->start), COLUMN(req->start), COMMAND_END)) + goto out; + if (i2c_getdata(t, end - req->start + 1, buf)) + goto out; + err = -EFAULT; + if (copy_to_user(req->buffer, buf, end - req->start + 1)) + goto out; + + /* Always get the time from buffer 4, since this stupid SAA5246A only + * updates the currently displayed buffer... + */ + if (REQ_CONTAINS_TIME(req)) { + start = max(req->start, POS_TIME_START); + end = min(req->end, POS_TIME_END); + size = end - start + 1; + err = -EINVAL; + if (size < 0) + goto out; + err = -EIO; + if (i2c_senddata(t, SAA5246A_REGISTER_R8, + R8_ACTIVE_CHAPTER_4 | R8_DO_NOT_CLEAR_MEMORY, + R9_CURSER_ROW_0, start, COMMAND_END)) + goto out; + if (i2c_getdata(t, size, buf)) + goto out; + err = -EFAULT; + if (copy_to_user(req->buffer + start - req->start, buf, size)) + goto out; + } + /* Insert the header from buffer 4 only, if acquisition circuit is still searching for a page */ + if (REQ_CONTAINS_HEADER(req) && t->is_searching[req->pgbuf]) { + start = max(req->start, POS_HEADER_START); + end = min(req->end, POS_HEADER_END); + size = end - start + 1; + err = -EINVAL; + if (size < 0) + goto out; + err = -EIO; + if (i2c_senddata(t, SAA5246A_REGISTER_R8, + R8_ACTIVE_CHAPTER_4 | R8_DO_NOT_CLEAR_MEMORY, + R9_CURSER_ROW_0, start, COMMAND_END)) + goto out; + if (i2c_getdata(t, end - start + 1, buf)) + goto out; + err = -EFAULT; + if (copy_to_user(req->buffer + start - req->start, buf, size)) + goto out; + } + err = 0; +out: + kfree(buf); + return err; +} + +/* Stops the acquisition circuit given in dau_no. The page buffer associated + * with this acquisition circuit will no more be updated. The other daus are + * not affected. + * + * Return value: 0 if successful + */ +static inline int saa5246a_stop_dau(struct saa5246a_device *t, + unsigned char dau_no) +{ + if (dau_no >= NUM_DAUS) + return -EINVAL; + if (i2c_senddata(t, SAA5246A_REGISTER_R2, + + R2_IN_R3_SELECT_PAGE_HUNDREDS | + dau_no << 4 | + R2_BANK_0 | + R2_HAMMING_CHECK_OFF, + + R3_PAGE_HUNDREDS_0 | + R3_HOLD_PAGE | + R3_PAGE_HUNDREDS_DO_NOT_CARE, + + COMMAND_END)) + { + return -EIO; + } + t->is_searching[dau_no] = FALSE; + return 0; +} + +/* Handles ioctls defined in videotext.h + * + * Returns 0 if successful + */ +static int do_saa5246a_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vd = video_devdata(file); + struct saa5246a_device *t=vd->priv; + switch(cmd) + { + case VTXIOCGETINFO: + { + vtx_info_t *info = arg; + + info->version_major = MAJOR_VERSION; + info->version_minor = MINOR_VERSION; + info->numpages = NUM_DAUS; + return 0; + } + + case VTXIOCCLRPAGE: + { + vtx_pagereq_t *req = arg; + + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + memset(t->pgbuf[req->pgbuf], ' ', sizeof(t->pgbuf[0])); + return 0; + } + + case VTXIOCCLRFOUND: + { + vtx_pagereq_t *req = arg; + + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + return(saa5246a_clear_found_bit(t, req->pgbuf)); + } + + case VTXIOCPAGEREQ: + { + vtx_pagereq_t *req = arg; + + return(saa5246a_request_page(t, req)); + } + + case VTXIOCGETSTAT: + { + vtx_pagereq_t *req = arg; + vtx_pageinfo_t info; + int rval; + + if ((rval = saa5246a_get_status(t, &info, req->pgbuf))) + return rval; + if(copy_to_user(req->buffer, &info, + sizeof(vtx_pageinfo_t))) + return -EFAULT; + return 0; + } + + case VTXIOCGETPAGE: + { + vtx_pagereq_t *req = arg; + + return(saa5246a_get_page(t, req)); + } + + case VTXIOCSTOPDAU: + { + vtx_pagereq_t *req = arg; + + return(saa5246a_stop_dau(t, req->pgbuf)); + } + + case VTXIOCPUTPAGE: + case VTXIOCSETDISP: + case VTXIOCPUTSTAT: + return 0; + + case VTXIOCCLRCACHE: + { + return 0; + } + + case VTXIOCSETVIRT: + { + /* I do not know what "virtual mode" means */ + return 0; + } + } + return -EINVAL; +} + +/* + * Translates old vtx IOCTLs to new ones + * + * This keeps new kernel versions compatible with old userspace programs. + */ +static inline unsigned int vtx_fix_command(unsigned int cmd) +{ + switch (cmd) { + case VTXIOCGETINFO_OLD: + cmd = VTXIOCGETINFO; + break; + case VTXIOCCLRPAGE_OLD: + cmd = VTXIOCCLRPAGE; + break; + case VTXIOCCLRFOUND_OLD: + cmd = VTXIOCCLRFOUND; + break; + case VTXIOCPAGEREQ_OLD: + cmd = VTXIOCPAGEREQ; + break; + case VTXIOCGETSTAT_OLD: + cmd = VTXIOCGETSTAT; + break; + case VTXIOCGETPAGE_OLD: + cmd = VTXIOCGETPAGE; + break; + case VTXIOCSTOPDAU_OLD: + cmd = VTXIOCSTOPDAU; + break; + case VTXIOCPUTPAGE_OLD: + cmd = VTXIOCPUTPAGE; + break; + case VTXIOCSETDISP_OLD: + cmd = VTXIOCSETDISP; + break; + case VTXIOCPUTSTAT_OLD: + cmd = VTXIOCPUTSTAT; + break; + case VTXIOCCLRCACHE_OLD: + cmd = VTXIOCCLRCACHE; + break; + case VTXIOCSETVIRT_OLD: + cmd = VTXIOCSETVIRT; + break; + } + return cmd; +} + +/* + * Handle the locking + */ +static int saa5246a_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vd = video_devdata(file); + struct saa5246a_device *t = vd->priv; + int err; + + cmd = vtx_fix_command(cmd); + mutex_lock(&t->lock); + err = video_usercopy(inode, file, cmd, arg, do_saa5246a_ioctl); + mutex_unlock(&t->lock); + return err; +} + +static int saa5246a_open(struct inode *inode, struct file *file) +{ + struct video_device *vd = video_devdata(file); + struct saa5246a_device *t = vd->priv; + int err; + + err = video_exclusive_open(inode,file); + if (err < 0) + return err; + + if (t->client==NULL) { + err = -ENODEV; + goto fail; + } + + if (i2c_senddata(t, SAA5246A_REGISTER_R0, + + R0_SELECT_R11 | + R0_PLL_TIME_CONSTANT_LONG | + R0_ENABLE_nODD_EVEN_OUTPUT | + R0_ENABLE_HDR_POLL | + R0_DO_NOT_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED | + R0_NO_FREE_RUN_PLL | + R0_NO_AUTOMATIC_FASTEXT_PROMPT, + + R1_NON_INTERLACED_312_312_LINES | + R1_DEW | + R1_EXTENDED_PACKET_DISABLE | + R1_DAUS_ALL_ON | + R1_8_BITS_NO_PARITY | + R1_VCS_TO_SCS, + + COMMAND_END) || + i2c_senddata(t, SAA5246A_REGISTER_R4, + + /* We do not care much for the TV display but nevertheless we + * need the currently displayed page later because only on that + * page the time is updated. */ + R4_DISPLAY_PAGE_4, + + COMMAND_END)) + { + err = -EIO; + goto fail; + } + + return 0; + +fail: + video_exclusive_release(inode,file); + return err; +} + +static int saa5246a_release(struct inode *inode, struct file *file) +{ + struct video_device *vd = video_devdata(file); + struct saa5246a_device *t = vd->priv; + + /* Stop all acquisition circuits. */ + i2c_senddata(t, SAA5246A_REGISTER_R1, + + R1_INTERLACED_312_AND_HALF_312_AND_HALF_LINES | + R1_DEW | + R1_EXTENDED_PACKET_DISABLE | + R1_DAUS_ALL_OFF | + R1_8_BITS_NO_PARITY | + R1_VCS_TO_SCS, + + COMMAND_END); + video_exclusive_release(inode,file); + return 0; +} + +static int __init init_saa_5246a (void) +{ + printk(KERN_INFO + "SAA5246A (or compatible) Teletext decoder driver version %d.%d\n", + MAJOR_VERSION, MINOR_VERSION); + return i2c_add_driver(&i2c_driver_videotext); +} + +static void __exit cleanup_saa_5246a (void) +{ + i2c_del_driver(&i2c_driver_videotext); +} + +module_init(init_saa_5246a); +module_exit(cleanup_saa_5246a); + +static struct file_operations saa_fops = { + .owner = THIS_MODULE, + .open = saa5246a_open, + .release = saa5246a_release, + .ioctl = saa5246a_ioctl, + .llseek = no_llseek, +}; + +static struct video_device saa_template = +{ + .owner = THIS_MODULE, + .name = IF_NAME, + .type = VID_TYPE_TELETEXT, + .hardware = VID_HARDWARE_SAA5249, + .fops = &saa_fops, + .release = video_device_release, + .minor = -1, +}; diff --git a/linux/drivers/media/video/saa5246a.h b/linux/drivers/media/video/saa5246a.h new file mode 100644 index 000000000..7b9111230 --- /dev/null +++ b/linux/drivers/media/video/saa5246a.h @@ -0,0 +1,364 @@ +/* + Driver for the SAA5246A or SAA5281 Teletext (=Videotext) decoder chips from + Philips. + + Copyright (C) 2004 Michael Geng (linux@MichaelGeng.de) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ +#ifndef __SAA5246A_H__ +#define __SAA5246A_H__ + +#define MAJOR_VERSION 1 /* driver major version number */ +#define MINOR_VERSION 8 /* driver minor version number */ + +#define IF_NAME "SAA5246A" + +#define I2C_ADDRESS 17 + +/* Number of DAUs = number of pages that can be searched at the same time. */ +#define NUM_DAUS 4 + +#define NUM_ROWS_PER_PAGE 40 + +/* first column is 0 (not 1) */ +#define POS_TIME_START 32 +#define POS_TIME_END 39 + +#define POS_HEADER_START 7 +#define POS_HEADER_END 31 + +/* Returns TRUE if the part of the videotext page described with req contains + (at least parts of) the time field */ +#define REQ_CONTAINS_TIME(p_req) \ + ((p_req)->start <= POS_TIME_END && \ + (p_req)->end >= POS_TIME_START) + +/* Returns TRUE if the part of the videotext page described with req contains + (at least parts of) the page header */ +#define REQ_CONTAINS_HEADER(p_req) \ + ((p_req)->start <= POS_HEADER_END && \ + (p_req)->end >= POS_HEADER_START) + +#ifndef FALSE +#define FALSE 0 +#define TRUE 1 +#endif + +/*****************************************************************************/ +/* Mode register numbers of the SAA5246A */ +/*****************************************************************************/ +#define SAA5246A_REGISTER_R0 0 +#define SAA5246A_REGISTER_R1 1 +#define SAA5246A_REGISTER_R2 2 +#define SAA5246A_REGISTER_R3 3 +#define SAA5246A_REGISTER_R4 4 +#define SAA5246A_REGISTER_R5 5 +#define SAA5246A_REGISTER_R6 6 +#define SAA5246A_REGISTER_R7 7 +#define SAA5246A_REGISTER_R8 8 +#define SAA5246A_REGISTER_R9 9 +#define SAA5246A_REGISTER_R10 10 +#define SAA5246A_REGISTER_R11 11 +#define SAA5246A_REGISTER_R11B 11 + +/* SAA5246A mode registers often autoincrement to the next register. + Therefore we use variable argument lists. The following macro indicates + the end of a command list. */ +#define COMMAND_END (- 1) + +/*****************************************************************************/ +/* Contents of the mode registers of the SAA5246A */ +/*****************************************************************************/ +/* Register R0 (Advanced Control) */ +#define R0_SELECT_R11 0x00 +#define R0_SELECT_R11B 0x01 + +#define R0_PLL_TIME_CONSTANT_LONG 0x00 +#define R0_PLL_TIME_CONSTANT_SHORT 0x02 + +#define R0_ENABLE_nODD_EVEN_OUTPUT 0x00 +#define R0_DISABLE_nODD_EVEN_OUTPUT 0x04 + +#define R0_ENABLE_HDR_POLL 0x00 +#define R0_DISABLE_HDR_POLL 0x10 + +#define R0_DO_NOT_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x00 +#define R0_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x20 + +#define R0_NO_FREE_RUN_PLL 0x00 +#define R0_FREE_RUN_PLL 0x40 + +#define R0_NO_AUTOMATIC_FASTEXT_PROMPT 0x00 +#define R0_AUTOMATIC_FASTEXT_PROMPT 0x80 + +/* Register R1 (Mode) */ +#define R1_INTERLACED_312_AND_HALF_312_AND_HALF_LINES 0x00 +#define R1_NON_INTERLACED_312_313_LINES 0x01 +#define R1_NON_INTERLACED_312_312_LINES 0x02 +#define R1_FFB_LEADING_EDGE_IN_FIRST_BROAD_PULSE 0x03 +#define R1_FFB_LEADING_EDGE_IN_SECOND_BROAD_PULSE 0x07 + +#define R1_DEW 0x00 +#define R1_FULL_FIELD 0x08 + +#define R1_EXTENDED_PACKET_DISABLE 0x00 +#define R1_EXTENDED_PACKET_ENABLE 0x10 + +#define R1_DAUS_ALL_ON 0x00 +#define R1_DAUS_ALL_OFF 0x20 + +#define R1_7_BITS_PLUS_PARITY 0x00 +#define R1_8_BITS_NO_PARITY 0x40 + +#define R1_VCS_TO_SCS 0x00 +#define R1_NO_VCS_TO_SCS 0x80 + +/* Register R2 (Page request address) */ +#define R2_IN_R3_SELECT_PAGE_HUNDREDS 0x00 +#define R2_IN_R3_SELECT_PAGE_TENS 0x01 +#define R2_IN_R3_SELECT_PAGE_UNITS 0x02 +#define R2_IN_R3_SELECT_HOURS_TENS 0x03 +#define R2_IN_R3_SELECT_HOURS_UNITS 0x04 +#define R2_IN_R3_SELECT_MINUTES_TENS 0x05 +#define R2_IN_R3_SELECT_MINUTES_UNITS 0x06 + +#define R2_DAU_0 0x00 +#define R2_DAU_1 0x10 +#define R2_DAU_2 0x20 +#define R2_DAU_3 0x30 + +#define R2_BANK_0 0x00 +#define R2_BANK 1 0x40 + +#define R2_HAMMING_CHECK_ON 0x80 +#define R2_HAMMING_CHECK_OFF 0x00 + +/* Register R3 (Page request data) */ +#define R3_PAGE_HUNDREDS_0 0x00 +#define R3_PAGE_HUNDREDS_1 0x01 +#define R3_PAGE_HUNDREDS_2 0x02 +#define R3_PAGE_HUNDREDS_3 0x03 +#define R3_PAGE_HUNDREDS_4 0x04 +#define R3_PAGE_HUNDREDS_5 0x05 +#define R3_PAGE_HUNDREDS_6 0x06 +#define R3_PAGE_HUNDREDS_7 0x07 + +#define R3_HOLD_PAGE 0x00 +#define R3_UPDATE_PAGE 0x08 + +#define R3_PAGE_HUNDREDS_DO_NOT_CARE 0x00 +#define R3_PAGE_HUNDREDS_DO_CARE 0x10 + +#define R3_PAGE_TENS_DO_NOT_CARE 0x00 +#define R3_PAGE_TENS_DO_CARE 0x10 + +#define R3_PAGE_UNITS_DO_NOT_CARE 0x00 +#define R3_PAGE_UNITS_DO_CARE 0x10 + +#define R3_HOURS_TENS_DO_NOT_CARE 0x00 +#define R3_HOURS_TENS_DO_CARE 0x10 + +#define R3_HOURS_UNITS_DO_NOT_CARE 0x00 +#define R3_HOURS_UNITS_DO_CARE 0x10 + +#define R3_MINUTES_TENS_DO_NOT_CARE 0x00 +#define R3_MINUTES_TENS_DO_CARE 0x10 + +#define R3_MINUTES_UNITS_DO_NOT_CARE 0x00 +#define R3_MINUTES_UNITS_DO_CARE 0x10 + +/* Register R4 (Display chapter) */ +#define R4_DISPLAY_PAGE_0 0x00 +#define R4_DISPLAY_PAGE_1 0x01 +#define R4_DISPLAY_PAGE_2 0x02 +#define R4_DISPLAY_PAGE_3 0x03 +#define R4_DISPLAY_PAGE_4 0x04 +#define R4_DISPLAY_PAGE_5 0x05 +#define R4_DISPLAY_PAGE_6 0x06 +#define R4_DISPLAY_PAGE_7 0x07 + +/* Register R5 (Normal display control) */ +#define R5_PICTURE_INSIDE_BOXING_OFF 0x00 +#define R5_PICTURE_INSIDE_BOXING_ON 0x01 + +#define R5_PICTURE_OUTSIDE_BOXING_OFF 0x00 +#define R5_PICTURE_OUTSIDE_BOXING_ON 0x02 + +#define R5_TEXT_INSIDE_BOXING_OFF 0x00 +#define R5_TEXT_INSIDE_BOXING_ON 0x04 + +#define R5_TEXT_OUTSIDE_BOXING_OFF 0x00 +#define R5_TEXT_OUTSIDE_BOXING_ON 0x08 + +#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00 +#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10 + +#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00 +#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20 + +#define R5_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00 +#define R5_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40 + +#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00 +#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80 + +/* Register R6 (Newsflash display) */ +#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_ON 0x01 + +#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_ON 0x02 + +#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_ON 0x04 + +#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_ON 0x08 + +#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10 + +#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20 + +#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40 + +#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80 + +/* Register R7 (Display mode) */ +#define R7_BOX_OFF_ROW_0 0x00 +#define R7_BOX_ON_ROW_0 0x01 + +#define R7_BOX_OFF_ROW_1_TO_23 0x00 +#define R7_BOX_ON_ROW_1_TO_23 0x02 + +#define R7_BOX_OFF_ROW_24 0x00 +#define R7_BOX_ON_ROW_24 0x04 + +#define R7_SINGLE_HEIGHT 0x00 +#define R7_DOUBLE_HEIGHT 0x08 + +#define R7_TOP_HALF 0x00 +#define R7_BOTTOM_HALF 0x10 + +#define R7_REVEAL_OFF 0x00 +#define R7_REVEAL_ON 0x20 + +#define R7_CURSER_OFF 0x00 +#define R7_CURSER_ON 0x40 + +#define R7_STATUS_BOTTOM 0x00 +#define R7_STATUS_TOP 0x80 + +/* Register R8 (Active chapter) */ +#define R8_ACTIVE_CHAPTER_0 0x00 +#define R8_ACTIVE_CHAPTER_1 0x01 +#define R8_ACTIVE_CHAPTER_2 0x02 +#define R8_ACTIVE_CHAPTER_3 0x03 +#define R8_ACTIVE_CHAPTER_4 0x04 +#define R8_ACTIVE_CHAPTER_5 0x05 +#define R8_ACTIVE_CHAPTER_6 0x06 +#define R8_ACTIVE_CHAPTER_7 0x07 + +#define R8_CLEAR_MEMORY 0x08 +#define R8_DO_NOT_CLEAR_MEMORY 0x00 + +/* Register R9 (Curser row) */ +#define R9_CURSER_ROW_0 0x00 +#define R9_CURSER_ROW_1 0x01 +#define R9_CURSER_ROW_2 0x02 +#define R9_CURSER_ROW_25 0x19 + +/* Register R10 (Curser column) */ +#define R10_CURSER_COLUMN_0 0x00 +#define R10_CURSER_COLUMN_6 0x06 +#define R10_CURSER_COLUMN_8 0x08 + +/*****************************************************************************/ +/* Row 25 control data in column 0 to 9 */ +/*****************************************************************************/ +#define ROW25_COLUMN0_PAGE_UNITS 0x0F + +#define ROW25_COLUMN1_PAGE_TENS 0x0F + +#define ROW25_COLUMN2_MINUTES_UNITS 0x0F + +#define ROW25_COLUMN3_MINUTES_TENS 0x07 +#define ROW25_COLUMN3_DELETE_PAGE 0x08 + +#define ROW25_COLUMN4_HOUR_UNITS 0x0F + +#define ROW25_COLUMN5_HOUR_TENS 0x03 +#define ROW25_COLUMN5_INSERT_HEADLINE 0x04 +#define ROW25_COLUMN5_INSERT_SUBTITLE 0x08 + +#define ROW25_COLUMN6_SUPPRESS_HEADER 0x01 +#define ROW25_COLUMN6_UPDATE_PAGE 0x02 +#define ROW25_COLUMN6_INTERRUPTED_SEQUENCE 0x04 +#define ROW25_COLUMN6_SUPPRESS_DISPLAY 0x08 + +#define ROW25_COLUMN7_SERIAL_MODE 0x01 +#define ROW25_COLUMN7_CHARACTER_SET 0x0E + +#define ROW25_COLUMN8_PAGE_HUNDREDS 0x07 +#define ROW25_COLUMN8_PAGE_NOT_FOUND 0x10 + +#define ROW25_COLUMN9_PAGE_BEING_LOOKED_FOR 0x20 + +#define ROW25_COLUMN0_TO_7_HAMMING_ERROR 0x10 + +/*****************************************************************************/ +/* Helper macros for extracting page, hour and minute digits */ +/*****************************************************************************/ +/* BYTE_POS 0 is at row 0, column 0, + BYTE_POS 1 is at row 0, column 1, + BYTE_POS 40 is at row 1, column 0, (with NUM_ROWS_PER_PAGE = 40) + BYTE_POS 41 is at row 1, column 1, (with NUM_ROWS_PER_PAGE = 40), + ... */ +#define ROW(BYTE_POS) (BYTE_POS / NUM_ROWS_PER_PAGE) +#define COLUMN(BYTE_POS) (BYTE_POS % NUM_ROWS_PER_PAGE) + +/*****************************************************************************/ +/* Helper macros for extracting page, hour and minute digits */ +/*****************************************************************************/ +/* Macros for extracting hundreds, tens and units of a page number which + must be in the range 0 ... 0x799. + Note that page is coded in hexadecimal, i.e. 0x123 means page 123. + page 0x.. means page 8.. */ +#define HUNDREDS_OF_PAGE(page) (((page) / 0x100) & 0x7) +#define TENS_OF_PAGE(page) (((page) / 0x10) & 0xF) +#define UNITS_OF_PAGE(page) ((page) & 0xF) + +/* Macros for extracting tens and units of a hour information which + must be in the range 0 ... 0x24. + Note that hour is coded in hexadecimal, i.e. 0x12 means 12 hours */ +#define TENS_OF_HOUR(hour) ((hour) / 0x10) +#define UNITS_OF_HOUR(hour) ((hour) & 0xF) + +/* Macros for extracting tens and units of a minute information which + must be in the range 0 ... 0x59. + Note that minute is coded in hexadecimal, i.e. 0x12 means 12 minutes */ +#define TENS_OF_MINUTE(minute) ((minute) / 0x10) +#define UNITS_OF_MINUTE(minute) ((minute) & 0xF) + +#define HOUR_MAX 0x23 +#define MINUTE_MAX 0x59 +#define PAGE_MAX 0x8FF + +#endif /* __SAA5246A_H__ */ diff --git a/linux/drivers/media/video/saa5249.c b/linux/drivers/media/video/saa5249.c new file mode 100644 index 000000000..42c2052e4 --- /dev/null +++ b/linux/drivers/media/video/saa5249.c @@ -0,0 +1,720 @@ +/* + * Modified in order to keep it compatible both with new and old videotext IOCTLs by + * Michael Geng + * + * Cleaned up to use existing videodev interface and allow the idea + * of multiple teletext decoders on the video4linux iface. Changed i2c + * to cover addressing clashes on device busses. It's also rebuilt so + * you can add arbitary multiple teletext devices to Linux video4linux + * now (well 32 anyway). + * + * Alan Cox + * + * The original driver was heavily modified to match the i2c interface + * It was truncated to use the WinTV boards, too. + * + * Copyright (c) 1998 Richard Guenther + * + * $Id: saa5249.c,v 1.1 1998/03/30 22:23:23 alan Exp $ + * + * Derived From + * + * vtx.c: + * This is a loadable character-device-driver for videotext-interfaces + * (aka teletext). Please check the Makefile/README for a list of supported + * interfaces. + * + * Copyright (c) 1994-97 Martin Buck + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "compat.h" +#include +#include + + +#include +#include + +#define VTX_VER_MAJ 1 +#define VTX_VER_MIN 8 + + + +#define NUM_DAUS 4 +#define NUM_BUFS 8 +#define IF_NAME "SAA5249" + +static const int disp_modes[8][3] = +{ + { 0x46, 0x03, 0x03 }, /* DISPOFF */ + { 0x46, 0xcc, 0xcc }, /* DISPNORM */ + { 0x44, 0x0f, 0x0f }, /* DISPTRANS */ + { 0x46, 0xcc, 0x46 }, /* DISPINS */ + { 0x44, 0x03, 0x03 }, /* DISPOFF, interlaced */ + { 0x44, 0xcc, 0xcc }, /* DISPNORM, interlaced */ + { 0x44, 0x0f, 0x0f }, /* DISPTRANS, interlaced */ + { 0x44, 0xcc, 0x46 } /* DISPINS, interlaced */ +}; + + + +#define PAGE_WAIT (300*HZ/1000) /* Time between requesting page and */ + /* checking status bits */ +#define PGBUF_EXPIRE (15*HZ) /* Time to wait before retransmitting */ + /* page regardless of infobits */ +typedef struct { + u8 pgbuf[VTX_VIRTUALSIZE]; /* Page-buffer */ + u8 laststat[10]; /* Last value of infobits for DAU */ + u8 sregs[7]; /* Page-request registers */ + unsigned long expire; /* Time when page will be expired */ + unsigned clrfound : 1; /* VTXIOCCLRFOUND has been called */ + unsigned stopped : 1; /* VTXIOCSTOPDAU has been called */ +} vdau_t; + +struct saa5249_device +{ + vdau_t vdau[NUM_DAUS]; /* Data for virtual DAUs (the 5249 only has one */ + /* real DAU, so we have to simulate some more) */ + int vtx_use_count; + int is_searching[NUM_DAUS]; + int disp_mode; + int virtual_mode; + struct i2c_client *client; + struct mutex lock; +}; + + +#define CCTWR 34 /* I²C write/read-address of vtx-chip */ +#define CCTRD 35 +#define NOACK_REPEAT 10 /* Retry access this many times on failure */ +#define CLEAR_DELAY (HZ/20) /* Time required to clear a page */ +#define READY_TIMEOUT (30*HZ/1000) /* Time to wait for ready signal of I²C-bus interface */ +#define INIT_DELAY 500 /* Time in usec to wait at initialization of CEA interface */ +#define START_DELAY 10 /* Time in usec to wait before starting write-cycle (CEA) */ + +#define VTX_DEV_MINOR 0 + +/* General defines and debugging support */ + +#ifndef FALSE +#define FALSE 0 +#define TRUE 1 +#endif + +#define RESCHED do { cond_resched(); } while(0) + +static struct video_device saa_template; /* Declared near bottom */ + +/* Addresses to scan */ +static unsigned short normal_i2c[] = {34>>1,I2C_CLIENT_END}; +I2C_CLIENT_INSMOD; + +static struct i2c_client client_template; + +static int saa5249_attach(struct i2c_adapter *adap, int addr, int kind) +{ + int pgbuf; + int err; + struct i2c_client *client; + struct video_device *vd; + struct saa5249_device *t; + + printk(KERN_INFO "saa5249: teletext chip found.\n"); + client=kmalloc(sizeof(*client), GFP_KERNEL); + if(client==NULL) + return -ENOMEM; + client_template.adapter = adap; + client_template.addr = addr; + memcpy(client, &client_template, sizeof(*client)); + t = kzalloc(sizeof(*t), GFP_KERNEL); + if(t==NULL) + { + kfree(client); + return -ENOMEM; + } + strlcpy(client->name, IF_NAME, I2C_NAME_SIZE); + mutex_init(&t->lock); + + /* + * Now create a video4linux device + */ + + vd = kmalloc(sizeof(struct video_device), GFP_KERNEL); + if(vd==NULL) + { + kfree(t); + kfree(client); + return -ENOMEM; + } + i2c_set_clientdata(client, vd); + memcpy(vd, &saa_template, sizeof(*vd)); + + for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) + { + memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); + memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs)); + memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat)); + t->vdau[pgbuf].expire = 0; + t->vdau[pgbuf].clrfound = TRUE; + t->vdau[pgbuf].stopped = TRUE; + t->is_searching[pgbuf] = FALSE; + } + vd->priv=t; + + + /* + * Register it + */ + + if((err=video_register_device(vd, VFL_TYPE_VTX,-1))<0) + { + kfree(t); + kfree(vd); + kfree(client); + return err; + } + t->client = client; + i2c_attach_client(client); + return 0; +} + +/* + * We do most of the hard work when we become a device on the i2c. + */ + +static int saa5249_probe(struct i2c_adapter *adap) +{ + if (adap->class & I2C_CLASS_TV_ANALOG) + return i2c_probe(adap, &addr_data, saa5249_attach); + return 0; +} + +static int saa5249_detach(struct i2c_client *client) +{ + struct video_device *vd = i2c_get_clientdata(client); + i2c_detach_client(client); + video_unregister_device(vd); + kfree(vd->priv); + kfree(vd); + kfree(client); + return 0; +} + +/* new I2C driver support */ + +static struct i2c_driver i2c_driver_videotext = +{ + .driver = { + .name = IF_NAME, /* name */ + }, + .id = I2C_DRIVERID_SAA5249, /* in i2c.h */ + .attach_adapter = saa5249_probe, + .detach_client = saa5249_detach, +}; + +static struct i2c_client client_template = { + .driver = &i2c_driver_videotext, + .name = "(unset)", +}; + +/* + * Wait the given number of jiffies (10ms). This calls the scheduler, so the actual + * delay may be longer. + */ + +static void jdelay(unsigned long delay) +{ + sigset_t oldblocked = current->blocked; + + spin_lock_irq(¤t->sighand->siglock); + sigfillset(¤t->blocked); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + msleep_interruptible(jiffies_to_msecs(delay)); + + spin_lock_irq(¤t->sighand->siglock); + current->blocked = oldblocked; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); +} + + +/* + * I2C interfaces + */ + +static int i2c_sendbuf(struct saa5249_device *t, int reg, int count, u8 *data) +{ + char buf[64]; + + buf[0] = reg; + memcpy(buf+1, data, count); + + if(i2c_master_send(t->client, buf, count+1)==count+1) + return 0; + return -1; +} + +static int i2c_senddata(struct saa5249_device *t, ...) +{ + unsigned char buf[64]; + int v; + int ct=0; + va_list argp; + va_start(argp,t); + + while((v=va_arg(argp,int))!=-1) + buf[ct++]=v; + return i2c_sendbuf(t, buf[0], ct-1, buf+1); +} + +/* Get count number of bytes from I²C-device at address adr, store them in buf. Start & stop + * handshaking is done by this routine, ack will be sent after the last byte to inhibit further + * sending of data. If uaccess is TRUE, data is written to user-space with put_user. + * Returns -1 if I²C-device didn't send acknowledge, 0 otherwise + */ + +static int i2c_getdata(struct saa5249_device *t, int count, u8 *buf) +{ + if(i2c_master_recv(t->client, buf, count)!=count) + return -1; + return 0; +} + + +/* + * Standard character-device-driver functions + */ + +static int do_saa5249_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + static int virtual_mode = FALSE; + struct video_device *vd = video_devdata(file); + struct saa5249_device *t=vd->priv; + + switch(cmd) + { + case VTXIOCGETINFO: + { + vtx_info_t *info = arg; + info->version_major = VTX_VER_MAJ; + info->version_minor = VTX_VER_MIN; + info->numpages = NUM_DAUS; + /*info->cct_type = CCT_TYPE;*/ + return 0; + } + + case VTXIOCCLRPAGE: + { + vtx_pagereq_t *req = arg; + + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + memset(t->vdau[req->pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); + t->vdau[req->pgbuf].clrfound = TRUE; + return 0; + } + + case VTXIOCCLRFOUND: + { + vtx_pagereq_t *req = arg; + + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + t->vdau[req->pgbuf].clrfound = TRUE; + return 0; + } + + case VTXIOCPAGEREQ: + { + vtx_pagereq_t *req = arg; + if (!(req->pagemask & PGMASK_PAGE)) + req->page = 0; + if (!(req->pagemask & PGMASK_HOUR)) + req->hour = 0; + if (!(req->pagemask & PGMASK_MINUTE)) + req->minute = 0; + if (req->page < 0 || req->page > 0x8ff) /* 7FF ?? */ + return -EINVAL; + req->page &= 0x7ff; + if (req->hour < 0 || req->hour > 0x3f || req->minute < 0 || req->minute > 0x7f || + req->pagemask < 0 || req->pagemask >= PGMASK_MAX || req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + t->vdau[req->pgbuf].sregs[0] = (req->pagemask & PG_HUND ? 0x10 : 0) | (req->page / 0x100); + t->vdau[req->pgbuf].sregs[1] = (req->pagemask & PG_TEN ? 0x10 : 0) | ((req->page / 0x10) & 0xf); + t->vdau[req->pgbuf].sregs[2] = (req->pagemask & PG_UNIT ? 0x10 : 0) | (req->page & 0xf); + t->vdau[req->pgbuf].sregs[3] = (req->pagemask & HR_TEN ? 0x10 : 0) | (req->hour / 0x10); + t->vdau[req->pgbuf].sregs[4] = (req->pagemask & HR_UNIT ? 0x10 : 0) | (req->hour & 0xf); + t->vdau[req->pgbuf].sregs[5] = (req->pagemask & MIN_TEN ? 0x10 : 0) | (req->minute / 0x10); + t->vdau[req->pgbuf].sregs[6] = (req->pagemask & MIN_UNIT ? 0x10 : 0) | (req->minute & 0xf); + t->vdau[req->pgbuf].stopped = FALSE; + t->vdau[req->pgbuf].clrfound = TRUE; + t->is_searching[req->pgbuf] = TRUE; + return 0; + } + + case VTXIOCGETSTAT: + { + vtx_pagereq_t *req = arg; + u8 infobits[10]; + vtx_pageinfo_t info; + int a; + + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + if (!t->vdau[req->pgbuf].stopped) + { + if (i2c_senddata(t, 2, 0, -1) || + i2c_sendbuf(t, 3, sizeof(t->vdau[0].sregs), t->vdau[req->pgbuf].sregs) || + i2c_senddata(t, 8, 0, 25, 0, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', -1) || + i2c_senddata(t, 2, 0, t->vdau[req->pgbuf].sregs[0] | 8, -1) || + i2c_senddata(t, 8, 0, 25, 0, -1)) + return -EIO; + jdelay(PAGE_WAIT); + if (i2c_getdata(t, 10, infobits)) + return -EIO; + + if (!(infobits[8] & 0x10) && !(infobits[7] & 0xf0) && /* check FOUND-bit */ + (memcmp(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)) || + time_after_eq(jiffies, t->vdau[req->pgbuf].expire))) + { /* check if new page arrived */ + if (i2c_senddata(t, 8, 0, 0, 0, -1) || + i2c_getdata(t, VTX_PAGESIZE, t->vdau[req->pgbuf].pgbuf)) + return -EIO; + t->vdau[req->pgbuf].expire = jiffies + PGBUF_EXPIRE; + memset(t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE, ' ', VTX_VIRTUALSIZE - VTX_PAGESIZE); + if (t->virtual_mode) + { + /* Packet X/24 */ + if (i2c_senddata(t, 8, 0, 0x20, 0, -1) || + i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 20 * 40)) + return -EIO; + /* Packet X/27/0 */ + if (i2c_senddata(t, 8, 0, 0x21, 0, -1) || + i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 16 * 40)) + return -EIO; + /* Packet 8/30/0...8/30/15 + * FIXME: AFAIK, the 5249 does hamming-decoding for some bytes in packet 8/30, + * so we should undo this here. + */ + if (i2c_senddata(t, 8, 0, 0x22, 0, -1) || + i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 23 * 40)) + return -EIO; + } + t->vdau[req->pgbuf].clrfound = FALSE; + memcpy(t->vdau[req->pgbuf].laststat, infobits, sizeof(infobits)); + } + else + { + memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)); + } + } + else + { + memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)); + } + + info.pagenum = ((infobits[8] << 8) & 0x700) | ((infobits[1] << 4) & 0xf0) | (infobits[0] & 0x0f); + if (info.pagenum < 0x100) + info.pagenum += 0x800; + info.hour = ((infobits[5] << 4) & 0x30) | (infobits[4] & 0x0f); + info.minute = ((infobits[3] << 4) & 0x70) | (infobits[2] & 0x0f); + info.charset = ((infobits[7] >> 1) & 7); + info.delete = !!(infobits[3] & 8); + info.headline = !!(infobits[5] & 4); + info.subtitle = !!(infobits[5] & 8); + info.supp_header = !!(infobits[6] & 1); + info.update = !!(infobits[6] & 2); + info.inter_seq = !!(infobits[6] & 4); + info.dis_disp = !!(infobits[6] & 8); + info.serial = !!(infobits[7] & 1); + info.notfound = !!(infobits[8] & 0x10); + info.pblf = !!(infobits[9] & 0x20); + info.hamming = 0; + for (a = 0; a <= 7; a++) + { + if (infobits[a] & 0xf0) + { + info.hamming = 1; + break; + } + } + if (t->vdau[req->pgbuf].clrfound) + info.notfound = 1; + if(copy_to_user(req->buffer, &info, sizeof(vtx_pageinfo_t))) + return -EFAULT; + if (!info.hamming && !info.notfound) + { + t->is_searching[req->pgbuf] = FALSE; + } + return 0; + } + + case VTXIOCGETPAGE: + { + vtx_pagereq_t *req = arg; + int start, end; + + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS || req->start < 0 || + req->start > req->end || req->end >= (virtual_mode ? VTX_VIRTUALSIZE : VTX_PAGESIZE)) + return -EINVAL; + if(copy_to_user(req->buffer, &t->vdau[req->pgbuf].pgbuf[req->start], req->end - req->start + 1)) + return -EFAULT; + + /* + * Always read the time directly from SAA5249 + */ + + if (req->start <= 39 && req->end >= 32) + { + int len; + char buf[16]; + start = max(req->start, 32); + end = min(req->end, 39); + len=end-start+1; + if (i2c_senddata(t, 8, 0, 0, start, -1) || + i2c_getdata(t, len, buf)) + return -EIO; + if(copy_to_user(req->buffer+start-req->start, buf, len)) + return -EFAULT; + } + /* Insert the current header if DAU is still searching for a page */ + if (req->start <= 31 && req->end >= 7 && t->is_searching[req->pgbuf]) + { + char buf[32]; + int len; + start = max(req->start, 7); + end = min(req->end, 31); + len=end-start+1; + if (i2c_senddata(t, 8, 0, 0, start, -1) || + i2c_getdata(t, len, buf)) + return -EIO; + if(copy_to_user(req->buffer+start-req->start, buf, len)) + return -EFAULT; + } + return 0; + } + + case VTXIOCSTOPDAU: + { + vtx_pagereq_t *req = arg; + + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + t->vdau[req->pgbuf].stopped = TRUE; + t->is_searching[req->pgbuf] = FALSE; + return 0; + } + + case VTXIOCPUTPAGE: + case VTXIOCSETDISP: + case VTXIOCPUTSTAT: + return 0; + + case VTXIOCCLRCACHE: + { + if (i2c_senddata(t, 0, NUM_DAUS, 0, 8, -1) || i2c_senddata(t, 11, + ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', + ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', -1)) + return -EIO; + if (i2c_senddata(t, 3, 0x20, -1)) + return -EIO; + jdelay(10 * CLEAR_DELAY); /* I have no idea how long we have to wait here */ + return 0; + } + + case VTXIOCSETVIRT: + { + /* The SAA5249 has virtual-row reception turned on always */ + t->virtual_mode = (int)(long)arg; + return 0; + } + } + return -EINVAL; +} + +/* + * Translates old vtx IOCTLs to new ones + * + * This keeps new kernel versions compatible with old userspace programs. + */ +static inline unsigned int vtx_fix_command(unsigned int cmd) +{ + switch (cmd) { + case VTXIOCGETINFO_OLD: + cmd = VTXIOCGETINFO; + break; + case VTXIOCCLRPAGE_OLD: + cmd = VTXIOCCLRPAGE; + break; + case VTXIOCCLRFOUND_OLD: + cmd = VTXIOCCLRFOUND; + break; + case VTXIOCPAGEREQ_OLD: + cmd = VTXIOCPAGEREQ; + break; + case VTXIOCGETSTAT_OLD: + cmd = VTXIOCGETSTAT; + break; + case VTXIOCGETPAGE_OLD: + cmd = VTXIOCGETPAGE; + break; + case VTXIOCSTOPDAU_OLD: + cmd = VTXIOCSTOPDAU; + break; + case VTXIOCPUTPAGE_OLD: + cmd = VTXIOCPUTPAGE; + break; + case VTXIOCSETDISP_OLD: + cmd = VTXIOCSETDISP; + break; + case VTXIOCPUTSTAT_OLD: + cmd = VTXIOCPUTSTAT; + break; + case VTXIOCCLRCACHE_OLD: + cmd = VTXIOCCLRCACHE; + break; + case VTXIOCSETVIRT_OLD: + cmd = VTXIOCSETVIRT; + break; + } + return cmd; +} + +/* + * Handle the locking + */ + +static int saa5249_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vd = video_devdata(file); + struct saa5249_device *t=vd->priv; + int err; + + cmd = vtx_fix_command(cmd); + mutex_lock(&t->lock); + err = video_usercopy(inode,file,cmd,arg,do_saa5249_ioctl); + mutex_unlock(&t->lock); + return err; +} + +static int saa5249_open(struct inode *inode, struct file *file) +{ + struct video_device *vd = video_devdata(file); + struct saa5249_device *t=vd->priv; + int err,pgbuf; + + err = video_exclusive_open(inode,file); + if (err < 0) + return err; + + if (t->client==NULL) { + err = -ENODEV; + goto fail; + } + + if (i2c_senddata(t, 0, 0, -1) || /* Select R11 */ + /* Turn off parity checks (we do this ourselves) */ + i2c_senddata(t, 1, disp_modes[t->disp_mode][0], 0, -1) || + /* Display TV-picture, no virtual rows */ + i2c_senddata(t, 4, NUM_DAUS, disp_modes[t->disp_mode][1], disp_modes[t->disp_mode][2], 7, -1)) /* Set display to page 4 */ + + { + err = -EIO; + goto fail; + } + + for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) + { + memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); + memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs)); + memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat)); + t->vdau[pgbuf].expire = 0; + t->vdau[pgbuf].clrfound = TRUE; + t->vdau[pgbuf].stopped = TRUE; + t->is_searching[pgbuf] = FALSE; + } + t->virtual_mode=FALSE; + return 0; + + fail: + video_exclusive_release(inode,file); + return err; +} + + + +static int saa5249_release(struct inode *inode, struct file *file) +{ + struct video_device *vd = video_devdata(file); + struct saa5249_device *t=vd->priv; + i2c_senddata(t, 1, 0x20, -1); /* Turn off CCT */ + i2c_senddata(t, 5, 3, 3, -1); /* Turn off TV-display */ + video_exclusive_release(inode,file); + return 0; +} + +static int __init init_saa_5249 (void) +{ + printk(KERN_INFO "SAA5249 driver (" IF_NAME " interface) for VideoText version %d.%d\n", + VTX_VER_MAJ, VTX_VER_MIN); + return i2c_add_driver(&i2c_driver_videotext); +} + +static void __exit cleanup_saa_5249 (void) +{ + i2c_del_driver(&i2c_driver_videotext); +} + +module_init(init_saa_5249); +module_exit(cleanup_saa_5249); + +static struct file_operations saa_fops = { + .owner = THIS_MODULE, + .open = saa5249_open, + .release = saa5249_release, + .ioctl = saa5249_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device saa_template = +{ + .owner = THIS_MODULE, + .name = IF_NAME, + .type = VID_TYPE_TELETEXT, /*| VID_TYPE_TUNER ?? */ + .hardware = VID_HARDWARE_SAA5249, + .fops = &saa_fops, +}; + +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/saa7110.c b/linux/drivers/media/video/saa7110.c new file mode 100644 index 000000000..b1797cbb6 --- /dev/null +++ b/linux/drivers/media/video/saa7110.c @@ -0,0 +1,604 @@ +/* + * saa7110 - Philips SAA7110(A) video decoder driver + * + * Copyright (C) 1998 Pauline Middelink + * + * Copyright (C) 1999 Wolfgang Scherr + * Copyright (C) 2000 Serguei Miridonov + * - some corrections for Pinnacle Systems Inc. DC10plus card. + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (1/1/2003) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Philips SAA7110 video decoder driver"); +MODULE_AUTHOR("Pauline Middelink"); +MODULE_LICENSE("GPL"); + +#include + +#define I2C_NAME(s) (s)->name + +#include "compat.h" +#include +#include + +static int debug = 0; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define dprintk(num, format, args...) \ + do { \ + if (debug >= num) \ + printk(format, ##args); \ + } while (0) + +#define SAA7110_MAX_INPUT 9 /* 6 CVBS, 3 SVHS */ +#define SAA7110_MAX_OUTPUT 0 /* its a decoder only */ + +#define I2C_SAA7110 0x9C /* or 0x9E */ + +#define SAA7110_NR_REG 0x35 + +struct saa7110 { + u8 reg[SAA7110_NR_REG]; + + int norm; + int input; + int enable; + int bright; + int contrast; + int hue; + int sat; + + wait_queue_head_t wq; +}; + +/* ----------------------------------------------------------------------- */ +/* I2C support functions */ +/* ----------------------------------------------------------------------- */ + +static int +saa7110_write (struct i2c_client *client, + u8 reg, + u8 value) +{ + struct saa7110 *decoder = i2c_get_clientdata(client); + + decoder->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int +saa7110_write_block (struct i2c_client *client, + const u8 *data, + unsigned int len) +{ + int ret = -1; + u8 reg = *data; /* first register to write to */ + + /* Sanity check */ + if (reg + (len - 1) > SAA7110_NR_REG) + return ret; + + /* the saa7110 has an autoincrement function, use it if + * the adapter understands raw I2C */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + struct saa7110 *decoder = i2c_get_clientdata(client); + + ret = i2c_master_send(client, data, len); + + /* Cache the written data */ + memcpy(decoder->reg + reg, data + 1, len - 1); + } else { + for (++data, --len; len; len--) { + if ((ret = saa7110_write(client, reg++, + *data++)) < 0) + break; + } + } + + return ret; +} + +static inline int +saa7110_read (struct i2c_client *client) +{ + return i2c_smbus_read_byte(client); +} + +/* ----------------------------------------------------------------------- */ +/* SAA7110 functions */ +/* ----------------------------------------------------------------------- */ + +#define FRESP_06H_COMPST 0x03 //0x13 +#define FRESP_06H_SVIDEO 0x83 //0xC0 + + +static int +saa7110_selmux (struct i2c_client *client, + int chan) +{ + static const unsigned char modes[9][8] = { + /* mode 0 */ + {FRESP_06H_COMPST, 0xD9, 0x17, 0x40, 0x03, + 0x44, 0x75, 0x16}, + /* mode 1 */ + {FRESP_06H_COMPST, 0xD8, 0x17, 0x40, 0x03, + 0x44, 0x75, 0x16}, + /* mode 2 */ + {FRESP_06H_COMPST, 0xBA, 0x07, 0x91, 0x03, + 0x60, 0xB5, 0x05}, + /* mode 3 */ + {FRESP_06H_COMPST, 0xB8, 0x07, 0x91, 0x03, + 0x60, 0xB5, 0x05}, + /* mode 4 */ + {FRESP_06H_COMPST, 0x7C, 0x07, 0xD2, 0x83, + 0x60, 0xB5, 0x03}, + /* mode 5 */ + {FRESP_06H_COMPST, 0x78, 0x07, 0xD2, 0x83, + 0x60, 0xB5, 0x03}, + /* mode 6 */ + {FRESP_06H_SVIDEO, 0x59, 0x17, 0x42, 0xA3, + 0x44, 0x75, 0x12}, + /* mode 7 */ + {FRESP_06H_SVIDEO, 0x9A, 0x17, 0xB1, 0x13, + 0x60, 0xB5, 0x14}, + /* mode 8 */ + {FRESP_06H_SVIDEO, 0x3C, 0x27, 0xC1, 0x23, + 0x44, 0x75, 0x21} + }; + struct saa7110 *decoder = i2c_get_clientdata(client); + const unsigned char *ptr = modes[chan]; + + saa7110_write(client, 0x06, ptr[0]); /* Luminance control */ + saa7110_write(client, 0x20, ptr[1]); /* Analog Control #1 */ + saa7110_write(client, 0x21, ptr[2]); /* Analog Control #2 */ + saa7110_write(client, 0x22, ptr[3]); /* Mixer Control #1 */ + saa7110_write(client, 0x2C, ptr[4]); /* Mixer Control #2 */ + saa7110_write(client, 0x30, ptr[5]); /* ADCs gain control */ + saa7110_write(client, 0x31, ptr[6]); /* Mixer Control #3 */ + saa7110_write(client, 0x21, ptr[7]); /* Analog Control #2 */ + decoder->input = chan; + + return 0; +} + +static const unsigned char initseq[1 + SAA7110_NR_REG] = { + 0, 0x4C, 0x3C, 0x0D, 0xEF, 0xBD, 0xF2, 0x03, 0x00, + /* 0x08 */ 0xF8, 0xF8, 0x60, 0x60, 0x00, 0x86, 0x18, 0x90, + /* 0x10 */ 0x00, 0x59, 0x40, 0x46, 0x42, 0x1A, 0xFF, 0xDA, + /* 0x18 */ 0xF2, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x20 */ 0xD9, 0x16, 0x40, 0x41, 0x80, 0x41, 0x80, 0x4F, + /* 0x28 */ 0xFE, 0x01, 0xCF, 0x0F, 0x03, 0x01, 0x03, 0x0C, + /* 0x30 */ 0x44, 0x71, 0x02, 0x8C, 0x02 +}; + +static int +determine_norm (struct i2c_client *client) +{ + DEFINE_WAIT(wait); + struct saa7110 *decoder = i2c_get_clientdata(client); + int status; + + /* mode changed, start automatic detection */ + saa7110_write_block(client, initseq, sizeof(initseq)); + saa7110_selmux(client, decoder->input); + prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/4); + finish_wait(&decoder->wq, &wait); + status = saa7110_read(client); + if (status & 0x40) { + dprintk(1, KERN_INFO "%s: status=0x%02x (no signal)\n", + I2C_NAME(client), status); + return decoder->norm; // no change + } + if ((status & 3) == 0) { + saa7110_write(client, 0x06, 0x83); + if (status & 0x20) { + dprintk(1, + KERN_INFO + "%s: status=0x%02x (NTSC/no color)\n", + I2C_NAME(client), status); + //saa7110_write(client,0x2E,0x81); + return VIDEO_MODE_NTSC; + } + dprintk(1, KERN_INFO "%s: status=0x%02x (PAL/no color)\n", + I2C_NAME(client), status); + //saa7110_write(client,0x2E,0x9A); + return VIDEO_MODE_PAL; + } + //saa7110_write(client,0x06,0x03); + if (status & 0x20) { /* 60Hz */ + dprintk(1, KERN_INFO "%s: status=0x%02x (NTSC)\n", + I2C_NAME(client), status); + saa7110_write(client, 0x0D, 0x86); + saa7110_write(client, 0x0F, 0x50); + saa7110_write(client, 0x11, 0x2C); + //saa7110_write(client,0x2E,0x81); + return VIDEO_MODE_NTSC; + } + + /* 50Hz -> PAL/SECAM */ + saa7110_write(client, 0x0D, 0x86); + saa7110_write(client, 0x0F, 0x10); + saa7110_write(client, 0x11, 0x59); + //saa7110_write(client,0x2E,0x9A); + + prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/4); + finish_wait(&decoder->wq, &wait); + + status = saa7110_read(client); + if ((status & 0x03) == 0x01) { + dprintk(1, KERN_INFO "%s: status=0x%02x (SECAM)\n", + I2C_NAME(client), status); + saa7110_write(client, 0x0D, 0x87); + return VIDEO_MODE_SECAM; + } + dprintk(1, KERN_INFO "%s: status=0x%02x (PAL)\n", I2C_NAME(client), + status); + return VIDEO_MODE_PAL; +} + +static int +saa7110_command (struct i2c_client *client, + unsigned int cmd, + void *arg) +{ + struct saa7110 *decoder = i2c_get_clientdata(client); + int v; + + switch (cmd) { + case 0: + //saa7110_write_block(client, initseq, sizeof(initseq)); + break; + + case DECODER_GET_CAPABILITIES: + { + struct video_decoder_capability *dc = arg; + + dc->flags = + VIDEO_DECODER_PAL | VIDEO_DECODER_NTSC | + VIDEO_DECODER_SECAM | VIDEO_DECODER_AUTO; + dc->inputs = SAA7110_MAX_INPUT; + dc->outputs = SAA7110_MAX_OUTPUT; + } + break; + + case DECODER_GET_STATUS: + { + int status; + int res = 0; + + status = saa7110_read(client); + dprintk(1, KERN_INFO "%s: status=0x%02x norm=%d\n", + I2C_NAME(client), status, decoder->norm); + if (!(status & 0x40)) + res |= DECODER_STATUS_GOOD; + if (status & 0x03) + res |= DECODER_STATUS_COLOR; + + switch (decoder->norm) { + case VIDEO_MODE_NTSC: + res |= DECODER_STATUS_NTSC; + break; + case VIDEO_MODE_PAL: + res |= DECODER_STATUS_PAL; + break; + case VIDEO_MODE_SECAM: + res |= DECODER_STATUS_SECAM; + break; + } + *(int *) arg = res; + } + break; + + case DECODER_SET_NORM: + v = *(int *) arg; + if (decoder->norm != v) { + decoder->norm = v; + //saa7110_write(client, 0x06, 0x03); + switch (v) { + case VIDEO_MODE_NTSC: + saa7110_write(client, 0x0D, 0x86); + saa7110_write(client, 0x0F, 0x50); + saa7110_write(client, 0x11, 0x2C); + //saa7110_write(client, 0x2E, 0x81); + dprintk(1, + KERN_INFO "%s: switched to NTSC\n", + I2C_NAME(client)); + break; + case VIDEO_MODE_PAL: + saa7110_write(client, 0x0D, 0x86); + saa7110_write(client, 0x0F, 0x10); + saa7110_write(client, 0x11, 0x59); + //saa7110_write(client, 0x2E, 0x9A); + dprintk(1, + KERN_INFO "%s: switched to PAL\n", + I2C_NAME(client)); + break; + case VIDEO_MODE_SECAM: + saa7110_write(client, 0x0D, 0x87); + saa7110_write(client, 0x0F, 0x10); + saa7110_write(client, 0x11, 0x59); + //saa7110_write(client, 0x2E, 0x9A); + dprintk(1, + KERN_INFO + "%s: switched to SECAM\n", + I2C_NAME(client)); + break; + case VIDEO_MODE_AUTO: + dprintk(1, + KERN_INFO + "%s: TV standard detection...\n", + I2C_NAME(client)); + decoder->norm = determine_norm(client); + *(int *) arg = decoder->norm; + break; + default: + return -EPERM; + } + } + break; + + case DECODER_SET_INPUT: + v = *(int *) arg; + if (v < 0 || v > SAA7110_MAX_INPUT) { + dprintk(1, + KERN_INFO "%s: input=%d not available\n", + I2C_NAME(client), v); + return -EINVAL; + } + if (decoder->input != v) { + saa7110_selmux(client, v); + dprintk(1, KERN_INFO "%s: switched to input=%d\n", + I2C_NAME(client), v); + } + break; + + case DECODER_SET_OUTPUT: + v = *(int *) arg; + /* not much choice of outputs */ + if (v != 0) + return -EINVAL; + break; + + case DECODER_ENABLE_OUTPUT: + v = *(int *) arg; + if (decoder->enable != v) { + decoder->enable = v; + saa7110_write(client, 0x0E, v ? 0x18 : 0x80); + dprintk(1, KERN_INFO "%s: YUV %s\n", I2C_NAME(client), + v ? "on" : "off"); + } + break; + + case DECODER_SET_PICTURE: + { + struct video_picture *pic = arg; + + if (decoder->bright != pic->brightness) { + /* We want 0 to 255 we get 0-65535 */ + decoder->bright = pic->brightness; + saa7110_write(client, 0x19, decoder->bright >> 8); + } + if (decoder->contrast != pic->contrast) { + /* We want 0 to 127 we get 0-65535 */ + decoder->contrast = pic->contrast; + saa7110_write(client, 0x13, + decoder->contrast >> 9); + } + if (decoder->sat != pic->colour) { + /* We want 0 to 127 we get 0-65535 */ + decoder->sat = pic->colour; + saa7110_write(client, 0x12, decoder->sat >> 9); + } + if (decoder->hue != pic->hue) { + /* We want -128 to 127 we get 0-65535 */ + decoder->hue = pic->hue; + saa7110_write(client, 0x07, + (decoder->hue >> 8) - 128); + } + } + break; + + case DECODER_DUMP: + for (v = 0; v < SAA7110_NR_REG; v += 16) { + int j; + dprintk(1, KERN_DEBUG "%s: %02x:", I2C_NAME(client), + v); + for (j = 0; j < 16 && v + j < SAA7110_NR_REG; j++) + dprintk(1, " %02x", decoder->reg[v + j]); + dprintk(1, "\n"); + } + break; + + default: + dprintk(1, KERN_INFO "unknown saa7110_command??(%d)\n", + cmd); + return -EINVAL; + } + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ +static unsigned short normal_i2c[] = { + I2C_SAA7110 >> 1, + (I2C_SAA7110 >> 1) + 1, + I2C_CLIENT_END +}; + +static unsigned short ignore = I2C_CLIENT_END; + +static struct i2c_client_address_data addr_data = { + .normal_i2c = normal_i2c, + .probe = &ignore, + .ignore = &ignore, +}; + +static struct i2c_driver i2c_driver_saa7110; + +static int +saa7110_detect_client (struct i2c_adapter *adapter, + int address, + int kind) +{ + struct i2c_client *client; + struct saa7110 *decoder; + int rv; + + dprintk(1, + KERN_INFO + "saa7110.c: detecting saa7110 client on address 0x%x\n", + address << 1); + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality + (adapter, + I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == 0) + return -ENOMEM; + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver_saa7110; + strlcpy(I2C_NAME(client), "saa7110", sizeof(I2C_NAME(client))); + + decoder = kzalloc(sizeof(struct saa7110), GFP_KERNEL); + if (decoder == 0) { + kfree(client); + return -ENOMEM; + } + decoder->norm = VIDEO_MODE_PAL; + decoder->input = 0; + decoder->enable = 1; + decoder->bright = 32768; + decoder->contrast = 32768; + decoder->hue = 32768; + decoder->sat = 32768; + init_waitqueue_head(&decoder->wq); + i2c_set_clientdata(client, decoder); + + rv = i2c_attach_client(client); + if (rv) { + kfree(client); + kfree(decoder); + return rv; + } + + rv = saa7110_write_block(client, initseq, sizeof(initseq)); + if (rv < 0) + dprintk(1, KERN_ERR "%s_attach: init status %d\n", + I2C_NAME(client), rv); + else { + int ver, status; + saa7110_write(client, 0x21, 0x10); + saa7110_write(client, 0x0e, 0x18); + saa7110_write(client, 0x0D, 0x04); + ver = saa7110_read(client); + saa7110_write(client, 0x0D, 0x06); + //mdelay(150); + status = saa7110_read(client); + dprintk(1, + KERN_INFO + "%s_attach: SAA7110A version %x at 0x%02x, status=0x%02x\n", + I2C_NAME(client), ver, client->addr << 1, status); + saa7110_write(client, 0x0D, 0x86); + saa7110_write(client, 0x0F, 0x10); + saa7110_write(client, 0x11, 0x59); + //saa7110_write(client, 0x2E, 0x9A); + } + + //saa7110_selmux(client,0); + //determine_norm(client); + /* setup and implicit mode 0 select has been performed */ + + return 0; +} + +static int +saa7110_attach_adapter (struct i2c_adapter *adapter) +{ + dprintk(1, + KERN_INFO + "saa7110.c: starting probe for adapter %s (0x%x)\n", + I2C_NAME(adapter), adapter->id); + return i2c_probe(adapter, &addr_data, &saa7110_detect_client); +} + +static int +saa7110_detach_client (struct i2c_client *client) +{ + struct saa7110 *decoder = i2c_get_clientdata(client); + int err; + + err = i2c_detach_client(client); + if (err) { + return err; + } + + kfree(decoder); + kfree(client); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver i2c_driver_saa7110 = { + .driver = { + .name = "saa7110", + }, + + .id = I2C_DRIVERID_SAA7110, + + .attach_adapter = saa7110_attach_adapter, + .detach_client = saa7110_detach_client, + .command = saa7110_command, +}; + +static int __init +saa7110_init (void) +{ + return i2c_add_driver(&i2c_driver_saa7110); +} + +static void __exit +saa7110_exit (void) +{ + i2c_del_driver(&i2c_driver_saa7110); +} + +module_init(saa7110_init); +module_exit(saa7110_exit); diff --git a/linux/drivers/media/video/saa7111.c b/linux/drivers/media/video/saa7111.c new file mode 100644 index 000000000..1fbd343c3 --- /dev/null +++ b/linux/drivers/media/video/saa7111.c @@ -0,0 +1,614 @@ +/* + * saa7111 - Philips SAA7111A video decoder driver version 0.0.3 + * + * Copyright (C) 1998 Dave Perks + * + * Slight changes for video timing and attachment output by + * Wolfgang Scherr + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (1/1/2003) + * + * Changes by Michael Hunold + * - implemented DECODER_SET_GPIO, DECODER_INIT, DECODER_SET_VBI_BYPASS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compat.h" +#include +#include + +MODULE_DESCRIPTION("Philips SAA7111 video decoder driver"); +MODULE_AUTHOR("Dave Perks"); +MODULE_LICENSE("GPL"); + +#include + +#define I2C_NAME(s) (s)->name + +#include + +static int debug = 0; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define dprintk(num, format, args...) \ + do { \ + if (debug >= num) \ + printk(format, ##args); \ + } while (0) + +/* ----------------------------------------------------------------------- */ + +#define SAA7111_NR_REG 0x18 + +struct saa7111 { + unsigned char reg[SAA7111_NR_REG]; + + int norm; + int input; + int enable; + int bright; + int contrast; + int hue; + int sat; +}; + +#define I2C_SAA7111 0x48 + +/* ----------------------------------------------------------------------- */ + +static inline int +saa7111_write (struct i2c_client *client, + u8 reg, + u8 value) +{ + struct saa7111 *decoder = i2c_get_clientdata(client); + + decoder->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int +saa7111_write_block (struct i2c_client *client, + const u8 *data, + unsigned int len) +{ + int ret = -1; + u8 reg; + + /* the saa7111 has an autoincrement function, use it if + * the adapter understands raw I2C */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + /* do raw I2C, not smbus compatible */ + struct saa7111 *decoder = i2c_get_clientdata(client); + u8 block_data[32]; + int block_len; + + while (len >= 2) { + block_len = 0; + block_data[block_len++] = reg = data[0]; + do { + block_data[block_len++] = + decoder->reg[reg++] = data[1]; + len -= 2; + data += 2; + } while (len >= 2 && data[0] == reg && + block_len < 32); + if ((ret = i2c_master_send(client, block_data, + block_len)) < 0) + break; + } + } else { + /* do some slow I2C emulation kind of thing */ + while (len >= 2) { + reg = *data++; + if ((ret = saa7111_write(client, reg, + *data++)) < 0) + break; + len -= 2; + } + } + + return ret; +} + +static int +saa7111_init_decoder (struct i2c_client *client, + struct video_decoder_init *init) +{ + return saa7111_write_block(client, init->data, init->len); +} + +static inline int +saa7111_read (struct i2c_client *client, + u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +/* ----------------------------------------------------------------------- */ + +static const unsigned char saa7111_i2c_init[] = { + 0x00, 0x00, /* 00 - ID byte */ + 0x01, 0x00, /* 01 - reserved */ + + /*front end */ + 0x02, 0xd0, /* 02 - FUSE=3, GUDL=2, MODE=0 */ + 0x03, 0x23, /* 03 - HLNRS=0, VBSL=1, WPOFF=0, + * HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */ + 0x04, 0x00, /* 04 - GAI1=256 */ + 0x05, 0x00, /* 05 - GAI2=256 */ + + /* decoder */ + 0x06, 0xf3, /* 06 - HSB at 13(50Hz) / 17(60Hz) + * pixels after end of last line */ + /*0x07, 0x13, * 07 - HSS at 113(50Hz) / 117(60Hz) pixels + * after end of last line */ + 0x07, 0xe8, /* 07 - HSS seems to be needed to + * work with NTSC, too */ + 0x08, 0xc8, /* 08 - AUFD=1, FSEL=1, EXFIL=0, + * VTRC=1, HPLL=0, VNOI=0 */ + 0x09, 0x01, /* 09 - BYPS=0, PREF=0, BPSS=0, + * VBLB=0, UPTCV=0, APER=1 */ + 0x0a, 0x80, /* 0a - BRIG=128 */ + 0x0b, 0x47, /* 0b - CONT=1.109 */ + 0x0c, 0x40, /* 0c - SATN=1.0 */ + 0x0d, 0x00, /* 0d - HUE=0 */ + 0x0e, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, + * FCTC=0, CHBW=1 */ + 0x0f, 0x00, /* 0f - reserved */ + 0x10, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */ + 0x11, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, + * OEYC=1, OEHV=1, VIPB=0, COLO=0 */ + 0x12, 0x00, /* 12 - output control 2 */ + 0x13, 0x00, /* 13 - output control 3 */ + 0x14, 0x00, /* 14 - reserved */ + 0x15, 0x00, /* 15 - VBI */ + 0x16, 0x00, /* 16 - VBI */ + 0x17, 0x00, /* 17 - VBI */ +}; + +static int +saa7111_command (struct i2c_client *client, + unsigned int cmd, + void *arg) +{ + struct saa7111 *decoder = i2c_get_clientdata(client); + + switch (cmd) { + + case 0: + break; + case DECODER_INIT: + { + struct video_decoder_init *init = arg; + if (NULL != init) + return saa7111_init_decoder(client, init); + else { + struct video_decoder_init vdi; + vdi.data = saa7111_i2c_init; + vdi.len = sizeof(saa7111_i2c_init); + return saa7111_init_decoder(client, &vdi); + } + } + + case DECODER_DUMP: + { + int i; + + for (i = 0; i < SAA7111_NR_REG; i += 16) { + int j; + + printk(KERN_DEBUG "%s: %03x", I2C_NAME(client), i); + for (j = 0; j < 16 && i + j < SAA7111_NR_REG; ++j) { + printk(" %02x", + saa7111_read(client, i + j)); + } + printk("\n"); + } + } + break; + + case DECODER_GET_CAPABILITIES: + { + struct video_decoder_capability *cap = arg; + + cap->flags = VIDEO_DECODER_PAL | + VIDEO_DECODER_NTSC | + VIDEO_DECODER_SECAM | + VIDEO_DECODER_AUTO | + VIDEO_DECODER_CCIR; + cap->inputs = 8; + cap->outputs = 1; + } + break; + + case DECODER_GET_STATUS: + { + int *iarg = arg; + int status; + int res; + + status = saa7111_read(client, 0x1f); + dprintk(1, KERN_DEBUG "%s status: 0x%02x\n", I2C_NAME(client), + status); + res = 0; + if ((status & (1 << 6)) == 0) { + res |= DECODER_STATUS_GOOD; + } + switch (decoder->norm) { + case VIDEO_MODE_NTSC: + res |= DECODER_STATUS_NTSC; + break; + case VIDEO_MODE_PAL: + res |= DECODER_STATUS_PAL; + break; + case VIDEO_MODE_SECAM: + res |= DECODER_STATUS_SECAM; + break; + default: + case VIDEO_MODE_AUTO: + if ((status & (1 << 5)) != 0) { + res |= DECODER_STATUS_NTSC; + } else { + res |= DECODER_STATUS_PAL; + } + break; + } + if ((status & (1 << 0)) != 0) { + res |= DECODER_STATUS_COLOR; + } + *iarg = res; + } + break; + + case DECODER_SET_GPIO: + { + int *iarg = arg; + if (0 != *iarg) { + saa7111_write(client, 0x11, + (decoder->reg[0x11] | 0x80)); + } else { + saa7111_write(client, 0x11, + (decoder->reg[0x11] & 0x7f)); + } + break; + } + + case DECODER_SET_VBI_BYPASS: + { + int *iarg = arg; + if (0 != *iarg) { + saa7111_write(client, 0x13, + (decoder->reg[0x13] & 0xf0) | 0x0a); + } else { + saa7111_write(client, 0x13, + (decoder->reg[0x13] & 0xf0)); + } + break; + } + + case DECODER_SET_NORM: + { + int *iarg = arg; + + switch (*iarg) { + + case VIDEO_MODE_NTSC: + saa7111_write(client, 0x08, + (decoder->reg[0x08] & 0x3f) | 0x40); + saa7111_write(client, 0x0e, + (decoder->reg[0x0e] & 0x8f)); + break; + + case VIDEO_MODE_PAL: + saa7111_write(client, 0x08, + (decoder->reg[0x08] & 0x3f) | 0x00); + saa7111_write(client, 0x0e, + (decoder->reg[0x0e] & 0x8f)); + break; + + case VIDEO_MODE_SECAM: + saa7111_write(client, 0x08, + (decoder->reg[0x08] & 0x3f) | 0x00); + saa7111_write(client, 0x0e, + (decoder->reg[0x0e] & 0x8f) | 0x50); + break; + + case VIDEO_MODE_AUTO: + saa7111_write(client, 0x08, + (decoder->reg[0x08] & 0x3f) | 0x80); + saa7111_write(client, 0x0e, + (decoder->reg[0x0e] & 0x8f)); + break; + + default: + return -EINVAL; + + } + decoder->norm = *iarg; + } + break; + + case DECODER_SET_INPUT: + { + int *iarg = arg; + + if (*iarg < 0 || *iarg > 7) { + return -EINVAL; + } + + if (decoder->input != *iarg) { + decoder->input = *iarg; + /* select mode */ + saa7111_write(client, 0x02, + (decoder-> + reg[0x02] & 0xf8) | decoder->input); + /* bypass chrominance trap for modes 4..7 */ + saa7111_write(client, 0x09, + (decoder-> + reg[0x09] & 0x7f) | ((decoder-> + input > + 3) ? 0x80 : + 0)); + } + } + break; + + case DECODER_SET_OUTPUT: + { + int *iarg = arg; + + /* not much choice of outputs */ + if (*iarg != 0) { + return -EINVAL; + } + } + break; + + case DECODER_ENABLE_OUTPUT: + { + int *iarg = arg; + int enable = (*iarg != 0); + + if (decoder->enable != enable) { + decoder->enable = enable; + + /* RJ: If output should be disabled (for + * playing videos), we also need a open PLL. + * The input is set to 0 (where no input + * source is connected), although this + * is not necessary. + * + * If output should be enabled, we have to + * reverse the above. + */ + + if (decoder->enable) { + saa7111_write(client, 0x02, + (decoder-> + reg[0x02] & 0xf8) | + decoder->input); + saa7111_write(client, 0x08, + (decoder->reg[0x08] & 0xfb)); + saa7111_write(client, 0x11, + (decoder-> + reg[0x11] & 0xf3) | 0x0c); + } else { + saa7111_write(client, 0x02, + (decoder->reg[0x02] & 0xf8)); + saa7111_write(client, 0x08, + (decoder-> + reg[0x08] & 0xfb) | 0x04); + saa7111_write(client, 0x11, + (decoder->reg[0x11] & 0xf3)); + } + } + } + break; + + case DECODER_SET_PICTURE: + { + struct video_picture *pic = arg; + + if (decoder->bright != pic->brightness) { + /* We want 0 to 255 we get 0-65535 */ + decoder->bright = pic->brightness; + saa7111_write(client, 0x0a, decoder->bright >> 8); + } + if (decoder->contrast != pic->contrast) { + /* We want 0 to 127 we get 0-65535 */ + decoder->contrast = pic->contrast; + saa7111_write(client, 0x0b, + decoder->contrast >> 9); + } + if (decoder->sat != pic->colour) { + /* We want 0 to 127 we get 0-65535 */ + decoder->sat = pic->colour; + saa7111_write(client, 0x0c, decoder->sat >> 9); + } + if (decoder->hue != pic->hue) { + /* We want -128 to 127 we get 0-65535 */ + decoder->hue = pic->hue; + saa7111_write(client, 0x0d, + (decoder->hue - 32768) >> 8); + } + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ +static unsigned short normal_i2c[] = { I2C_SAA7111 >> 1, I2C_CLIENT_END }; + +static unsigned short ignore = I2C_CLIENT_END; + +static struct i2c_client_address_data addr_data = { + .normal_i2c = normal_i2c, + .probe = &ignore, + .ignore = &ignore, +}; + +static struct i2c_driver i2c_driver_saa7111; + +static int +saa7111_detect_client (struct i2c_adapter *adapter, + int address, + int kind) +{ + int i; + struct i2c_client *client; + struct saa7111 *decoder; + struct video_decoder_init vdi; + + dprintk(1, + KERN_INFO + "saa7111.c: detecting saa7111 client on address 0x%x\n", + address << 1); + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == 0) + return -ENOMEM; + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver_saa7111; + strlcpy(I2C_NAME(client), "saa7111", sizeof(I2C_NAME(client))); + + decoder = kzalloc(sizeof(struct saa7111), GFP_KERNEL); + if (decoder == NULL) { + kfree(client); + return -ENOMEM; + } + decoder->norm = VIDEO_MODE_NTSC; + decoder->input = 0; + decoder->enable = 1; + decoder->bright = 32768; + decoder->contrast = 32768; + decoder->hue = 32768; + decoder->sat = 32768; + i2c_set_clientdata(client, decoder); + + i = i2c_attach_client(client); + if (i) { + kfree(client); + kfree(decoder); + return i; + } + + vdi.data = saa7111_i2c_init; + vdi.len = sizeof(saa7111_i2c_init); + i = saa7111_init_decoder(client, &vdi); + if (i < 0) { + dprintk(1, KERN_ERR "%s_attach error: init status %d\n", + I2C_NAME(client), i); + } else { + dprintk(1, + KERN_INFO + "%s_attach: chip version %x at address 0x%x\n", + I2C_NAME(client), saa7111_read(client, 0x00) >> 4, + client->addr << 1); + } + + return 0; +} + +static int +saa7111_attach_adapter (struct i2c_adapter *adapter) +{ + dprintk(1, + KERN_INFO + "saa7111.c: starting probe for adapter %s (0x%x)\n", + I2C_NAME(adapter), adapter->id); + return i2c_probe(adapter, &addr_data, &saa7111_detect_client); +} + +static int +saa7111_detach_client (struct i2c_client *client) +{ + struct saa7111 *decoder = i2c_get_clientdata(client); + int err; + + err = i2c_detach_client(client); + if (err) { + return err; + } + + kfree(decoder); + kfree(client); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver i2c_driver_saa7111 = { + .driver = { + .name = "saa7111", + }, + + .id = I2C_DRIVERID_SAA7111A, + + .attach_adapter = saa7111_attach_adapter, + .detach_client = saa7111_detach_client, + .command = saa7111_command, +}; + +static int __init +saa7111_init (void) +{ + return i2c_add_driver(&i2c_driver_saa7111); +} + +static void __exit +saa7111_exit (void) +{ + i2c_del_driver(&i2c_driver_saa7111); +} + +module_init(saa7111_init); +module_exit(saa7111_exit); diff --git a/linux/drivers/media/video/saa7114.c b/linux/drivers/media/video/saa7114.c new file mode 100644 index 000000000..45242ad54 --- /dev/null +++ b/linux/drivers/media/video/saa7114.c @@ -0,0 +1,1221 @@ +/* + * saa7114 - Philips SAA7114H video decoder driver version 0.0.1 + * + * Copyright (C) 2002 Maxim Yevtyushkin + * + * Based on saa7111 driver by Dave Perks + * + * Copyright (C) 1998 Dave Perks + * + * Slight changes for video timing and attachment output by + * Wolfgang Scherr + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (1/1/2003) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compat.h" +#include +#include + +MODULE_DESCRIPTION("Philips SAA7114H video decoder driver"); +MODULE_AUTHOR("Maxim Yevtyushkin"); +MODULE_LICENSE("GPL"); + +#include + +#define I2C_NAME(x) (x)->name + +#include + +static int debug = 0; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define dprintk(num, format, args...) \ + do { \ + if (debug >= num) \ + printk(format, ##args); \ + } while (0) + +/* ----------------------------------------------------------------------- */ + +struct saa7114 { + unsigned char reg[0xf0 * 2]; + + int norm; + int input; + int enable; + int bright; + int contrast; + int hue; + int sat; + int playback; +}; + +#define I2C_SAA7114 0x42 +#define I2C_SAA7114A 0x40 + +#define I2C_DELAY 10 + + +//#define SAA_7114_NTSC_HSYNC_START (-3) +//#define SAA_7114_NTSC_HSYNC_STOP (-18) + +#define SAA_7114_NTSC_HSYNC_START (-17) +#define SAA_7114_NTSC_HSYNC_STOP (-32) + +//#define SAA_7114_NTSC_HOFFSET (5) +#define SAA_7114_NTSC_HOFFSET (6) +#define SAA_7114_NTSC_VOFFSET (10) +#define SAA_7114_NTSC_WIDTH (720) +#define SAA_7114_NTSC_HEIGHT (250) + +#define SAA_7114_SECAM_HSYNC_START (-17) +#define SAA_7114_SECAM_HSYNC_STOP (-32) + +#define SAA_7114_SECAM_HOFFSET (2) +#define SAA_7114_SECAM_VOFFSET (10) +#define SAA_7114_SECAM_WIDTH (720) +#define SAA_7114_SECAM_HEIGHT (300) + +#define SAA_7114_PAL_HSYNC_START (-17) +#define SAA_7114_PAL_HSYNC_STOP (-32) + +#define SAA_7114_PAL_HOFFSET (2) +#define SAA_7114_PAL_VOFFSET (10) +#define SAA_7114_PAL_WIDTH (720) +#define SAA_7114_PAL_HEIGHT (300) + + + +#define SAA_7114_VERTICAL_CHROMA_OFFSET 0 //0x50504040 +#define SAA_7114_VERTICAL_LUMA_OFFSET 0 + +#define REG_ADDR(x) (((x) << 1) + 1) +#define LOBYTE(x) ((unsigned char)((x) & 0xff)) +#define HIBYTE(x) ((unsigned char)(((x) >> 8) & 0xff)) +#define LOWORD(x) ((unsigned short int)((x) & 0xffff)) +#define HIWORD(x) ((unsigned short int)(((x) >> 16) & 0xffff)) + + +/* ----------------------------------------------------------------------- */ + +static inline int +saa7114_write (struct i2c_client *client, + u8 reg, + u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int +saa7114_write_block (struct i2c_client *client, + const u8 *data, + unsigned int len) +{ + int ret = -1; + u8 reg; + + /* the saa7114 has an autoincrement function, use it if + * the adapter understands raw I2C */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + /* do raw I2C, not smbus compatible */ + u8 block_data[32]; + int block_len; + + while (len >= 2) { + block_len = 0; + block_data[block_len++] = reg = data[0]; + do { + block_data[block_len++] = data[1]; + reg++; + len -= 2; + data += 2; + } while (len >= 2 && data[0] == reg && + block_len < 32); + if ((ret = i2c_master_send(client, block_data, + block_len)) < 0) + break; + } + } else { + /* do some slow I2C emulation kind of thing */ + while (len >= 2) { + reg = *data++; + if ((ret = saa7114_write(client, reg, + *data++)) < 0) + break; + len -= 2; + } + } + + return ret; +} + +static inline int +saa7114_read (struct i2c_client *client, + u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +/* ----------------------------------------------------------------------- */ + +// initially set NTSC, composite + + +static const unsigned char init[] = { + 0x00, 0x00, /* 00 - ID byte , chip version, + * read only */ + 0x01, 0x08, /* 01 - X,X,X,X, IDEL3 to IDEL0 - + * horizontal increment delay, + * recommended position */ + 0x02, 0x00, /* 02 - FUSE=3, GUDL=2, MODE=0 ; + * input control */ + 0x03, 0x10, /* 03 - HLNRS=0, VBSL=1, WPOFF=0, + * HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */ + 0x04, 0x90, /* 04 - GAI1=256 */ + 0x05, 0x90, /* 05 - GAI2=256 */ + 0x06, SAA_7114_NTSC_HSYNC_START, /* 06 - HSB: hsync start, + * depends on the video standard */ + 0x07, SAA_7114_NTSC_HSYNC_STOP, /* 07 - HSS: hsync stop, depends + *on the video standard */ + 0x08, 0xb8, /* 08 - AUFD=1, FSEL=1, EXFIL=0, VTRC=1, + * HPLL: free running in playback, locked + * in capture, VNOI=0 */ + 0x09, 0x80, /* 09 - BYPS=0, PREF=0, BPSS=0, VBLB=0, + * UPTCV=0, APER=1; depends from input */ + 0x0a, 0x80, /* 0a - BRIG=128 */ + 0x0b, 0x44, /* 0b - CONT=1.109 */ + 0x0c, 0x40, /* 0c - SATN=1.0 */ + 0x0d, 0x00, /* 0d - HUE=0 */ + 0x0e, 0x84, /* 0e - CDTO, CSTD2 to 0, DCVF, FCTC, + * CCOMB; depends from video standard */ + 0x0f, 0x24, /* 0f - ACGC,CGAIN6 to CGAIN0; depends + * from video standard */ + 0x10, 0x03, /* 10 - OFFU1 to 0, OFFV1 to 0, CHBW, + * LCBW2 to 0 */ + 0x11, 0x59, /* 11 - COLO, RTP1, HEDL1 to 0, RTP0, + * YDEL2 to 0 */ + 0x12, 0xc9, /* 12 - RT signal control RTSE13 to 10 + * and 03 to 00 */ + 0x13, 0x80, /* 13 - RT/X port output control */ + 0x14, 0x00, /* 14 - analog, ADC, compatibility control */ + 0x15, 0x00, /* 15 - VGATE start FID change */ + 0x16, 0xfe, /* 16 - VGATE stop */ + 0x17, 0x00, /* 17 - Misc., VGATE MSBs */ + 0x18, 0x40, /* RAWG */ + 0x19, 0x80, /* RAWO */ + 0x1a, 0x00, + 0x1b, 0x00, + 0x1c, 0x00, + 0x1d, 0x00, + 0x1e, 0x00, + 0x1f, 0x00, /* status byte, read only */ + 0x20, 0x00, /* video decoder reserved part */ + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x24, 0x00, + 0x25, 0x00, + 0x26, 0x00, + 0x27, 0x00, + 0x28, 0x00, + 0x29, 0x00, + 0x2a, 0x00, + 0x2b, 0x00, + 0x2c, 0x00, + 0x2d, 0x00, + 0x2e, 0x00, + 0x2f, 0x00, + 0x30, 0xbc, /* audio clock generator */ + 0x31, 0xdf, + 0x32, 0x02, + 0x33, 0x00, + 0x34, 0xcd, + 0x35, 0xcc, + 0x36, 0x3a, + 0x37, 0x00, + 0x38, 0x03, + 0x39, 0x10, + 0x3a, 0x00, + 0x3b, 0x00, + 0x3c, 0x00, + 0x3d, 0x00, + 0x3e, 0x00, + 0x3f, 0x00, + 0x40, 0x00, /* VBI data slicer */ + 0x41, 0xff, + 0x42, 0xff, + 0x43, 0xff, + 0x44, 0xff, + 0x45, 0xff, + 0x46, 0xff, + 0x47, 0xff, + 0x48, 0xff, + 0x49, 0xff, + 0x4a, 0xff, + 0x4b, 0xff, + 0x4c, 0xff, + 0x4d, 0xff, + 0x4e, 0xff, + 0x4f, 0xff, + 0x50, 0xff, + 0x51, 0xff, + 0x52, 0xff, + 0x53, 0xff, + 0x54, 0xff, + 0x55, 0xff, + 0x56, 0xff, + 0x57, 0xff, + 0x58, 0x40, // framing code + 0x59, 0x47, // horizontal offset + 0x5a, 0x06, // vertical offset + 0x5b, 0x83, // field offset + 0x5c, 0x00, // reserved + 0x5d, 0x3e, // header and data + 0x5e, 0x00, // sliced data + 0x5f, 0x00, // reserved + 0x60, 0x00, /* video decoder reserved part */ + 0x61, 0x00, + 0x62, 0x00, + 0x63, 0x00, + 0x64, 0x00, + 0x65, 0x00, + 0x66, 0x00, + 0x67, 0x00, + 0x68, 0x00, + 0x69, 0x00, + 0x6a, 0x00, + 0x6b, 0x00, + 0x6c, 0x00, + 0x6d, 0x00, + 0x6e, 0x00, + 0x6f, 0x00, + 0x70, 0x00, /* video decoder reserved part */ + 0x71, 0x00, + 0x72, 0x00, + 0x73, 0x00, + 0x74, 0x00, + 0x75, 0x00, + 0x76, 0x00, + 0x77, 0x00, + 0x78, 0x00, + 0x79, 0x00, + 0x7a, 0x00, + 0x7b, 0x00, + 0x7c, 0x00, + 0x7d, 0x00, + 0x7e, 0x00, + 0x7f, 0x00, + 0x80, 0x00, /* X-port, I-port and scaler */ + 0x81, 0x00, + 0x82, 0x00, + 0x83, 0x00, + 0x84, 0xc5, + 0x85, 0x0d, // hsync and vsync ? + 0x86, 0x40, + 0x87, 0x01, + 0x88, 0x00, + 0x89, 0x00, + 0x8a, 0x00, + 0x8b, 0x00, + 0x8c, 0x00, + 0x8d, 0x00, + 0x8e, 0x00, + 0x8f, 0x00, + 0x90, 0x03, /* Task A definition */ + 0x91, 0x08, + 0x92, 0x00, + 0x93, 0x40, + 0x94, 0x00, // window settings + 0x95, 0x00, + 0x96, 0x00, + 0x97, 0x00, + 0x98, 0x00, + 0x99, 0x00, + 0x9a, 0x00, + 0x9b, 0x00, + 0x9c, 0x00, + 0x9d, 0x00, + 0x9e, 0x00, + 0x9f, 0x00, + 0xa0, 0x01, /* horizontal integer prescaling ratio */ + 0xa1, 0x00, /* horizontal prescaler accumulation + * sequence length */ + 0xa2, 0x00, /* UV FIR filter, Y FIR filter, prescaler + * DC gain */ + 0xa3, 0x00, + 0xa4, 0x80, // luminance brightness + 0xa5, 0x40, // luminance gain + 0xa6, 0x40, // chrominance saturation + 0xa7, 0x00, + 0xa8, 0x00, // horizontal luminance scaling increment + 0xa9, 0x04, + 0xaa, 0x00, // horizontal luminance phase offset + 0xab, 0x00, + 0xac, 0x00, // horizontal chrominance scaling increment + 0xad, 0x02, + 0xae, 0x00, // horizontal chrominance phase offset + 0xaf, 0x00, + 0xb0, 0x00, // vertical luminance scaling increment + 0xb1, 0x04, + 0xb2, 0x00, // vertical chrominance scaling increment + 0xb3, 0x04, + 0xb4, 0x00, + 0xb5, 0x00, + 0xb6, 0x00, + 0xb7, 0x00, + 0xb8, 0x00, + 0xb9, 0x00, + 0xba, 0x00, + 0xbb, 0x00, + 0xbc, 0x00, + 0xbd, 0x00, + 0xbe, 0x00, + 0xbf, 0x00, + 0xc0, 0x02, // Task B definition + 0xc1, 0x08, + 0xc2, 0x00, + 0xc3, 0x40, + 0xc4, 0x00, // window settings + 0xc5, 0x00, + 0xc6, 0x00, + 0xc7, 0x00, + 0xc8, 0x00, + 0xc9, 0x00, + 0xca, 0x00, + 0xcb, 0x00, + 0xcc, 0x00, + 0xcd, 0x00, + 0xce, 0x00, + 0xcf, 0x00, + 0xd0, 0x01, // horizontal integer prescaling ratio + 0xd1, 0x00, // horizontal prescaler accumulation sequence length + 0xd2, 0x00, // UV FIR filter, Y FIR filter, prescaler DC gain + 0xd3, 0x00, + 0xd4, 0x80, // luminance brightness + 0xd5, 0x40, // luminance gain + 0xd6, 0x40, // chrominance saturation + 0xd7, 0x00, + 0xd8, 0x00, // horizontal luminance scaling increment + 0xd9, 0x04, + 0xda, 0x00, // horizontal luminance phase offset + 0xdb, 0x00, + 0xdc, 0x00, // horizontal chrominance scaling increment + 0xdd, 0x02, + 0xde, 0x00, // horizontal chrominance phase offset + 0xdf, 0x00, + 0xe0, 0x00, // vertical luminance scaling increment + 0xe1, 0x04, + 0xe2, 0x00, // vertical chrominance scaling increment + 0xe3, 0x04, + 0xe4, 0x00, + 0xe5, 0x00, + 0xe6, 0x00, + 0xe7, 0x00, + 0xe8, 0x00, + 0xe9, 0x00, + 0xea, 0x00, + 0xeb, 0x00, + 0xec, 0x00, + 0xed, 0x00, + 0xee, 0x00, + 0xef, 0x00 +}; + +static int +saa7114_command (struct i2c_client *client, + unsigned int cmd, + void *arg) +{ + struct saa7114 *decoder = i2c_get_clientdata(client); + + switch (cmd) { + + case 0: + //dprintk(1, KERN_INFO "%s: writing init\n", I2C_NAME(client)); + //saa7114_write_block(client, init, sizeof(init)); + break; + + case DECODER_DUMP: + { + int i; + + dprintk(1, KERN_INFO "%s: decoder dump\n", I2C_NAME(client)); + + for (i = 0; i < 32; i += 16) { + int j; + + printk(KERN_DEBUG "%s: %03x", I2C_NAME(client), i); + for (j = 0; j < 16; ++j) { + printk(" %02x", + saa7114_read(client, i + j)); + } + printk("\n"); + } + } + break; + + case DECODER_GET_CAPABILITIES: + { + struct video_decoder_capability *cap = arg; + + dprintk(1, KERN_DEBUG "%s: decoder get capabilities\n", + I2C_NAME(client)); + + cap->flags = VIDEO_DECODER_PAL | + VIDEO_DECODER_NTSC | + VIDEO_DECODER_AUTO | + VIDEO_DECODER_CCIR; + cap->inputs = 8; + cap->outputs = 1; + } + break; + + case DECODER_GET_STATUS: + { + int *iarg = arg; + int status; + int res; + + status = saa7114_read(client, 0x1f); + + dprintk(1, KERN_DEBUG "%s status: 0x%02x\n", I2C_NAME(client), + status); + res = 0; + if ((status & (1 << 6)) == 0) { + res |= DECODER_STATUS_GOOD; + } + switch (decoder->norm) { + case VIDEO_MODE_NTSC: + res |= DECODER_STATUS_NTSC; + break; + case VIDEO_MODE_PAL: + res |= DECODER_STATUS_PAL; + break; + case VIDEO_MODE_SECAM: + res |= DECODER_STATUS_SECAM; + break; + default: + case VIDEO_MODE_AUTO: + if ((status & (1 << 5)) != 0) { + res |= DECODER_STATUS_NTSC; + } else { + res |= DECODER_STATUS_PAL; + } + break; + } + if ((status & (1 << 0)) != 0) { + res |= DECODER_STATUS_COLOR; + } + *iarg = res; + } + break; + + case DECODER_SET_NORM: + { + int *iarg = arg; + + short int hoff = 0, voff = 0, w = 0, h = 0; + + dprintk(1, KERN_DEBUG "%s: decoder set norm ", + I2C_NAME(client)); + switch (*iarg) { + + case VIDEO_MODE_NTSC: + dprintk(1, "NTSC\n"); + decoder->reg[REG_ADDR(0x06)] = + SAA_7114_NTSC_HSYNC_START; + decoder->reg[REG_ADDR(0x07)] = + SAA_7114_NTSC_HSYNC_STOP; + + decoder->reg[REG_ADDR(0x08)] = decoder->playback ? 0x7c : 0xb8; // PLL free when playback, PLL close when capture + + decoder->reg[REG_ADDR(0x0e)] = 0x85; + decoder->reg[REG_ADDR(0x0f)] = 0x24; + + hoff = SAA_7114_NTSC_HOFFSET; + voff = SAA_7114_NTSC_VOFFSET; + w = SAA_7114_NTSC_WIDTH; + h = SAA_7114_NTSC_HEIGHT; + + break; + + case VIDEO_MODE_PAL: + dprintk(1, "PAL\n"); + decoder->reg[REG_ADDR(0x06)] = + SAA_7114_PAL_HSYNC_START; + decoder->reg[REG_ADDR(0x07)] = + SAA_7114_PAL_HSYNC_STOP; + + decoder->reg[REG_ADDR(0x08)] = decoder->playback ? 0x7c : 0xb8; // PLL free when playback, PLL close when capture + + decoder->reg[REG_ADDR(0x0e)] = 0x81; + decoder->reg[REG_ADDR(0x0f)] = 0x24; + + hoff = SAA_7114_PAL_HOFFSET; + voff = SAA_7114_PAL_VOFFSET; + w = SAA_7114_PAL_WIDTH; + h = SAA_7114_PAL_HEIGHT; + + break; + + default: + dprintk(1, " Unknown video mode!!!\n"); + return -EINVAL; + + } + + + decoder->reg[REG_ADDR(0x94)] = LOBYTE(hoff); // hoffset low + decoder->reg[REG_ADDR(0x95)] = HIBYTE(hoff) & 0x0f; // hoffset high + decoder->reg[REG_ADDR(0x96)] = LOBYTE(w); // width low + decoder->reg[REG_ADDR(0x97)] = HIBYTE(w) & 0x0f; // width high + decoder->reg[REG_ADDR(0x98)] = LOBYTE(voff); // voffset low + decoder->reg[REG_ADDR(0x99)] = HIBYTE(voff) & 0x0f; // voffset high + decoder->reg[REG_ADDR(0x9a)] = LOBYTE(h + 2); // height low + decoder->reg[REG_ADDR(0x9b)] = HIBYTE(h + 2) & 0x0f; // height high + decoder->reg[REG_ADDR(0x9c)] = LOBYTE(w); // out width low + decoder->reg[REG_ADDR(0x9d)] = HIBYTE(w) & 0x0f; // out width high + decoder->reg[REG_ADDR(0x9e)] = LOBYTE(h); // out height low + decoder->reg[REG_ADDR(0x9f)] = HIBYTE(h) & 0x0f; // out height high + + decoder->reg[REG_ADDR(0xc4)] = LOBYTE(hoff); // hoffset low + decoder->reg[REG_ADDR(0xc5)] = HIBYTE(hoff) & 0x0f; // hoffset high + decoder->reg[REG_ADDR(0xc6)] = LOBYTE(w); // width low + decoder->reg[REG_ADDR(0xc7)] = HIBYTE(w) & 0x0f; // width high + decoder->reg[REG_ADDR(0xc8)] = LOBYTE(voff); // voffset low + decoder->reg[REG_ADDR(0xc9)] = HIBYTE(voff) & 0x0f; // voffset high + decoder->reg[REG_ADDR(0xca)] = LOBYTE(h + 2); // height low + decoder->reg[REG_ADDR(0xcb)] = HIBYTE(h + 2) & 0x0f; // height high + decoder->reg[REG_ADDR(0xcc)] = LOBYTE(w); // out width low + decoder->reg[REG_ADDR(0xcd)] = HIBYTE(w) & 0x0f; // out width high + decoder->reg[REG_ADDR(0xce)] = LOBYTE(h); // out height low + decoder->reg[REG_ADDR(0xcf)] = HIBYTE(h) & 0x0f; // out height high + + + saa7114_write(client, 0x80, 0x06); // i-port and scaler back end clock selection, task A&B off + saa7114_write(client, 0x88, 0xd8); // sw reset scaler + saa7114_write(client, 0x88, 0xf8); // sw reset scaler release + + saa7114_write_block(client, decoder->reg + (0x06 << 1), + 3 << 1); + saa7114_write_block(client, decoder->reg + (0x0e << 1), + 2 << 1); + saa7114_write_block(client, decoder->reg + (0x5a << 1), + 2 << 1); + + saa7114_write_block(client, decoder->reg + (0x94 << 1), + (0x9f + 1 - 0x94) << 1); + saa7114_write_block(client, decoder->reg + (0xc4 << 1), + (0xcf + 1 - 0xc4) << 1); + + saa7114_write(client, 0x88, 0xd8); // sw reset scaler + saa7114_write(client, 0x88, 0xf8); // sw reset scaler release + saa7114_write(client, 0x80, 0x36); // i-port and scaler back end clock selection + + decoder->norm = *iarg; + } + break; + + case DECODER_SET_INPUT: + { + int *iarg = arg; + + dprintk(1, KERN_DEBUG "%s: decoder set input (%d)\n", + I2C_NAME(client), *iarg); + if (*iarg < 0 || *iarg > 7) { + return -EINVAL; + } + + if (decoder->input != *iarg) { + dprintk(1, KERN_DEBUG "%s: now setting %s input\n", + I2C_NAME(client), + *iarg >= 6 ? "S-Video" : "Composite"); + decoder->input = *iarg; + + /* select mode */ + decoder->reg[REG_ADDR(0x02)] = + (decoder-> + reg[REG_ADDR(0x02)] & 0xf0) | (decoder-> + input < + 6 ? 0x0 : 0x9); + saa7114_write(client, 0x02, + decoder->reg[REG_ADDR(0x02)]); + + /* bypass chrominance trap for modes 6..9 */ + decoder->reg[REG_ADDR(0x09)] = + (decoder-> + reg[REG_ADDR(0x09)] & 0x7f) | (decoder-> + input < + 6 ? 0x0 : + 0x80); + saa7114_write(client, 0x09, + decoder->reg[REG_ADDR(0x09)]); + + decoder->reg[REG_ADDR(0x0e)] = + decoder->input < + 6 ? decoder-> + reg[REG_ADDR(0x0e)] | 1 : decoder-> + reg[REG_ADDR(0x0e)] & ~1; + saa7114_write(client, 0x0e, + decoder->reg[REG_ADDR(0x0e)]); + } + } + break; + + case DECODER_SET_OUTPUT: + { + int *iarg = arg; + + dprintk(1, KERN_DEBUG "%s: decoder set output\n", + I2C_NAME(client)); + + /* not much choice of outputs */ + if (*iarg != 0) { + return -EINVAL; + } + } + break; + + case DECODER_ENABLE_OUTPUT: + { + int *iarg = arg; + int enable = (*iarg != 0); + + dprintk(1, KERN_DEBUG "%s: decoder %s output\n", + I2C_NAME(client), enable ? "enable" : "disable"); + + decoder->playback = !enable; + + if (decoder->enable != enable) { + decoder->enable = enable; + + /* RJ: If output should be disabled (for + * playing videos), we also need a open PLL. + * The input is set to 0 (where no input + * source is connected), although this + * is not necessary. + * + * If output should be enabled, we have to + * reverse the above. + */ + + if (decoder->enable) { + decoder->reg[REG_ADDR(0x08)] = 0xb8; + decoder->reg[REG_ADDR(0x12)] = 0xc9; + decoder->reg[REG_ADDR(0x13)] = 0x80; + decoder->reg[REG_ADDR(0x87)] = 0x01; + } else { + decoder->reg[REG_ADDR(0x08)] = 0x7c; + decoder->reg[REG_ADDR(0x12)] = 0x00; + decoder->reg[REG_ADDR(0x13)] = 0x00; + decoder->reg[REG_ADDR(0x87)] = 0x00; + } + + saa7114_write_block(client, + decoder->reg + (0x12 << 1), + 2 << 1); + saa7114_write(client, 0x08, + decoder->reg[REG_ADDR(0x08)]); + saa7114_write(client, 0x87, + decoder->reg[REG_ADDR(0x87)]); + saa7114_write(client, 0x88, 0xd8); // sw reset scaler + saa7114_write(client, 0x88, 0xf8); // sw reset scaler release + saa7114_write(client, 0x80, 0x36); + + } + } + break; + + case DECODER_SET_PICTURE: + { + struct video_picture *pic = arg; + + dprintk(1, + KERN_DEBUG + "%s: decoder set picture bright=%d contrast=%d saturation=%d hue=%d\n", + I2C_NAME(client), pic->brightness, pic->contrast, + pic->colour, pic->hue); + + if (decoder->bright != pic->brightness) { + /* We want 0 to 255 we get 0-65535 */ + decoder->bright = pic->brightness; + saa7114_write(client, 0x0a, decoder->bright >> 8); + } + if (decoder->contrast != pic->contrast) { + /* We want 0 to 127 we get 0-65535 */ + decoder->contrast = pic->contrast; + saa7114_write(client, 0x0b, + decoder->contrast >> 9); + } + if (decoder->sat != pic->colour) { + /* We want 0 to 127 we get 0-65535 */ + decoder->sat = pic->colour; + saa7114_write(client, 0x0c, decoder->sat >> 9); + } + if (decoder->hue != pic->hue) { + /* We want -128 to 127 we get 0-65535 */ + decoder->hue = pic->hue; + saa7114_write(client, 0x0d, + (decoder->hue - 32768) >> 8); + } + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ +static unsigned short normal_i2c[] = + { I2C_SAA7114 >> 1, I2C_SAA7114A >> 1, I2C_CLIENT_END }; + +static unsigned short ignore = I2C_CLIENT_END; + +static struct i2c_client_address_data addr_data = { + .normal_i2c = normal_i2c, + .probe = &ignore, + .ignore = &ignore, +}; + +static struct i2c_driver i2c_driver_saa7114; + +static int +saa7114_detect_client (struct i2c_adapter *adapter, + int address, + int kind) +{ + int i, err[30]; + short int hoff = SAA_7114_NTSC_HOFFSET; + short int voff = SAA_7114_NTSC_VOFFSET; + short int w = SAA_7114_NTSC_WIDTH; + short int h = SAA_7114_NTSC_HEIGHT; + struct i2c_client *client; + struct saa7114 *decoder; + + dprintk(1, + KERN_INFO + "saa7114.c: detecting saa7114 client on address 0x%x\n", + address << 1); + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == 0) + return -ENOMEM; + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver_saa7114; + strlcpy(I2C_NAME(client), "saa7114", sizeof(I2C_NAME(client))); + + decoder = kzalloc(sizeof(struct saa7114), GFP_KERNEL); + if (decoder == NULL) { + kfree(client); + return -ENOMEM; + } + decoder->norm = VIDEO_MODE_NTSC; + decoder->input = -1; + decoder->enable = 1; + decoder->bright = 32768; + decoder->contrast = 32768; + decoder->hue = 32768; + decoder->sat = 32768; + decoder->playback = 0; // initially capture mode useda + i2c_set_clientdata(client, decoder); + + memcpy(decoder->reg, init, sizeof(init)); + + decoder->reg[REG_ADDR(0x94)] = LOBYTE(hoff); // hoffset low + decoder->reg[REG_ADDR(0x95)] = HIBYTE(hoff) & 0x0f; // hoffset high + decoder->reg[REG_ADDR(0x96)] = LOBYTE(w); // width low + decoder->reg[REG_ADDR(0x97)] = HIBYTE(w) & 0x0f; // width high + decoder->reg[REG_ADDR(0x98)] = LOBYTE(voff); // voffset low + decoder->reg[REG_ADDR(0x99)] = HIBYTE(voff) & 0x0f; // voffset high + decoder->reg[REG_ADDR(0x9a)] = LOBYTE(h + 2); // height low + decoder->reg[REG_ADDR(0x9b)] = HIBYTE(h + 2) & 0x0f; // height high + decoder->reg[REG_ADDR(0x9c)] = LOBYTE(w); // out width low + decoder->reg[REG_ADDR(0x9d)] = HIBYTE(w) & 0x0f; // out width high + decoder->reg[REG_ADDR(0x9e)] = LOBYTE(h); // out height low + decoder->reg[REG_ADDR(0x9f)] = HIBYTE(h) & 0x0f; // out height high + + decoder->reg[REG_ADDR(0xc4)] = LOBYTE(hoff); // hoffset low + decoder->reg[REG_ADDR(0xc5)] = HIBYTE(hoff) & 0x0f; // hoffset high + decoder->reg[REG_ADDR(0xc6)] = LOBYTE(w); // width low + decoder->reg[REG_ADDR(0xc7)] = HIBYTE(w) & 0x0f; // width high + decoder->reg[REG_ADDR(0xc8)] = LOBYTE(voff); // voffset low + decoder->reg[REG_ADDR(0xc9)] = HIBYTE(voff) & 0x0f; // voffset high + decoder->reg[REG_ADDR(0xca)] = LOBYTE(h + 2); // height low + decoder->reg[REG_ADDR(0xcb)] = HIBYTE(h + 2) & 0x0f; // height high + decoder->reg[REG_ADDR(0xcc)] = LOBYTE(w); // out width low + decoder->reg[REG_ADDR(0xcd)] = HIBYTE(w) & 0x0f; // out width high + decoder->reg[REG_ADDR(0xce)] = LOBYTE(h); // out height low + decoder->reg[REG_ADDR(0xcf)] = HIBYTE(h) & 0x0f; // out height high + + decoder->reg[REG_ADDR(0xb8)] = + LOBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET)); + decoder->reg[REG_ADDR(0xb9)] = + HIBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET)); + decoder->reg[REG_ADDR(0xba)] = + LOBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET)); + decoder->reg[REG_ADDR(0xbb)] = + HIBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET)); + + decoder->reg[REG_ADDR(0xbc)] = + LOBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET)); + decoder->reg[REG_ADDR(0xbd)] = + HIBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET)); + decoder->reg[REG_ADDR(0xbe)] = + LOBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET)); + decoder->reg[REG_ADDR(0xbf)] = + HIBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET)); + + decoder->reg[REG_ADDR(0xe8)] = + LOBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET)); + decoder->reg[REG_ADDR(0xe9)] = + HIBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET)); + decoder->reg[REG_ADDR(0xea)] = + LOBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET)); + decoder->reg[REG_ADDR(0xeb)] = + HIBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET)); + + decoder->reg[REG_ADDR(0xec)] = + LOBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET)); + decoder->reg[REG_ADDR(0xed)] = + HIBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET)); + decoder->reg[REG_ADDR(0xee)] = + LOBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET)); + decoder->reg[REG_ADDR(0xef)] = + HIBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET)); + + + decoder->reg[REG_ADDR(0x13)] = 0x80; // RTC0 on + decoder->reg[REG_ADDR(0x87)] = 0x01; // I-Port + decoder->reg[REG_ADDR(0x12)] = 0xc9; // RTS0 + + decoder->reg[REG_ADDR(0x02)] = 0xc0; // set composite1 input, aveasy + decoder->reg[REG_ADDR(0x09)] = 0x00; // chrominance trap + decoder->reg[REG_ADDR(0x0e)] |= 1; // combfilter on + + + dprintk(1, KERN_DEBUG "%s_attach: starting decoder init\n", + I2C_NAME(client)); + + err[0] = + saa7114_write_block(client, decoder->reg + (0x20 << 1), + 0x10 << 1); + err[1] = + saa7114_write_block(client, decoder->reg + (0x30 << 1), + 0x10 << 1); + err[2] = + saa7114_write_block(client, decoder->reg + (0x63 << 1), + (0x7f + 1 - 0x63) << 1); + err[3] = + saa7114_write_block(client, decoder->reg + (0x89 << 1), + 6 << 1); + err[4] = + saa7114_write_block(client, decoder->reg + (0xb8 << 1), + 8 << 1); + err[5] = + saa7114_write_block(client, decoder->reg + (0xe8 << 1), + 8 << 1); + + + for (i = 0; i <= 5; i++) { + if (err[i] < 0) { + dprintk(1, + KERN_ERR + "%s_attach: init error %d at stage %d, leaving attach.\n", + I2C_NAME(client), i, err[i]); + kfree(decoder); + kfree(client); + return 0; + } + } + + for (i = 6; i < 8; i++) { + dprintk(1, + KERN_DEBUG + "%s_attach: reg[0x%02x] = 0x%02x (0x%02x)\n", + I2C_NAME(client), i, saa7114_read(client, i), + decoder->reg[REG_ADDR(i)]); + } + + dprintk(1, + KERN_DEBUG + "%s_attach: performing decoder reset sequence\n", + I2C_NAME(client)); + + err[6] = saa7114_write(client, 0x80, 0x06); // i-port and scaler backend clock selection, task A&B off + err[7] = saa7114_write(client, 0x88, 0xd8); // sw reset scaler + err[8] = saa7114_write(client, 0x88, 0xf8); // sw reset scaler release + + for (i = 6; i <= 8; i++) { + if (err[i] < 0) { + dprintk(1, + KERN_ERR + "%s_attach: init error %d at stage %d, leaving attach.\n", + I2C_NAME(client), i, err[i]); + kfree(decoder); + kfree(client); + return 0; + } + } + + dprintk(1, KERN_INFO "%s_attach: performing the rest of init\n", + I2C_NAME(client)); + + + err[9] = saa7114_write(client, 0x01, decoder->reg[REG_ADDR(0x01)]); + err[10] = saa7114_write_block(client, decoder->reg + (0x03 << 1), (0x1e + 1 - 0x03) << 1); // big seq + err[11] = saa7114_write_block(client, decoder->reg + (0x40 << 1), (0x5f + 1 - 0x40) << 1); // slicer + err[12] = saa7114_write_block(client, decoder->reg + (0x81 << 1), 2 << 1); // ? + err[13] = saa7114_write_block(client, decoder->reg + (0x83 << 1), 5 << 1); // ? + err[14] = saa7114_write_block(client, decoder->reg + (0x90 << 1), 4 << 1); // Task A + err[15] = + saa7114_write_block(client, decoder->reg + (0x94 << 1), + 12 << 1); + err[16] = + saa7114_write_block(client, decoder->reg + (0xa0 << 1), + 8 << 1); + err[17] = + saa7114_write_block(client, decoder->reg + (0xa8 << 1), + 8 << 1); + err[18] = + saa7114_write_block(client, decoder->reg + (0xb0 << 1), + 8 << 1); + err[19] = saa7114_write_block(client, decoder->reg + (0xc0 << 1), 4 << 1); // Task B + err[15] = + saa7114_write_block(client, decoder->reg + (0xc4 << 1), + 12 << 1); + err[16] = + saa7114_write_block(client, decoder->reg + (0xd0 << 1), + 8 << 1); + err[17] = + saa7114_write_block(client, decoder->reg + (0xd8 << 1), + 8 << 1); + err[18] = + saa7114_write_block(client, decoder->reg + (0xe0 << 1), + 8 << 1); + + for (i = 9; i <= 18; i++) { + if (err[i] < 0) { + dprintk(1, + KERN_ERR + "%s_attach: init error %d at stage %d, leaving attach.\n", + I2C_NAME(client), i, err[i]); + kfree(decoder); + kfree(client); + return 0; + } + } + + + for (i = 6; i < 8; i++) { + dprintk(1, + KERN_DEBUG + "%s_attach: reg[0x%02x] = 0x%02x (0x%02x)\n", + I2C_NAME(client), i, saa7114_read(client, i), + decoder->reg[REG_ADDR(i)]); + } + + + for (i = 0x11; i <= 0x13; i++) { + dprintk(1, + KERN_DEBUG + "%s_attach: reg[0x%02x] = 0x%02x (0x%02x)\n", + I2C_NAME(client), i, saa7114_read(client, i), + decoder->reg[REG_ADDR(i)]); + } + + + dprintk(1, KERN_DEBUG "%s_attach: setting video input\n", + I2C_NAME(client)); + + err[19] = + saa7114_write(client, 0x02, decoder->reg[REG_ADDR(0x02)]); + err[20] = + saa7114_write(client, 0x09, decoder->reg[REG_ADDR(0x09)]); + err[21] = + saa7114_write(client, 0x0e, decoder->reg[REG_ADDR(0x0e)]); + + for (i = 19; i <= 21; i++) { + if (err[i] < 0) { + dprintk(1, + KERN_ERR + "%s_attach: init error %d at stage %d, leaving attach.\n", + I2C_NAME(client), i, err[i]); + kfree(decoder); + kfree(client); + return 0; + } + } + + dprintk(1, + KERN_DEBUG + "%s_attach: performing decoder reset sequence\n", + I2C_NAME(client)); + + err[22] = saa7114_write(client, 0x88, 0xd8); // sw reset scaler + err[23] = saa7114_write(client, 0x88, 0xf8); // sw reset scaler release + err[24] = saa7114_write(client, 0x80, 0x36); // i-port and scaler backend clock selection, task A&B off + + + for (i = 22; i <= 24; i++) { + if (err[i] < 0) { + dprintk(1, + KERN_ERR + "%s_attach: init error %d at stage %d, leaving attach.\n", + I2C_NAME(client), i, err[i]); + kfree(decoder); + kfree(client); + return 0; + } + } + + err[25] = saa7114_write(client, 0x06, init[REG_ADDR(0x06)]); + err[26] = saa7114_write(client, 0x07, init[REG_ADDR(0x07)]); + err[27] = saa7114_write(client, 0x10, init[REG_ADDR(0x10)]); + + dprintk(1, + KERN_INFO + "%s_attach: chip version %x, decoder status 0x%02x\n", + I2C_NAME(client), saa7114_read(client, 0x00) >> 4, + saa7114_read(client, 0x1f)); + dprintk(1, + KERN_DEBUG + "%s_attach: power save control: 0x%02x, scaler status: 0x%02x\n", + I2C_NAME(client), saa7114_read(client, 0x88), + saa7114_read(client, 0x8f)); + + + for (i = 0x94; i < 0x96; i++) { + dprintk(1, + KERN_DEBUG + "%s_attach: reg[0x%02x] = 0x%02x (0x%02x)\n", + I2C_NAME(client), i, saa7114_read(client, i), + decoder->reg[REG_ADDR(i)]); + } + + i = i2c_attach_client(client); + if (i) { + kfree(client); + kfree(decoder); + return i; + } + + //i = saa7114_write_block(client, init, sizeof(init)); + i = 0; + if (i < 0) { + dprintk(1, KERN_ERR "%s_attach error: init status %d\n", + I2C_NAME(client), i); + } else { + dprintk(1, + KERN_INFO + "%s_attach: chip version %x at address 0x%x\n", + I2C_NAME(client), saa7114_read(client, 0x00) >> 4, + client->addr << 1); + } + + return 0; +} + +static int +saa7114_attach_adapter (struct i2c_adapter *adapter) +{ + dprintk(1, + KERN_INFO + "saa7114.c: starting probe for adapter %s (0x%x)\n", + I2C_NAME(adapter), adapter->id); + return i2c_probe(adapter, &addr_data, &saa7114_detect_client); +} + +static int +saa7114_detach_client (struct i2c_client *client) +{ + struct saa7114 *decoder = i2c_get_clientdata(client); + int err; + + err = i2c_detach_client(client); + if (err) { + return err; + } + + kfree(decoder); + kfree(client); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver i2c_driver_saa7114 = { + .driver = { + .name = "saa7114", + }, + + .id = I2C_DRIVERID_SAA7114, + + .attach_adapter = saa7114_attach_adapter, + .detach_client = saa7114_detach_client, + .command = saa7114_command, +}; + +static int __init +saa7114_init (void) +{ + return i2c_add_driver(&i2c_driver_saa7114); +} + +static void __exit +saa7114_exit (void) +{ + i2c_del_driver(&i2c_driver_saa7114); +} + +module_init(saa7114_init); +module_exit(saa7114_exit); diff --git a/linux/drivers/media/video/saa7121.h b/linux/drivers/media/video/saa7121.h new file mode 100644 index 000000000..66967ae37 --- /dev/null +++ b/linux/drivers/media/video/saa7121.h @@ -0,0 +1,132 @@ +/* saa7121.h - saa7121 initializations + Copyright (C) 1999 Nathan Laredo (laredo@gnu.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, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ +#ifndef __SAA7121_H__ +#define __SAA7121_H__ + +#define NTSC_BURST_START 0x19 /* 28 */ +#define NTSC_BURST_END 0x1d /* 29 */ +#define NTSC_CHROMA_PHASE 0x67 /* 5a */ +#define NTSC_GAINU 0x76 /* 5b */ +#define NTSC_GAINV 0xa5 /* 5c */ +#define NTSC_BLACK_LEVEL 0x2a /* 5d */ +#define NTSC_BLANKING_LEVEL 0x2e /* 5e */ +#define NTSC_VBI_BLANKING 0x2e /* 5f */ +#define NTSC_DAC_CONTROL 0x11 /* 61 */ +#define NTSC_BURST_AMP 0x3f /* 62 */ +#define NTSC_SUBC3 0x1f /* 63 */ +#define NTSC_SUBC2 0x7c /* 64 */ +#define NTSC_SUBC1 0xf0 /* 65 */ +#define NTSC_SUBC0 0x21 /* 66 */ +#define NTSC_HTRIG 0x72 /* 6c */ +#define NTSC_VTRIG 0x00 /* 6c */ +#define NTSC_MULTI 0x30 /* 6e */ +#define NTSC_CCTTX 0x11 /* 6f */ +#define NTSC_FIRST_ACTIVE 0x12 /* 7a */ +#define NTSC_LAST_ACTIVE 0x02 /* 7b */ +#define NTSC_MSB_VERTICAL 0x40 /* 7c */ + +#define PAL_BURST_START 0x21 /* 28 */ +#define PAL_BURST_END 0x1d /* 29 */ +#define PAL_CHROMA_PHASE 0x3f /* 5a */ +#define PAL_GAINU 0x7d /* 5b */ +#define PAL_GAINV 0xaf /* 5c */ +#define PAL_BLACK_LEVEL 0x23 /* 5d */ +#define PAL_BLANKING_LEVEL 0x35 /* 5e */ +#define PAL_VBI_BLANKING 0x35 /* 5f */ +#define PAL_DAC_CONTROL 0x02 /* 61 */ +#define PAL_BURST_AMP 0x2f /* 62 */ +#define PAL_SUBC3 0xcb /* 63 */ +#define PAL_SUBC2 0x8a /* 64 */ +#define PAL_SUBC1 0x09 /* 65 */ +#define PAL_SUBC0 0x2a /* 66 */ +#define PAL_HTRIG 0x86 /* 6c */ +#define PAL_VTRIG 0x04 /* 6d */ +#define PAL_MULTI 0x20 /* 6e */ +#define PAL_CCTTX 0x15 /* 6f */ +#define PAL_FIRST_ACTIVE 0x16 /* 7a */ +#define PAL_LAST_ACTIVE 0x36 /* 7b */ +#define PAL_MSB_VERTICAL 0x40 /* 7c */ + +/* Initialization Sequence */ + +static __u8 init7121ntsc[] = { + 0x26, 0x0, 0x27, 0x0, + 0x28, NTSC_BURST_START, 0x29, NTSC_BURST_END, + 0x2a, 0x0, 0x2b, 0x0, 0x2c, 0x0, 0x2d, 0x0, + 0x2e, 0x0, 0x2f, 0x0, 0x30, 0x0, 0x31, 0x0, + 0x32, 0x0, 0x33, 0x0, 0x34, 0x0, 0x35, 0x0, + 0x36, 0x0, 0x37, 0x0, 0x38, 0x0, 0x39, 0x0, + 0x3a, 0x03, 0x3b, 0x0, 0x3c, 0x0, 0x3d, 0x0, + 0x3e, 0x0, 0x3f, 0x0, 0x40, 0x0, 0x41, 0x0, + 0x42, 0x0, 0x43, 0x0, 0x44, 0x0, 0x45, 0x0, + 0x46, 0x0, 0x47, 0x0, 0x48, 0x0, 0x49, 0x0, + 0x4a, 0x0, 0x4b, 0x0, 0x4c, 0x0, 0x4d, 0x0, + 0x4e, 0x0, 0x4f, 0x0, 0x50, 0x0, 0x51, 0x0, + 0x52, 0x0, 0x53, 0x0, 0x54, 0x0, 0x55, 0x0, + 0x56, 0x0, 0x57, 0x0, 0x58, 0x0, 0x59, 0x0, + 0x5a, NTSC_CHROMA_PHASE, 0x5b, NTSC_GAINU, + 0x5c, NTSC_GAINV, 0x5d, NTSC_BLACK_LEVEL, + 0x5e, NTSC_BLANKING_LEVEL, 0x5f, NTSC_VBI_BLANKING, + 0x60, 0x0, 0x61, NTSC_DAC_CONTROL, + 0x62, NTSC_BURST_AMP, 0x63, NTSC_SUBC3, + 0x64, NTSC_SUBC2, 0x65, NTSC_SUBC1, + 0x66, NTSC_SUBC0, 0x67, 0x80, 0x68, 0x80, + 0x69, 0x80, 0x6a, 0x80, 0x6b, 0x29, + 0x6c, NTSC_HTRIG, 0x6d, NTSC_VTRIG, + 0x6e, NTSC_MULTI, 0x6f, NTSC_CCTTX, + 0x70, 0xc9, 0x71, 0x68, 0x72, 0x60, 0x73, 0x0, + 0x74, 0x0, 0x75, 0x0, 0x76, 0x0, 0x77, 0x0, + 0x78, 0x0, 0x79, 0x0, 0x7a, NTSC_FIRST_ACTIVE, + 0x7b, NTSC_LAST_ACTIVE, 0x7c, NTSC_MSB_VERTICAL, + 0x7d, 0x0, 0x7e, 0x0, 0x7f, 0x0 +}; +#define INIT7121LEN (sizeof(init7121ntsc)/2) + +static __u8 init7121pal[] = { + 0x26, 0x0, 0x27, 0x0, + 0x28, PAL_BURST_START, 0x29, PAL_BURST_END, + 0x2a, 0x0, 0x2b, 0x0, 0x2c, 0x0, 0x2d, 0x0, + 0x2e, 0x0, 0x2f, 0x0, 0x30, 0x0, 0x31, 0x0, + 0x32, 0x0, 0x33, 0x0, 0x34, 0x0, 0x35, 0x0, + 0x36, 0x0, 0x37, 0x0, 0x38, 0x0, 0x39, 0x0, + 0x3a, 0x03, 0x3b, 0x0, 0x3c, 0x0, 0x3d, 0x0, + 0x3e, 0x0, 0x3f, 0x0, 0x40, 0x0, 0x41, 0x0, + 0x42, 0x0, 0x43, 0x0, 0x44, 0x0, 0x45, 0x0, + 0x46, 0x0, 0x47, 0x0, 0x48, 0x0, 0x49, 0x0, + 0x4a, 0x0, 0x4b, 0x0, 0x4c, 0x0, 0x4d, 0x0, + 0x4e, 0x0, 0x4f, 0x0, 0x50, 0x0, 0x51, 0x0, + 0x52, 0x0, 0x53, 0x0, 0x54, 0x0, 0x55, 0x0, + 0x56, 0x0, 0x57, 0x0, 0x58, 0x0, 0x59, 0x0, + 0x5a, PAL_CHROMA_PHASE, 0x5b, PAL_GAINU, + 0x5c, PAL_GAINV, 0x5d, PAL_BLACK_LEVEL, + 0x5e, PAL_BLANKING_LEVEL, 0x5f, PAL_VBI_BLANKING, + 0x60, 0x0, 0x61, PAL_DAC_CONTROL, + 0x62, PAL_BURST_AMP, 0x63, PAL_SUBC3, + 0x64, PAL_SUBC2, 0x65, PAL_SUBC1, + 0x66, PAL_SUBC0, 0x67, 0x80, 0x68, 0x80, + 0x69, 0x80, 0x6a, 0x80, 0x6b, 0x29, + 0x6c, PAL_HTRIG, 0x6d, PAL_VTRIG, + 0x6e, PAL_MULTI, 0x6f, PAL_CCTTX, + 0x70, 0xc9, 0x71, 0x68, 0x72, 0x60, 0x73, 0x0, + 0x74, 0x0, 0x75, 0x0, 0x76, 0x0, 0x77, 0x0, + 0x78, 0x0, 0x79, 0x0, 0x7a, PAL_FIRST_ACTIVE, + 0x7b, PAL_LAST_ACTIVE, 0x7c, PAL_MSB_VERTICAL, + 0x7d, 0x0, 0x7e, 0x0, 0x7f, 0x0 +}; +#endif diff --git a/linux/drivers/media/video/saa7146.h b/linux/drivers/media/video/saa7146.h new file mode 100644 index 000000000..2830b5e33 --- /dev/null +++ b/linux/drivers/media/video/saa7146.h @@ -0,0 +1,114 @@ +/* + saa7146.h - definitions philips saa7146 based cards + Copyright (C) 1999 Nathan Laredo (laredo@gnu.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, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __SAA7146__ +#define __SAA7146__ + +#define SAA7146_VERSION_CODE 0x000101 + +#include +#include + +#include + +#ifndef O_NONCAP +#define O_NONCAP O_TRUNC +#endif + +#define MAX_GBUFFERS 2 +#define FBUF_SIZE 0x190000 + +#ifdef __KERNEL__ + +struct saa7146_window +{ + int x, y; + ushort width, height; + ushort bpp, bpl; + ushort swidth, sheight; + short cropx, cropy; + ushort cropwidth, cropheight; + unsigned long vidadr; + int color_fmt; + ushort depth; +}; + +/* Per-open data for handling multiple opens on one device */ +struct device_open +{ + int isopen; + int noncapturing; + struct saa7146 *dev; +}; +#define MAX_OPENS 3 + +struct saa7146 +{ + struct video_device video_dev; + struct video_picture picture; + struct video_audio audio_dev; + struct video_info vidinfo; + int user; + int cap; + int capuser; + int irqstate; /* irq routine is state driven */ + int writemode; + int playmode; + unsigned int nr; + unsigned long irq; /* IRQ used by SAA7146 card */ + unsigned short id; + unsigned char revision; + unsigned char boardcfg[64]; /* 64 bytes of config from eeprom */ + unsigned long saa7146_adr; /* bus address of IO mem from PCI BIOS */ + struct saa7146_window win; + unsigned char __iomem *saa7146_mem; /* pointer to mapped IO memory */ + struct device_open open_data[MAX_OPENS]; +#define MAX_MARKS 16 + /* for a/v sync */ + int endmark[MAX_MARKS], endmarkhead, endmarktail; + u32 *dmaRPS1, *pageRPS1, *dmaRPS2, *pageRPS2, *dmavid1, *dmavid2, + *dmavid3, *dmaa1in, *dmaa1out, *dmaa2in, *dmaa2out, + *pagedebi, *pagevid1, *pagevid2, *pagevid3, *pagea1in, + *pagea1out, *pagea2in, *pagea2out; + wait_queue_head_t i2cq, debiq, audq, vidq; + u8 *vidbuf, *audbuf, *osdbuf, *dmadebi; + int audhead, vidhead, osdhead, audtail, vidtail, osdtail; + spinlock_t lock; /* the device lock */ +}; +#endif + +#ifdef _ALPHA_SAA7146 +#define saawrite(dat,adr) writel((dat), saa->saa7146_adr+(adr)) +#define saaread(adr) readl(saa->saa7146_adr+(adr)) +#else +#define saawrite(dat,adr) writel((dat), saa->saa7146_mem+(adr)) +#define saaread(adr) readl(saa->saa7146_mem+(adr)) +#endif + +#define saaand(dat,adr) saawrite((dat) & saaread(adr), adr) +#define saaor(dat,adr) saawrite((dat) | saaread(adr), adr) +#define saaaor(dat,mask,adr) saawrite((dat) | ((mask) & saaread(adr)), adr) + +/* bitmask of attached hardware found */ +#define SAA7146_UNKNOWN 0x00000000 +#define SAA7146_SAA7111 0x00000001 +#define SAA7146_SAA7121 0x00000002 +#define SAA7146_IBMMPEG 0x00000004 + +#endif diff --git a/linux/drivers/media/video/saa7146reg.h b/linux/drivers/media/video/saa7146reg.h new file mode 100644 index 000000000..80ec2c146 --- /dev/null +++ b/linux/drivers/media/video/saa7146reg.h @@ -0,0 +1,283 @@ +/* + saa7146.h - definitions philips saa7146 based cards + Copyright (C) 1999 Nathan Laredo (laredo@gnu.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, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __SAA7146_REG__ +#define __SAA7146_REG__ +#define SAA7146_BASE_ODD1 0x00 +#define SAA7146_BASE_EVEN1 0x04 +#define SAA7146_PROT_ADDR1 0x08 +#define SAA7146_PITCH1 0x0c +#define SAA7146_PAGE1 0x10 +#define SAA7146_NUM_LINE_BYTE1 0x14 +#define SAA7146_BASE_ODD2 0x18 +#define SAA7146_BASE_EVEN2 0x1c +#define SAA7146_PROT_ADDR2 0x20 +#define SAA7146_PITCH2 0x24 +#define SAA7146_PAGE2 0x28 +#define SAA7146_NUM_LINE_BYTE2 0x2c +#define SAA7146_BASE_ODD3 0x30 +#define SAA7146_BASE_EVEN3 0x34 +#define SAA7146_PROT_ADDR3 0x38 +#define SAA7146_PITCH3 0x3c +#define SAA7146_PAGE3 0x40 +#define SAA7146_NUM_LINE_BYTE3 0x44 +#define SAA7146_PCI_BT_V1 0x48 +#define SAA7146_PCI_BT_V2 0x49 +#define SAA7146_PCI_BT_V3 0x4a +#define SAA7146_PCI_BT_DEBI 0x4b +#define SAA7146_PCI_BT_A 0x4c +#define SAA7146_DD1_INIT 0x50 +#define SAA7146_DD1_STREAM_B 0x54 +#define SAA7146_DD1_STREAM_A 0x56 +#define SAA7146_BRS_CTRL 0x58 +#define SAA7146_HPS_CTRL 0x5c +#define SAA7146_HPS_V_SCALE 0x60 +#define SAA7146_HPS_V_GAIN 0x64 +#define SAA7146_HPS_H_PRESCALE 0x68 +#define SAA7146_HPS_H_SCALE 0x6c +#define SAA7146_BCS_CTRL 0x70 +#define SAA7146_CHROMA_KEY_RANGE 0x74 +#define SAA7146_CLIP_FORMAT_CTRL 0x78 +#define SAA7146_DEBI_CONFIG 0x7c +#define SAA7146_DEBI_COMMAND 0x80 +#define SAA7146_DEBI_PAGE 0x84 +#define SAA7146_DEBI_AD 0x88 +#define SAA7146_I2C_TRANSFER 0x8c +#define SAA7146_I2C_STATUS 0x90 +#define SAA7146_BASE_A1_IN 0x94 +#define SAA7146_PROT_A1_IN 0x98 +#define SAA7146_PAGE_A1_IN 0x9C +#define SAA7146_BASE_A1_OUT 0xa0 +#define SAA7146_PROT_A1_OUT 0xa4 +#define SAA7146_PAGE_A1_OUT 0xa8 +#define SAA7146_BASE_A2_IN 0xac +#define SAA7146_PROT_A2_IN 0xb0 +#define SAA7146_PAGE_A2_IN 0xb4 +#define SAA7146_BASE_A2_OUT 0xb8 +#define SAA7146_PROT_A2_OUT 0xbc +#define SAA7146_PAGE_A2_OUT 0xc0 +#define SAA7146_RPS_PAGE0 0xc4 +#define SAA7146_RPS_PAGE1 0xc8 +#define SAA7146_RPS_THRESH0 0xcc +#define SAA7146_RPS_THRESH1 0xd0 +#define SAA7146_RPS_TOV0 0xd4 +#define SAA7146_RPS_TOV1 0xd8 +#define SAA7146_IER 0xdc +#define SAA7146_GPIO_CTRL 0xe0 +#define SAA7146_EC1SSR 0xe4 +#define SAA7146_EC2SSR 0xe8 +#define SAA7146_ECT1R 0xec +#define SAA7146_ECT2R 0xf0 +#define SAA7146_ACON1 0xf4 +#define SAA7146_ACON2 0xf8 +#define SAA7146_MC1 0xfc +#define SAA7146_MC2 0x100 +#define SAA7146_RPS_ADDR0 0x104 +#define SAA7146_RPS_ADDR1 0x108 +#define SAA7146_ISR 0x10c +#define SAA7146_PSR 0x110 +#define SAA7146_SSR 0x114 +#define SAA7146_EC1R 0x118 +#define SAA7146_EC2R 0x11c +#define SAA7146_VDP1 0x120 +#define SAA7146_VDP2 0x124 +#define SAA7146_VDP3 0x128 +#define SAA7146_ADP1 0x12c +#define SAA7146_ADP2 0x130 +#define SAA7146_ADP3 0x134 +#define SAA7146_ADP4 0x138 +#define SAA7146_DDP 0x13c +#define SAA7146_LEVEL_REP 0x140 +#define SAA7146_FB_BUFFER1 0x144 +#define SAA7146_FB_BUFFER2 0x148 +#define SAA7146_A_TIME_SLOT1 0x180 +#define SAA7146_A_TIME_SLOT2 0x1C0 + +/* bitfield defines */ +#define MASK_31 0x80000000 +#define MASK_30 0x40000000 +#define MASK_29 0x20000000 +#define MASK_28 0x10000000 +#define MASK_27 0x08000000 +#define MASK_26 0x04000000 +#define MASK_25 0x02000000 +#define MASK_24 0x01000000 +#define MASK_23 0x00800000 +#define MASK_22 0x00400000 +#define MASK_21 0x00200000 +#define MASK_20 0x00100000 +#define MASK_19 0x00080000 +#define MASK_18 0x00040000 +#define MASK_17 0x00020000 +#define MASK_16 0x00010000 +#define MASK_15 0x00008000 +#define MASK_14 0x00004000 +#define MASK_13 0x00002000 +#define MASK_12 0x00001000 +#define MASK_11 0x00000800 +#define MASK_10 0x00000400 +#define MASK_09 0x00000200 +#define MASK_08 0x00000100 +#define MASK_07 0x00000080 +#define MASK_06 0x00000040 +#define MASK_05 0x00000020 +#define MASK_04 0x00000010 +#define MASK_03 0x00000008 +#define MASK_02 0x00000004 +#define MASK_01 0x00000002 +#define MASK_00 0x00000001 +#define MASK_B0 0x000000ff +#define MASK_B1 0x0000ff00 +#define MASK_B2 0x00ff0000 +#define MASK_B3 0xff000000 +#define MASK_W0 0x0000ffff +#define MASK_W1 0xffff0000 +#define MASK_PA 0xfffffffc +#define MASK_PR 0xfffffffe +#define MASK_ER 0xffffffff +#define MASK_NONE 0x00000000 + +#define SAA7146_PAGE_MAP_EN MASK_11 +/* main control register 1 */ +#define SAA7146_MC1_MRST_N MASK_15 +#define SAA7146_MC1_ERPS1 MASK_13 +#define SAA7146_MC1_ERPS0 MASK_12 +#define SAA7146_MC1_EDP MASK_11 +#define SAA7146_MC1_EVP MASK_10 +#define SAA7146_MC1_EAP MASK_09 +#define SAA7146_MC1_EI2C MASK_08 +#define SAA7146_MC1_TR_E_DEBI MASK_07 +#define SAA7146_MC1_TR_E_1 MASK_06 +#define SAA7146_MC1_TR_E_2 MASK_05 +#define SAA7146_MC1_TR_E_3 MASK_04 +#define SAA7146_MC1_TR_E_A2_OUT MASK_03 +#define SAA7146_MC1_TR_E_A2_IN MASK_02 +#define SAA7146_MC1_TR_E_A1_OUT MASK_01 +#define SAA7146_MC1_TR_E_A1_IN MASK_00 +/* main control register 2 */ +#define SAA7146_MC2_RPS_SIG4 MASK_15 +#define SAA7146_MC2_RPS_SIG3 MASK_14 +#define SAA7146_MC2_RPS_SIG2 MASK_13 +#define SAA7146_MC2_RPS_SIG1 MASK_12 +#define SAA7146_MC2_RPS_SIG0 MASK_11 +#define SAA7146_MC2_UPLD_D1_B MASK_10 +#define SAA7146_MC2_UPLD_D1_A MASK_09 +#define SAA7146_MC2_UPLD_BRS MASK_08 +#define SAA7146_MC2_UPLD_HPS_H MASK_06 +#define SAA7146_MC2_UPLD_HPS_V MASK_05 +#define SAA7146_MC2_UPLD_DMA3 MASK_04 +#define SAA7146_MC2_UPLD_DMA2 MASK_03 +#define SAA7146_MC2_UPLD_DMA1 MASK_02 +#define SAA7146_MC2_UPLD_DEBI MASK_01 +#define SAA7146_MC2_UPLD_I2C MASK_00 +/* Primary Status Register and Interrupt Enable/Status Registers */ +#define SAA7146_PSR_PPEF MASK_31 +#define SAA7146_PSR_PABO MASK_30 +#define SAA7146_PSR_PPED MASK_29 +#define SAA7146_PSR_RPS_I1 MASK_28 +#define SAA7146_PSR_RPS_I0 MASK_27 +#define SAA7146_PSR_RPS_LATE1 MASK_26 +#define SAA7146_PSR_RPS_LATE0 MASK_25 +#define SAA7146_PSR_RPS_E1 MASK_24 +#define SAA7146_PSR_RPS_E0 MASK_23 +#define SAA7146_PSR_RPS_TO1 MASK_22 +#define SAA7146_PSR_RPS_TO0 MASK_21 +#define SAA7146_PSR_UPLD MASK_20 +#define SAA7146_PSR_DEBI_S MASK_19 +#define SAA7146_PSR_DEBI_E MASK_18 +#define SAA7146_PSR_I2C_S MASK_17 +#define SAA7146_PSR_I2C_E MASK_16 +#define SAA7146_PSR_A2_IN MASK_15 +#define SAA7146_PSR_A2_OUT MASK_14 +#define SAA7146_PSR_A1_IN MASK_13 +#define SAA7146_PSR_A1_OUT MASK_12 +#define SAA7146_PSR_AFOU MASK_11 +#define SAA7146_PSR_V_PE MASK_10 +#define SAA7146_PSR_VFOU MASK_09 +#define SAA7146_PSR_FIDA MASK_08 +#define SAA7146_PSR_FIDB MASK_07 +#define SAA7146_PSR_PIN3 MASK_06 +#define SAA7146_PSR_PIN2 MASK_05 +#define SAA7146_PSR_PIN1 MASK_04 +#define SAA7146_PSR_PIN0 MASK_03 +#define SAA7146_PSR_ECS MASK_02 +#define SAA7146_PSR_EC3S MASK_01 +#define SAA7146_PSR_EC0S MASK_00 +/* Secondary Status Register */ +#define SAA7146_SSR_PRQ MASK_31 +#define SAA7146_SSR_PMA MASK_30 +#define SAA7146_SSR_RPS_RE1 MASK_29 +#define SAA7146_SSR_RPS_PE1 MASK_28 +#define SAA7146_SSR_RPS_A1 MASK_27 +#define SAA7146_SSR_RPS_RE0 MASK_26 +#define SAA7146_SSR_RPS_PE0 MASK_25 +#define SAA7146_SSR_RPS_A0 MASK_24 +#define SAA7146_SSR_DEBI_TO MASK_23 +#define SAA7146_SSR_DEBI_EF MASK_22 +#define SAA7146_SSR_I2C_EA MASK_21 +#define SAA7146_SSR_I2C_EW MASK_20 +#define SAA7146_SSR_I2C_ER MASK_19 +#define SAA7146_SSR_I2C_EL MASK_18 +#define SAA7146_SSR_I2C_EF MASK_17 +#define SAA7146_SSR_V3P MASK_16 +#define SAA7146_SSR_V2P MASK_15 +#define SAA7146_SSR_V1P MASK_14 +#define SAA7146_SSR_VF3 MASK_13 +#define SAA7146_SSR_VF2 MASK_12 +#define SAA7146_SSR_VF1 MASK_11 +#define SAA7146_SSR_AF2_IN MASK_10 +#define SAA7146_SSR_AF2_OUT MASK_09 +#define SAA7146_SSR_AF1_IN MASK_08 +#define SAA7146_SSR_AF1_OUT MASK_07 +#define SAA7146_SSR_VGT MASK_05 +#define SAA7146_SSR_LNQG MASK_04 +#define SAA7146_SSR_EC5S MASK_03 +#define SAA7146_SSR_EC4S MASK_02 +#define SAA7146_SSR_EC2S MASK_01 +#define SAA7146_SSR_EC1S MASK_00 +/* I2C status register */ +#define SAA7146_I2C_ABORT MASK_07 +#define SAA7146_I2C_SPERR MASK_06 +#define SAA7146_I2C_APERR MASK_05 +#define SAA7146_I2C_DTERR MASK_04 +#define SAA7146_I2C_DRERR MASK_03 +#define SAA7146_I2C_AL MASK_02 +#define SAA7146_I2C_ERR MASK_01 +#define SAA7146_I2C_BUSY MASK_00 +/* output formats */ +#define SAA7146_YUV422 0 +#define SAA7146_RGB16 0 +#define SAA7146_YUV444 1 +#define SAA7146_RGB24 1 +#define SAA7146_ARGB32 2 +#define SAA7146_YUV411 3 +#define SAA7146_ARGB15 3 +#define SAA7146_YUV2 4 +#define SAA7146_RGAB15 4 +#define SAA7146_Y8 6 +#define SAA7146_YUV8 7 +#define SAA7146_RGB8 7 +#define SAA7146_YUV444p 8 +#define SAA7146_YUV422p 9 +#define SAA7146_YUV420p 10 +#define SAA7146_YUV1620 11 +#define SAA7146_Y1 13 +#define SAA7146_Y2 14 +#define SAA7146_YUV1 15 +#endif diff --git a/linux/drivers/media/video/saa7185.c b/linux/drivers/media/video/saa7185.c new file mode 100644 index 000000000..a9b45c25a --- /dev/null +++ b/linux/drivers/media/video/saa7185.c @@ -0,0 +1,508 @@ +/* + * saa7185 - Philips SAA7185B video encoder driver version 0.0.3 + * + * Copyright (C) 1998 Dave Perks + * + * Slight changes for video timing and attachment output by + * Wolfgang Scherr + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (1/1/2003) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "compat.h" +#include +#include + +MODULE_DESCRIPTION("Philips SAA7185 video encoder driver"); +MODULE_AUTHOR("Dave Perks"); +MODULE_LICENSE("GPL"); + +#include + +#define I2C_NAME(s) (s)->name + +#include + +static int debug = 0; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define dprintk(num, format, args...) \ + do { \ + if (debug >= num) \ + printk(format, ##args); \ + } while (0) + +/* ----------------------------------------------------------------------- */ + +struct saa7185 { + unsigned char reg[128]; + + int norm; + int enable; + int bright; + int contrast; + int hue; + int sat; +}; + +#define I2C_SAA7185 0x88 + +/* ----------------------------------------------------------------------- */ + +static inline int +saa7185_read (struct i2c_client *client) +{ + return i2c_smbus_read_byte(client); +} + +static int +saa7185_write (struct i2c_client *client, + u8 reg, + u8 value) +{ + struct saa7185 *encoder = i2c_get_clientdata(client); + + dprintk(1, KERN_DEBUG "SAA7185: %02x set to %02x\n", reg, value); + encoder->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int +saa7185_write_block (struct i2c_client *client, + const u8 *data, + unsigned int len) +{ + int ret = -1; + u8 reg; + + /* the adv7175 has an autoincrement function, use it if + * the adapter understands raw I2C */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + /* do raw I2C, not smbus compatible */ + struct saa7185 *encoder = i2c_get_clientdata(client); + u8 block_data[32]; + int block_len; + + while (len >= 2) { + block_len = 0; + block_data[block_len++] = reg = data[0]; + do { + block_data[block_len++] = + encoder->reg[reg++] = data[1]; + len -= 2; + data += 2; + } while (len >= 2 && data[0] == reg && + block_len < 32); + if ((ret = i2c_master_send(client, block_data, + block_len)) < 0) + break; + } + } else { + /* do some slow I2C emulation kind of thing */ + while (len >= 2) { + reg = *data++; + if ((ret = saa7185_write(client, reg, + *data++)) < 0) + break; + len -= 2; + } + } + + return ret; +} + +/* ----------------------------------------------------------------------- */ + +static const unsigned char init_common[] = { + 0x3a, 0x0f, /* CBENB=0, V656=0, VY2C=1, + * YUV2C=1, MY2C=1, MUV2C=1 */ + + 0x42, 0x6b, /* OVLY0=107 */ + 0x43, 0x00, /* OVLU0=0 white */ + 0x44, 0x00, /* OVLV0=0 */ + 0x45, 0x22, /* OVLY1=34 */ + 0x46, 0xac, /* OVLU1=172 yellow */ + 0x47, 0x0e, /* OVLV1=14 */ + 0x48, 0x03, /* OVLY2=3 */ + 0x49, 0x1d, /* OVLU2=29 cyan */ + 0x4a, 0xac, /* OVLV2=172 */ + 0x4b, 0xf0, /* OVLY3=240 */ + 0x4c, 0xc8, /* OVLU3=200 green */ + 0x4d, 0xb9, /* OVLV3=185 */ + 0x4e, 0xd4, /* OVLY4=212 */ + 0x4f, 0x38, /* OVLU4=56 magenta */ + 0x50, 0x47, /* OVLV4=71 */ + 0x51, 0xc1, /* OVLY5=193 */ + 0x52, 0xe3, /* OVLU5=227 red */ + 0x53, 0x54, /* OVLV5=84 */ + 0x54, 0xa3, /* OVLY6=163 */ + 0x55, 0x54, /* OVLU6=84 blue */ + 0x56, 0xf2, /* OVLV6=242 */ + 0x57, 0x90, /* OVLY7=144 */ + 0x58, 0x00, /* OVLU7=0 black */ + 0x59, 0x00, /* OVLV7=0 */ + + 0x5a, 0x00, /* CHPS=0 */ + 0x5b, 0x76, /* GAINU=118 */ + 0x5c, 0xa5, /* GAINV=165 */ + 0x5d, 0x3c, /* BLCKL=60 */ + 0x5e, 0x3a, /* BLNNL=58 */ + 0x5f, 0x3a, /* CCRS=0, BLNVB=58 */ + 0x60, 0x00, /* NULL */ + + /* 0x61 - 0x66 set according to norm */ + + 0x67, 0x00, /* 0 : caption 1st byte odd field */ + 0x68, 0x00, /* 0 : caption 2nd byte odd field */ + 0x69, 0x00, /* 0 : caption 1st byte even field */ + 0x6a, 0x00, /* 0 : caption 2nd byte even field */ + + 0x6b, 0x91, /* MODIN=2, PCREF=0, SCCLN=17 */ + 0x6c, 0x20, /* SRCV1=0, TRCV2=1, ORCV1=0, PRCV1=0, + * CBLF=0, ORCV2=0, PRCV2=0 */ + 0x6d, 0x00, /* SRCM1=0, CCEN=0 */ + + 0x6e, 0x0e, /* HTRIG=0x005, approx. centered, at + * least for PAL */ + 0x6f, 0x00, /* HTRIG upper bits */ + 0x70, 0x20, /* PHRES=0, SBLN=1, VTRIG=0 */ + + /* The following should not be needed */ + + 0x71, 0x15, /* BMRQ=0x115 */ + 0x72, 0x90, /* EMRQ=0x690 */ + 0x73, 0x61, /* EMRQ=0x690, BMRQ=0x115 */ + 0x74, 0x00, /* NULL */ + 0x75, 0x00, /* NULL */ + 0x76, 0x00, /* NULL */ + 0x77, 0x15, /* BRCV=0x115 */ + 0x78, 0x90, /* ERCV=0x690 */ + 0x79, 0x61, /* ERCV=0x690, BRCV=0x115 */ + + /* Field length controls */ + + 0x7a, 0x70, /* FLC=0 */ + + /* The following should not be needed if SBLN = 1 */ + + 0x7b, 0x16, /* FAL=22 */ + 0x7c, 0x35, /* LAL=244 */ + 0x7d, 0x20, /* LAL=244, FAL=22 */ +}; + +static const unsigned char init_pal[] = { + 0x61, 0x1e, /* FISE=0, PAL=1, SCBW=1, RTCE=1, + * YGS=1, INPI=0, DOWN=0 */ + 0x62, 0xc8, /* DECTYP=1, BSTA=72 */ + 0x63, 0xcb, /* FSC0 */ + 0x64, 0x8a, /* FSC1 */ + 0x65, 0x09, /* FSC2 */ + 0x66, 0x2a, /* FSC3 */ +}; + +static const unsigned char init_ntsc[] = { + 0x61, 0x1d, /* FISE=1, PAL=0, SCBW=1, RTCE=1, + * YGS=1, INPI=0, DOWN=0 */ + 0x62, 0xe6, /* DECTYP=1, BSTA=102 */ + 0x63, 0x1f, /* FSC0 */ + 0x64, 0x7c, /* FSC1 */ + 0x65, 0xf0, /* FSC2 */ + 0x66, 0x21, /* FSC3 */ +}; + +static int +saa7185_command (struct i2c_client *client, + unsigned int cmd, + void *arg) +{ + struct saa7185 *encoder = i2c_get_clientdata(client); + + switch (cmd) { + + case 0: + saa7185_write_block(client, init_common, + sizeof(init_common)); + switch (encoder->norm) { + + case VIDEO_MODE_NTSC: + saa7185_write_block(client, init_ntsc, + sizeof(init_ntsc)); + break; + + case VIDEO_MODE_PAL: + saa7185_write_block(client, init_pal, + sizeof(init_pal)); + break; + } + + break; + + case ENCODER_GET_CAPABILITIES: + { + struct video_encoder_capability *cap = arg; + + cap->flags = + VIDEO_ENCODER_PAL | VIDEO_ENCODER_NTSC | + VIDEO_ENCODER_SECAM | VIDEO_ENCODER_CCIR; + cap->inputs = 1; + cap->outputs = 1; + } + break; + + case ENCODER_SET_NORM: + { + int *iarg = arg; + + //saa7185_write_block(client, init_common, sizeof(init_common)); + + switch (*iarg) { + + case VIDEO_MODE_NTSC: + saa7185_write_block(client, init_ntsc, + sizeof(init_ntsc)); + break; + + case VIDEO_MODE_PAL: + saa7185_write_block(client, init_pal, + sizeof(init_pal)); + break; + + case VIDEO_MODE_SECAM: + default: + return -EINVAL; + + } + encoder->norm = *iarg; + } + break; + + case ENCODER_SET_INPUT: + { + int *iarg = arg; + + /* RJ: *iarg = 0: input is from SA7111 + *iarg = 1: input is from ZR36060 */ + + switch (*iarg) { + + case 0: + /* Switch RTCE to 1 */ + saa7185_write(client, 0x61, + (encoder->reg[0x61] & 0xf7) | 0x08); + saa7185_write(client, 0x6e, 0x01); + break; + + case 1: + /* Switch RTCE to 0 */ + saa7185_write(client, 0x61, + (encoder->reg[0x61] & 0xf7) | 0x00); + /* SW: a slight sync problem... */ + saa7185_write(client, 0x6e, 0x00); + break; + + default: + return -EINVAL; + + } + } + break; + + case ENCODER_SET_OUTPUT: + { + int *iarg = arg; + + /* not much choice of outputs */ + if (*iarg != 0) { + return -EINVAL; + } + } + break; + + case ENCODER_ENABLE_OUTPUT: + { + int *iarg = arg; + + encoder->enable = !!*iarg; + saa7185_write(client, 0x61, + (encoder->reg[0x61] & 0xbf) | + (encoder->enable ? 0x00 : 0x40)); + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ +static unsigned short normal_i2c[] = { I2C_SAA7185 >> 1, I2C_CLIENT_END }; + +static unsigned short ignore = I2C_CLIENT_END; + +static struct i2c_client_address_data addr_data = { + .normal_i2c = normal_i2c, + .probe = &ignore, + .ignore = &ignore, +}; + +static struct i2c_driver i2c_driver_saa7185; + +static int +saa7185_detect_client (struct i2c_adapter *adapter, + int address, + int kind) +{ + int i; + struct i2c_client *client; + struct saa7185 *encoder; + + dprintk(1, + KERN_INFO + "saa7185.c: detecting saa7185 client on address 0x%x\n", + address << 1); + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == 0) + return -ENOMEM; + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver_saa7185; + strlcpy(I2C_NAME(client), "saa7185", sizeof(I2C_NAME(client))); + + encoder = kzalloc(sizeof(struct saa7185), GFP_KERNEL); + if (encoder == NULL) { + kfree(client); + return -ENOMEM; + } + encoder->norm = VIDEO_MODE_NTSC; + encoder->enable = 1; + i2c_set_clientdata(client, encoder); + + i = i2c_attach_client(client); + if (i) { + kfree(client); + kfree(encoder); + return i; + } + + i = saa7185_write_block(client, init_common, sizeof(init_common)); + if (i >= 0) { + i = saa7185_write_block(client, init_ntsc, + sizeof(init_ntsc)); + } + if (i < 0) { + dprintk(1, KERN_ERR "%s_attach: init error %d\n", + I2C_NAME(client), i); + } else { + dprintk(1, + KERN_INFO + "%s_attach: chip version %d at address 0x%x\n", + I2C_NAME(client), saa7185_read(client) >> 5, + client->addr << 1); + } + + return 0; +} + +static int +saa7185_attach_adapter (struct i2c_adapter *adapter) +{ + dprintk(1, + KERN_INFO + "saa7185.c: starting probe for adapter %s (0x%x)\n", + I2C_NAME(adapter), adapter->id); + return i2c_probe(adapter, &addr_data, &saa7185_detect_client); +} + +static int +saa7185_detach_client (struct i2c_client *client) +{ + struct saa7185 *encoder = i2c_get_clientdata(client); + int err; + + err = i2c_detach_client(client); + if (err) { + return err; + } + + saa7185_write(client, 0x61, (encoder->reg[0x61]) | 0x40); /* SW: output off is active */ + //saa7185_write(client, 0x3a, (encoder->reg[0x3a]) | 0x80); /* SW: color bar */ + + kfree(encoder); + kfree(client); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver i2c_driver_saa7185 = { + .driver = { + .name = "saa7185", /* name */ + }, + + .id = I2C_DRIVERID_SAA7185B, + + .attach_adapter = saa7185_attach_adapter, + .detach_client = saa7185_detach_client, + .command = saa7185_command, +}; + +static int __init +saa7185_init (void) +{ + return i2c_add_driver(&i2c_driver_saa7185); +} + +static void __exit +saa7185_exit (void) +{ + i2c_del_driver(&i2c_driver_saa7185); +} + +module_init(saa7185_init); +module_exit(saa7185_exit); diff --git a/linux/drivers/media/video/saa7191.c b/linux/drivers/media/video/saa7191.c new file mode 100644 index 000000000..746cadb8f --- /dev/null +++ b/linux/drivers/media/video/saa7191.c @@ -0,0 +1,808 @@ +/* + * saa7191.c - Philips SAA7191 video decoder driver + * + * Copyright (C) 2003 Ladislav Michl + * Copyright (C) 2004,2005 Mikael Nousiainen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "saa7191.h" + +#define SAA7191_MODULE_VERSION "0.0.5" + +MODULE_DESCRIPTION("Philips SAA7191 video decoder driver"); +MODULE_VERSION(SAA7191_MODULE_VERSION); +MODULE_AUTHOR("Mikael Nousiainen "); +MODULE_LICENSE("GPL"); + +// #define SAA7191_DEBUG + +#ifdef SAA7191_DEBUG +#define dprintk(x...) printk("SAA7191: " x); +#else +#define dprintk(x...) +#endif + +#define SAA7191_SYNC_COUNT 30 +#define SAA7191_SYNC_DELAY 100 /* milliseconds */ + +struct saa7191 { + struct i2c_client *client; + + /* the register values are stored here as the actual + * I2C-registers are write-only */ + u8 reg[25]; + + int input; + int norm; +}; + +static struct i2c_driver i2c_driver_saa7191; + +static const u8 initseq[] = { + 0, /* Subaddress */ + + 0x50, /* (0x50) SAA7191_REG_IDEL */ + + /* 50 Hz signal timing */ + 0x30, /* (0x30) SAA7191_REG_HSYB */ + 0x00, /* (0x00) SAA7191_REG_HSYS */ + 0xe8, /* (0xe8) SAA7191_REG_HCLB */ + 0xb6, /* (0xb6) SAA7191_REG_HCLS */ + 0xf4, /* (0xf4) SAA7191_REG_HPHI */ + + /* control */ + SAA7191_LUMA_APER_1, /* (0x01) SAA7191_REG_LUMA - CVBS mode */ + 0x00, /* (0x00) SAA7191_REG_HUEC */ + 0xf8, /* (0xf8) SAA7191_REG_CKTQ */ + 0xf8, /* (0xf8) SAA7191_REG_CKTS */ + 0x90, /* (0x90) SAA7191_REG_PLSE */ + 0x90, /* (0x90) SAA7191_REG_SESE */ + 0x00, /* (0x00) SAA7191_REG_GAIN */ + SAA7191_STDC_NFEN | SAA7191_STDC_HRMV, /* (0x0c) SAA7191_REG_STDC + * - not SECAM, + * slow time constant */ + SAA7191_IOCK_OEDC | SAA7191_IOCK_OEHS | SAA7191_IOCK_OEVS + | SAA7191_IOCK_OEDY, /* (0x78) SAA7191_REG_IOCK + * - chroma from CVBS, GPSW1 & 2 off */ + SAA7191_CTL3_AUFD | SAA7191_CTL3_SCEN | SAA7191_CTL3_OFTS + | SAA7191_CTL3_YDEL0, /* (0x99) SAA7191_REG_CTL3 + * - automatic field detection */ + 0x00, /* (0x00) SAA7191_REG_CTL4 */ + 0x2c, /* (0x2c) SAA7191_REG_CHCV - PAL nominal value */ + 0x00, /* unused */ + 0x00, /* unused */ + + /* 60 Hz signal timing */ + 0x34, /* (0x34) SAA7191_REG_HS6B */ + 0x0a, /* (0x0a) SAA7191_REG_HS6S */ + 0xf4, /* (0xf4) SAA7191_REG_HC6B */ + 0xce, /* (0xce) SAA7191_REG_HC6S */ + 0xf4, /* (0xf4) SAA7191_REG_HP6I */ +}; + +/* SAA7191 register handling */ + +static u8 saa7191_read_reg(struct i2c_client *client, + u8 reg) +{ + return ((struct saa7191 *)i2c_get_clientdata(client))->reg[reg]; +} + +static int saa7191_read_status(struct i2c_client *client, + u8 *value) +{ + int ret; + + ret = i2c_master_recv(client, value, 1); + if (ret < 0) { + printk(KERN_ERR "SAA7191: saa7191_read_status(): read failed\n"); + return ret; + } + + return 0; +} + + +static int saa7191_write_reg(struct i2c_client *client, u8 reg, + u8 value) +{ + ((struct saa7191 *)i2c_get_clientdata(client))->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* the first byte of data must be the first subaddress number (register) */ +static int saa7191_write_block(struct i2c_client *client, + u8 length, u8 *data) +{ + int i; + int ret; + + struct saa7191 *decoder = (struct saa7191 *)i2c_get_clientdata(client); + for (i = 0; i < (length - 1); i++) { + decoder->reg[data[0] + i] = data[i + 1]; + } + + ret = i2c_master_send(client, data, length); + if (ret < 0) { + printk(KERN_ERR "SAA7191: saa7191_write_block(): " + "write failed\n"); + return ret; + } + + return 0; +} + +/* Helper functions */ + +static int saa7191_set_input(struct i2c_client *client, int input) +{ + struct saa7191 *decoder = i2c_get_clientdata(client); + u8 luma = saa7191_read_reg(client, SAA7191_REG_LUMA); + u8 iock = saa7191_read_reg(client, SAA7191_REG_IOCK); + int err; + + switch (input) { + case SAA7191_INPUT_COMPOSITE: /* Set Composite input */ + iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1 + | SAA7191_IOCK_GPSW2); + /* Chrominance trap active */ + luma &= ~SAA7191_LUMA_BYPS; + break; + case SAA7191_INPUT_SVIDEO: /* Set S-Video input */ + iock |= SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW2; + /* Chrominance trap bypassed */ + luma |= SAA7191_LUMA_BYPS; + break; + default: + return -EINVAL; + } + + err = saa7191_write_reg(client, SAA7191_REG_LUMA, luma); + if (err) + return -EIO; + err = saa7191_write_reg(client, SAA7191_REG_IOCK, iock); + if (err) + return -EIO; + + decoder->input = input; + + return 0; +} + +static int saa7191_set_norm(struct i2c_client *client, int norm) +{ + struct saa7191 *decoder = i2c_get_clientdata(client); + u8 stdc = saa7191_read_reg(client, SAA7191_REG_STDC); + u8 ctl3 = saa7191_read_reg(client, SAA7191_REG_CTL3); + u8 chcv = saa7191_read_reg(client, SAA7191_REG_CHCV); + int err; + + switch(norm) { + case SAA7191_NORM_PAL: + stdc &= ~SAA7191_STDC_SECS; + ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); + chcv = SAA7191_CHCV_PAL; + break; + case SAA7191_NORM_NTSC: + stdc &= ~SAA7191_STDC_SECS; + ctl3 &= ~SAA7191_CTL3_AUFD; + ctl3 |= SAA7191_CTL3_FSEL; + chcv = SAA7191_CHCV_NTSC; + break; + case SAA7191_NORM_SECAM: + stdc |= SAA7191_STDC_SECS; + ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); + chcv = SAA7191_CHCV_PAL; + break; + default: + return -EINVAL; + } + + err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3); + if (err) + return -EIO; + err = saa7191_write_reg(client, SAA7191_REG_STDC, stdc); + if (err) + return -EIO; + err = saa7191_write_reg(client, SAA7191_REG_CHCV, chcv); + if (err) + return -EIO; + + decoder->norm = norm; + + dprintk("ctl3: %02x stdc: %02x chcv: %02x\n", ctl3, + stdc, chcv); + dprintk("norm: %d\n", norm); + + return 0; +} + +static int saa7191_wait_for_signal(struct i2c_client *client, u8 *status) +{ + int i = 0; + + dprintk("Checking for signal...\n"); + + for (i = 0; i < SAA7191_SYNC_COUNT; i++) { + if (saa7191_read_status(client, status)) + return -EIO; + + if (((*status) & SAA7191_STATUS_HLCK) == 0) { + dprintk("Signal found\n"); + return 0; + } + + msleep(SAA7191_SYNC_DELAY); + } + + dprintk("No signal\n"); + + return -EBUSY; +} + +static int saa7191_autodetect_norm_extended(struct i2c_client *client) +{ + u8 stdc = saa7191_read_reg(client, SAA7191_REG_STDC); + u8 ctl3 = saa7191_read_reg(client, SAA7191_REG_CTL3); + u8 status; + int err = 0; + + dprintk("SAA7191 extended signal auto-detection...\n"); + + stdc &= ~SAA7191_STDC_SECS; + ctl3 &= ~(SAA7191_CTL3_FSEL); + + err = saa7191_write_reg(client, SAA7191_REG_STDC, stdc); + if (err) { + err = -EIO; + goto out; + } + err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3); + if (err) { + err = -EIO; + goto out; + } + + ctl3 |= SAA7191_CTL3_AUFD; + err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3); + if (err) { + err = -EIO; + goto out; + } + + msleep(SAA7191_SYNC_DELAY); + + err = saa7191_wait_for_signal(client, &status); + if (err) + goto out; + + if (status & SAA7191_STATUS_FIDT) { + /* 60Hz signal -> NTSC */ + dprintk("60Hz signal: NTSC\n"); + return saa7191_set_norm(client, SAA7191_NORM_NTSC); + } + + /* 50Hz signal */ + dprintk("50Hz signal: Trying PAL...\n"); + + /* try PAL first */ + err = saa7191_set_norm(client, SAA7191_NORM_PAL); + if (err) + goto out; + + msleep(SAA7191_SYNC_DELAY); + + err = saa7191_wait_for_signal(client, &status); + if (err) + goto out; + + /* not 50Hz ? */ + if (status & SAA7191_STATUS_FIDT) { + dprintk("No 50Hz signal\n"); + err = -EAGAIN; + goto out; + } + + if (status & SAA7191_STATUS_CODE) { + dprintk("PAL\n"); + return 0; + } + + dprintk("No color detected with PAL - Trying SECAM...\n"); + + /* no color detected ? -> try SECAM */ + err = saa7191_set_norm(client, + SAA7191_NORM_SECAM); + if (err) + goto out; + + msleep(SAA7191_SYNC_DELAY); + + err = saa7191_wait_for_signal(client, &status); + if (err) + goto out; + + /* not 50Hz ? */ + if (status & SAA7191_STATUS_FIDT) { + dprintk("No 50Hz signal\n"); + err = -EAGAIN; + goto out; + } + + if (status & SAA7191_STATUS_CODE) { + /* Color detected -> SECAM */ + dprintk("SECAM\n"); + return 0; + } + + dprintk("No color detected with SECAM - Going back to PAL.\n"); + + /* still no color detected ? + * -> set norm back to PAL */ + err = saa7191_set_norm(client, + SAA7191_NORM_PAL); + if (err) + goto out; + +out: + ctl3 = saa7191_read_reg(client, SAA7191_REG_CTL3); + if (ctl3 & SAA7191_CTL3_AUFD) { + ctl3 &= ~(SAA7191_CTL3_AUFD); + err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3); + if (err) { + err = -EIO; + } + } + + return err; +} + +static int saa7191_autodetect_norm(struct i2c_client *client) +{ + u8 status; + + dprintk("SAA7191 signal auto-detection...\n"); + + dprintk("Reading status...\n"); + + if (saa7191_read_status(client, &status)) + return -EIO; + + dprintk("Checking for signal...\n"); + + /* no signal ? */ + if (status & SAA7191_STATUS_HLCK) { + dprintk("No signal\n"); + return -EBUSY; + } + + dprintk("Signal found\n"); + + if (status & SAA7191_STATUS_FIDT) { + /* 60hz signal -> NTSC */ + dprintk("NTSC\n"); + return saa7191_set_norm(client, SAA7191_NORM_NTSC); + } else { + /* 50hz signal -> PAL */ + dprintk("PAL\n"); + return saa7191_set_norm(client, SAA7191_NORM_PAL); + } +} + +static int saa7191_get_control(struct i2c_client *client, + struct saa7191_control *ctrl) +{ + u8 reg; + int ret = 0; + + switch (ctrl->type) { + case SAA7191_CONTROL_BANDPASS: + case SAA7191_CONTROL_BANDPASS_WEIGHT: + case SAA7191_CONTROL_CORING: + reg = saa7191_read_reg(client, SAA7191_REG_LUMA); + switch (ctrl->type) { + case SAA7191_CONTROL_BANDPASS: + ctrl->value = ((s32)reg & SAA7191_LUMA_BPSS_MASK) + >> SAA7191_LUMA_BPSS_SHIFT; + break; + case SAA7191_CONTROL_BANDPASS_WEIGHT: + ctrl->value = ((s32)reg & SAA7191_LUMA_APER_MASK) + >> SAA7191_LUMA_APER_SHIFT; + break; + case SAA7191_CONTROL_CORING: + ctrl->value = ((s32)reg & SAA7191_LUMA_CORI_MASK) + >> SAA7191_LUMA_CORI_SHIFT; + break; + } + break; + case SAA7191_CONTROL_FORCE_COLOUR: + case SAA7191_CONTROL_CHROMA_GAIN: + reg = saa7191_read_reg(client, SAA7191_REG_GAIN); + if (ctrl->type == SAA7191_CONTROL_FORCE_COLOUR) + ctrl->value = ((s32)reg & SAA7191_GAIN_COLO) ? 1 : 0; + else + ctrl->value = ((s32)reg & SAA7191_GAIN_LFIS_MASK) + >> SAA7191_GAIN_LFIS_SHIFT; + break; + case SAA7191_CONTROL_HUE: + reg = saa7191_read_reg(client, SAA7191_REG_HUEC); + if (reg < 0x80) + reg += 0x80; + else + reg -= 0x80; + ctrl->value = (s32)reg; + break; + case SAA7191_CONTROL_VTRC: + reg = saa7191_read_reg(client, SAA7191_REG_STDC); + ctrl->value = ((s32)reg & SAA7191_STDC_VTRC) ? 1 : 0; + break; + case SAA7191_CONTROL_LUMA_DELAY: + reg = saa7191_read_reg(client, SAA7191_REG_CTL3); + ctrl->value = ((s32)reg & SAA7191_CTL3_YDEL_MASK) + >> SAA7191_CTL3_YDEL_SHIFT; + if (ctrl->value >= 4) + ctrl->value -= 8; + break; + case SAA7191_CONTROL_VNR: + reg = saa7191_read_reg(client, SAA7191_REG_CTL4); + ctrl->value = ((s32)reg & SAA7191_CTL4_VNOI_MASK) + >> SAA7191_CTL4_VNOI_SHIFT; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int saa7191_set_control(struct i2c_client *client, + struct saa7191_control *ctrl) +{ + u8 reg; + int ret = 0; + + switch (ctrl->type) { + case SAA7191_CONTROL_BANDPASS: + case SAA7191_CONTROL_BANDPASS_WEIGHT: + case SAA7191_CONTROL_CORING: + reg = saa7191_read_reg(client, SAA7191_REG_LUMA); + switch (ctrl->type) { + case SAA7191_CONTROL_BANDPASS: + reg &= ~SAA7191_LUMA_BPSS_MASK; + reg |= (ctrl->value << SAA7191_LUMA_BPSS_SHIFT) + & SAA7191_LUMA_BPSS_MASK; + break; + case SAA7191_CONTROL_BANDPASS_WEIGHT: + reg &= ~SAA7191_LUMA_APER_MASK; + reg |= (ctrl->value << SAA7191_LUMA_APER_SHIFT) + & SAA7191_LUMA_APER_MASK; + break; + case SAA7191_CONTROL_CORING: + reg &= ~SAA7191_LUMA_CORI_MASK; + reg |= (ctrl->value << SAA7191_LUMA_CORI_SHIFT) + & SAA7191_LUMA_CORI_MASK; + break; + } + ret = saa7191_write_reg(client, SAA7191_REG_LUMA, reg); + break; + case SAA7191_CONTROL_FORCE_COLOUR: + case SAA7191_CONTROL_CHROMA_GAIN: + reg = saa7191_read_reg(client, SAA7191_REG_GAIN); + if (ctrl->type == SAA7191_CONTROL_FORCE_COLOUR) { + if (ctrl->value) + reg |= SAA7191_GAIN_COLO; + else + reg &= ~SAA7191_GAIN_COLO; + } else { + reg &= ~SAA7191_GAIN_LFIS_MASK; + reg |= (ctrl->value << SAA7191_GAIN_LFIS_SHIFT) + & SAA7191_GAIN_LFIS_MASK; + } + ret = saa7191_write_reg(client, SAA7191_REG_GAIN, reg); + break; + case SAA7191_CONTROL_HUE: + reg = ctrl->value & 0xff; + if (reg < 0x80) + reg += 0x80; + else + reg -= 0x80; + ret = saa7191_write_reg(client, SAA7191_REG_HUEC, reg); + break; + case SAA7191_CONTROL_VTRC: + reg = saa7191_read_reg(client, SAA7191_REG_STDC); + if (ctrl->value) + reg |= SAA7191_STDC_VTRC; + else + reg &= ~SAA7191_STDC_VTRC; + ret = saa7191_write_reg(client, SAA7191_REG_STDC, reg); + break; + case SAA7191_CONTROL_LUMA_DELAY: { + s32 value = ctrl->value; + if (value < 0) + value += 8; + reg = saa7191_read_reg(client, SAA7191_REG_CTL3); + reg &= ~SAA7191_CTL3_YDEL_MASK; + reg |= (value << SAA7191_CTL3_YDEL_SHIFT) + & SAA7191_CTL3_YDEL_MASK; + ret = saa7191_write_reg(client, SAA7191_REG_CTL3, reg); + break; + } + case SAA7191_CONTROL_VNR: + reg = saa7191_read_reg(client, SAA7191_REG_CTL4); + reg &= ~SAA7191_CTL4_VNOI_MASK; + reg |= (ctrl->value << SAA7191_CTL4_VNOI_SHIFT) + & SAA7191_CTL4_VNOI_MASK; + ret = saa7191_write_reg(client, SAA7191_REG_CTL4, reg); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +/* I2C-interface */ + +static int saa7191_attach(struct i2c_adapter *adap, int addr, int kind) +{ + int err = 0; + struct saa7191 *decoder; + struct i2c_client *client; + + printk(KERN_INFO "Philips SAA7191 driver version %s\n", + SAA7191_MODULE_VERSION); + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return -ENOMEM; + decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); + if (!decoder) { + err = -ENOMEM; + goto out_free_client; + } + + client->addr = addr; + client->adapter = adap; + client->driver = &i2c_driver_saa7191; + client->flags = 0; + strcpy(client->name, "saa7191 client"); + i2c_set_clientdata(client, decoder); + + decoder->client = client; + + err = i2c_attach_client(client); + if (err) + goto out_free_decoder; + + err = saa7191_write_block(client, sizeof(initseq), (u8 *)initseq); + if (err) { + printk(KERN_ERR "SAA7191 initialization failed\n"); + goto out_detach_client; + } + + printk(KERN_INFO "SAA7191 initialized\n"); + + decoder->input = SAA7191_INPUT_COMPOSITE; + decoder->norm = SAA7191_NORM_PAL; + + err = saa7191_autodetect_norm(client); + if (err && (err != -EBUSY)) { + printk(KERN_ERR "SAA7191: Signal auto-detection failed\n"); + } + + return 0; + +out_detach_client: + i2c_detach_client(client); +out_free_decoder: + kfree(decoder); +out_free_client: + kfree(client); + return err; +} + +static int saa7191_probe(struct i2c_adapter *adap) +{ + /* Always connected to VINO */ + if (adap->id == I2C_HW_SGI_VINO) + return saa7191_attach(adap, SAA7191_ADDR, 0); + /* Feel free to add probe here :-) */ + return -ENODEV; +} + +static int saa7191_detach(struct i2c_client *client) +{ + struct saa7191 *decoder = i2c_get_clientdata(client); + + i2c_detach_client(client); + kfree(decoder); + kfree(client); + return 0; +} + +static int saa7191_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + struct saa7191 *decoder = i2c_get_clientdata(client); + + switch (cmd) { + case DECODER_GET_CAPABILITIES: { + struct video_decoder_capability *cap = arg; + + cap->flags = VIDEO_DECODER_PAL | VIDEO_DECODER_NTSC | + VIDEO_DECODER_SECAM | VIDEO_DECODER_AUTO; + cap->inputs = (client->adapter->id == I2C_HW_SGI_VINO) ? 2 : 1; + cap->outputs = 1; + break; + } + case DECODER_GET_STATUS: { + int *iarg = arg; + u8 status; + int res = 0; + + if (saa7191_read_status(client, &status)) { + return -EIO; + } + if ((status & SAA7191_STATUS_HLCK) == 0) + res |= DECODER_STATUS_GOOD; + if (status & SAA7191_STATUS_CODE) + res |= DECODER_STATUS_COLOR; + switch (decoder->norm) { + case SAA7191_NORM_NTSC: + res |= DECODER_STATUS_NTSC; + break; + case SAA7191_NORM_PAL: + res |= DECODER_STATUS_PAL; + break; + case SAA7191_NORM_SECAM: + res |= DECODER_STATUS_SECAM; + break; + case SAA7191_NORM_AUTO: + default: + if (status & SAA7191_STATUS_FIDT) + res |= DECODER_STATUS_NTSC; + else + res |= DECODER_STATUS_PAL; + break; + } + *iarg = res; + break; + } + case DECODER_SET_NORM: { + int *iarg = arg; + + switch (*iarg) { + case VIDEO_MODE_AUTO: + return saa7191_autodetect_norm(client); + case VIDEO_MODE_PAL: + return saa7191_set_norm(client, SAA7191_NORM_PAL); + case VIDEO_MODE_NTSC: + return saa7191_set_norm(client, SAA7191_NORM_NTSC); + case VIDEO_MODE_SECAM: + return saa7191_set_norm(client, SAA7191_NORM_SECAM); + default: + return -EINVAL; + } + break; + } + case DECODER_SET_INPUT: { + int *iarg = arg; + + switch (client->adapter->id) { + case I2C_HW_SGI_VINO: + return saa7191_set_input(client, *iarg); + default: + if (*iarg != 0) + return -EINVAL; + } + break; + } + case DECODER_SET_OUTPUT: { + int *iarg = arg; + + /* not much choice of outputs */ + if (*iarg != 0) + return -EINVAL; + break; + } + case DECODER_ENABLE_OUTPUT: { + /* Always enabled */ + break; + } + case DECODER_SET_PICTURE: { + struct video_picture *pic = arg; + unsigned val; + int err; + + val = (pic->hue >> 8) - 0x80; + + err = saa7191_write_reg(client, SAA7191_REG_HUEC, val); + if (err) + return -EIO; + + break; + } + case DECODER_SAA7191_GET_STATUS: { + struct saa7191_status *status = arg; + u8 status_reg; + + if (saa7191_read_status(client, &status_reg)) + return -EIO; + + status->signal = ((status_reg & SAA7191_STATUS_HLCK) == 0) + ? 1 : 0; + status->signal_60hz = (status_reg & SAA7191_STATUS_FIDT) + ? 1 : 0; + status->color = (status_reg & SAA7191_STATUS_CODE) ? 1 : 0; + + status->input = decoder->input; + status->norm = decoder->norm; + + break; + } + case DECODER_SAA7191_SET_NORM: { + int *norm = arg; + + switch (*norm) { + case SAA7191_NORM_AUTO: + return saa7191_autodetect_norm(client); + case SAA7191_NORM_AUTO_EXT: + return saa7191_autodetect_norm_extended(client); + default: + return saa7191_set_norm(client, *norm); + } + } + case DECODER_SAA7191_GET_CONTROL: { + return saa7191_get_control(client, arg); + } + case DECODER_SAA7191_SET_CONTROL: { + return saa7191_set_control(client, arg); + } + default: + return -EINVAL; + } + + return 0; +} + +static struct i2c_driver i2c_driver_saa7191 = { + .driver = { + .name = "saa7191", + }, + .id = I2C_DRIVERID_SAA7191, + .attach_adapter = saa7191_probe, + .detach_client = saa7191_detach, + .command = saa7191_command +}; + +static int saa7191_init(void) +{ + return i2c_add_driver(&i2c_driver_saa7191); +} + +static void saa7191_exit(void) +{ + i2c_del_driver(&i2c_driver_saa7191); +} + +module_init(saa7191_init); +module_exit(saa7191_exit); diff --git a/linux/drivers/media/video/saa7191.h b/linux/drivers/media/video/saa7191.h new file mode 100644 index 000000000..a2310da19 --- /dev/null +++ b/linux/drivers/media/video/saa7191.h @@ -0,0 +1,255 @@ +/* + * saa7191.h - Philips SAA7191 video decoder driver + * + * Copyright (C) 2003 Ladislav Michl + * Copyright (C) 2004,2005 Mikael Nousiainen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _SAA7191_H_ +#define _SAA7191_H_ + +/* Philips SAA7191 DMSD I2C bus address */ +#define SAA7191_ADDR 0x8a + +/* Register subaddresses. */ +#define SAA7191_REG_IDEL 0x00 +#define SAA7191_REG_HSYB 0x01 +#define SAA7191_REG_HSYS 0x02 +#define SAA7191_REG_HCLB 0x03 +#define SAA7191_REG_HCLS 0x04 +#define SAA7191_REG_HPHI 0x05 +#define SAA7191_REG_LUMA 0x06 +#define SAA7191_REG_HUEC 0x07 +#define SAA7191_REG_CKTQ 0x08 /* bits 3-7 */ +#define SAA7191_REG_CKTS 0x09 /* bits 3-7 */ +#define SAA7191_REG_PLSE 0x0a +#define SAA7191_REG_SESE 0x0b +#define SAA7191_REG_GAIN 0x0c +#define SAA7191_REG_STDC 0x0d +#define SAA7191_REG_IOCK 0x0e +#define SAA7191_REG_CTL3 0x0f +#define SAA7191_REG_CTL4 0x10 +#define SAA7191_REG_CHCV 0x11 +#define SAA7191_REG_HS6B 0x14 +#define SAA7191_REG_HS6S 0x15 +#define SAA7191_REG_HC6B 0x16 +#define SAA7191_REG_HC6S 0x17 +#define SAA7191_REG_HP6I 0x18 +#define SAA7191_REG_STATUS 0xff /* not really a subaddress */ + +/* Status Register definitions */ +#define SAA7191_STATUS_CODE 0x01 /* color detected flag */ +#define SAA7191_STATUS_FIDT 0x20 /* signal type 50/60 Hz */ +#define SAA7191_STATUS_HLCK 0x40 /* PLL unlocked(1)/locked(0) */ +#define SAA7191_STATUS_STTC 0x80 /* tv/vtr time constant */ + +/* Luminance Control Register definitions */ +/* input mode select bit: + * 0=CVBS (chrominance trap active), 1=S-Video (trap bypassed) */ +#define SAA7191_LUMA_BYPS 0x80 +/* pre-filter (only when chrominance trap is active) */ +#define SAA7191_LUMA_PREF 0x40 +/* aperture bandpass to select different characteristics with maximums + * (bits 4-5) */ +#define SAA7191_LUMA_BPSS_MASK 0x30 +#define SAA7191_LUMA_BPSS_SHIFT 4 +#define SAA7191_LUMA_BPSS_3 0x30 +#define SAA7191_LUMA_BPSS_2 0x20 +#define SAA7191_LUMA_BPSS_1 0x10 +#define SAA7191_LUMA_BPSS_0 0x00 +/* coring range for high frequency components according to 8-bit luminance + * (bits 2-3) + * 0=coring off, n= (+-)n LSB */ +#define SAA7191_LUMA_CORI_MASK 0x0c +#define SAA7191_LUMA_CORI_SHIFT 2 +#define SAA7191_LUMA_CORI_3 0x0c +#define SAA7191_LUMA_CORI_2 0x08 +#define SAA7191_LUMA_CORI_1 0x04 +#define SAA7191_LUMA_CORI_0 0x00 +/* aperture bandpass filter weights high frequency components of luminance + * signal (bits 0-1) + * 0=factor 0, 1=0.25, 2=0.5, 3=1 */ +#define SAA7191_LUMA_APER_MASK 0x03 +#define SAA7191_LUMA_APER_SHIFT 0 +#define SAA7191_LUMA_APER_3 0x03 +#define SAA7191_LUMA_APER_2 0x02 +#define SAA7191_LUMA_APER_1 0x01 +#define SAA7191_LUMA_APER_0 0x00 + +/* Chrominance Gain Control Settings Register definitions */ +/* colour on: 0=automatic colour-killer enabled, 1=forced colour on */ +#define SAA7191_GAIN_COLO 0x80 +/* chrominance gain control (AGC filter) + * 0=loop filter time constant slow, 1=medium, 2=fast, 3=actual gain */ +#define SAA7191_GAIN_LFIS_MASK 0x60 +#define SAA7191_GAIN_LFIS_SHIFT 5 +#define SAA7191_GAIN_LFIS_3 0x60 +#define SAA7191_GAIN_LFIS_2 0x40 +#define SAA7191_GAIN_LFIS_1 0x20 +#define SAA7191_GAIN_LFIS_0 0x00 + +/* Standard/Mode Control Register definitions */ +/* tv/vtr mode bit: 0=TV mode (slow time constant), + * 1=VTR mode (fast time constant) */ +#define SAA7191_STDC_VTRC 0x80 +/* SAA7191B-specific functions enable (RTCO, ODD and GPSW0 outputs) + * 0=outputs set to high-impedance (circuit equals SAA7191), 1=enabled */ +#define SAA7191_STDC_NFEN 0x08 +/* HREF generation: 0=like SAA7191, 1=HREF is 8xLLC2 clocks earlier */ +#define SAA7191_STDC_HRMV 0x04 +/* general purpose switch 0 + * (not used with VINO afaik) */ +#define SAA7191_STDC_GPSW0 0x02 +/* SECAM mode bit: 0=other standards, 1=SECAM */ +#define SAA7191_STDC_SECS 0x01 + +/* I/O and Clock Control Register definitions */ +/* horizontal clock PLL: 0=PLL closed, + * 1=PLL circuit open and horizontal freq fixed */ +#define SAA7191_IOCK_HPLL 0x80 +/* colour-difference output enable (outputs UV0-UV7) */ +#define SAA7191_IOCK_OEDC 0x40 +/* H-sync output enable */ +#define SAA7191_IOCK_OEHS 0x20 +/* V-sync output enable */ +#define SAA7191_IOCK_OEVS 0x10 +/* luminance output enable (outputs Y0-Y7) */ +#define SAA7191_IOCK_OEDY 0x08 +/* S-VHS bit (chrominance from CVBS or from chrominance input): + * 0=controlled by BYPS-bit, 1=from chrominance input */ +#define SAA7191_IOCK_CHRS 0x04 +/* general purpose switch 2 + * VINO-specific: 0=used with CVBS, 1=used with S-Video */ +#define SAA7191_IOCK_GPSW2 0x02 +/* general purpose switch 1 */ +/* VINO-specific: 0=always, 1=not used!*/ +#define SAA7191_IOCK_GPSW1 0x01 + +/* Miscellaneous Control #1 Register definitions */ +/* automatic field detection (50/60Hz standard) */ +#define SAA7191_CTL3_AUFD 0x80 +/* field select: (if AUFD=0) + * 0=50Hz (625 lines), 1=60Hz (525 lines) */ +#define SAA7191_CTL3_FSEL 0x40 +/* SECAM cross-colour reduction enable */ +#define SAA7191_CTL3_SXCR 0x20 +/* sync and clamping pulse enable (HCL and HSY outputs) */ +#define SAA7191_CTL3_SCEN 0x10 +/* output format: 0=4:1:1, 1=4:2:2 (4:2:2 for VINO) */ +#define SAA7191_CTL3_OFTS 0x08 +/* luminance delay compensation + * 0=0*2/LLC, 1=+1*2/LLC, 2=+2*2/LLC, 3=+3*2/LLC, + * 4=-4*2/LLC, 5=-3*2/LLC, 6=-2*2/LLC, 7=-1*2/LLC + * step size = 2/LLC = 67.8ns for 50Hz, 81.5ns for 60Hz */ +#define SAA7191_CTL3_YDEL_MASK 0x07 +#define SAA7191_CTL3_YDEL_SHIFT 0 +#define SAA7191_CTL3_YDEL2 0x04 +#define SAA7191_CTL3_YDEL1 0x02 +#define SAA7191_CTL3_YDEL0 0x01 + +/* Miscellaneous Control #2 Register definitions */ +/* select HREF position + * 0=normal, HREF is matched to YUV output port, + * 1=HREF is matched to CVBS input port */ +#define SAA7191_CTL4_HRFS 0x04 +/* vertical noise reduction + * 0=normal, 1=searching window, 2=auto-deflection, 3=reduction bypassed */ +#define SAA7191_CTL4_VNOI_MASK 0x03 +#define SAA7191_CTL4_VNOI_SHIFT 0 +#define SAA7191_CTL4_VNOI_3 0x03 +#define SAA7191_CTL4_VNOI_2 0x02 +#define SAA7191_CTL4_VNOI_1 0x01 +#define SAA7191_CTL4_VNOI_0 0x00 + +/* Chrominance Gain Control Register definitions + * - for QAM-modulated input signals, effects output amplitude + * (SECAM gain fixed) + * (nominal values for UV CCIR level) */ +#define SAA7191_CHCV_NTSC 0x2c +#define SAA7191_CHCV_PAL 0x59 + +/* Driver interface definitions */ +#define SAA7191_INPUT_COMPOSITE 0 +#define SAA7191_INPUT_SVIDEO 1 + +#define SAA7191_NORM_AUTO 0 +#define SAA7191_NORM_PAL 1 +#define SAA7191_NORM_NTSC 2 +#define SAA7191_NORM_SECAM 3 +#define SAA7191_NORM_AUTO_EXT 4 /* extended auto-detection */ + +struct saa7191_status { + /* 0=no signal, 1=signal detected */ + int signal; + /* 0=50hz (pal) signal, 1=60hz (ntsc) signal */ + int signal_60hz; + /* 0=no color detected, 1=color detected */ + int color; + + /* current SAA7191_INPUT_ */ + int input; + /* current SAA7191_NORM_ */ + int norm; +}; + +#define SAA7191_BANDPASS_MIN 0x00 +#define SAA7191_BANDPASS_MAX 0x03 +#define SAA7191_BANDPASS_DEFAULT 0x00 + +#define SAA7191_BANDPASS_WEIGHT_MIN 0x00 +#define SAA7191_BANDPASS_WEIGHT_MAX 0x03 +#define SAA7191_BANDPASS_WEIGHT_DEFAULT 0x01 + +#define SAA7191_CORING_MIN 0x00 +#define SAA7191_CORING_MAX 0x03 +#define SAA7191_CORING_DEFAULT 0x00 + +#define SAA7191_HUE_MIN 0x00 +#define SAA7191_HUE_MAX 0xff +#define SAA7191_HUE_DEFAULT 0x80 + +#define SAA7191_VTRC_MIN 0x00 +#define SAA7191_VTRC_MAX 0x01 +#define SAA7191_VTRC_DEFAULT 0x00 + +#define SAA7191_FORCE_COLOUR_MIN 0x00 +#define SAA7191_FORCE_COLOUR_MAX 0x01 +#define SAA7191_FORCE_COLOUR_DEFAULT 0x00 + +#define SAA7191_CHROMA_GAIN_MIN 0x00 +#define SAA7191_CHROMA_GAIN_MAX 0x03 +#define SAA7191_CHROMA_GAIN_DEFAULT 0x00 + +#define SAA7191_LUMA_DELAY_MIN -0x04 +#define SAA7191_LUMA_DELAY_MAX 0x03 +#define SAA7191_LUMA_DELAY_DEFAULT 0x01 + +#define SAA7191_VNR_MIN 0x00 +#define SAA7191_VNR_MAX 0x03 +#define SAA7191_VNR_DEFAULT 0x00 + +#define SAA7191_CONTROL_BANDPASS 0 +#define SAA7191_CONTROL_BANDPASS_WEIGHT 1 +#define SAA7191_CONTROL_CORING 2 +#define SAA7191_CONTROL_FORCE_COLOUR 3 /* boolean */ +#define SAA7191_CONTROL_CHROMA_GAIN 4 +#define SAA7191_CONTROL_HUE 5 +#define SAA7191_CONTROL_VTRC 6 /* boolean */ +#define SAA7191_CONTROL_LUMA_DELAY 7 +#define SAA7191_CONTROL_VNR 8 + +struct saa7191_control { + u8 type; + s32 value; +}; + +#define DECODER_SAA7191_GET_STATUS _IOR('d', 195, struct saa7191_status) +#define DECODER_SAA7191_SET_NORM _IOW('d', 196, int) +#define DECODER_SAA7191_GET_CONTROL _IOR('d', 197, struct saa7191_control) +#define DECODER_SAA7191_SET_CONTROL _IOW('d', 198, struct saa7191_control) + +#endif diff --git a/linux/drivers/media/video/saa7196.h b/linux/drivers/media/video/saa7196.h new file mode 100644 index 000000000..cd4b6354a --- /dev/null +++ b/linux/drivers/media/video/saa7196.h @@ -0,0 +1,117 @@ +/* + Definitions for the Philips SAA7196 digital video decoder, + scaler, and clock generator circuit (DESCpro), as used in + the PlanB video input of the Powermac 7x00/8x00 series. + + Copyright (C) 1998 Michel Lanners (mlan@cpu.lu) + + The register defines are shamelessly copied from the meteor + driver out of NetBSD (with permission), + and are copyrighted (c) 1995 Mark Tinguely and Jim Lowe + (Thanks !) + + Additional debugging and coding by Takashi Oe (toe@unlinfo.unl.edu) + + The default values used for PlanB are my mistakes. +*/ + +/* $Id: saa7196.h,v 1.5 1999/03/26 23:28:47 mlan Exp $ */ + +#ifndef _SAA7196_H_ +#define _SAA7196_H_ + +#define SAA7196_NUMREGS 0x31 /* Number of registers (used)*/ +#define NUM_SUPPORTED_NORM 3 /* Number of supported norms by PlanB */ + +/* Decoder part: */ +#define SAA7196_IDEL 0x00 /* Increment delay */ +#define SAA7196_HSB5 0x01 /* H-sync begin; 50 hz */ +#define SAA7196_HSS5 0x02 /* H-sync stop; 50 hz */ +#define SAA7196_HCB5 0x03 /* H-clamp begin; 50 hz */ +#define SAA7196_HCS5 0x04 /* H-clamp stop; 50 hz */ +#define SAA7196_HSP5 0x05 /* H-sync after PHI1; 50 hz */ +#define SAA7196_LUMC 0x06 /* Luminance control */ +#define SAA7196_HUEC 0x07 /* Hue control */ +#define SAA7196_CKTQ 0x08 /* Colour Killer Threshold QAM (PAL, NTSC) */ +#define SAA7196_CKTS 0x09 /* Colour Killer Threshold SECAM */ +#define SAA7196_PALS 0x0a /* PAL switch sensitivity */ +#define SAA7196_SECAMS 0x0b /* SECAM switch sensitivity */ +#define SAA7196_CGAINC 0x0c /* Chroma gain control */ +#define SAA7196_STDC 0x0d /* Standard/Mode control */ +#define SAA7196_IOCC 0x0e /* I/O and Clock Control */ +#define SAA7196_CTRL1 0x0f /* Control #1 */ +#define SAA7196_CTRL2 0x10 /* Control #2 */ +#define SAA7196_CGAINR 0x11 /* Chroma Gain Reference */ +#define SAA7196_CSAT 0x12 /* Chroma Saturation */ +#define SAA7196_CONT 0x13 /* Luminance Contrast */ +#define SAA7196_HSB6 0x14 /* H-sync begin; 60 hz */ +#define SAA7196_HSS6 0x15 /* H-sync stop; 60 hz */ +#define SAA7196_HCB6 0x16 /* H-clamp begin; 60 hz */ +#define SAA7196_HCS6 0x17 /* H-clamp stop; 60 hz */ +#define SAA7196_HSP6 0x18 /* H-sync after PHI1; 60 hz */ +#define SAA7196_BRIG 0x19 /* Luminance Brightness */ + +/* Scaler part: */ +#define SAA7196_FMTS 0x20 /* Formats and sequence */ +#define SAA7196_OUTPIX 0x21 /* Output data pixel/line */ +#define SAA7196_INPIX 0x22 /* Input data pixel/line */ +#define SAA7196_HWS 0x23 /* Horiz. window start */ +#define SAA7196_HFILT 0x24 /* Horiz. filter */ +#define SAA7196_OUTLINE 0x25 /* Output data lines/field */ +#define SAA7196_INLINE 0x26 /* Input data lines/field */ +#define SAA7196_VWS 0x27 /* Vertical window start */ +#define SAA7196_VYP 0x28 /* AFS/vertical Y processing */ +#define SAA7196_VBS 0x29 /* Vertical Bypass start */ +#define SAA7196_VBCNT 0x2a /* Vertical Bypass count */ +#define SAA7196_VBP 0x2b /* veritcal Bypass Polarity */ +#define SAA7196_VLOW 0x2c /* Colour-keying lower V limit */ +#define SAA7196_VHIGH 0x2d /* Colour-keying upper V limit */ +#define SAA7196_ULOW 0x2e /* Colour-keying lower U limit */ +#define SAA7196_UHIGH 0x2f /* Colour-keying upper U limit */ +#define SAA7196_DPATH 0x30 /* Data path setting */ + +/* Initialization default values: */ + +unsigned char saa_regs[NUM_SUPPORTED_NORM][SAA7196_NUMREGS] = { + +/* PAL, 768x576 (no scaling), composite video-in */ +/* Decoder: */ + { 0x50, 0x30, 0x00, 0xe8, 0xb6, 0xe5, 0x63, 0xff, + 0xfe, 0xf0, 0xfe, 0xe0, 0x20, 0x06, 0x3b, 0x98, + 0x00, 0x59, 0x41, 0x45, 0x34, 0x0a, 0xf4, 0xd2, + 0xe9, 0xa2, +/* Padding */ + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, +/* Scaler: */ + 0x72, 0x80, 0x00, 0x03, 0x8d, 0x20, 0x20, 0x12, + 0xa5, 0x12, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x87 }, + +/* NTSC, 640x480? (no scaling), composite video-in */ +/* Decoder: */ + { 0x50, 0x30, 0x00, 0xe8, 0xb6, 0xe5, 0x50, 0x00, + 0xf8, 0xf0, 0xfe, 0xe0, 0x00, 0x06, 0x3b, 0x98, + 0x00, 0x2c, 0x3d, 0x40, 0x34, 0x0a, 0xf4, 0xd2, + 0xe9, 0x98, +/* Padding */ + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, +/* Scaler: */ + 0x72, 0x80, 0x80, 0x03, 0x89, 0xf0, 0xf0, 0x0d, + 0xa0, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x87 }, + +/* SECAM, 768x576 (no scaling), composite video-in */ +/* Decoder: */ + { 0x50, 0x30, 0x00, 0xe8, 0xb6, 0xe5, 0x63, 0xff, + 0xfe, 0xf0, 0xfe, 0xe0, 0x20, 0x07, 0x3b, 0x98, + 0x00, 0x59, 0x41, 0x45, 0x34, 0x0a, 0xf4, 0xd2, + 0xe9, 0xa2, +/* Padding */ + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, +/* Scaler: */ + 0x72, 0x80, 0x00, 0x03, 0x8d, 0x20, 0x20, 0x12, + 0xa5, 0x12, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x87 } + }; + +#endif /* _SAA7196_H_ */ diff --git a/linux/drivers/media/video/se401.c b/linux/drivers/media/video/se401.c new file mode 100644 index 000000000..a846ebc78 --- /dev/null +++ b/linux/drivers/media/video/se401.c @@ -0,0 +1,1435 @@ +/* + * Endpoints (formerly known as AOX) se401 USB Camera Driver + * + * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org) + * + * Still somewhat based on the Linux ov511 driver. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * Thanks to Endpoints Inc. (www.endpoints.com) for making documentation on + * their chipset available and supporting me while writing this driver. + * - Jeroen Vreeken + */ + +static const char version[] = "0.24"; + +#include +#include +#include +#include +#include +#include +#include +#include "se401.h" + +static int flickerless=0; +static int video_nr = -1; + +static struct usb_device_id device_table [] = { + { USB_DEVICE(0x03e8, 0x0004) },/* Endpoints/Aox SE401 */ + { USB_DEVICE(0x0471, 0x030b) },/* Philips PCVC665K */ + { USB_DEVICE(0x047d, 0x5001) },/* Kensington 67014 */ + { USB_DEVICE(0x047d, 0x5002) },/* Kensington 6701(5/7) */ + { USB_DEVICE(0x047d, 0x5003) },/* Kensington 67016 */ + { } +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +MODULE_AUTHOR("Jeroen Vreeken "); +MODULE_DESCRIPTION("SE401 USB Camera Driver"); +MODULE_LICENSE("GPL"); +module_param(flickerless, int, 0); +MODULE_PARM_DESC(flickerless, "Net frequency to adjust exposure time to (0/50/60)"); +module_param(video_nr, int, 0); + +static struct usb_driver se401_driver; + + +/********************************************************************** + * + * Memory management + * + **********************************************************************/ +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + +static void rvfree(void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + + + +/**************************************************************************** + * + * se401 register read/write functions + * + ***************************************************************************/ + +static int se401_sndctrl(int set, struct usb_se401 *se401, unsigned short req, + unsigned short value, unsigned char *cp, int size) +{ + return usb_control_msg ( + se401->dev, + set ? usb_sndctrlpipe(se401->dev, 0) : usb_rcvctrlpipe(se401->dev, 0), + req, + (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + 0, + cp, + size, + 1000 + ); +} + +static int se401_set_feature(struct usb_se401 *se401, unsigned short selector, + unsigned short param) +{ + /* specs say that the selector (address) should go in the value field + and the param in index, but in the logs of the windows driver they do + this the other way around... + */ + return usb_control_msg ( + se401->dev, + usb_sndctrlpipe(se401->dev, 0), + SE401_REQ_SET_EXT_FEATURE, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + param, + selector, + NULL, + 0, + 1000 + ); +} + +static unsigned short se401_get_feature(struct usb_se401 *se401, + unsigned short selector) +{ + /* For 'set' the selecetor should be in index, not sure if the spec is + wrong here to.... + */ + unsigned char cp[2]; + usb_control_msg ( + se401->dev, + usb_rcvctrlpipe(se401->dev, 0), + SE401_REQ_GET_EXT_FEATURE, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, + selector, + cp, + 2, + 1000 + ); + return cp[0]+cp[1]*256; +} + +/**************************************************************************** + * + * Camera control + * + ***************************************************************************/ + + +static int se401_send_pict(struct usb_se401 *se401) +{ + se401_set_feature(se401, HV7131_REG_TITL, se401->expose_l);/* integration time low */ + se401_set_feature(se401, HV7131_REG_TITM, se401->expose_m);/* integration time mid */ + se401_set_feature(se401, HV7131_REG_TITU, se401->expose_h);/* integration time mid */ + se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel);/* reset level value */ + se401_set_feature(se401, HV7131_REG_ARCG, se401->rgain);/* red color gain */ + se401_set_feature(se401, HV7131_REG_AGCG, se401->ggain);/* green color gain */ + se401_set_feature(se401, HV7131_REG_ABCG, se401->bgain);/* blue color gain */ + + return 0; +} + +static void se401_set_exposure(struct usb_se401 *se401, int brightness) +{ + int integration=brightness<<5; + + if (flickerless==50) { + integration=integration-integration%106667; + } + if (flickerless==60) { + integration=integration-integration%88889; + } + se401->brightness=integration>>5; + se401->expose_h=(integration>>16)&0xff; + se401->expose_m=(integration>>8)&0xff; + se401->expose_l=integration&0xff; +} + +static int se401_get_pict(struct usb_se401 *se401, struct video_picture *p) +{ + p->brightness=se401->brightness; + if (se401->enhance) { + p->whiteness=32768; + } else { + p->whiteness=0; + } + p->colour=65535; + p->contrast=65535; + p->hue=se401->rgain<<10; + p->palette=se401->palette; + p->depth=3; /* rgb24 */ + return 0; +} + + +static int se401_set_pict(struct usb_se401 *se401, struct video_picture *p) +{ + if (p->palette != VIDEO_PALETTE_RGB24) + return 1; + se401->palette=p->palette; + if (p->hue!=se401->hue) { + se401->rgain= p->hue>>10; + se401->bgain= 0x40-(p->hue>>10); + se401->hue=p->hue; + } + if (p->brightness!=se401->brightness) { + se401_set_exposure(se401, p->brightness); + } + if (p->whiteness>=32768) { + se401->enhance=1; + } else { + se401->enhance=0; + } + se401_send_pict(se401); + se401_send_pict(se401); + return 0; +} + +/* + Hyundai have some really nice docs about this and other sensor related + stuff on their homepage: www.hei.co.kr +*/ +static void se401_auto_resetlevel(struct usb_se401 *se401) +{ + unsigned int ahrc, alrc; + int oldreset=se401->resetlevel; + + /* For some reason this normally read-only register doesn't get reset + to zero after reading them just once... + */ + se401_get_feature(se401, HV7131_REG_HIREFNOH); + se401_get_feature(se401, HV7131_REG_HIREFNOL); + se401_get_feature(se401, HV7131_REG_LOREFNOH); + se401_get_feature(se401, HV7131_REG_LOREFNOL); + ahrc=256*se401_get_feature(se401, HV7131_REG_HIREFNOH) + + se401_get_feature(se401, HV7131_REG_HIREFNOL); + alrc=256*se401_get_feature(se401, HV7131_REG_LOREFNOH) + + se401_get_feature(se401, HV7131_REG_LOREFNOL); + + /* Not an exact science, but it seems to work pretty well... */ + if (alrc > 10) { + while (alrc>=10 && se401->resetlevel < 63) { + se401->resetlevel++; + alrc /=2; + } + } else if (ahrc > 20) { + while (ahrc>=20 && se401->resetlevel > 0) { + se401->resetlevel--; + ahrc /=2; + } + } + if (se401->resetlevel!=oldreset) + se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel); + + return; +} + +/* irq handler for snapshot button */ +static void se401_button_irq(struct urb *urb, struct pt_regs *regs) +{ + struct usb_se401 *se401 = urb->context; + int status; + + if (!se401->dev) { + info("ohoh: device vapourished"); + return; + } + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + goto exit; + } + + if (urb->actual_length >=2) { + if (se401->button) + se401->buttonpressed=1; + } +exit: + status = usb_submit_urb (urb, GFP_ATOMIC); + if (status) + err ("%s - usb_submit_urb failed with result %d", + __FUNCTION__, status); +} + +static void se401_video_irq(struct urb *urb, struct pt_regs *regs) +{ + struct usb_se401 *se401 = urb->context; + int length = urb->actual_length; + + /* ohoh... */ + if (!se401->streaming) + return; + + if (!se401->dev) { + info ("ohoh: device vapourished"); + return; + } + + /* 0 sized packets happen if we are to fast, but sometimes the camera + keeps sending them forever... + */ + if (length && !urb->status) { + se401->nullpackets=0; + switch(se401->scratch[se401->scratch_next].state) { + case BUFFER_READY: + case BUFFER_BUSY: { + se401->dropped++; + break; + } + case BUFFER_UNUSED: { + memcpy(se401->scratch[se401->scratch_next].data, (unsigned char *)urb->transfer_buffer, length); + se401->scratch[se401->scratch_next].state=BUFFER_READY; + se401->scratch[se401->scratch_next].offset=se401->bayeroffset; + se401->scratch[se401->scratch_next].length=length; + if (waitqueue_active(&se401->wq)) { + wake_up_interruptible(&se401->wq); + } + se401->scratch_overflow=0; + se401->scratch_next++; + if (se401->scratch_next>=SE401_NUMSCRATCH) + se401->scratch_next=0; + break; + } + } + se401->bayeroffset+=length; + if (se401->bayeroffset>=se401->cheight*se401->cwidth) { + se401->bayeroffset=0; + } + } else { + se401->nullpackets++; + if (se401->nullpackets > SE401_MAX_NULLPACKETS) { + if (waitqueue_active(&se401->wq)) { + wake_up_interruptible(&se401->wq); + } + } + } + + /* Resubmit urb for new data */ + urb->status=0; + urb->dev=se401->dev; + if(usb_submit_urb(urb, GFP_KERNEL)) + info("urb burned down"); + return; +} + +static void se401_send_size(struct usb_se401 *se401, int width, int height) +{ + int i=0; + int mode=0x03; /* No compression */ + int sendheight=height; + int sendwidth=width; + + /* JangGu compression can only be used with the camera supported sizes, + but bayer seems to work with any size that fits on the sensor. + We check if we can use compression with the current size with either + 4 or 16 times subcapturing, if not we use uncompressed bayer data + but this will result in cutouts of the maximum size.... + */ + while (isizes && !(se401->width[i]==width && se401->height[i]==height)) + i++; + while (isizes) { + if (se401->width[i]==width*2 && se401->height[i]==height*2) { + sendheight=se401->height[i]; + sendwidth=se401->width[i]; + mode=0x40; + } + if (se401->width[i]==width*4 && se401->height[i]==height*4) { + sendheight=se401->height[i]; + sendwidth=se401->width[i]; + mode=0x42; + } + i++; + } + + se401_sndctrl(1, se401, SE401_REQ_SET_WIDTH, sendwidth, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_SET_HEIGHT, sendheight, NULL, 0); + se401_set_feature(se401, SE401_OPERATINGMODE, mode); + + if (mode==0x03) { + se401->format=FMT_BAYER; + } else { + se401->format=FMT_JANGGU; + } + + return; +} + +/* + In this function se401_send_pict is called several times, + for some reason (depending on the state of the sensor and the phase of + the moon :) doing this only in either place doesn't always work... +*/ +static int se401_start_stream(struct usb_se401 *se401) +{ + struct urb *urb; + int err=0, i; + se401->streaming=1; + + se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); + + /* Set picture settings */ + se401_set_feature(se401, HV7131_REG_MODE_B, 0x05);/*windowed + pix intg */ + se401_send_pict(se401); + + se401_send_size(se401, se401->cwidth, se401->cheight); + + se401_sndctrl(1, se401, SE401_REQ_START_CONTINUOUS_CAPTURE, 0, NULL, 0); + + /* Do some memory allocation */ + for (i=0; iframe[i].data=se401->fbuf + i * se401->maxframesize; + se401->frame[i].curpix=0; + } + for (i=0; isbuf[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL); + } + + se401->bayeroffset=0; + se401->scratch_next=0; + se401->scratch_use=0; + se401->scratch_overflow=0; + for (i=0; iscratch[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL); + se401->scratch[i].state=BUFFER_UNUSED; + } + + for (i=0; idev, + usb_rcvbulkpipe(se401->dev, SE401_VIDEO_ENDPOINT), + se401->sbuf[i].data, SE401_PACKETSIZE, + se401_video_irq, + se401); + + se401->urb[i]=urb; + + err=usb_submit_urb(se401->urb[i], GFP_KERNEL); + if(err) + err("urb burned down"); + } + + se401->framecount=0; + + return 0; +} + +static int se401_stop_stream(struct usb_se401 *se401) +{ + int i; + + if (!se401->streaming || !se401->dev) + return 1; + + se401->streaming=0; + + se401_sndctrl(1, se401, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, NULL, 0); + + se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0); + + for (i=0; iurb[i]) { + usb_kill_urb(se401->urb[i]); + usb_free_urb(se401->urb[i]); + se401->urb[i]=NULL; + kfree(se401->sbuf[i].data); + } + for (i=0; iscratch[i].data); + se401->scratch[i].data=NULL; + } + + return 0; +} + +static int se401_set_size(struct usb_se401 *se401, int width, int height) +{ + int wasstreaming=se401->streaming; + /* Check to see if we need to change */ + if (se401->cwidth==width && se401->cheight==height) + return 0; + + /* Check for a valid mode */ + if (!width || !height) + return 1; + if ((width & 1) || (height & 1)) + return 1; + if (width>se401->width[se401->sizes-1]) + return 1; + if (height>se401->height[se401->sizes-1]) + return 1; + + /* Stop a current stream and start it again at the new size */ + if (wasstreaming) + se401_stop_stream(se401); + se401->cwidth=width; + se401->cheight=height; + if (wasstreaming) + se401_start_stream(se401); + return 0; +} + + +/**************************************************************************** + * + * Video Decoding + * + ***************************************************************************/ + +/* + This shouldn't really be done in a v4l driver.... + But it does make the image look a lot more usable. + Basically it lifts the dark pixels more than the light pixels. +*/ +static inline void enhance_picture(unsigned char *frame, int len) +{ + while (len--) { + *frame=(((*frame^255)*(*frame^255))/255)^255; + frame++; + } +} + +static inline void decode_JangGu_integrate(struct usb_se401 *se401, int data) +{ + struct se401_frame *frame=&se401->frame[se401->curframe]; + int linelength=se401->cwidth*3; + + if (frame->curlinepix >= linelength) { + frame->curlinepix=0; + frame->curline+=linelength; + } + + /* First three are absolute, all others relative. + * Format is rgb from right to left (mirrorred image), + * we flip it to get bgr from left to right. */ + if (frame->curlinepix < 3) { + *(frame->curline-frame->curlinepix)=1+data*4; + } else { + *(frame->curline-frame->curlinepix)= + *(frame->curline-frame->curlinepix+3)+data*4; + } + frame->curlinepix++; +} + +static inline void decode_JangGu_vlc (struct usb_se401 *se401, unsigned char *data, int bit_exp, int packetlength) +{ + int pos=0; + int vlc_cod=0; + int vlc_size=0; + int vlc_data=0; + int bit_cur; + int bit; + data+=4; + while (pos < packetlength) { + bit_cur=8; + while (bit_cur && bit_exp) { + bit=((*data)>>(bit_cur-1))&1; + if (!vlc_cod) { + if (bit) { + vlc_size++; + } else { + if (!vlc_size) { + decode_JangGu_integrate(se401, 0); + } else { + vlc_cod=2; + vlc_data=0; + } + } + } else { + if (vlc_cod==2) { + if (!bit) + vlc_data = -(1<data; + int len=buffer->length; + int bit_exp=0, pix_exp=0, frameinfo=0, packetlength=0, size; + int datapos=0; + + /* New image? */ + if (!se401->frame[se401->curframe].curpix) { + se401->frame[se401->curframe].curlinepix=0; + se401->frame[se401->curframe].curline= + se401->frame[se401->curframe].data+ + se401->cwidth*3-1; + if (se401->frame[se401->curframe].grabstate==FRAME_READY) + se401->frame[se401->curframe].grabstate=FRAME_GRABBING; + se401->vlcdatapos=0; + } + while (datapos < len) { + size=1024-se401->vlcdatapos; + if (size+datapos > len) + size=len-datapos; + memcpy(se401->vlcdata+se401->vlcdatapos, data+datapos, size); + se401->vlcdatapos+=size; + packetlength=0; + if (se401->vlcdatapos >= 4) { + bit_exp=se401->vlcdata[3]+(se401->vlcdata[2]<<8); + pix_exp=se401->vlcdata[1]+((se401->vlcdata[0]&0x3f)<<8); + frameinfo=se401->vlcdata[0]&0xc0; + packetlength=((bit_exp+47)>>4)<<1; + if (packetlength > 1024) { + se401->vlcdatapos=0; + datapos=len; + packetlength=0; + se401->error++; + se401->frame[se401->curframe].curpix=0; + } + } + if (packetlength && se401->vlcdatapos >= packetlength) { + decode_JangGu_vlc(se401, se401->vlcdata, bit_exp, packetlength); + se401->frame[se401->curframe].curpix+=pix_exp*3; + datapos+=size-(se401->vlcdatapos-packetlength); + se401->vlcdatapos=0; + if (se401->frame[se401->curframe].curpix>=se401->cwidth*se401->cheight*3) { + if (se401->frame[se401->curframe].curpix==se401->cwidth*se401->cheight*3) { + if (se401->frame[se401->curframe].grabstate==FRAME_GRABBING) { + se401->frame[se401->curframe].grabstate=FRAME_DONE; + se401->framecount++; + se401->readcount++; + } + if (se401->frame[(se401->curframe+1)&(SE401_NUMFRAMES-1)].grabstate==FRAME_READY) { + se401->curframe=(se401->curframe+1) & (SE401_NUMFRAMES-1); + } + } else { + se401->error++; + } + se401->frame[se401->curframe].curpix=0; + datapos=len; + } + } else { + datapos+=size; + } + } +} + +static inline void decode_bayer (struct usb_se401 *se401, struct se401_scratch *buffer) +{ + unsigned char *data=buffer->data; + int len=buffer->length; + int offset=buffer->offset; + int datasize=se401->cwidth*se401->cheight; + struct se401_frame *frame=&se401->frame[se401->curframe]; + + unsigned char *framedata=frame->data, *curline, *nextline; + int width=se401->cwidth; + int blineoffset=0, bline; + int linelength=width*3, i; + + + if (frame->curpix==0) { + if (frame->grabstate==FRAME_READY) { + frame->grabstate=FRAME_GRABBING; + } + frame->curline=framedata+linelength; + frame->curlinepix=0; + } + + if (offset!=frame->curpix) { + /* Regard frame as lost :( */ + frame->curpix=0; + se401->error++; + return; + } + + /* Check if we have to much data */ + if (frame->curpix+len > datasize) { + len=datasize-frame->curpix; + } + if (se401->cheight%4) + blineoffset=1; + bline=frame->curpix/se401->cwidth+blineoffset; + + curline=frame->curline; + nextline=curline+linelength; + if (nextline >= framedata+datasize*3) + nextline=curline; + while (len) { + if (frame->curlinepix>=width) { + frame->curlinepix-=width; + bline=frame->curpix/width+blineoffset; + curline+=linelength*2; + nextline+=linelength*2; + if (curline >= framedata+datasize*3) { + frame->curlinepix++; + curline-=3; + nextline-=3; + len--; + data++; + frame->curpix++; + } + if (nextline >= framedata+datasize*3) + nextline=curline; + } + if ((bline&1)) { + if ((frame->curlinepix&1)) { + *(curline+2)=*data; + *(curline-1)=*data; + *(nextline+2)=*data; + *(nextline-1)=*data; + } else { + *(curline+1)= + (*(curline+1)+*data)/2; + *(curline-2)= + (*(curline-2)+*data)/2; + *(nextline+1)=*data; + *(nextline-2)=*data; + } + } else { + if ((frame->curlinepix&1)) { + *(curline+1)= + (*(curline+1)+*data)/2; + *(curline-2)= + (*(curline-2)+*data)/2; + *(nextline+1)=*data; + *(nextline-2)=*data; + } else { + *curline=*data; + *(curline-3)=*data; + *nextline=*data; + *(nextline-3)=*data; + } + } + frame->curlinepix++; + curline-=3; + nextline-=3; + len--; + data++; + frame->curpix++; + } + frame->curline=curline; + + if (frame->curpix>=datasize) { + /* Fix the top line */ + framedata+=linelength; + for (i=0; icheight; i++) { + *framedata=*(framedata+3); + *(framedata+1)=*(framedata+4); + *(framedata+2)=*(framedata+5); + framedata+=linelength; + } + frame->curpix=0; + frame->grabstate=FRAME_DONE; + se401->framecount++; + se401->readcount++; + if (se401->frame[(se401->curframe+1)&(SE401_NUMFRAMES-1)].grabstate==FRAME_READY) { + se401->curframe=(se401->curframe+1) & (SE401_NUMFRAMES-1); + } + } +} + +static int se401_newframe(struct usb_se401 *se401, int framenr) +{ + DECLARE_WAITQUEUE(wait, current); + int errors=0; + + while (se401->streaming && + (se401->frame[framenr].grabstate==FRAME_READY || + se401->frame[framenr].grabstate==FRAME_GRABBING) ) { + if(!se401->frame[framenr].curpix) { + errors++; + } + wait_interruptible( + se401->scratch[se401->scratch_use].state!=BUFFER_READY, + &se401->wq, + &wait + ); + if (se401->nullpackets > SE401_MAX_NULLPACKETS) { + se401->nullpackets=0; + info("to many null length packets, restarting capture"); + se401_stop_stream(se401); + se401_start_stream(se401); + } else { + if (se401->scratch[se401->scratch_use].state!=BUFFER_READY) { + se401->frame[framenr].grabstate=FRAME_ERROR; + return -EIO; + } + se401->scratch[se401->scratch_use].state=BUFFER_BUSY; + if (se401->format==FMT_JANGGU) { + decode_JangGu(se401, &se401->scratch[se401->scratch_use]); + } else { + decode_bayer(se401, &se401->scratch[se401->scratch_use]); + } + se401->scratch[se401->scratch_use].state=BUFFER_UNUSED; + se401->scratch_use++; + if (se401->scratch_use>=SE401_NUMSCRATCH) + se401->scratch_use=0; + if (errors > SE401_MAX_ERRORS) { + errors=0; + info("to much errors, restarting capture"); + se401_stop_stream(se401); + se401_start_stream(se401); + } + } + } + + if (se401->frame[framenr].grabstate==FRAME_DONE) + if (se401->enhance) + enhance_picture(se401->frame[framenr].data, se401->cheight*se401->cwidth*3); + return 0; +} + +static void usb_se401_remove_disconnected (struct usb_se401 *se401) +{ + int i; + + se401->dev = NULL; + + for (i=0; iurb[i]) { + usb_kill_urb(se401->urb[i]); + usb_free_urb(se401->urb[i]); + se401->urb[i] = NULL; + kfree(se401->sbuf[i].data); + } + for (i=0; iscratch[i].data); + } + if (se401->inturb) { + usb_kill_urb(se401->inturb); + usb_free_urb(se401->inturb); + } + info("%s disconnected", se401->camera_name); + + /* Free the memory */ + kfree(se401->width); + kfree(se401->height); + kfree(se401); +} + + + +/**************************************************************************** + * + * Video4Linux + * + ***************************************************************************/ + + +static int se401_open(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct usb_se401 *se401 = (struct usb_se401 *)dev; + int err = 0; + + if (se401->user) + return -EBUSY; + se401->fbuf = rvmalloc(se401->maxframesize * SE401_NUMFRAMES); + if (se401->fbuf) + file->private_data = dev; + else + err = -ENOMEM; + se401->user = !err; + + return err; +} + +static int se401_close(struct inode *inode, struct file *file) +{ + struct video_device *dev = file->private_data; + struct usb_se401 *se401 = (struct usb_se401 *)dev; + int i; + + rvfree(se401->fbuf, se401->maxframesize * SE401_NUMFRAMES); + if (se401->removed) { + usb_se401_remove_disconnected(se401); + info("device unregistered"); + } else { + for (i=0; iframe[i].grabstate=FRAME_UNUSED; + if (se401->streaming) + se401_stop_stream(se401); + se401->user=0; + } + file->private_data = NULL; + return 0; +} + +static int se401_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vdev = file->private_data; + struct usb_se401 *se401 = (struct usb_se401 *)vdev; + + if (!se401->dev) + return -EIO; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability *b = arg; + strcpy(b->name, se401->camera_name); + b->type = VID_TYPE_CAPTURE; + b->channels = 1; + b->audios = 0; + b->maxwidth = se401->width[se401->sizes-1]; + b->maxheight = se401->height[se401->sizes-1]; + b->minwidth = se401->width[0]; + b->minheight = se401->height[0]; + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + + if (v->channel != 0) + return -EINVAL; + v->flags = 0; + v->tuners = 0; + v->type = VIDEO_TYPE_CAMERA; + strcpy(v->name, "Camera"); + return 0; + } + case VIDIOCSCHAN: + { + struct video_channel *v = arg; + + if (v->channel != 0) + return -EINVAL; + return 0; + } + case VIDIOCGPICT: + { + struct video_picture *p = arg; + + se401_get_pict(se401, p); + return 0; + } + case VIDIOCSPICT: + { + struct video_picture *p = arg; + + if (se401_set_pict(se401, p)) + return -EINVAL; + return 0; + } + case VIDIOCSWIN: + { + struct video_window *vw = arg; + + if (vw->flags) + return -EINVAL; + if (vw->clipcount) + return -EINVAL; + if (se401_set_size(se401, vw->width, vw->height)) + return -EINVAL; + return 0; + } + case VIDIOCGWIN: + { + struct video_window *vw = arg; + + vw->x = 0; /* FIXME */ + vw->y = 0; + vw->chromakey = 0; + vw->flags = 0; + vw->clipcount = 0; + vw->width = se401->cwidth; + vw->height = se401->cheight; + return 0; + } + case VIDIOCGMBUF: + { + struct video_mbuf *vm = arg; + int i; + + memset(vm, 0, sizeof(*vm)); + vm->size = SE401_NUMFRAMES * se401->maxframesize; + vm->frames = SE401_NUMFRAMES; + for (i=0; ioffsets[i] = se401->maxframesize * i; + return 0; + } + case VIDIOCMCAPTURE: + { + struct video_mmap *vm = arg; + + if (vm->format != VIDEO_PALETTE_RGB24) + return -EINVAL; + if (vm->frame >= SE401_NUMFRAMES) + return -EINVAL; + if (se401->frame[vm->frame].grabstate != FRAME_UNUSED) + return -EBUSY; + + /* Is this according to the v4l spec??? */ + if (se401_set_size(se401, vm->width, vm->height)) + return -EINVAL; + se401->frame[vm->frame].grabstate=FRAME_READY; + + if (!se401->streaming) + se401_start_stream(se401); + + /* Set the picture properties */ + if (se401->framecount==0) + se401_send_pict(se401); + /* Calibrate the reset level after a few frames. */ + if (se401->framecount%20==1) + se401_auto_resetlevel(se401); + + return 0; + } + case VIDIOCSYNC: + { + int *frame = arg; + int ret=0; + + if(*frame <0 || *frame >= SE401_NUMFRAMES) + return -EINVAL; + + ret=se401_newframe(se401, *frame); + se401->frame[*frame].grabstate=FRAME_UNUSED; + return ret; + } + case VIDIOCGFBUF: + { + struct video_buffer *vb = arg; + + memset(vb, 0, sizeof(*vb)); + return 0; + } + case VIDIOCKEY: + return 0; + case VIDIOCCAPTURE: + return -EINVAL; + case VIDIOCSFBUF: + return -EINVAL; + case VIDIOCGTUNER: + case VIDIOCSTUNER: + return -EINVAL; + case VIDIOCGFREQ: + case VIDIOCSFREQ: + return -EINVAL; + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; + } /* end switch */ + + return 0; +} + +static int se401_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, se401_do_ioctl); +} + +static ssize_t se401_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int realcount=count, ret=0; + struct video_device *dev = file->private_data; + struct usb_se401 *se401 = (struct usb_se401 *)dev; + + + if (se401->dev == NULL) + return -EIO; + if (realcount > se401->cwidth*se401->cheight*3) + realcount=se401->cwidth*se401->cheight*3; + + /* Shouldn't happen: */ + if (se401->frame[0].grabstate==FRAME_GRABBING) + return -EBUSY; + se401->frame[0].grabstate=FRAME_READY; + se401->frame[1].grabstate=FRAME_UNUSED; + se401->curframe=0; + + if (!se401->streaming) + se401_start_stream(se401); + + /* Set the picture properties */ + if (se401->framecount==0) + se401_send_pict(se401); + /* Calibrate the reset level after a few frames. */ + if (se401->framecount%20==1) + se401_auto_resetlevel(se401); + + ret=se401_newframe(se401, 0); + + se401->frame[0].grabstate=FRAME_UNUSED; + if (ret) + return ret; + if (copy_to_user(buf, se401->frame[0].data, realcount)) + return -EFAULT; + + return realcount; +} + +static int se401_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *dev = file->private_data; + struct usb_se401 *se401 = (struct usb_se401 *)dev; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end-vma->vm_start; + unsigned long page, pos; + + mutex_lock(&se401->lock); + + if (se401->dev == NULL) { + mutex_unlock(&se401->lock); + return -EIO; + } + if (size > (((SE401_NUMFRAMES * se401->maxframesize) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) { + mutex_unlock(&se401->lock); + return -EINVAL; + } + pos = (unsigned long)se401->fbuf; + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { + mutex_unlock(&se401->lock); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + mutex_unlock(&se401->lock); + + return 0; +} + +static struct file_operations se401_fops = { + .owner = THIS_MODULE, + .open = se401_open, + .release = se401_close, + .read = se401_read, + .mmap = se401_mmap, + .ioctl = se401_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; +static struct video_device se401_template = { + .owner = THIS_MODULE, + .name = "se401 USB camera", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_SE401, + .fops = &se401_fops, +}; + + + +/***************************/ +static int se401_init(struct usb_se401 *se401, int button) +{ + int i=0, rc; + unsigned char cp[0x40]; + char temp[200]; + + /* led on */ + se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); + + /* get camera descriptor */ + rc=se401_sndctrl(0, se401, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0, cp, sizeof(cp)); + if (cp[1]!=0x41) { + err("Wrong descriptor type"); + return 1; + } + sprintf (temp, "ExtraFeatures: %d", cp[3]); + + se401->sizes=cp[4]+cp[5]*256; + se401->width=kmalloc(se401->sizes*sizeof(int), GFP_KERNEL); + if (!se401->width) + return 1; + se401->height=kmalloc(se401->sizes*sizeof(int), GFP_KERNEL); + if (!se401->height) { + kfree(se401->width); + return 1; + } + for (i=0; isizes; i++) { + se401->width[i]=cp[6+i*4+0]+cp[6+i*4+1]*256; + se401->height[i]=cp[6+i*4+2]+cp[6+i*4+3]*256; + } + sprintf (temp, "%s Sizes:", temp); + for (i=0; isizes; i++) { + sprintf(temp, "%s %dx%d", temp, se401->width[i], se401->height[i]); + } + info("%s", temp); + se401->maxframesize=se401->width[se401->sizes-1]*se401->height[se401->sizes-1]*3; + + rc=se401_sndctrl(0, se401, SE401_REQ_GET_WIDTH, 0, cp, sizeof(cp)); + se401->cwidth=cp[0]+cp[1]*256; + rc=se401_sndctrl(0, se401, SE401_REQ_GET_HEIGHT, 0, cp, sizeof(cp)); + se401->cheight=cp[0]+cp[1]*256; + + if (!cp[2] && SE401_FORMAT_BAYER) { + err("Bayer format not supported!"); + return 1; + } + /* set output mode (BAYER) */ + se401_sndctrl(1, se401, SE401_REQ_SET_OUTPUT_MODE, SE401_FORMAT_BAYER, NULL, 0); + + rc=se401_sndctrl(0, se401, SE401_REQ_GET_BRT, 0, cp, sizeof(cp)); + se401->brightness=cp[0]+cp[1]*256; + /* some default values */ + se401->resetlevel=0x2d; + se401->rgain=0x20; + se401->ggain=0x20; + se401->bgain=0x20; + se401_set_exposure(se401, 20000); + se401->palette=VIDEO_PALETTE_RGB24; + se401->enhance=1; + se401->dropped=0; + se401->error=0; + se401->framecount=0; + se401->readcount=0; + + /* Start interrupt transfers for snapshot button */ + if (button) { + se401->inturb=usb_alloc_urb(0, GFP_KERNEL); + if (!se401->inturb) { + info("Allocation of inturb failed"); + return 1; + } + usb_fill_int_urb(se401->inturb, se401->dev, + usb_rcvintpipe(se401->dev, SE401_BUTTON_ENDPOINT), + &se401->button, sizeof(se401->button), + se401_button_irq, + se401, + 8 + ); + if (usb_submit_urb(se401->inturb, GFP_KERNEL)) { + info("int urb burned down"); + return 1; + } + } else + se401->inturb=NULL; + + /* Flash the led */ + se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0); + + return 0; +} + +static int se401_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_interface_descriptor *interface; + struct usb_se401 *se401; + char *camera_name=NULL; + int button=1; + + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return -ENODEV; + + interface = &intf->cur_altsetting->desc; + + /* Is it an se401? */ + if (le16_to_cpu(dev->descriptor.idVendor) == 0x03e8 && + le16_to_cpu(dev->descriptor.idProduct) == 0x0004) { + camera_name="Endpoints/Aox SE401"; + } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x0471 && + le16_to_cpu(dev->descriptor.idProduct) == 0x030b) { + camera_name="Philips PCVC665K"; + } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && + le16_to_cpu(dev->descriptor.idProduct) == 0x5001) { + camera_name="Kensington VideoCAM 67014"; + } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && + le16_to_cpu(dev->descriptor.idProduct) == 0x5002) { + camera_name="Kensington VideoCAM 6701(5/7)"; + } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && + le16_to_cpu(dev->descriptor.idProduct) == 0x5003) { + camera_name="Kensington VideoCAM 67016"; + button=0; + } else + return -ENODEV; + + /* Checking vendor/product should be enough, but what the hell */ + if (interface->bInterfaceClass != 0x00) + return -ENODEV; + if (interface->bInterfaceSubClass != 0x00) + return -ENODEV; + + /* We found one */ + info("SE401 camera found: %s", camera_name); + + if ((se401 = kzalloc(sizeof(*se401), GFP_KERNEL)) == NULL) { + err("couldn't kmalloc se401 struct"); + return -ENOMEM; + } + + se401->dev = dev; + se401->iface = interface->bInterfaceNumber; + se401->camera_name = camera_name; + + info("firmware version: %02x", le16_to_cpu(dev->descriptor.bcdDevice) & 255); + + if (se401_init(se401, button)) { + kfree(se401); + return -EIO; + } + + memcpy(&se401->vdev, &se401_template, sizeof(se401_template)); + memcpy(se401->vdev.name, se401->camera_name, strlen(se401->camera_name)); + init_waitqueue_head(&se401->wq); + mutex_init(&se401->lock); + wmb(); + + if (video_register_device(&se401->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { + kfree(se401); + err("video_register_device failed"); + return -EIO; + } + info("registered new video device: video%d", se401->vdev.minor); + + usb_set_intfdata (intf, se401); + return 0; +} + +static void se401_disconnect(struct usb_interface *intf) +{ + struct usb_se401 *se401 = usb_get_intfdata (intf); + + usb_set_intfdata (intf, NULL); + if (se401) { + video_unregister_device(&se401->vdev); + if (!se401->user){ + usb_se401_remove_disconnected(se401); + } else { + se401->frame[0].grabstate = FRAME_ERROR; + se401->frame[0].grabstate = FRAME_ERROR; + + se401->streaming = 0; + + wake_up_interruptible(&se401->wq); + se401->removed = 1; + } + } +} + +static struct usb_driver se401_driver = { + .name = "se401", + .id_table = device_table, + .probe = se401_probe, + .disconnect = se401_disconnect, +}; + + + +/**************************************************************************** + * + * Module routines + * + ***************************************************************************/ + +static int __init usb_se401_init(void) +{ + info("SE401 usb camera driver version %s registering", version); + if (flickerless) + if (flickerless!=50 && flickerless!=60) { + info("Invallid flickerless value, use 0, 50 or 60."); + return -1; + } + return usb_register(&se401_driver); +} + +static void __exit usb_se401_exit(void) +{ + usb_deregister(&se401_driver); + info("SE401 driver deregistered"); +} + +module_init(usb_se401_init); +module_exit(usb_se401_exit); diff --git a/linux/drivers/media/video/se401.h b/linux/drivers/media/video/se401.h new file mode 100644 index 000000000..a78aa0720 --- /dev/null +++ b/linux/drivers/media/video/se401.h @@ -0,0 +1,235 @@ + +#ifndef __LINUX_se401_H +#define __LINUX_se401_H + +#include +#include "compat.h" +#include +#include +#include + +#define se401_DEBUG /* Turn on debug messages */ + +#ifdef se401_DEBUG +# define PDEBUG(level, fmt, args...) \ +if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args) +#else +# define PDEBUG(level, fmt, args...) do {} while(0) +#endif + +/* An almost drop-in replacement for sleep_on_interruptible */ +#define wait_interruptible(test, queue, wait) \ +{ \ + add_wait_queue(queue, wait); \ + set_current_state(TASK_INTERRUPTIBLE); \ + if (test) \ + schedule(); \ + remove_wait_queue(queue, wait); \ + set_current_state(TASK_RUNNING); \ + if (signal_pending(current)) \ + break; \ +} + +#define SE401_REQ_GET_CAMERA_DESCRIPTOR 0x06 +#define SE401_REQ_START_CONTINUOUS_CAPTURE 0x41 +#define SE401_REQ_STOP_CONTINUOUS_CAPTURE 0x42 +#define SE401_REQ_CAPTURE_FRAME 0x43 +#define SE401_REQ_GET_BRT 0x44 +#define SE401_REQ_SET_BRT 0x45 +#define SE401_REQ_GET_WIDTH 0x4c +#define SE401_REQ_SET_WIDTH 0x4d +#define SE401_REQ_GET_HEIGHT 0x4e +#define SE401_REQ_SET_HEIGHT 0x4f +#define SE401_REQ_GET_OUTPUT_MODE 0x50 +#define SE401_REQ_SET_OUTPUT_MODE 0x51 +#define SE401_REQ_GET_EXT_FEATURE 0x52 +#define SE401_REQ_SET_EXT_FEATURE 0x53 +#define SE401_REQ_CAMERA_POWER 0x56 +#define SE401_REQ_LED_CONTROL 0x57 +#define SE401_REQ_BIOS 0xff + +#define SE401_BIOS_READ 0x07 + +#define SE401_FORMAT_BAYER 0x40 + +/* Hyundai hv7131b registers + 7121 and 7141 should be the same (haven't really checked...) */ +/* Mode registers: */ +#define HV7131_REG_MODE_A 0x00 +#define HV7131_REG_MODE_B 0x01 +#define HV7131_REG_MODE_C 0x02 +/* Frame registers: */ +#define HV7131_REG_FRSU 0x10 +#define HV7131_REG_FRSL 0x11 +#define HV7131_REG_FCSU 0x12 +#define HV7131_REG_FCSL 0x13 +#define HV7131_REG_FWHU 0x14 +#define HV7131_REG_FWHL 0x15 +#define HV7131_REG_FWWU 0x16 +#define HV7131_REG_FWWL 0x17 +/* Timing registers: */ +#define HV7131_REG_THBU 0x20 +#define HV7131_REG_THBL 0x21 +#define HV7131_REG_TVBU 0x22 +#define HV7131_REG_TVBL 0x23 +#define HV7131_REG_TITU 0x25 +#define HV7131_REG_TITM 0x26 +#define HV7131_REG_TITL 0x27 +#define HV7131_REG_TMCD 0x28 +/* Adjust Registers: */ +#define HV7131_REG_ARLV 0x30 +#define HV7131_REG_ARCG 0x31 +#define HV7131_REG_AGCG 0x32 +#define HV7131_REG_ABCG 0x33 +#define HV7131_REG_APBV 0x34 +#define HV7131_REG_ASLP 0x54 +/* Offset Registers: */ +#define HV7131_REG_OFSR 0x50 +#define HV7131_REG_OFSG 0x51 +#define HV7131_REG_OFSB 0x52 +/* REset level statistics registers: */ +#define HV7131_REG_LOREFNOH 0x57 +#define HV7131_REG_LOREFNOL 0x58 +#define HV7131_REG_HIREFNOH 0x59 +#define HV7131_REG_HIREFNOL 0x5a + +/* se401 registers */ +#define SE401_OPERATINGMODE 0x2000 + + +/* size of usb transfers */ +#define SE401_PACKETSIZE 4096 +/* number of queued bulk transfers to use, should be about 8 */ +#define SE401_NUMSBUF 1 +/* read the usb specs for this one :) */ +#define SE401_VIDEO_ENDPOINT 1 +#define SE401_BUTTON_ENDPOINT 2 +/* number of frames supported by the v4l part */ +#define SE401_NUMFRAMES 2 +/* scratch buffers for passing data to the decoders */ +#define SE401_NUMSCRATCH 32 +/* maximum amount of data in a JangGu packet */ +#define SE401_VLCDATALEN 1024 +/* number of nul sized packets to receive before kicking the camera */ +#define SE401_MAX_NULLPACKETS 4000 +/* number of decoding errors before kicking the camera */ +#define SE401_MAX_ERRORS 200 + +struct usb_device; + +struct se401_sbuf { + unsigned char *data; +}; + +enum { + FRAME_UNUSED, /* Unused (no MCAPTURE) */ + FRAME_READY, /* Ready to start grabbing */ + FRAME_GRABBING, /* In the process of being grabbed into */ + FRAME_DONE, /* Finished grabbing, but not been synced yet */ + FRAME_ERROR, /* Something bad happened while processing */ +}; + +enum { + FMT_BAYER, + FMT_JANGGU, +}; + +enum { + BUFFER_UNUSED, + BUFFER_READY, + BUFFER_BUSY, + BUFFER_DONE, +}; + +struct se401_scratch { + unsigned char *data; + volatile int state; + int offset; + int length; +}; + +struct se401_frame { + unsigned char *data; /* Frame buffer */ + + volatile int grabstate; /* State of grabbing */ + + unsigned char *curline; + int curlinepix; + int curpix; +}; + +struct usb_se401 { + struct video_device vdev; + + /* Device structure */ + struct usb_device *dev; + + unsigned char iface; + + char *camera_name; + + int change; + int brightness; + int hue; + int rgain; + int ggain; + int bgain; + int expose_h; + int expose_m; + int expose_l; + int resetlevel; + + int enhance; + + int format; + int sizes; + int *width; + int *height; + int cwidth; /* current width */ + int cheight; /* current height */ + int palette; + int maxframesize; + int cframesize; /* current framesize */ + + struct mutex lock; + int user; /* user count for exclusive use */ + int removed; /* device disconnected */ + + int streaming; /* Are we streaming video? */ + + char *fbuf; /* Videodev buffer area */ + + struct urb *urb[SE401_NUMSBUF]; + struct urb *inturb; + + int button; + int buttonpressed; + + int curframe; /* Current receiving frame */ + struct se401_frame frame[SE401_NUMFRAMES]; + int readcount; + int framecount; + int error; + int dropped; + + int scratch_next; + int scratch_use; + int scratch_overflow; + struct se401_scratch scratch[SE401_NUMSCRATCH]; + + /* Decoder specific data: */ + unsigned char vlcdata[SE401_VLCDATALEN]; + int vlcdatapos; + int bayeroffset; + + struct se401_sbuf sbuf[SE401_NUMSBUF]; + + wait_queue_head_t wq; /* Processes waiting */ + + int nullpackets; +}; + + + +#endif + diff --git a/linux/drivers/media/video/stradis.c b/linux/drivers/media/video/stradis.c new file mode 100644 index 000000000..9aad8d66d --- /dev/null +++ b/linux/drivers/media/video/stradis.c @@ -0,0 +1,2213 @@ +/* + * stradis.c - stradis 4:2:2 mpeg decoder driver + * + * Stradis 4:2:2 MPEG-2 Decoder Driver + * Copyright (C) 1999 Nathan Laredo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "compat.h" +#include + +#include "saa7146.h" +#include "saa7146reg.h" +#include "ibmmpeg2.h" +#include "saa7121.h" +#include "cs8420.h" + +#define DEBUG(x) /* debug driver */ +#undef IDEBUG /* debug irq handler */ +#undef MDEBUG /* debug memory management */ + +#define SAA7146_MAX 6 + +static struct saa7146 saa7146s[SAA7146_MAX]; + +static int saa_num = 0; /* number of SAA7146s in use */ + +static int video_nr = -1; +module_param(video_nr, int, 0); +MODULE_LICENSE("GPL"); + +#define nDebNormal 0x00480000 +#define nDebNoInc 0x00480000 +#define nDebVideo 0xd0480000 +#define nDebAudio 0xd0400000 +#define nDebDMA 0x02c80000 + +#define oDebNormal 0x13c80000 +#define oDebNoInc 0x13c80000 +#define oDebVideo 0xd1080000 +#define oDebAudio 0xd1080000 +#define oDebDMA 0x03080000 + +#define NewCard (saa->boardcfg[3]) +#define ChipControl (saa->boardcfg[1]) +#define NTSCFirstActive (saa->boardcfg[4]) +#define PALFirstActive (saa->boardcfg[5]) +#define NTSCLastActive (saa->boardcfg[54]) +#define PALLastActive (saa->boardcfg[55]) +#define Have2MB (saa->boardcfg[18] & 0x40) +#define HaveCS8420 (saa->boardcfg[18] & 0x04) +#define IBMMPEGCD20 (saa->boardcfg[18] & 0x20) +#define HaveCS3310 (saa->boardcfg[18] & 0x01) +#define CS3310MaxLvl ((saa->boardcfg[30] << 8) | saa->boardcfg[31]) +#define HaveCS4341 (saa->boardcfg[40] == 2) +#define SDIType (saa->boardcfg[27]) +#define CurrentMode (saa->boardcfg[2]) + +#define debNormal (NewCard ? nDebNormal : oDebNormal) +#define debNoInc (NewCard ? nDebNoInc : oDebNoInc) +#define debVideo (NewCard ? nDebVideo : oDebVideo) +#define debAudio (NewCard ? nDebAudio : oDebAudio) +#define debDMA (NewCard ? nDebDMA : oDebDMA) + +#ifdef USE_RESCUE_EEPROM_SDM275 +static unsigned char rescue_eeprom[64] = { + 0x00, 0x01, 0x04, 0x13, 0x26, 0x0f, 0x10, 0x00, 0x00, 0x00, 0x43, 0x63, + 0x22, 0x01, 0x29, 0x15, 0x73, 0x00, 0x1f, 'd', 'e', 'c', 'x', 'l', + 'd', 'v', 'a', 0x02, 0x00, 0x01, 0x00, 0xcc, 0xa4, 0x63, 0x09, 0xe2, + 0x10, 0x00, 0x0a, 0x00, 0x02, 0x02, 'd', 'e', 'c', 'x', 'l', 'a', + 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; +#endif + +/* ----------------------------------------------------------------------- */ +/* Hardware I2C functions */ +static void I2CWipe(struct saa7146 *saa) +{ + int i; + /* set i2c to ~=100kHz, abort transfer, clear busy */ + saawrite(0x600 | SAA7146_I2C_ABORT, SAA7146_I2C_STATUS); + saawrite((SAA7146_MC2_UPLD_I2C << 16) | + SAA7146_MC2_UPLD_I2C, SAA7146_MC2); + /* wait for i2c registers to be programmed */ + for (i = 0; i < 1000 && + !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) + schedule(); + saawrite(0x600, SAA7146_I2C_STATUS); + saawrite((SAA7146_MC2_UPLD_I2C << 16) | + SAA7146_MC2_UPLD_I2C, SAA7146_MC2); + /* wait for i2c registers to be programmed */ + for (i = 0; i < 1000 && + !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) + schedule(); + saawrite(0x600, SAA7146_I2C_STATUS); + saawrite((SAA7146_MC2_UPLD_I2C << 16) | + SAA7146_MC2_UPLD_I2C, SAA7146_MC2); + /* wait for i2c registers to be programmed */ + for (i = 0; i < 1000 && + !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) + schedule(); +} + +/* read I2C */ +static int I2CRead(struct saa7146 *saa, unsigned char addr, + unsigned char subaddr, int dosub) +{ + int i; + + if (saaread(SAA7146_I2C_STATUS) & 0x3c) + I2CWipe(saa); + for (i = 0; + i < 1000 && (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); + i++) + schedule(); + if (i == 1000) + I2CWipe(saa); + if (dosub) + saawrite(((addr & 0xfe) << 24) | (((addr | 1) & 0xff) << 8) | + ((subaddr & 0xff) << 16) | 0xed, SAA7146_I2C_TRANSFER); + else + saawrite(((addr & 0xfe) << 24) | (((addr | 1) & 0xff) << 16) | + 0xf1, SAA7146_I2C_TRANSFER); + saawrite((SAA7146_MC2_UPLD_I2C << 16) | + SAA7146_MC2_UPLD_I2C, SAA7146_MC2); + /* wait for i2c registers to be programmed */ + for (i = 0; i < 1000 && + !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) + schedule(); + /* wait for valid data */ + for (i = 0; i < 1000 && + (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); i++) + schedule(); + if (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_ERR) + return -1; + if (i == 1000) + printk("i2c setup read timeout\n"); + saawrite(0x41, SAA7146_I2C_TRANSFER); + saawrite((SAA7146_MC2_UPLD_I2C << 16) | + SAA7146_MC2_UPLD_I2C, SAA7146_MC2); + /* wait for i2c registers to be programmed */ + for (i = 0; i < 1000 && + !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) + schedule(); + /* wait for valid data */ + for (i = 0; i < 1000 && + (saaread(SAA7146_I2C_TRANSFER) & SAA7146_I2C_BUSY); i++) + schedule(); + if (saaread(SAA7146_I2C_TRANSFER) & SAA7146_I2C_ERR) + return -1; + if (i == 1000) + printk("i2c read timeout\n"); + return ((saaread(SAA7146_I2C_TRANSFER) >> 24) & 0xff); +} + +/* set both to write both bytes, reset it to write only b1 */ + +static int I2CWrite(struct saa7146 *saa, unsigned char addr, unsigned char b1, + unsigned char b2, int both) +{ + int i; + u32 data; + + if (saaread(SAA7146_I2C_STATUS) & 0x3c) + I2CWipe(saa); + for (i = 0; i < 1000 && + (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); i++) + schedule(); + if (i == 1000) + I2CWipe(saa); + data = ((addr & 0xfe) << 24) | ((b1 & 0xff) << 16); + if (both) + data |= ((b2 & 0xff) << 8) | 0xe5; + else + data |= 0xd1; + saawrite(data, SAA7146_I2C_TRANSFER); + saawrite((SAA7146_MC2_UPLD_I2C << 16) | SAA7146_MC2_UPLD_I2C, + SAA7146_MC2); + return 0; +} + +static void attach_inform(struct saa7146 *saa, int id) +{ + int i; + + DEBUG(printk(KERN_DEBUG "stradis%d: i2c: device found=%02x\n", saa->nr, + id)); + if (id == 0xa0) { /* we have rev2 or later board, fill in info */ + for (i = 0; i < 64; i++) + saa->boardcfg[i] = I2CRead(saa, 0xa0, i, 1); +#ifdef USE_RESCUE_EEPROM_SDM275 + if (saa->boardcfg[0] != 0) { + printk("stradis%d: WARNING: EEPROM STORED VALUES HAVE " + "BEEN IGNORED\n", saa->nr); + for (i = 0; i < 64; i++) + saa->boardcfg[i] = rescue_eeprom[i]; + } +#endif + printk("stradis%d: config =", saa->nr); + for (i = 0; i < 51; i++) { + printk(" %02x", saa->boardcfg[i]); + } + printk("\n"); + } +} + +static void I2CBusScan(struct saa7146 *saa) +{ + int i; + for (i = 0; i < 0xff; i += 2) + if ((I2CRead(saa, i, 0, 0)) >= 0) + attach_inform(saa, i); +} + +static int debiwait_maxwait = 0; + +static int wait_for_debi_done(struct saa7146 *saa) +{ + int i; + + /* wait for registers to be programmed */ + for (i = 0; i < 100000 && + !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_DEBI); i++) + saaread(SAA7146_MC2); + /* wait for transfer to complete */ + for (i = 0; i < 500000 && + (saaread(SAA7146_PSR) & SAA7146_PSR_DEBI_S); i++) + saaread(SAA7146_MC2); + + if (i > debiwait_maxwait) + printk("wait-for-debi-done maxwait: %d\n", + debiwait_maxwait = i); + + if (i == 500000) + return -1; + + return 0; +} + +static int debiwrite(struct saa7146 *saa, u32 config, int addr, + u32 val, int count) +{ + u32 cmd; + if (count <= 0 || count > 32764) + return -1; + if (wait_for_debi_done(saa) < 0) + return -1; + saawrite(config, SAA7146_DEBI_CONFIG); + if (count <= 4) /* immediate transfer */ + saawrite(val, SAA7146_DEBI_AD); + else /* block transfer */ + saawrite(virt_to_bus(saa->dmadebi), SAA7146_DEBI_AD); + saawrite((cmd = (count << 17) | (addr & 0xffff)), SAA7146_DEBI_COMMAND); + saawrite((SAA7146_MC2_UPLD_DEBI << 16) | SAA7146_MC2_UPLD_DEBI, + SAA7146_MC2); + return 0; +} + +static u32 debiread(struct saa7146 *saa, u32 config, int addr, int count) +{ + u32 result = 0; + + if (count > 32764 || count <= 0) + return 0; + if (wait_for_debi_done(saa) < 0) + return 0; + saawrite(virt_to_bus(saa->dmadebi), SAA7146_DEBI_AD); + saawrite((count << 17) | 0x10000 | (addr & 0xffff), + SAA7146_DEBI_COMMAND); + saawrite(config, SAA7146_DEBI_CONFIG); + saawrite((SAA7146_MC2_UPLD_DEBI << 16) | SAA7146_MC2_UPLD_DEBI, + SAA7146_MC2); + if (count > 4) /* not an immediate transfer */ + return count; + wait_for_debi_done(saa); + result = saaread(SAA7146_DEBI_AD); + if (count == 1) + result &= 0xff; + if (count == 2) + result &= 0xffff; + if (count == 3) + result &= 0xffffff; + return result; +} + +static void do_irq_send_data(struct saa7146 *saa) +{ + int split, audbytes, vidbytes; + + saawrite(SAA7146_PSR_PIN1, SAA7146_IER); + /* if special feature mode in effect, disable audio sending */ + if (saa->playmode != VID_PLAY_NORMAL) + saa->audtail = saa->audhead = 0; + if (saa->audhead <= saa->audtail) + audbytes = saa->audtail - saa->audhead; + else + audbytes = 65536 - (saa->audhead - saa->audtail); + if (saa->vidhead <= saa->vidtail) + vidbytes = saa->vidtail - saa->vidhead; + else + vidbytes = 524288 - (saa->vidhead - saa->vidtail); + if (audbytes == 0 && vidbytes == 0 && saa->osdtail == saa->osdhead) { + saawrite(0, SAA7146_IER); + return; + } + /* if at least 1 block audio waiting and audio fifo isn't full */ + if (audbytes >= 2048 && (debiread(saa, debNormal, IBM_MP2_AUD_FIFO, 2) + & 0xff) < 60) { + if (saa->audhead > saa->audtail) + split = 65536 - saa->audhead; + else + split = 0; + audbytes = 2048; + if (split > 0 && split < 2048) { + memcpy(saa->dmadebi, saa->audbuf + saa->audhead, split); + saa->audhead = 0; + audbytes -= split; + } else + split = 0; + memcpy(saa->dmadebi + split, saa->audbuf + saa->audhead, + audbytes); + saa->audhead += audbytes; + saa->audhead &= 0xffff; + debiwrite(saa, debAudio, (NewCard ? IBM_MP2_AUD_FIFO : + IBM_MP2_AUD_FIFOW), 0, 2048); + wake_up_interruptible(&saa->audq); + /* if at least 1 block video waiting and video fifo isn't full */ + } else if (vidbytes >= 30720 && (debiread(saa, debNormal, + IBM_MP2_FIFO, 2)) < 16384) { + if (saa->vidhead > saa->vidtail) + split = 524288 - saa->vidhead; + else + split = 0; + vidbytes = 30720; + if (split > 0 && split < 30720) { + memcpy(saa->dmadebi, saa->vidbuf + saa->vidhead, split); + saa->vidhead = 0; + vidbytes -= split; + } else + split = 0; + memcpy(saa->dmadebi + split, saa->vidbuf + saa->vidhead, + vidbytes); + saa->vidhead += vidbytes; + saa->vidhead &= 0x7ffff; + debiwrite(saa, debVideo, (NewCard ? IBM_MP2_FIFO : + IBM_MP2_FIFOW), 0, 30720); + wake_up_interruptible(&saa->vidq); + } + saawrite(SAA7146_PSR_DEBI_S | SAA7146_PSR_PIN1, SAA7146_IER); +} + +static void send_osd_data(struct saa7146 *saa) +{ + int size = saa->osdtail - saa->osdhead; + if (size > 30720) + size = 30720; + /* ensure some multiple of 8 bytes is transferred */ + size = 8 * ((size + 8) >> 3); + if (size) { + debiwrite(saa, debNormal, IBM_MP2_OSD_ADDR, + (saa->osdhead >> 3), 2); + memcpy(saa->dmadebi, &saa->osdbuf[saa->osdhead], size); + saa->osdhead += size; + /* block transfer of next 8 bytes to ~32k bytes */ + debiwrite(saa, debNormal, IBM_MP2_OSD_DATA, 0, size); + } + if (saa->osdhead >= saa->osdtail) { + saa->osdhead = saa->osdtail = 0; + debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2); + } +} + +static irqreturn_t saa7146_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct saa7146 *saa = dev_id; + u32 stat, astat; + int count; + int handled = 0; + + count = 0; + while (1) { + /* get/clear interrupt status bits */ + stat = saaread(SAA7146_ISR); + astat = stat & saaread(SAA7146_IER); + if (!astat) + break; + handled = 1; + saawrite(astat, SAA7146_ISR); + if (astat & SAA7146_PSR_DEBI_S) { + do_irq_send_data(saa); + } + if (astat & SAA7146_PSR_PIN1) { + int istat; + /* the following read will trigger DEBI_S */ + istat = debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); + if (istat & 1) { + saawrite(0, SAA7146_IER); + send_osd_data(saa); + saawrite(SAA7146_PSR_DEBI_S | + SAA7146_PSR_PIN1, SAA7146_IER); + } + if (istat & 0x20) { /* Video Start */ + saa->vidinfo.frame_count++; + } + if (istat & 0x400) { /* Picture Start */ + /* update temporal reference */ + } + if (istat & 0x200) { /* Picture Resolution Change */ + /* read new resolution */ + } + if (istat & 0x100) { /* New User Data found */ + /* read new user data */ + } + if (istat & 0x1000) { /* new GOP/SMPTE */ + /* read new SMPTE */ + } + if (istat & 0x8000) { /* Sequence Start Code */ + /* reset frame counter, load sizes */ + saa->vidinfo.frame_count = 0; + saa->vidinfo.h_size = 704; + saa->vidinfo.v_size = 480; +#if 0 + if (saa->endmarkhead != saa->endmarktail) { + saa->audhead = + saa->endmark[saa->endmarkhead]; + saa->endmarkhead++; + if (saa->endmarkhead >= MAX_MARKS) + saa->endmarkhead = 0; + } +#endif + } + if (istat & 0x4000) { /* Sequence Error Code */ + if (saa->endmarkhead != saa->endmarktail) { + saa->audhead = + saa->endmark[saa->endmarkhead]; + saa->endmarkhead++; + if (saa->endmarkhead >= MAX_MARKS) + saa->endmarkhead = 0; + } + } + } +#ifdef IDEBUG + if (astat & SAA7146_PSR_PPEF) { + IDEBUG(printk("stradis%d irq: PPEF\n", saa->nr)); + } + if (astat & SAA7146_PSR_PABO) { + IDEBUG(printk("stradis%d irq: PABO\n", saa->nr)); + } + if (astat & SAA7146_PSR_PPED) { + IDEBUG(printk("stradis%d irq: PPED\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_I1) { + IDEBUG(printk("stradis%d irq: RPS_I1\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_I0) { + IDEBUG(printk("stradis%d irq: RPS_I0\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_LATE1) { + IDEBUG(printk("stradis%d irq: RPS_LATE1\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_LATE0) { + IDEBUG(printk("stradis%d irq: RPS_LATE0\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_E1) { + IDEBUG(printk("stradis%d irq: RPS_E1\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_E0) { + IDEBUG(printk("stradis%d irq: RPS_E0\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_TO1) { + IDEBUG(printk("stradis%d irq: RPS_TO1\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_TO0) { + IDEBUG(printk("stradis%d irq: RPS_TO0\n", saa->nr)); + } + if (astat & SAA7146_PSR_UPLD) { + IDEBUG(printk("stradis%d irq: UPLD\n", saa->nr)); + } + if (astat & SAA7146_PSR_DEBI_E) { + IDEBUG(printk("stradis%d irq: DEBI_E\n", saa->nr)); + } + if (astat & SAA7146_PSR_I2C_S) { + IDEBUG(printk("stradis%d irq: I2C_S\n", saa->nr)); + } + if (astat & SAA7146_PSR_I2C_E) { + IDEBUG(printk("stradis%d irq: I2C_E\n", saa->nr)); + } + if (astat & SAA7146_PSR_A2_IN) { + IDEBUG(printk("stradis%d irq: A2_IN\n", saa->nr)); + } + if (astat & SAA7146_PSR_A2_OUT) { + IDEBUG(printk("stradis%d irq: A2_OUT\n", saa->nr)); + } + if (astat & SAA7146_PSR_A1_IN) { + IDEBUG(printk("stradis%d irq: A1_IN\n", saa->nr)); + } + if (astat & SAA7146_PSR_A1_OUT) { + IDEBUG(printk("stradis%d irq: A1_OUT\n", saa->nr)); + } + if (astat & SAA7146_PSR_AFOU) { + IDEBUG(printk("stradis%d irq: AFOU\n", saa->nr)); + } + if (astat & SAA7146_PSR_V_PE) { + IDEBUG(printk("stradis%d irq: V_PE\n", saa->nr)); + } + if (astat & SAA7146_PSR_VFOU) { + IDEBUG(printk("stradis%d irq: VFOU\n", saa->nr)); + } + if (astat & SAA7146_PSR_FIDA) { + IDEBUG(printk("stradis%d irq: FIDA\n", saa->nr)); + } + if (astat & SAA7146_PSR_FIDB) { + IDEBUG(printk("stradis%d irq: FIDB\n", saa->nr)); + } + if (astat & SAA7146_PSR_PIN3) { + IDEBUG(printk("stradis%d irq: PIN3\n", saa->nr)); + } + if (astat & SAA7146_PSR_PIN2) { + IDEBUG(printk("stradis%d irq: PIN2\n", saa->nr)); + } + if (astat & SAA7146_PSR_PIN0) { + IDEBUG(printk("stradis%d irq: PIN0\n", saa->nr)); + } + if (astat & SAA7146_PSR_ECS) { + IDEBUG(printk("stradis%d irq: ECS\n", saa->nr)); + } + if (astat & SAA7146_PSR_EC3S) { + IDEBUG(printk("stradis%d irq: EC3S\n", saa->nr)); + } + if (astat & SAA7146_PSR_EC0S) { + IDEBUG(printk("stradis%d irq: EC0S\n", saa->nr)); + } +#endif + count++; + if (count > 15) + printk(KERN_WARNING "stradis%d: irq loop %d\n", + saa->nr, count); + if (count > 20) { + saawrite(0, SAA7146_IER); + printk(KERN_ERR + "stradis%d: IRQ loop cleared\n", saa->nr); + } + } + return IRQ_RETVAL(handled); +} + +static int ibm_send_command(struct saa7146 *saa, + int command, int data, int chain) +{ + int i; + + if (chain) + debiwrite(saa, debNormal, IBM_MP2_COMMAND, (command << 1)| 1,2); + else + debiwrite(saa, debNormal, IBM_MP2_COMMAND, command << 1, 2); + debiwrite(saa, debNormal, IBM_MP2_CMD_DATA, data, 2); + debiwrite(saa, debNormal, IBM_MP2_CMD_STAT, 1, 2); + for (i = 0; i < 100 && + (debiread(saa, debNormal, IBM_MP2_CMD_STAT, 2) & 1); i++) + schedule(); + if (i == 100) + return -1; + return 0; +} + +static void cs4341_setlevel(struct saa7146 *saa, int left, int right) +{ + I2CWrite(saa, 0x22, 0x03, left > 94 ? 94 : left, 2); + I2CWrite(saa, 0x22, 0x04, right > 94 ? 94 : right, 2); +} + +static void initialize_cs4341(struct saa7146 *saa) +{ + int i; + for (i = 0; i < 200; i++) { + /* auto mute off, power on, no de-emphasis */ + /* I2S data up to 24-bit 64xFs internal SCLK */ + I2CWrite(saa, 0x22, 0x01, 0x11, 2); + /* ATAPI mixer settings */ + I2CWrite(saa, 0x22, 0x02, 0x49, 2); + /* attenuation left 3db */ + I2CWrite(saa, 0x22, 0x03, 0x00, 2); + /* attenuation right 3db */ + I2CWrite(saa, 0x22, 0x04, 0x00, 2); + I2CWrite(saa, 0x22, 0x01, 0x10, 2); + if (I2CRead(saa, 0x22, 0x02, 1) == 0x49) + break; + schedule(); + } + printk("stradis%d: CS4341 initialized (%d)\n", saa->nr, i); + return; +} + +static void initialize_cs8420(struct saa7146 *saa, int pro) +{ + int i; + u8 *sequence; + if (pro) + sequence = mode8420pro; + else + sequence = mode8420con; + for (i = 0; i < INIT8420LEN; i++) + I2CWrite(saa, 0x20, init8420[i * 2], init8420[i * 2 + 1], 2); + for (i = 0; i < MODE8420LEN; i++) + I2CWrite(saa, 0x20, sequence[i * 2], sequence[i * 2 + 1], 2); + printk("stradis%d: CS8420 initialized\n", saa->nr); +} + +static void initialize_saa7121(struct saa7146 *saa, int dopal) +{ + int i, mod; + u8 *sequence; + if (dopal) + sequence = init7121pal; + else + sequence = init7121ntsc; + mod = saaread(SAA7146_PSR) & 0x08; + /* initialize PAL/NTSC video encoder */ + for (i = 0; i < INIT7121LEN; i++) { + if (NewCard) { /* handle new card encoder differences */ + if (sequence[i * 2] == 0x3a) + I2CWrite(saa, 0x88, 0x3a, 0x13, 2); + else if (sequence[i * 2] == 0x6b) + I2CWrite(saa, 0x88, 0x6b, 0x20, 2); + else if (sequence[i * 2] == 0x6c) + I2CWrite(saa, 0x88, 0x6c, + dopal ? 0x09 : 0xf5, 2); + else if (sequence[i * 2] == 0x6d) + I2CWrite(saa, 0x88, 0x6d, + dopal ? 0x20 : 0x00, 2); + else if (sequence[i * 2] == 0x7a) + I2CWrite(saa, 0x88, 0x7a, + dopal ? (PALFirstActive - 1) : + (NTSCFirstActive - 4), 2); + else if (sequence[i * 2] == 0x7b) + I2CWrite(saa, 0x88, 0x7b, + dopal ? PALLastActive : + NTSCLastActive, 2); + else + I2CWrite(saa, 0x88, sequence[i * 2], + sequence[i * 2 + 1], 2); + } else { + if (sequence[i * 2] == 0x6b && mod) + I2CWrite(saa, 0x88, 0x6b, + (sequence[i * 2 + 1] ^ 0x09), 2); + else if (sequence[i * 2] == 0x7a) + I2CWrite(saa, 0x88, 0x7a, + dopal ? (PALFirstActive - 1) : + (NTSCFirstActive - 4), 2); + else if (sequence[i * 2] == 0x7b) + I2CWrite(saa, 0x88, 0x7b, + dopal ? PALLastActive : + NTSCLastActive, 2); + else + I2CWrite(saa, 0x88, sequence[i * 2], + sequence[i * 2 + 1], 2); + } + } +} + +static void set_genlock_offset(struct saa7146 *saa, int noffset) +{ + int nCode; + int PixelsPerLine = 858; + if (CurrentMode == VIDEO_MODE_PAL) + PixelsPerLine = 864; + if (noffset > 500) + noffset = 500; + else if (noffset < -500) + noffset = -500; + nCode = noffset + 0x100; + if (nCode == 1) + nCode = 0x401; + else if (nCode < 1) + nCode = 0x400 + PixelsPerLine + nCode; + debiwrite(saa, debNormal, XILINX_GLDELAY, nCode, 2); +} + +static void set_out_format(struct saa7146 *saa, int mode) +{ + initialize_saa7121(saa, (mode == VIDEO_MODE_NTSC ? 0 : 1)); + saa->boardcfg[2] = mode; + /* do not adjust analog video parameters here, use saa7121 init */ + /* you will affect the SDI output on the new card */ + if (mode == VIDEO_MODE_PAL) { /* PAL */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x0808, 2); + mdelay(50); + saawrite(0x012002c0, SAA7146_NUM_LINE_BYTE1); + if (NewCard) { + debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, 0xe100, 2); + mdelay(50); + } + debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, + NewCard ? 0xe500 : 0x6500, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_DLY, + (1 << 8) | + (NewCard ? PALFirstActive : PALFirstActive - 6), 2); + } else { /* NTSC */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x0800, 2); + mdelay(50); + saawrite(0x00f002c0, SAA7146_NUM_LINE_BYTE1); + debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, + NewCard ? 0xe100 : 0x6100, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_DLY, + (1 << 8) | + (NewCard ? NTSCFirstActive : NTSCFirstActive - 6), 2); + } +} + +/* Intialize bitmangler to map from a byte value to the mangled word that + * must be output to program the Xilinx part through the DEBI port. + * Xilinx Data Bit->DEBI Bit: 0->15 1->7 2->6 3->12 4->11 5->2 6->1 7->0 + * transfer FPGA code, init IBM chip, transfer IBM microcode + * rev2 card mangles: 0->7 1->6 2->5 3->4 4->3 5->2 6->1 7->0 + */ +static u16 bitmangler[256]; + +static int initialize_fpga(struct video_code *bitdata) +{ + int i, num, startindex, failure = 0, loadtwo, loadfile = 0; + u16 *dmabuf; + u8 *newdma; + struct saa7146 *saa; + + /* verify fpga code */ + for (startindex = 0; startindex < bitdata->datasize; startindex++) + if (bitdata->data[startindex] == 255) + break; + if (startindex == bitdata->datasize) { + printk(KERN_INFO "stradis: bad fpga code\n"); + return -1; + } + /* initialize all detected cards */ + for (num = 0; num < saa_num; num++) { + saa = &saa7146s[num]; + if (saa->boardcfg[0] > 20) + continue; /* card was programmed */ + loadtwo = (saa->boardcfg[18] & 0x10); + if (!NewCard) /* we have an old board */ + for (i = 0; i < 256; i++) + bitmangler[i] = ((i & 0x01) << 15) | + ((i & 0x02) << 6) | ((i & 0x04) << 4) | + ((i & 0x08) << 9) | ((i & 0x10) << 7) | + ((i & 0x20) >> 3) | ((i & 0x40) >> 5) | + ((i & 0x80) >> 7); + else /* else we have a new board */ + for (i = 0; i < 256; i++) + bitmangler[i] = ((i & 0x01) << 7) | + ((i & 0x02) << 5) | ((i & 0x04) << 3) | + ((i & 0x08) << 1) | ((i & 0x10) >> 1) | + ((i & 0x20) >> 3) | ((i & 0x40) >> 5) | + ((i & 0x80) >> 7); + + dmabuf = (u16 *) saa->dmadebi; + newdma = (u8 *) saa->dmadebi; + if (NewCard) { /* SDM2xxx */ + if (!strncmp(bitdata->loadwhat, "decoder2", 8)) + continue; /* fpga not for this card */ + if (!strncmp(&saa->boardcfg[42], bitdata->loadwhat, 8)) + loadfile = 1; + else if (loadtwo && !strncmp(&saa->boardcfg[19], + bitdata->loadwhat, 8)) + loadfile = 2; + else if (!saa->boardcfg[42] && !strncmp("decxl", + bitdata->loadwhat, 8)) + loadfile = 1; /* special */ + else + continue; /* fpga not for this card */ + if (loadfile != 1 && loadfile != 2) + continue; /* skip to next card */ + if (saa->boardcfg[0] && loadfile == 1) + continue; /* skip to next card */ + if (saa->boardcfg[0] != 1 && loadfile == 2) + continue; /* skip to next card */ + saa->boardcfg[0]++; /* mark fpga handled */ + printk("stradis%d: loading %s\n", saa->nr, + bitdata->loadwhat); + if (loadtwo && loadfile == 2) + goto send_fpga_stuff; + /* turn on the Audio interface to set PROG low */ + saawrite(0x00400040, SAA7146_GPIO_CTRL); + saaread(SAA7146_PSR); /* ensure posted write */ + /* wait for everyone to reset */ + mdelay(10); + saawrite(0x00400000, SAA7146_GPIO_CTRL); + } else { /* original card */ + if (strncmp(bitdata->loadwhat, "decoder2", 8)) + continue; /* fpga not for this card */ + /* Pull the Xilinx PROG signal WS3 low */ + saawrite(0x02000200, SAA7146_MC1); + /* Turn on the Audio interface so can set PROG low */ + saawrite(0x000000c0, SAA7146_ACON1); + /* Pull the Xilinx INIT signal (GPIO2) low */ + saawrite(0x00400000, SAA7146_GPIO_CTRL); + /* Make sure everybody resets */ + saaread(SAA7146_PSR); /* ensure posted write */ + mdelay(10); + /* Release the Xilinx PROG signal */ + saawrite(0x00000000, SAA7146_ACON1); + /* Turn off the Audio interface */ + saawrite(0x02000000, SAA7146_MC1); + } + /* Release Xilinx INIT signal (WS2) */ + saawrite(0x00000000, SAA7146_GPIO_CTRL); + /* Wait for the INIT to go High */ + for (i = 0; + i < 10000 && !(saaread(SAA7146_PSR) & SAA7146_PSR_PIN2); + i++) + schedule(); + if (i == 1000) { + printk(KERN_INFO "stradis%d: no fpga INIT\n", saa->nr); + return -1; + } +send_fpga_stuff: + if (NewCard) { + for (i = startindex; i < bitdata->datasize; i++) + newdma[i - startindex] = + bitmangler[bitdata->data[i]]; + debiwrite(saa, 0x01420000, 0, 0, + ((bitdata->datasize - startindex) + 5)); + if (loadtwo && loadfile == 1) { + printk("stradis%d: awaiting 2nd FPGA bitfile\n", + saa->nr); + continue; /* skip to next card */ + } + } else { + for (i = startindex; i < bitdata->datasize; i++) + dmabuf[i - startindex] = + bitmangler[bitdata->data[i]]; + debiwrite(saa, 0x014a0000, 0, 0, + ((bitdata->datasize - startindex) + 5) * 2); + } + for (i = 0; + i < 1000 && !(saaread(SAA7146_PSR) & SAA7146_PSR_PIN2); + i++) + schedule(); + if (i == 1000) { + printk(KERN_INFO "stradis%d: FPGA load failed\n", + saa->nr); + failure++; + continue; + } + if (!NewCard) { + /* Pull the Xilinx INIT signal (GPIO2) low */ + saawrite(0x00400000, SAA7146_GPIO_CTRL); + saaread(SAA7146_PSR); /* ensure posted write */ + mdelay(2); + saawrite(0x00000000, SAA7146_GPIO_CTRL); + mdelay(2); + } + printk(KERN_INFO "stradis%d: FPGA Loaded\n", saa->nr); + saa->boardcfg[0] = 26; /* mark fpga programmed */ + /* set VXCO to its lowest frequency */ + debiwrite(saa, debNormal, XILINX_PWM, 0, 2); + if (NewCard) { + /* mute CS3310 */ + if (HaveCS3310) + debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, + 0, 2); + /* set VXCO to PWM mode, release reset, blank on */ + debiwrite(saa, debNormal, XILINX_CTL0, 0xffc4, 2); + mdelay(10); + /* unmute CS3310 */ + if (HaveCS3310) + debiwrite(saa, debNormal, XILINX_CTL0, + 0x2020, 2); + } + /* set source Black */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x1707, 2); + saa->boardcfg[4] = 22; /* set NTSC First Active Line */ + saa->boardcfg[5] = 23; /* set PAL First Active Line */ + saa->boardcfg[54] = 2; /* set NTSC Last Active Line - 256 */ + saa->boardcfg[55] = 54; /* set PAL Last Active Line - 256 */ + set_out_format(saa, VIDEO_MODE_NTSC); + mdelay(50); + /* begin IBM chip init */ + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 4, 2); + saaread(SAA7146_PSR); /* wait for reset */ + mdelay(5); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0, 2); + debiread(saa, debNormal, IBM_MP2_CHIP_CONTROL, 2); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0x10, 2); + debiwrite(saa, debNormal, IBM_MP2_CMD_ADDR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_CHIP_MODE, 0x2e, 2); + if (NewCard) { + mdelay(5); + /* set i2s rate converter to 48KHz */ + debiwrite(saa, debNormal, 0x80c0, 6, 2); + /* we must init CS8420 first since rev b pulls i2s */ + /* master clock low and CS4341 needs i2s master to */ + /* run the i2c port. */ + if (HaveCS8420) + /* 0=consumer, 1=pro */ + initialize_cs8420(saa, 0); + + mdelay(5); + if (HaveCS4341) + initialize_cs4341(saa); + } + debiwrite(saa, debNormal, IBM_MP2_INFC_CTL, 0x48, 2); + debiwrite(saa, debNormal, IBM_MP2_BEEP_CTL, 0xa000, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_LBOR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_TBOR, 0, 2); + if (NewCard) + set_genlock_offset(saa, 0); + debiwrite(saa, debNormal, IBM_MP2_FRNT_ATTEN, 0, 2); +#if 0 + /* enable genlock */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x8000, 2); +#else + /* disable genlock */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x8080, 2); +#endif + } + + return failure; +} + +static int do_ibm_reset(struct saa7146 *saa) +{ + /* failure if decoder not previously programmed */ + if (saa->boardcfg[0] < 37) + return -EIO; + /* mute CS3310 */ + if (HaveCS3310) + debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, 0, 2); + /* disable interrupts */ + saawrite(0, SAA7146_IER); + saa->audhead = saa->audtail = 0; + saa->vidhead = saa->vidtail = 0; + /* tristate debi bus, disable debi transfers */ + saawrite(0x00880000, SAA7146_MC1); + /* ensure posted write */ + saaread(SAA7146_MC1); + mdelay(50); + /* re-enable debi transfers */ + saawrite(0x00880088, SAA7146_MC1); + /* set source Black */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x1707, 2); + /* begin IBM chip init */ + set_out_format(saa, CurrentMode); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 4, 2); + saaread(SAA7146_PSR); /* wait for reset */ + mdelay(5); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0, 2); + debiread(saa, debNormal, IBM_MP2_CHIP_CONTROL, 2); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2); + debiwrite(saa, debNormal, IBM_MP2_CHIP_MODE, 0x2e, 2); + if (NewCard) { + mdelay(5); + /* set i2s rate converter to 48KHz */ + debiwrite(saa, debNormal, 0x80c0, 6, 2); + /* we must init CS8420 first since rev b pulls i2s */ + /* master clock low and CS4341 needs i2s master to */ + /* run the i2c port. */ + if (HaveCS8420) + /* 0=consumer, 1=pro */ + initialize_cs8420(saa, 1); + + mdelay(5); + if (HaveCS4341) + initialize_cs4341(saa); + } + debiwrite(saa, debNormal, IBM_MP2_INFC_CTL, 0x48, 2); + debiwrite(saa, debNormal, IBM_MP2_BEEP_CTL, 0xa000, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_LBOR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_TBOR, 0, 2); + if (NewCard) + set_genlock_offset(saa, 0); + debiwrite(saa, debNormal, IBM_MP2_FRNT_ATTEN, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0x2000, 2); + debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4552, 2); + if (ibm_send_command(saa, IBM_MP2_CONFIG_DECODER, + (ChipControl == 0x43 ? 0xe800 : 0xe000), 1)) { + printk(KERN_ERR "stradis%d: IBM config failed\n", saa->nr); + } + if (HaveCS3310) { + int i = CS3310MaxLvl; + debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, ((i << 8)| i),2); + } + /* start video decoder */ + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2); + /* 256k vid, 3520 bytes aud */ + debiwrite(saa, debNormal, IBM_MP2_RB_THRESHOLD, 0x4037, 2); + debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4573, 2); + ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); + /* enable buffer threshold irq */ + debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2); + /* clear pending interrupts */ + debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); + debiwrite(saa, debNormal, XILINX_CTL0, 0x1711, 2); + + return 0; +} + +/* load the decoder microcode */ +static int initialize_ibmmpeg2(struct video_code *microcode) +{ + int i, num; + struct saa7146 *saa; + + for (num = 0; num < saa_num; num++) { + saa = &saa7146s[num]; + /* check that FPGA is loaded */ + debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0xa55a, 2); + i = debiread(saa, debNormal, IBM_MP2_OSD_SIZE, 2); + if (i != 0xa55a) { + printk(KERN_INFO "stradis%d: %04x != 0xa55a\n", + saa->nr, i); +#if 0 + return -1; +#endif + } + if (!strncmp(microcode->loadwhat, "decoder.vid", 11)) { + if (saa->boardcfg[0] > 27) + continue; /* skip to next card */ + /* load video control store */ + saa->boardcfg[1] = 0x13; /* no-sync default */ + debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 1, 2); + debiwrite(saa, debNormal, IBM_MP2_PROC_IADDR, 0, 2); + for (i = 0; i < microcode->datasize / 2; i++) + debiwrite(saa, debNormal, IBM_MP2_PROC_IDATA, + (microcode->data[i * 2] << 8) | + microcode->data[i * 2 + 1], 2); + debiwrite(saa, debNormal, IBM_MP2_PROC_IADDR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, + ChipControl, 2); + saa->boardcfg[0] = 28; + } + if (!strncmp(microcode->loadwhat, "decoder.aud", 11)) { + if (saa->boardcfg[0] > 35) + continue; /* skip to next card */ + /* load audio control store */ + debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 1, 2); + debiwrite(saa, debNormal, IBM_MP2_AUD_IADDR, 0, 2); + for (i = 0; i < microcode->datasize; i++) + debiwrite(saa, debNormal, IBM_MP2_AUD_IDATA, + microcode->data[i], 1); + debiwrite(saa, debNormal, IBM_MP2_AUD_IADDR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0x2000, 2); + debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4552, 2); + if (ibm_send_command(saa, IBM_MP2_CONFIG_DECODER, + 0xe000, 1)) { + printk(KERN_ERR "stradis%d: IBM config " + "failed\n", saa->nr); + return -1; + } + /* set PWM to center value */ + if (NewCard) { + debiwrite(saa, debNormal, XILINX_PWM, + saa->boardcfg[14] + + (saa->boardcfg[13] << 8), 2); + } else + debiwrite(saa, debNormal, XILINX_PWM, 0x46, 2); + + if (HaveCS3310) { + i = CS3310MaxLvl; + debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, + (i << 8) | i, 2); + } + printk(KERN_INFO "stradis%d: IBM MPEGCD%d Inited\n", + saa->nr, 18 + (debiread(saa, debNormal, + IBM_MP2_CHIP_CONTROL, 2) >> 12)); + /* start video decoder */ + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, + ChipControl, 2); + debiwrite(saa, debNormal, IBM_MP2_RB_THRESHOLD, 0x4037, + 2); /* 256k vid, 3520 bytes aud */ + debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4573, 2); + ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); + /* enable buffer threshold irq */ + debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2); + debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); + /* enable gpio irq */ + saawrite(0x00002000, SAA7146_GPIO_CTRL); + /* enable decoder output to HPS */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x1711, 2); + saa->boardcfg[0] = 37; + } + } + + return 0; +} + +static u32 palette2fmt[] = { /* some of these YUV translations are wrong */ + 0xffffffff, 0x86000000, 0x87000000, 0x80000000, 0x8100000, 0x82000000, + 0x83000000, 0x00000000, 0x03000000, 0x03000000, 0x0a00000, 0x03000000, + 0x06000000, 0x00000000, 0x03000000, 0x0a000000, 0x0300000 +}; +static int bpp2fmt[4] = { + VIDEO_PALETTE_HI240, VIDEO_PALETTE_RGB565, VIDEO_PALETTE_RGB24, + VIDEO_PALETTE_RGB32 +}; + +/* I wish I could find a formula to calculate these... */ +static u32 h_prescale[64] = { + 0x10000000, 0x18040202, 0x18080000, 0x380c0606, 0x38100204, 0x38140808, + 0x38180000, 0x381c0000, 0x3820161c, 0x38242a3b, 0x38281230, 0x382c4460, + 0x38301040, 0x38340080, 0x38380000, 0x383c0000, 0x3840fefe, 0x3844ee9f, + 0x3848ee9f, 0x384cee9f, 0x3850ee9f, 0x38542a3b, 0x38581230, 0x385c0000, + 0x38600000, 0x38640000, 0x38680000, 0x386c0000, 0x38700000, 0x38740000, + 0x38780000, 0x387c0000, 0x30800000, 0x38840000, 0x38880000, 0x388c0000, + 0x38900000, 0x38940000, 0x38980000, 0x389c0000, 0x38a00000, 0x38a40000, + 0x38a80000, 0x38ac0000, 0x38b00000, 0x38b40000, 0x38b80000, 0x38bc0000, + 0x38c00000, 0x38c40000, 0x38c80000, 0x38cc0000, 0x38d00000, 0x38d40000, + 0x38d80000, 0x38dc0000, 0x38e00000, 0x38e40000, 0x38e80000, 0x38ec0000, + 0x38f00000, 0x38f40000, 0x38f80000, 0x38fc0000, +}; +static u32 v_gain[64] = { + 0x016000ff, 0x016100ff, 0x016100ff, 0x016200ff, 0x016200ff, 0x016200ff, + 0x016200ff, 0x016300ff, 0x016300ff, 0x016300ff, 0x016300ff, 0x016300ff, + 0x016300ff, 0x016300ff, 0x016300ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, +}; + +static void saa7146_set_winsize(struct saa7146 *saa) +{ + u32 format; + int offset, yacl, ysci; + saa->win.color_fmt = format = + (saa->win.depth == 15) ? palette2fmt[VIDEO_PALETTE_RGB555] : + palette2fmt[bpp2fmt[(saa->win.bpp - 1) & 3]]; + offset = saa->win.x * saa->win.bpp + saa->win.y * saa->win.bpl; + saawrite(saa->win.vidadr + offset, SAA7146_BASE_EVEN1); + saawrite(saa->win.vidadr + offset + saa->win.bpl, SAA7146_BASE_ODD1); + saawrite(saa->win.bpl * 2, SAA7146_PITCH1); + saawrite(saa->win.vidadr + saa->win.bpl * saa->win.sheight, + SAA7146_PROT_ADDR1); + saawrite(0, SAA7146_PAGE1); + saawrite(format | 0x60, SAA7146_CLIP_FORMAT_CTRL); + offset = (704 / (saa->win.width - 1)) & 0x3f; + saawrite(h_prescale[offset], SAA7146_HPS_H_PRESCALE); + offset = (720896 / saa->win.width) / (offset + 1); + saawrite((offset << 12) | 0x0c, SAA7146_HPS_H_SCALE); + if (CurrentMode == VIDEO_MODE_NTSC) { + yacl = /*(480 / saa->win.height - 1) & 0x3f */ 0; + ysci = 1024 - (saa->win.height * 1024 / 480); + } else { + yacl = /*(576 / saa->win.height - 1) & 0x3f */ 0; + ysci = 1024 - (saa->win.height * 1024 / 576); + } + saawrite((1 << 31) | (ysci << 21) | (yacl << 15), SAA7146_HPS_V_SCALE); + saawrite(v_gain[yacl], SAA7146_HPS_V_GAIN); + saawrite(((SAA7146_MC2_UPLD_DMA1 | SAA7146_MC2_UPLD_HPS_V | + SAA7146_MC2_UPLD_HPS_H) << 16) | (SAA7146_MC2_UPLD_DMA1 | + SAA7146_MC2_UPLD_HPS_V | SAA7146_MC2_UPLD_HPS_H), SAA7146_MC2); +} + +/* clip_draw_rectangle(cm,x,y,w,h) -- handle clipping an area + * bitmap is fixed width, 128 bytes (1024 pixels represented) + * arranged most-sigificant-bit-left in 32-bit words + * based on saa7146 clipping hardware, it swaps bytes if LE + * much of this makes up for egcs brain damage -- so if you + * are wondering "why did he do this?" it is because the C + * was adjusted to generate the optimal asm output without + * writing non-portable __asm__ directives. + */ + +static void clip_draw_rectangle(u32 *clipmap, int x, int y, int w, int h) +{ + register int startword, endword; + register u32 bitsleft, bitsright; + u32 *temp; + if (x < 0) { + w += x; + x = 0; + } + if (y < 0) { + h += y; + y = 0; + } + if (w <= 0 || h <= 0 || x > 1023 || y > 639) + return; /* throw away bad clips */ + if (x + w > 1024) + w = 1024 - x; + if (y + h > 640) + h = 640 - y; + startword = (x >> 5); + endword = ((x + w) >> 5); + bitsleft = (0xffffffff >> (x & 31)); + bitsright = (0xffffffff << (~((x + w) - (endword << 5)))); + temp = &clipmap[(y << 5) + startword]; + w = endword - startword; + if (!w) { + bitsleft |= bitsright; + for (y = 0; y < h; y++) { + *temp |= bitsleft; + temp += 32; + } + } else { + for (y = 0; y < h; y++) { + *temp++ |= bitsleft; + for (x = 1; x < w; x++) + *temp++ = 0xffffffff; + *temp |= bitsright; + temp += (32 - w); + } + } +} + +static void make_clip_tab(struct saa7146 *saa, struct video_clip *cr, int ncr) +{ + int i, width, height; + u32 *clipmap; + + clipmap = saa->dmavid2; + if ((width = saa->win.width) > 1023) + width = 1023; /* sanity check */ + if ((height = saa->win.height) > 640) + height = 639; /* sanity check */ + if (ncr > 0) { /* rectangles pased */ + /* convert rectangular clips to a bitmap */ + memset(clipmap, 0, VIDEO_CLIPMAP_SIZE); /* clear map */ + for (i = 0; i < ncr; i++) + clip_draw_rectangle(clipmap, cr[i].x, cr[i].y, + cr[i].width, cr[i].height); + } + /* clip against viewing window AND screen + so we do not have to rely on the user program + */ + clip_draw_rectangle(clipmap, (saa->win.x + width > saa->win.swidth) ? + (saa->win.swidth - saa->win.x) : width, 0, 1024, 768); + clip_draw_rectangle(clipmap, 0, + (saa->win.y + height > saa->win.sheight) ? + (saa->win.sheight - saa->win.y) : height, 1024, 768); + if (saa->win.x < 0) + clip_draw_rectangle(clipmap, 0, 0, -saa->win.x, 768); + if (saa->win.y < 0) + clip_draw_rectangle(clipmap, 0, 0, 1024, -saa->win.y); +} + +static int saa_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long argl) +{ + struct saa7146 *saa = file->private_data; + void __user *arg = (void __user *)argl; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability b; + strcpy(b.name, saa->video_dev.name); + b.type = VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | + VID_TYPE_CLIPPING | VID_TYPE_FRAMERAM | + VID_TYPE_SCALES; + b.channels = 1; + b.audios = 1; + b.maxwidth = 768; + b.maxheight = 576; + b.minwidth = 32; + b.minheight = 32; + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + return 0; + } + case VIDIOCGPICT: + { + struct video_picture p = saa->picture; + if (saa->win.depth == 8) + p.palette = VIDEO_PALETTE_HI240; + if (saa->win.depth == 15) + p.palette = VIDEO_PALETTE_RGB555; + if (saa->win.depth == 16) + p.palette = VIDEO_PALETTE_RGB565; + if (saa->win.depth == 24) + p.palette = VIDEO_PALETTE_RGB24; + if (saa->win.depth == 32) + p.palette = VIDEO_PALETTE_RGB32; + if (copy_to_user(arg, &p, sizeof(p))) + return -EFAULT; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture p; + u32 format; + if (copy_from_user(&p, arg, sizeof(p))) + return -EFAULT; + if (p.palette < sizeof(palette2fmt) / sizeof(u32)) { + format = palette2fmt[p.palette]; + saa->win.color_fmt = format; + saawrite(format | 0x60, + SAA7146_CLIP_FORMAT_CTRL); + } + saawrite(((p.brightness & 0xff00) << 16) | + ((p.contrast & 0xfe00) << 7) | + ((p.colour & 0xfe00) >> 9), SAA7146_BCS_CTRL); + saa->picture = p; + /* upload changed registers */ + saawrite(((SAA7146_MC2_UPLD_HPS_H | + SAA7146_MC2_UPLD_HPS_V) << 16) | + SAA7146_MC2_UPLD_HPS_H | + SAA7146_MC2_UPLD_HPS_V, SAA7146_MC2); + return 0; + } + case VIDIOCSWIN: + { + struct video_window vw; + struct video_clip *vcp = NULL; + + if (copy_from_user(&vw, arg, sizeof(vw))) + return -EFAULT; + + /* stop capture */ + if (vw.flags || vw.width < 16 || vw.height < 16) { + saawrite((SAA7146_MC1_TR_E_1 << 16), + SAA7146_MC1); + return -EINVAL; + } + /* 32-bit align start and adjust width */ + if (saa->win.bpp < 4) { + int i = vw.x; + vw.x = (vw.x + 3) & ~3; + i = vw.x - i; + vw.width -= i; + } + saa->win.x = vw.x; + saa->win.y = vw.y; + saa->win.width = vw.width; + if (saa->win.width > 768) + saa->win.width = 768; + saa->win.height = vw.height; + if (CurrentMode == VIDEO_MODE_NTSC) { + if (saa->win.height > 480) + saa->win.height = 480; + } else { + if (saa->win.height > 576) + saa->win.height = 576; + } + + /* stop capture */ + saawrite((SAA7146_MC1_TR_E_1 << 16), SAA7146_MC1); + saa7146_set_winsize(saa); + + /* + * Do any clips. + */ + if (vw.clipcount < 0) { + if (copy_from_user(saa->dmavid2, vw.clips, + VIDEO_CLIPMAP_SIZE)) + return -EFAULT; + } else if (vw.clipcount > 16384) { + return -EINVAL; + } else if (vw.clipcount > 0) { + vcp = vmalloc(sizeof(struct video_clip) * + vw.clipcount); + if (vcp == NULL) + return -ENOMEM; + if (copy_from_user(vcp, vw.clips, + sizeof(struct video_clip) * + vw.clipcount)) { + vfree(vcp); + return -EFAULT; + } + } else /* nothing clipped */ + memset(saa->dmavid2, 0, VIDEO_CLIPMAP_SIZE); + + make_clip_tab(saa, vcp, vw.clipcount); + if (vw.clipcount > 0) + vfree(vcp); + + /* start capture & clip dma if we have an address */ + if ((saa->cap & 3) && saa->win.vidadr != 0) + saawrite(((SAA7146_MC1_TR_E_1 | + SAA7146_MC1_TR_E_2) << 16) | 0xffff, + SAA7146_MC1); + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + vw.x = saa->win.x; + vw.y = saa->win.y; + vw.width = saa->win.width; + vw.height = saa->win.height; + vw.chromakey = 0; + vw.flags = 0; + if (copy_to_user(arg, &vw, sizeof(vw))) + return -EFAULT; + return 0; + } + case VIDIOCCAPTURE: + { + int v; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if (v == 0) { + saa->cap &= ~1; + saawrite((SAA7146_MC1_TR_E_1 << 16), + SAA7146_MC1); + } else { + if (saa->win.vidadr == 0 || saa->win.width == 0 + || saa->win.height == 0) + return -EINVAL; + saa->cap |= 1; + saawrite((SAA7146_MC1_TR_E_1 << 16) | 0xffff, + SAA7146_MC1); + } + return 0; + } + case VIDIOCGFBUF: + { + struct video_buffer v; + v.base = (void *)saa->win.vidadr; + v.height = saa->win.sheight; + v.width = saa->win.swidth; + v.depth = saa->win.depth; + v.bytesperline = saa->win.bpl; + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + + } + case VIDIOCSFBUF: + { + struct video_buffer v; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if (v.depth != 8 && v.depth != 15 && v.depth != 16 && + v.depth != 24 && v.depth != 32 && v.width > 16 && + v.height > 16 && v.bytesperline > 16) + return -EINVAL; + if (v.base) + saa->win.vidadr = (unsigned long)v.base; + saa->win.sheight = v.height; + saa->win.swidth = v.width; + saa->win.bpp = ((v.depth + 7) & 0x38) / 8; + saa->win.depth = v.depth; + saa->win.bpl = v.bytesperline; + + DEBUG(printk("Display at %p is %d by %d, bytedepth %d, " + "bpl %d\n", v.base, v.width, v.height, + saa->win.bpp, saa->win.bpl)); + saa7146_set_winsize(saa); + return 0; + } + case VIDIOCKEY: + { + /* Will be handled higher up .. */ + return 0; + } + + case VIDIOCGAUDIO: + { + struct video_audio v; + v = saa->audio_dev; + v.flags &= ~(VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE); + v.flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME; + strcpy(v.name, "MPEG"); + v.mode = VIDEO_SOUND_STEREO; + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio v; + int i; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + i = (~(v.volume >> 8)) & 0xff; + if (!HaveCS4341) { + if (v.flags & VIDEO_AUDIO_MUTE) + debiwrite(saa, debNormal, + IBM_MP2_FRNT_ATTEN, 0xffff, 2); + if (!(v.flags & VIDEO_AUDIO_MUTE)) + debiwrite(saa, debNormal, + IBM_MP2_FRNT_ATTEN, 0x0000, 2); + if (v.flags & VIDEO_AUDIO_VOLUME) + debiwrite(saa, debNormal, + IBM_MP2_FRNT_ATTEN, + (i << 8) | i, 2); + } else { + if (v.flags & VIDEO_AUDIO_MUTE) + cs4341_setlevel(saa, 0xff, 0xff); + if (!(v.flags & VIDEO_AUDIO_MUTE)) + cs4341_setlevel(saa, 0, 0); + if (v.flags & VIDEO_AUDIO_VOLUME) + cs4341_setlevel(saa, i, i); + } + saa->audio_dev = v; + return 0; + } + + case VIDIOCGUNIT: + { + struct video_unit vu; + vu.video = saa->video_dev.minor; + vu.vbi = VIDEO_NO_UNIT; + vu.radio = VIDEO_NO_UNIT; + vu.audio = VIDEO_NO_UNIT; + vu.teletext = VIDEO_NO_UNIT; + if (copy_to_user(arg, &vu, sizeof(vu))) + return -EFAULT; + return 0; + } + case VIDIOCSPLAYMODE: + { + struct video_play_mode pmode; + if (copy_from_user((void *)&pmode, arg, + sizeof(struct video_play_mode))) + return -EFAULT; + switch (pmode.mode) { + case VID_PLAY_VID_OUT_MODE: + if (pmode.p1 != VIDEO_MODE_NTSC && + pmode.p1 != VIDEO_MODE_PAL) + return -EINVAL; + set_out_format(saa, pmode.p1); + return 0; + case VID_PLAY_GENLOCK: + debiwrite(saa, debNormal, XILINX_CTL0, + pmode.p1 ? 0x8000 : 0x8080, 2); + if (NewCard) + set_genlock_offset(saa, pmode.p2); + return 0; + case VID_PLAY_NORMAL: + debiwrite(saa, debNormal, + IBM_MP2_CHIP_CONTROL, ChipControl, 2); + ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_PAUSE: + /* IBM removed the PAUSE command */ + /* they say use SINGLE_FRAME now */ + case VID_PLAY_SINGLE_FRAME: + ibm_send_command(saa, IBM_MP2_SINGLE_FRAME,0,0); + if (saa->playmode == pmode.mode) { + debiwrite(saa, debNormal, + IBM_MP2_CHIP_CONTROL, + ChipControl, 2); + } + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_FAST_FORWARD: + ibm_send_command(saa, IBM_MP2_FAST_FORWARD,0,0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_SLOW_MOTION: + ibm_send_command(saa, IBM_MP2_SLOW_MOTION, + pmode.p1, 0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_IMMEDIATE_NORMAL: + /* ensure transfers resume */ + debiwrite(saa, debNormal, + IBM_MP2_CHIP_CONTROL, ChipControl, 2); + ibm_send_command(saa, IBM_MP2_IMED_NORM_PLAY, + 0, 0); + saa->playmode = VID_PLAY_NORMAL; + return 0; + case VID_PLAY_SWITCH_CHANNELS: + saa->audhead = saa->audtail = 0; + saa->vidhead = saa->vidtail = 0; + ibm_send_command(saa, IBM_MP2_FREEZE_FRAME,0,1); + ibm_send_command(saa, IBM_MP2_RESET_AUD_RATE, + 0, 1); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, + 0, 2); + ibm_send_command(saa, IBM_MP2_CHANNEL_SWITCH, + 0, 1); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, + ChipControl, 2); + ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); + saa->playmode = VID_PLAY_NORMAL; + return 0; + case VID_PLAY_FREEZE_FRAME: + ibm_send_command(saa, IBM_MP2_FREEZE_FRAME,0,0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_STILL_MODE: + ibm_send_command(saa, IBM_MP2_SET_STILL_MODE, + 0, 0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_MASTER_MODE: + if (pmode.p1 == VID_PLAY_MASTER_NONE) + saa->boardcfg[1] = 0x13; + else if (pmode.p1 == VID_PLAY_MASTER_VIDEO) + saa->boardcfg[1] = 0x23; + else if (pmode.p1 == VID_PLAY_MASTER_AUDIO) + saa->boardcfg[1] = 0x43; + else + return -EINVAL; + debiwrite(saa, debNormal, + IBM_MP2_CHIP_CONTROL, ChipControl, 2); + return 0; + case VID_PLAY_ACTIVE_SCANLINES: + if (CurrentMode == VIDEO_MODE_PAL) { + if (pmode.p1 < 1 || pmode.p2 > 625) + return -EINVAL; + saa->boardcfg[5] = pmode.p1; + saa->boardcfg[55] = (pmode.p1 + + (pmode.p2 / 2) - 1) & 0xff; + } else { + if (pmode.p1 < 4 || pmode.p2 > 525) + return -EINVAL; + saa->boardcfg[4] = pmode.p1; + saa->boardcfg[54] = (pmode.p1 + + (pmode.p2 / 2) - 4) & 0xff; + } + set_out_format(saa, CurrentMode); + case VID_PLAY_RESET: + return do_ibm_reset(saa); + case VID_PLAY_END_MARK: + if (saa->endmarktail < saa->endmarkhead) { + if (saa->endmarkhead - + saa->endmarktail < 2) + return -ENOSPC; + } else if (saa->endmarkhead <=saa->endmarktail){ + if (saa->endmarktail - saa->endmarkhead + > (MAX_MARKS - 2)) + return -ENOSPC; + } else + return -ENOSPC; + saa->endmark[saa->endmarktail] = saa->audtail; + saa->endmarktail++; + if (saa->endmarktail >= MAX_MARKS) + saa->endmarktail = 0; + } + return -EINVAL; + } + case VIDIOCSWRITEMODE: + { + int mode; + if (copy_from_user((void *)&mode, arg, sizeof(int))) + return -EFAULT; + if (mode == VID_WRITE_MPEG_AUD || + mode == VID_WRITE_MPEG_VID || + mode == VID_WRITE_CC || + mode == VID_WRITE_TTX || + mode == VID_WRITE_OSD) { + saa->writemode = mode; + return 0; + } + return -EINVAL; + } + case VIDIOCSMICROCODE: + { + struct video_code ucode; + __u8 *udata; + int i; + if (copy_from_user(&ucode, arg, sizeof(ucode))) + return -EFAULT; + if (ucode.datasize > 65536 || ucode.datasize < 1024 || + strncmp(ucode.loadwhat, "dec", 3)) + return -EINVAL; + if ((udata = vmalloc(ucode.datasize)) == NULL) + return -ENOMEM; + if (copy_from_user(udata, ucode.data, ucode.datasize)) { + vfree(udata); + return -EFAULT; + } + ucode.data = udata; + if (!strncmp(ucode.loadwhat, "decoder.aud", 11) || + !strncmp(ucode.loadwhat, "decoder.vid", 11)) + i = initialize_ibmmpeg2(&ucode); + else + i = initialize_fpga(&ucode); + vfree(udata); + if (i) + return -EINVAL; + return 0; + + } + case VIDIOCGCHAN: /* this makes xawtv happy */ + { + struct video_channel v; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + v.flags = VIDEO_VC_AUDIO; + v.tuners = 0; + v.type = VID_TYPE_MPEG_DECODER; + v.norm = CurrentMode; + strcpy(v.name, "MPEG2"); + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSCHAN: /* this makes xawtv happy */ + { + struct video_channel v; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + /* do nothing */ + return 0; + } + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int saa_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct saa7146 *saa = file->private_data; + printk(KERN_DEBUG "stradis%d: saa_mmap called\n", saa->nr); + return -EINVAL; +} + +static ssize_t saa_read(struct file *file, char __user * buf, + size_t count, loff_t * ppos) +{ + return -EINVAL; +} + +static ssize_t saa_write(struct file *file, const char __user * buf, + size_t count, loff_t * ppos) +{ + struct saa7146 *saa = file->private_data; + unsigned long todo = count; + int blocksize, split; + unsigned long flags; + + while (todo > 0) { + if (saa->writemode == VID_WRITE_MPEG_AUD) { + spin_lock_irqsave(&saa->lock, flags); + if (saa->audhead <= saa->audtail) + blocksize = 65536 - + (saa->audtail - saa->audhead); + else + blocksize = saa->audhead - saa->audtail; + spin_unlock_irqrestore(&saa->lock, flags); + if (blocksize < 16384) { + saawrite(SAA7146_PSR_DEBI_S | + SAA7146_PSR_PIN1, SAA7146_IER); + saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); + /* wait for buffer space to open */ + interruptible_sleep_on(&saa->audq); + } + spin_lock_irqsave(&saa->lock, flags); + if (saa->audhead <= saa->audtail) { + blocksize = 65536 - + (saa->audtail - saa->audhead); + split = 65536 - saa->audtail; + } else { + blocksize = saa->audhead - saa->audtail; + split = 65536; + } + spin_unlock_irqrestore(&saa->lock, flags); + blocksize--; + if (blocksize > todo) + blocksize = todo; + /* double check that we really have space */ + if (!blocksize) + return -ENOSPC; + if (split < blocksize) { + if (copy_from_user(saa->audbuf + + saa->audtail, buf, split)) + return -EFAULT; + buf += split; + todo -= split; + blocksize -= split; + saa->audtail = 0; + } + if (copy_from_user(saa->audbuf + saa->audtail, buf, + blocksize)) + return -EFAULT; + saa->audtail += blocksize; + todo -= blocksize; + buf += blocksize; + saa->audtail &= 0xffff; + } else if (saa->writemode == VID_WRITE_MPEG_VID) { + spin_lock_irqsave(&saa->lock, flags); + if (saa->vidhead <= saa->vidtail) + blocksize = 524288 - + (saa->vidtail - saa->vidhead); + else + blocksize = saa->vidhead - saa->vidtail; + spin_unlock_irqrestore(&saa->lock, flags); + if (blocksize < 65536) { + saawrite(SAA7146_PSR_DEBI_S | + SAA7146_PSR_PIN1, SAA7146_IER); + saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); + /* wait for buffer space to open */ + interruptible_sleep_on(&saa->vidq); + } + spin_lock_irqsave(&saa->lock, flags); + if (saa->vidhead <= saa->vidtail) { + blocksize = 524288 - + (saa->vidtail - saa->vidhead); + split = 524288 - saa->vidtail; + } else { + blocksize = saa->vidhead - saa->vidtail; + split = 524288; + } + spin_unlock_irqrestore(&saa->lock, flags); + blocksize--; + if (blocksize > todo) + blocksize = todo; + /* double check that we really have space */ + if (!blocksize) + return -ENOSPC; + if (split < blocksize) { + if (copy_from_user(saa->vidbuf + + saa->vidtail, buf, split)) + return -EFAULT; + buf += split; + todo -= split; + blocksize -= split; + saa->vidtail = 0; + } + if (copy_from_user(saa->vidbuf + saa->vidtail, buf, + blocksize)) + return -EFAULT; + saa->vidtail += blocksize; + todo -= blocksize; + buf += blocksize; + saa->vidtail &= 0x7ffff; + } else if (saa->writemode == VID_WRITE_OSD) { + if (count > 131072) + return -ENOSPC; + if (copy_from_user(saa->osdbuf, buf, count)) + return -EFAULT; + buf += count; + saa->osdhead = 0; + saa->osdtail = count; + debiwrite(saa, debNormal, IBM_MP2_OSD_ADDR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_OSD_LINK_ADDR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00d, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, + debiread(saa, debNormal, + IBM_MP2_DISP_MODE, 2) | 1, 2); + /* trigger osd data transfer */ + saawrite(SAA7146_PSR_DEBI_S | + SAA7146_PSR_PIN1, SAA7146_IER); + saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); + } + } + return count; +} + +static int saa_open(struct inode *inode, struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct saa7146 *saa = container_of(vdev, struct saa7146, video_dev); + + file->private_data = saa; + + saa->user++; + if (saa->user > 1) + return 0; /* device open already, don't reset */ + saa->writemode = VID_WRITE_MPEG_VID; /* default to video */ + return 0; +} + +static int saa_release(struct inode *inode, struct file *file) +{ + struct saa7146 *saa = file->private_data; + saa->user--; + + if (saa->user > 0) /* still someone using device */ + return 0; + saawrite(0x007f0000, SAA7146_MC1); /* stop all overlay dma */ + return 0; +} + +static struct file_operations saa_fops = { + .owner = THIS_MODULE, + .open = saa_open, + .release = saa_release, + .ioctl = saa_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .read = saa_read, + .llseek = no_llseek, + .write = saa_write, + .mmap = saa_mmap, +}; + +/* template for video_device-structure */ +static struct video_device saa_template = { + .name = "SAA7146A", + .type = VID_TYPE_CAPTURE | VID_TYPE_OVERLAY, + .hardware = VID_HARDWARE_SAA7146, + .fops = &saa_fops, + .minor = -1, +}; + +static int __devinit configure_saa7146(struct pci_dev *pdev, int num) +{ + int retval; + struct saa7146 *saa = pci_get_drvdata(pdev); + + saa->endmarkhead = saa->endmarktail = 0; + saa->win.x = saa->win.y = 0; + saa->win.width = saa->win.cropwidth = 720; + saa->win.height = saa->win.cropheight = 480; + saa->win.cropx = saa->win.cropy = 0; + saa->win.bpp = 2; + saa->win.depth = 16; + saa->win.color_fmt = palette2fmt[VIDEO_PALETTE_RGB565]; + saa->win.bpl = 1024 * saa->win.bpp; + saa->win.swidth = 1024; + saa->win.sheight = 768; + saa->picture.brightness = 32768; + saa->picture.contrast = 38768; + saa->picture.colour = 32768; + saa->cap = 0; + saa->nr = num; + saa->playmode = VID_PLAY_NORMAL; + memset(saa->boardcfg, 0, 64); /* clear board config area */ + saa->saa7146_mem = NULL; + saa->dmavid1 = saa->dmavid2 = saa->dmavid3 = saa->dmaa1in = + saa->dmaa1out = saa->dmaa2in = saa->dmaa2out = + saa->pagevid1 = saa->pagevid2 = saa->pagevid3 = saa->pagea1in = + saa->pagea1out = saa->pagea2in = saa->pagea2out = + saa->pagedebi = saa->dmaRPS1 = saa->dmaRPS2 = saa->pageRPS1 = + saa->pageRPS2 = NULL; + saa->audbuf = saa->vidbuf = saa->osdbuf = saa->dmadebi = NULL; + saa->audhead = saa->vidtail = 0; + + init_waitqueue_head(&saa->i2cq); + init_waitqueue_head(&saa->audq); + init_waitqueue_head(&saa->debiq); + init_waitqueue_head(&saa->vidq); + spin_lock_init(&saa->lock); + + retval = pci_enable_device(pdev); + if (retval) { + dev_err(&pdev->dev, "%d: pci_enable_device failed!\n", num); + goto err; + } + + saa->id = pdev->device; + saa->irq = pdev->irq; + saa->video_dev.minor = -1; + saa->saa7146_adr = pci_resource_start(pdev, 0); + pci_read_config_byte(pdev, PCI_CLASS_REVISION, &saa->revision); + + saa->saa7146_mem = ioremap(saa->saa7146_adr, 0x200); + if (saa->saa7146_mem == NULL) { + dev_err(&pdev->dev, "%d: ioremap failed!\n", num); + retval = -EIO; + goto err; + } + + memcpy(&saa->video_dev, &saa_template, sizeof(saa_template)); + saawrite(0, SAA7146_IER); /* turn off all interrupts */ + + retval = request_irq(saa->irq, saa7146_irq, SA_SHIRQ | SA_INTERRUPT, + "stradis", saa); + if (retval == -EINVAL) + dev_err(&pdev->dev, "%d: Bad irq number or handler\n", num); + else if (retval == -EBUSY) + dev_err(&pdev->dev, "%d: IRQ %ld busy, change your PnP config " + "in BIOS\n", num, saa->irq); + if (retval < 0) + goto errio; + + pci_set_master(pdev); + retval = video_register_device(&saa->video_dev, VFL_TYPE_GRABBER, + video_nr); + if (retval < 0) { + dev_err(&pdev->dev, "%d: error in registering video device!\n", + num); + goto errio; + } + + return 0; +errio: + iounmap(saa->saa7146_mem); +err: + return retval; +} + +static int __devinit init_saa7146(struct pci_dev *pdev) +{ + struct saa7146 *saa = pci_get_drvdata(pdev); + + saa->user = 0; + /* reset the saa7146 */ + saawrite(0xffff0000, SAA7146_MC1); + mdelay(5); + /* enable debi and i2c transfers and pins */ + saawrite(((SAA7146_MC1_EDP | SAA7146_MC1_EI2C | + SAA7146_MC1_TR_E_DEBI) << 16) | 0xffff, SAA7146_MC1); + /* ensure proper state of chip */ + saawrite(0x00000000, SAA7146_PAGE1); + saawrite(0x00f302c0, SAA7146_NUM_LINE_BYTE1); + saawrite(0x00000000, SAA7146_PAGE2); + saawrite(0x01400080, SAA7146_NUM_LINE_BYTE2); + saawrite(0x00000000, SAA7146_DD1_INIT); + saawrite(0x00000000, SAA7146_DD1_STREAM_B); + saawrite(0x00000000, SAA7146_DD1_STREAM_A); + saawrite(0x00000000, SAA7146_BRS_CTRL); + saawrite(0x80400040, SAA7146_BCS_CTRL); + saawrite(0x0000e000 /*| (1<<29) */ , SAA7146_HPS_CTRL); + saawrite(0x00000060, SAA7146_CLIP_FORMAT_CTRL); + saawrite(0x00000000, SAA7146_ACON1); + saawrite(0x00000000, SAA7146_ACON2); + saawrite(0x00000600, SAA7146_I2C_STATUS); + saawrite(((SAA7146_MC2_UPLD_D1_B | SAA7146_MC2_UPLD_D1_A | + SAA7146_MC2_UPLD_BRS | SAA7146_MC2_UPLD_HPS_H | + SAA7146_MC2_UPLD_HPS_V | SAA7146_MC2_UPLD_DMA2 | + SAA7146_MC2_UPLD_DMA1 | SAA7146_MC2_UPLD_I2C) << 16) | 0xffff, + SAA7146_MC2); + /* setup arbitration control registers */ + saawrite(0x1412121a, SAA7146_PCI_BT_V1); + + /* allocate 32k dma buffer + 4k for page table */ + if ((saa->dmadebi = kmalloc(32768 + 4096, GFP_KERNEL)) == NULL) { + dev_err(&pdev->dev, "%d: debi kmalloc failed\n", saa->nr); + goto err; + } +#if 0 + saa->pagedebi = saa->dmadebi + 32768; /* top 4k is for mmu */ + saawrite(virt_to_bus(saa->pagedebi) /*|0x800 */ , SAA7146_DEBI_PAGE); + for (i = 0; i < 12; i++) /* setup mmu page table */ + saa->pagedebi[i] = virt_to_bus((saa->dmadebi + i * 4096)); +#endif + saa->audhead = saa->vidhead = saa->osdhead = 0; + saa->audtail = saa->vidtail = saa->osdtail = 0; + if (saa->vidbuf == NULL && (saa->vidbuf = vmalloc(524288)) == NULL) { + dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); + goto err; + } + if (saa->audbuf == NULL && (saa->audbuf = vmalloc(65536)) == NULL) { + dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); + goto errfree; + } + if (saa->osdbuf == NULL && (saa->osdbuf = vmalloc(131072)) == NULL) { + dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); + goto errfree; + } + /* allocate 81920 byte buffer for clipping */ + if ((saa->dmavid2 = kzalloc(VIDEO_CLIPMAP_SIZE, GFP_KERNEL)) == NULL) { + dev_err(&pdev->dev, "%d: clip kmalloc failed\n", saa->nr); + goto errfree; + } + /* setup clipping registers */ + saawrite(virt_to_bus(saa->dmavid2), SAA7146_BASE_EVEN2); + saawrite(virt_to_bus(saa->dmavid2) + 128, SAA7146_BASE_ODD2); + saawrite(virt_to_bus(saa->dmavid2) + VIDEO_CLIPMAP_SIZE, + SAA7146_PROT_ADDR2); + saawrite(256, SAA7146_PITCH2); + saawrite(4, SAA7146_PAGE2); /* dma direction: read, no byteswap */ + saawrite(((SAA7146_MC2_UPLD_DMA2) << 16) | SAA7146_MC2_UPLD_DMA2, + SAA7146_MC2); + I2CBusScan(saa); + + return 0; +errfree: + vfree(saa->osdbuf); + vfree(saa->audbuf); + vfree(saa->vidbuf); + saa->audbuf = saa->osdbuf = saa->vidbuf = NULL; +err: + return -ENOMEM; +} + +static void stradis_release_saa(struct pci_dev *pdev) +{ + u8 command; + struct saa7146 *saa = pci_get_drvdata(pdev); + + /* turn off all capturing, DMA and IRQs */ + saawrite(0xffff0000, SAA7146_MC1); /* reset chip */ + saawrite(0, SAA7146_MC2); + saawrite(0, SAA7146_IER); + saawrite(0xffffffffUL, SAA7146_ISR); + + /* disable PCI bus-mastering */ + pci_read_config_byte(pdev, PCI_COMMAND, &command); + command &= ~PCI_COMMAND_MASTER; + pci_write_config_byte(pdev, PCI_COMMAND, command); + + /* unmap and free memory */ + saa->audhead = saa->audtail = saa->osdhead = 0; + saa->vidhead = saa->vidtail = saa->osdtail = 0; + vfree(saa->vidbuf); + vfree(saa->audbuf); + vfree(saa->osdbuf); + kfree(saa->dmavid2); + saa->audbuf = saa->vidbuf = saa->osdbuf = NULL; + saa->dmavid2 = NULL; + kfree(saa->dmadebi); + kfree(saa->dmavid1); + kfree(saa->dmavid3); + kfree(saa->dmaa1in); + kfree(saa->dmaa1out); + kfree(saa->dmaa2in); + kfree(saa->dmaa2out); + kfree(saa->dmaRPS1); + kfree(saa->dmaRPS2); + free_irq(saa->irq, saa); + if (saa->saa7146_mem) + iounmap(saa->saa7146_mem); + if (saa->video_dev.minor != -1) + video_unregister_device(&saa->video_dev); +} + +static int __devinit stradis_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int retval = -EINVAL; + + if (saa_num >= SAA7146_MAX) + goto err; + + if (!pdev->subsystem_vendor) + dev_info(&pdev->dev, "%d: rev1 decoder\n", saa_num); + else + dev_info(&pdev->dev, "%d: SDM2xx found\n", saa_num); + + pci_set_drvdata(pdev, &saa7146s[saa_num]); + + retval = configure_saa7146(pdev, saa_num); + if (retval) { + dev_err(&pdev->dev, "%d: error in configuring\n", saa_num); + goto err; + } + + if (init_saa7146(pdev) < 0) { + dev_err(&pdev->dev, "%d: error in initialization\n", saa_num); + retval = -EIO; + goto errrel; + } + + saa_num++; + + return 0; +errrel: + stradis_release_saa(pdev); +err: + return retval; +} + +static void __devexit stradis_remove(struct pci_dev *pdev) +{ + stradis_release_saa(pdev); +} + +static struct pci_device_id stradis_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146) }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, stradis_pci_tbl); + +static struct pci_driver stradis_driver = { + .name = "stradis", + .id_table = stradis_pci_tbl, + .probe = stradis_probe, + .remove = __devexit_p(stradis_remove) +}; + +int __init stradis_init(void) +{ + int retval; + + saa_num = 0; + + retval = pci_register_driver(&stradis_driver); + if (retval) + printk(KERN_ERR "stradis: Unable to register pci driver.\n"); + + return retval; +} + +void __exit stradis_exit(void) +{ + pci_unregister_driver(&stradis_driver); + printk(KERN_INFO "stradis: module cleanup complete\n"); +} + +module_init(stradis_init); +module_exit(stradis_exit); diff --git a/linux/drivers/media/video/stv680.c b/linux/drivers/media/video/stv680.c new file mode 100644 index 000000000..b33ed6f73 --- /dev/null +++ b/linux/drivers/media/video/stv680.c @@ -0,0 +1,1509 @@ +/* + * STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net) + * + * Thanks to STMicroelectronics for information on the usb commands, and + * to Steve Miller at STM for his help and encouragement while I was + * writing this driver. + * + * This driver is based heavily on the + * Endpoints (formerly known as AOX) se401 USB Camera Driver + * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org) + * + * Still somewhat based on the Linux ov511 driver. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History: + * ver 0.1 October, 2001. Initial attempt. + * + * ver 0.2 November, 2001. Fixed asbility to resize, added brightness + * function, made more stable (?) + * + * ver 0.21 Nov, 2001. Added gamma correction and white balance, + * due to Alexander Schwartz. Still trying to + * improve stablility. Moved stuff into stv680.h + * + * ver 0.22 Nov, 2001. Added sharpen function (by Michael Sweet, + * mike@easysw.com) from GIMP, also used in pencam. + * Simple, fast, good integer math routine. + * + * ver 0.23 Dec, 2001 (gkh) + * Took out sharpen function, ran code through + * Lindent, and did other minor tweaks to get + * things to work properly with 2.5.1 + * + * ver 0.24 Jan, 2002 (kjs) + * Fixed the problem with webcam crashing after + * two pictures. Changed the way pic is halved to + * improve quality. Got rid of green line around + * frame. Fix brightness reset when changing size + * bug. Adjusted gamma filters slightly. + * + * ver 0.25 Jan, 2002 (kjs) + * Fixed a bug in which the driver sometimes attempted + * to set to a non-supported size. This allowed + * gnomemeeting to work. + * Fixed proc entry removal bug. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "compat.h" +#include +#include +#include + +#include "stv680.h" + +static int video_nr = -1; +static int swapRGB = 0; /* default for auto sleect */ +static int swapRGB_on = 0; /* default to allow auto select; -1=swap never, +1= swap always */ + +static unsigned int debug = 0; + +#define PDEBUG(level, fmt, args...) \ + do { \ + if (debug >= level) \ + info("[%s:%d] " fmt, __FUNCTION__, __LINE__ , ## args); \ + } while (0) + + +/* + * Version Information + */ +#define DRIVER_VERSION "v0.25" +#define DRIVER_AUTHOR "Kevin Sisson " +#define DRIVER_DESC "STV0680 USB Camera Driver" + +MODULE_AUTHOR (DRIVER_AUTHOR); +MODULE_DESCRIPTION (DRIVER_DESC); +MODULE_LICENSE ("GPL"); +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC (debug, "Debug enabled or not"); +module_param(swapRGB_on, int, 0); +MODULE_PARM_DESC (swapRGB_on, "Red/blue swap: 1=always, 0=auto, -1=never"); +module_param(video_nr, int, 0); + +/******************************************************************** + * + * Memory management + * + * This is a shameless copy from the USB-cpia driver (linux kernel + * version 2.3.29 or so, I have no idea what this code actually does ;). + * Actually it seems to be a copy of a shameless copy of the bttv-driver. + * Or that is a copy of a shameless copy of ... (To the powers: is there + * no generic kernel-function to do this sort of stuff?) + * + * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says + * there will be one, but apparentely not yet -jerdfelt + * + * So I copied it again for the ov511 driver -claudio + * + * Same for the se401 driver -Jeroen + * + * And the STV0680 driver - Kevin + ********************************************************************/ +static void *rvmalloc (unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32 (size); + if (!mem) + return NULL; + + memset (mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + return mem; +} + +static void rvfree (void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree (mem); +} + + +/********************************************************************* + * pencam read/write functions + ********************************************************************/ + +static int stv_sndctrl (int set, struct usb_stv *stv680, unsigned short req, unsigned short value, unsigned char *buffer, int size) +{ + int ret = -1; + + switch (set) { + case 0: /* 0xc1 */ + ret = usb_control_msg (stv680->udev, + usb_rcvctrlpipe (stv680->udev, 0), + req, + (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT), + value, 0, buffer, size, PENCAM_TIMEOUT); + break; + + case 1: /* 0x41 */ + ret = usb_control_msg (stv680->udev, + usb_sndctrlpipe (stv680->udev, 0), + req, + (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT), + value, 0, buffer, size, PENCAM_TIMEOUT); + break; + + case 2: /* 0x80 */ + ret = usb_control_msg (stv680->udev, + usb_rcvctrlpipe (stv680->udev, 0), + req, + (USB_DIR_IN | USB_RECIP_DEVICE), + value, 0, buffer, size, PENCAM_TIMEOUT); + break; + + case 3: /* 0x40 */ + ret = usb_control_msg (stv680->udev, + usb_sndctrlpipe (stv680->udev, 0), + req, + (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE), + value, 0, buffer, size, PENCAM_TIMEOUT); + break; + + } + if ((ret < 0) && (req != 0x0a)) { + PDEBUG (1, "STV(e): usb_control_msg error %i, request = 0x%x, error = %i", set, req, ret); + } + return ret; +} + +static int stv_set_config (struct usb_stv *dev, int configuration, int interface, int alternate) +{ + + if (configuration != dev->udev->actconfig->desc.bConfigurationValue + || usb_reset_configuration (dev->udev) < 0) { + PDEBUG (1, "STV(e): FAILED to reset configuration %i", configuration); + return -1; + } + if (usb_set_interface (dev->udev, interface, alternate) < 0) { + PDEBUG (1, "STV(e): FAILED to set alternate interface %i", alternate); + return -1; + } + return 0; +} + +static int stv_stop_video (struct usb_stv *dev) +{ + int i; + unsigned char *buf; + + buf = kmalloc (40, GFP_KERNEL); + if (buf == NULL) { + PDEBUG (0, "STV(e): Out of (small buf) memory"); + return -1; + } + + /* this is a high priority command; it stops all lower order commands */ + if ((i = stv_sndctrl (1, dev, 0x04, 0x0000, buf, 0x0)) < 0) { + i = stv_sndctrl (0, dev, 0x80, 0, buf, 0x02); /* Get Last Error; 2 = busy */ + PDEBUG (1, "STV(i): last error: %i, command = 0x%x", buf[0], buf[1]); + } else { + PDEBUG (1, "STV(i): Camera reset to idle mode."); + } + + if ((i = stv_set_config (dev, 1, 0, 0)) < 0) + PDEBUG (1, "STV(e): Reset config during exit failed"); + + /* get current mode */ + buf[0] = 0xf0; + if ((i = stv_sndctrl (0, dev, 0x87, 0, buf, 0x08)) != 0x08) /* get mode */ + PDEBUG (0, "STV(e): Stop_video: problem setting original mode"); + if (dev->origMode != buf[0]) { + memset (buf, 0, 8); + buf[0] = (unsigned char) dev->origMode; + if ((i = stv_sndctrl (3, dev, 0x07, 0x0100, buf, 0x08)) != 0x08) { + PDEBUG (0, "STV(e): Stop_video: Set_Camera_Mode failed"); + i = -1; + } + buf[0] = 0xf0; + i = stv_sndctrl (0, dev, 0x87, 0, buf, 0x08); + if ((i != 0x08) || (buf[0] != dev->origMode)) { + PDEBUG (0, "STV(e): camera NOT set to original resolution."); + i = -1; + } else + PDEBUG (0, "STV(i): Camera set to original resolution"); + } + /* origMode */ + kfree(buf); + return i; +} + +static int stv_set_video_mode (struct usb_stv *dev) +{ + int i, stop_video = 1; + unsigned char *buf; + + buf = kmalloc (40, GFP_KERNEL); + if (buf == NULL) { + PDEBUG (0, "STV(e): Out of (small buf) memory"); + return -1; + } + + if ((i = stv_set_config (dev, 1, 0, 0)) < 0) { + kfree(buf); + return i; + } + + i = stv_sndctrl (2, dev, 0x06, 0x0100, buf, 0x12); + if (!(i > 0) && (buf[8] == 0x53) && (buf[9] == 0x05)) { + PDEBUG (1, "STV(e): Could not get descriptor 0100."); + goto error; + } + + /* set alternate interface 1 */ + if ((i = stv_set_config (dev, 1, 0, 1)) < 0) + goto error; + + if ((i = stv_sndctrl (0, dev, 0x85, 0, buf, 0x10)) != 0x10) + goto error; + PDEBUG (1, "STV(i): Setting video mode."); + /* Switch to Video mode: 0x0100 = VGA (640x480), 0x0000 = CIF (352x288) 0x0300 = QVGA (320x240) */ + if ((i = stv_sndctrl (1, dev, 0x09, dev->VideoMode, buf, 0x0)) < 0) { + stop_video = 0; + goto error; + } + goto exit; + +error: + kfree(buf); + if (stop_video == 1) + stv_stop_video (dev); + return -1; + +exit: + kfree(buf); + return 0; +} + +static int stv_init (struct usb_stv *stv680) +{ + int i = 0; + unsigned char *buffer; + unsigned long int bufsize; + + buffer = kzalloc (40, GFP_KERNEL); + if (buffer == NULL) { + PDEBUG (0, "STV(e): Out of (small buf) memory"); + return -1; + } + udelay (100); + + /* set config 1, interface 0, alternate 0 */ + if ((i = stv_set_config (stv680, 1, 0, 0)) < 0) { + kfree(buffer); + PDEBUG (0, "STV(e): set config 1,0,0 failed"); + return -1; + } + /* ping camera to be sure STV0680 is present */ + if ((i = stv_sndctrl (0, stv680, 0x88, 0x5678, buffer, 0x02)) != 0x02) + goto error; + if ((buffer[0] != 0x56) || (buffer[1] != 0x78)) { + PDEBUG (1, "STV(e): camera ping failed!!"); + goto error; + } + + /* get camera descriptor */ + if ((i = stv_sndctrl (2, stv680, 0x06, 0x0200, buffer, 0x09)) != 0x09) + goto error; + i = stv_sndctrl (2, stv680, 0x06, 0x0200, buffer, 0x22); + if (!(i >= 0) && (buffer[7] == 0xa0) && (buffer[8] == 0x23)) { + PDEBUG (1, "STV(e): Could not get descriptor 0200."); + goto error; + } + if ((i = stv_sndctrl (0, stv680, 0x8a, 0, buffer, 0x02)) != 0x02) + goto error; + if ((i = stv_sndctrl (0, stv680, 0x8b, 0, buffer, 0x24)) != 0x24) + goto error; + if ((i = stv_sndctrl (0, stv680, 0x85, 0, buffer, 0x10)) != 0x10) + goto error; + + stv680->SupportedModes = buffer[7]; + i = stv680->SupportedModes; + stv680->CIF = 0; + stv680->VGA = 0; + stv680->QVGA = 0; + if (i & 1) + stv680->CIF = 1; + if (i & 2) + stv680->VGA = 1; + if (i & 8) + stv680->QVGA = 1; + if (stv680->SupportedModes == 0) { + PDEBUG (0, "STV(e): There are NO supported STV680 modes!!"); + i = -1; + goto error; + } else { + if (stv680->CIF) + PDEBUG (0, "STV(i): CIF is supported"); + if (stv680->QVGA) + PDEBUG (0, "STV(i): QVGA is supported"); + } + /* FW rev, ASIC rev, sensor ID */ + PDEBUG (1, "STV(i): Firmware rev is %i.%i", buffer[0], buffer[1]); + PDEBUG (1, "STV(i): ASIC rev is %i.%i", buffer[2], buffer[3]); + PDEBUG (1, "STV(i): Sensor ID is %i", (buffer[4]*16) + (buffer[5]>>4)); + + /* set alternate interface 1 */ + if ((i = stv_set_config (stv680, 1, 0, 1)) < 0) + goto error; + + if ((i = stv_sndctrl (0, stv680, 0x85, 0, buffer, 0x10)) != 0x10) + goto error; + if ((i = stv_sndctrl (0, stv680, 0x8d, 0, buffer, 0x08)) != 0x08) + goto error; + i = buffer[3]; + PDEBUG (0, "STV(i): Camera has %i pictures.", i); + + /* get current mode */ + if ((i = stv_sndctrl (0, stv680, 0x87, 0, buffer, 0x08)) != 0x08) + goto error; + stv680->origMode = buffer[0]; /* 01 = VGA, 03 = QVGA, 00 = CIF */ + + /* This will attemp CIF mode, if supported. If not, set to QVGA */ + memset (buffer, 0, 8); + if (stv680->CIF) + buffer[0] = 0x00; + else if (stv680->QVGA) + buffer[0] = 0x03; + if ((i = stv_sndctrl (3, stv680, 0x07, 0x0100, buffer, 0x08)) != 0x08) { + PDEBUG (0, "STV(i): Set_Camera_Mode failed"); + i = -1; + goto error; + } + buffer[0] = 0xf0; + stv_sndctrl (0, stv680, 0x87, 0, buffer, 0x08); + if (((stv680->CIF == 1) && (buffer[0] != 0x00)) || ((stv680->QVGA == 1) && (buffer[0] != 0x03))) { + PDEBUG (0, "STV(e): Error setting camera video mode!"); + i = -1; + goto error; + } else { + if (buffer[0] == 0) { + stv680->VideoMode = 0x0000; + PDEBUG (0, "STV(i): Video Mode set to CIF"); + } + if (buffer[0] == 0x03) { + stv680->VideoMode = 0x0300; + PDEBUG (0, "STV(i): Video Mode set to QVGA"); + } + } + if ((i = stv_sndctrl (0, stv680, 0x8f, 0, buffer, 0x10)) != 0x10) + goto error; + bufsize = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | (buffer[3]); + stv680->cwidth = (buffer[4] << 8) | (buffer[5]); /* ->camera = 322, 356, 644 */ + stv680->cheight = (buffer[6] << 8) | (buffer[7]); /* ->camera = 242, 292, 484 */ + stv680->origGain = buffer[12]; + + goto exit; + +error: + i = stv_sndctrl (0, stv680, 0x80, 0, buffer, 0x02); /* Get Last Error */ + PDEBUG (1, "STV(i): last error: %i, command = 0x%x", buffer[0], buffer[1]); + kfree(buffer); + return -1; + +exit: + kfree(buffer); + + /* video = 320x240, 352x288 */ + if (stv680->CIF == 1) { + stv680->maxwidth = 352; + stv680->maxheight = 288; + stv680->vwidth = 352; + stv680->vheight = 288; + } + if (stv680->QVGA == 1) { + stv680->maxwidth = 320; + stv680->maxheight = 240; + stv680->vwidth = 320; + stv680->vheight = 240; + } + + stv680->rawbufsize = bufsize; /* must be ./. by 8 */ + stv680->maxframesize = bufsize * 3; /* RGB size */ + PDEBUG (2, "STV(i): cwidth = %i, cheight = %i", stv680->cwidth, stv680->cheight); + PDEBUG (1, "STV(i): width = %i, height = %i, rawbufsize = %li", stv680->vwidth, stv680->vheight, stv680->rawbufsize); + + /* some default values */ + stv680->bulk_in_endpointAddr = 0x82; + stv680->dropped = 0; + stv680->error = 0; + stv680->framecount = 0; + stv680->readcount = 0; + stv680->streaming = 0; + /* bright, white, colour, hue, contrast are set by software, not in stv0680 */ + stv680->brightness = 32767; + stv680->chgbright = 0; + stv680->whiteness = 0; /* only for greyscale */ + stv680->colour = 32767; + stv680->contrast = 32767; + stv680->hue = 32767; + stv680->palette = STV_VIDEO_PALETTE; + stv680->depth = 24; /* rgb24 bits */ + if ((swapRGB_on == 0) && (swapRGB == 0)) + PDEBUG (1, "STV(i): swapRGB is (auto) OFF"); + else if ((swapRGB_on == 0) && (swapRGB == 1)) + PDEBUG (1, "STV(i): swapRGB is (auto) ON"); + else if (swapRGB_on == 1) + PDEBUG (1, "STV(i): swapRGB is (forced) ON"); + else if (swapRGB_on == -1) + PDEBUG (1, "STV(i): swapRGB is (forced) OFF"); + + if (stv_set_video_mode (stv680) < 0) { + PDEBUG (0, "STV(e): Could not set video mode in stv_init"); + return -1; + } + + return 0; +} + +/***************** last of pencam routines *******************/ + +/**************************************************************************** + * sysfs + ***************************************************************************/ +#define stv680_file(name, variable, field) \ +static ssize_t show_##name(struct class_device *class_dev, char *buf) \ +{ \ + struct video_device *vdev = to_video_device(class_dev); \ + struct usb_stv *stv = video_get_drvdata(vdev); \ + return sprintf(buf, field, stv->variable); \ +} \ +static CLASS_DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); + +stv680_file(model, camera_name, "%s\n"); +stv680_file(in_use, user, "%d\n"); +stv680_file(streaming, streaming, "%d\n"); +stv680_file(palette, palette, "%i\n"); +stv680_file(frames_total, readcount, "%d\n"); +stv680_file(frames_read, framecount, "%d\n"); +stv680_file(packets_dropped, dropped, "%d\n"); +stv680_file(decoding_errors, error, "%d\n"); + +static void stv680_create_sysfs_files(struct video_device *vdev) +{ + video_device_create_file(vdev, &class_device_attr_model); + video_device_create_file(vdev, &class_device_attr_in_use); + video_device_create_file(vdev, &class_device_attr_streaming); + video_device_create_file(vdev, &class_device_attr_palette); + video_device_create_file(vdev, &class_device_attr_frames_total); + video_device_create_file(vdev, &class_device_attr_frames_read); + video_device_create_file(vdev, &class_device_attr_packets_dropped); + video_device_create_file(vdev, &class_device_attr_decoding_errors); +} + +static void stv680_remove_sysfs_files(struct video_device *vdev) +{ + video_device_remove_file(vdev, &class_device_attr_model); + video_device_remove_file(vdev, &class_device_attr_in_use); + video_device_remove_file(vdev, &class_device_attr_streaming); + video_device_remove_file(vdev, &class_device_attr_palette); + video_device_remove_file(vdev, &class_device_attr_frames_total); + video_device_remove_file(vdev, &class_device_attr_frames_read); + video_device_remove_file(vdev, &class_device_attr_packets_dropped); + video_device_remove_file(vdev, &class_device_attr_decoding_errors); +} + +/******************************************************************** + * Camera control + *******************************************************************/ + +static int stv680_get_pict (struct usb_stv *stv680, struct video_picture *p) +{ + /* This sets values for v4l interface. max/min = 65535/0 */ + + p->brightness = stv680->brightness; + p->whiteness = stv680->whiteness; /* greyscale */ + p->colour = stv680->colour; + p->contrast = stv680->contrast; + p->hue = stv680->hue; + p->palette = stv680->palette; + p->depth = stv680->depth; + return 0; +} + +static int stv680_set_pict (struct usb_stv *stv680, struct video_picture *p) +{ + /* See above stv680_get_pict */ + + if (p->palette != STV_VIDEO_PALETTE) { + PDEBUG (2, "STV(e): Palette set error in _set_pic"); + return 1; + } + + if (stv680->brightness != p->brightness) { + stv680->chgbright = 1; + stv680->brightness = p->brightness; + } + + stv680->whiteness = p->whiteness; /* greyscale */ + stv680->colour = p->colour; + stv680->contrast = p->contrast; + stv680->hue = p->hue; + stv680->palette = p->palette; + stv680->depth = p->depth; + + return 0; +} + +static void stv680_video_irq (struct urb *urb, struct pt_regs *regs) +{ + struct usb_stv *stv680 = urb->context; + int length = urb->actual_length; + + if (length < stv680->rawbufsize) + PDEBUG (2, "STV(i): Lost data in transfer: exp %li, got %i", stv680->rawbufsize, length); + + /* ohoh... */ + if (!stv680->streaming) + return; + + if (!stv680->udev) { + PDEBUG (0, "STV(e): device vapourished in video_irq"); + return; + } + + /* 0 sized packets happen if we are to fast, but sometimes the camera + keeps sending them forever... + */ + if (length && !urb->status) { + stv680->nullpackets = 0; + switch (stv680->scratch[stv680->scratch_next].state) { + case BUFFER_READY: + case BUFFER_BUSY: + stv680->dropped++; + break; + + case BUFFER_UNUSED: + memcpy (stv680->scratch[stv680->scratch_next].data, + (unsigned char *) urb->transfer_buffer, length); + stv680->scratch[stv680->scratch_next].state = BUFFER_READY; + stv680->scratch[stv680->scratch_next].length = length; + if (waitqueue_active (&stv680->wq)) { + wake_up_interruptible (&stv680->wq); + } + stv680->scratch_overflow = 0; + stv680->scratch_next++; + if (stv680->scratch_next >= STV680_NUMSCRATCH) + stv680->scratch_next = 0; + break; + } /* switch */ + } else { + stv680->nullpackets++; + if (stv680->nullpackets > STV680_MAX_NULLPACKETS) { + if (waitqueue_active (&stv680->wq)) { + wake_up_interruptible (&stv680->wq); + } + } + } /* if - else */ + + /* Resubmit urb for new data */ + urb->status = 0; + urb->dev = stv680->udev; + if (usb_submit_urb (urb, GFP_ATOMIC)) + PDEBUG (0, "STV(e): urb burned down in video irq"); + return; +} /* _video_irq */ + +static int stv680_start_stream (struct usb_stv *stv680) +{ + struct urb *urb; + int err = 0, i; + + stv680->streaming = 1; + + /* Do some memory allocation */ + for (i = 0; i < STV680_NUMFRAMES; i++) { + stv680->frame[i].data = stv680->fbuf + i * stv680->maxframesize; + stv680->frame[i].curpix = 0; + } + /* packet size = 4096 */ + for (i = 0; i < STV680_NUMSBUF; i++) { + stv680->sbuf[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL); + if (stv680->sbuf[i].data == NULL) { + PDEBUG (0, "STV(e): Could not kmalloc raw data buffer %i", i); + return -1; + } + } + + stv680->scratch_next = 0; + stv680->scratch_use = 0; + stv680->scratch_overflow = 0; + for (i = 0; i < STV680_NUMSCRATCH; i++) { + stv680->scratch[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL); + if (stv680->scratch[i].data == NULL) { + PDEBUG (0, "STV(e): Could not kmalloc raw scratch buffer %i", i); + return -1; + } + stv680->scratch[i].state = BUFFER_UNUSED; + } + + for (i = 0; i < STV680_NUMSBUF; i++) { + urb = usb_alloc_urb (0, GFP_KERNEL); + if (!urb) + return -ENOMEM; + + /* sbuf is urb->transfer_buffer, later gets memcpyed to scratch */ + usb_fill_bulk_urb (urb, stv680->udev, + usb_rcvbulkpipe (stv680->udev, stv680->bulk_in_endpointAddr), + stv680->sbuf[i].data, stv680->rawbufsize, + stv680_video_irq, stv680); + stv680->urb[i] = urb; + err = usb_submit_urb (stv680->urb[i], GFP_KERNEL); + if (err) + PDEBUG (0, "STV(e): urb burned down in start stream"); + } /* i STV680_NUMSBUF */ + + stv680->framecount = 0; + return 0; +} + +static int stv680_stop_stream (struct usb_stv *stv680) +{ + int i; + + if (!stv680->streaming || !stv680->udev) + return 1; + + stv680->streaming = 0; + + for (i = 0; i < STV680_NUMSBUF; i++) + if (stv680->urb[i]) { + usb_kill_urb (stv680->urb[i]); + usb_free_urb (stv680->urb[i]); + stv680->urb[i] = NULL; + kfree(stv680->sbuf[i].data); + } + for (i = 0; i < STV680_NUMSCRATCH; i++) { + kfree(stv680->scratch[i].data); + stv680->scratch[i].data = NULL; + } + + return 0; +} + +static int stv680_set_size (struct usb_stv *stv680, int width, int height) +{ + int wasstreaming = stv680->streaming; + + /* Check to see if we need to change */ + if ((stv680->vwidth == width) && (stv680->vheight == height)) + return 0; + + PDEBUG (1, "STV(i): size request for %i x %i", width, height); + /* Check for a valid mode */ + if ((!width || !height) || ((width & 1) || (height & 1))) { + PDEBUG (1, "STV(e): set_size error: request: v.width = %i, v.height = %i actual: stv.width = %i, stv.height = %i", width, height, stv680->vwidth, stv680->vheight); + return 1; + } + + if ((width < (stv680->maxwidth / 2)) || (height < (stv680->maxheight / 2))) { + width = stv680->maxwidth / 2; + height = stv680->maxheight / 2; + } else if ((width >= 158) && (width <= 166) && (stv680->QVGA == 1)) { + width = 160; + height = 120; + } else if ((width >= 172) && (width <= 180) && (stv680->CIF == 1)) { + width = 176; + height = 144; + } else if ((width >= 318) && (width <= 350) && (stv680->QVGA == 1)) { + width = 320; + height = 240; + } else if ((width >= 350) && (width <= 358) && (stv680->CIF == 1)) { + width = 352; + height = 288; + } else { + PDEBUG (1, "STV(e): request for non-supported size: request: v.width = %i, v.height = %i actual: stv.width = %i, stv.height = %i", width, height, stv680->vwidth, stv680->vheight); + return 1; + } + + /* Stop a current stream and start it again at the new size */ + if (wasstreaming) + stv680_stop_stream (stv680); + stv680->vwidth = width; + stv680->vheight = height; + PDEBUG (1, "STV(i): size set to %i x %i", stv680->vwidth, stv680->vheight); + if (wasstreaming) + stv680_start_stream (stv680); + + return 0; +} + +/********************************************************************** + * Video Decoding + **********************************************************************/ + +/******* routines from the pencam program; hey, they work! ********/ + +/* + * STV0680 Vision Camera Chipset Driver + * Copyright (C) 2000 Adam Harrison +*/ + +#define RED 0 +#define GREEN 1 +#define BLUE 2 +#define AD(x, y, w) (((y)*(w)+(x))*3) + +static void bayer_unshuffle (struct usb_stv *stv680, struct stv680_scratch *buffer) +{ + int x, y, i; + int w = stv680->cwidth; + int vw = stv680->cwidth, vh = stv680->cheight; + unsigned int p = 0; + int colour = 0, bayer = 0; + unsigned char *raw = buffer->data; + struct stv680_frame *frame = &stv680->frame[stv680->curframe]; + unsigned char *output = frame->data; + unsigned char *temp = frame->data; + int offset = buffer->offset; + + if (frame->curpix == 0) { + if (frame->grabstate == FRAME_READY) { + frame->grabstate = FRAME_GRABBING; + } + } + if (offset != frame->curpix) { /* Regard frame as lost :( */ + frame->curpix = 0; + stv680->error++; + return; + } + + if ((stv680->vwidth == 320) || (stv680->vwidth == 160)) { + vw = 320; + vh = 240; + } + if ((stv680->vwidth == 352) || (stv680->vwidth == 176)) { + vw = 352; + vh = 288; + } + + memset (output, 0, 3 * vw * vh); /* clear output matrix. */ + + for (y = 0; y < vh; y++) { + for (x = 0; x < vw; x++) { + if (x & 1) + p = *(raw + y * w + (x >> 1)); + else + p = *(raw + y * w + (x >> 1) + (w >> 1)); + + if (y & 1) + bayer = 2; + else + bayer = 0; + if (x & 1) + bayer++; + + switch (bayer) { + case 0: + case 3: + colour = 1; + break; + case 1: + colour = 0; + break; + case 2: + colour = 2; + break; + } + i = (y * vw + x) * 3; + *(output + i + colour) = (unsigned char) p; + } /* for x */ + + } /* for y */ + + /****** gamma correction plus hardcoded white balance */ + /* Thanks to Alexander Schwartx for this code. + Correction values red[], green[], blue[], are generated by + (pow(i/256.0, GAMMA)*255.0)*white balanceRGB where GAMMA=0.55, 1> 1; + *(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x, y - 1, vw) + RED) + (int) *(output + AD (x, y + 1, vw) + RED)) >> 1; + break; + + case 1: /* blue. green lrtb, red diagonals */ + *(output + AD (x, y, vw) + GREEN) = ((int) *(output + AD (x - 1, y, vw) + GREEN) + (int) *(output + AD (x + 1, y, vw) + GREEN) + (int) *(output + AD (x, y - 1, vw) + GREEN) + (int) *(output + AD (x, y + 1, vw) + GREEN)) >> 2; + *(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x - 1, y - 1, vw) + RED) + (int) *(output + AD (x - 1, y + 1, vw) + RED) + (int) *(output + AD (x + 1, y - 1, vw) + RED) + (int) *(output + AD (x + 1, y + 1, vw) + RED)) >> 2; + break; + + case 2: /* red. green lrtb, blue diagonals */ + *(output + AD (x, y, vw) + GREEN) = ((int) *(output + AD (x - 1, y, vw) + GREEN) + (int) *(output + AD (x + 1, y, vw) + GREEN) + (int) *(output + AD (x, y - 1, vw) + GREEN) + (int) *(output + AD (x, y + 1, vw) + GREEN)) >> 2; + *(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x - 1, y - 1, vw) + BLUE) + (int) *(output + AD (x + 1, y - 1, vw) + BLUE) + (int) *(output + AD (x - 1, y + 1, vw) + BLUE) + (int) *(output + AD (x + 1, y + 1, vw) + BLUE)) >> 2; + break; + + case 3: /* green. red lr, blue tb */ + *(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x - 1, y, vw) + RED) + (int) *(output + AD (x + 1, y, vw) + RED)) >> 1; + *(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x, y - 1, vw) + BLUE) + (int) *(output + AD (x, y + 1, vw) + BLUE)) >> 1; + break; + } /* switch */ + } /* for x */ + } /* for y - end demosaic */ + + /* fix top and bottom row, left and right side */ + i = vw * 3; + memcpy (output, (output + i), i); + memcpy ((output + (vh * i)), (output + ((vh - 1) * i)), i); + for (y = 0; y < vh; y++) { + i = y * vw * 3; + memcpy ((output + i), (output + i + 3), 3); + memcpy ((output + i + (vw * 3)), (output + i + (vw - 1) * 3), 3); + } + + /* process all raw data, then trim to size if necessary */ + if ((stv680->vwidth == 160) || (stv680->vwidth == 176)) { + i = 0; + for (y = 0; y < vh; y++) { + if (!(y & 1)) { + for (x = 0; x < vw; x++) { + p = (y * vw + x) * 3; + if (!(x & 1)) { + *(output + i) = *(output + p); + *(output + i + 1) = *(output + p + 1); + *(output + i + 2) = *(output + p + 2); + i += 3; + } + } /* for x */ + } + } /* for y */ + } + /* reset to proper width */ + if ((stv680->vwidth == 160)) { + vw = 160; + vh = 120; + } + if ((stv680->vwidth == 176)) { + vw = 176; + vh = 144; + } + + /* output is RGB; some programs want BGR */ + /* swapRGB_on=0 -> program decides; swapRGB_on=1, always swap */ + /* swapRGB_on=-1, never swap */ + if (((swapRGB == 1) && (swapRGB_on != -1)) || (swapRGB_on == 1)) { + for (y = 0; y < vh; y++) { + for (x = 0; x < vw; x++) { + i = (y * vw + x) * 3; + *(temp) = *(output + i); + *(output + i) = *(output + i + 2); + *(output + i + 2) = *(temp); + } + } + } + /* brightness */ + if (stv680->chgbright == 1) { + if (stv680->brightness >= 32767) { + p = (stv680->brightness - 32767) / 256; + for (x = 0; x < (vw * vh * 3); x++) { + if ((*(output + x) + (unsigned char) p) > 255) + *(output + x) = 255; + else + *(output + x) += (unsigned char) p; + } /* for */ + } else { + p = (32767 - stv680->brightness) / 256; + for (x = 0; x < (vw * vh * 3); x++) { + if ((unsigned char) p > *(output + x)) + *(output + x) = 0; + else + *(output + x) -= (unsigned char) p; + } /* for */ + } /* else */ + } + /* if */ + frame->curpix = 0; + frame->curlinepix = 0; + frame->grabstate = FRAME_DONE; + stv680->framecount++; + stv680->readcount++; + if (stv680->frame[(stv680->curframe + 1) & (STV680_NUMFRAMES - 1)].grabstate == FRAME_READY) { + stv680->curframe = (stv680->curframe + 1) & (STV680_NUMFRAMES - 1); + } + +} /* bayer_unshuffle */ + +/******* end routines from the pencam program *********/ + +static int stv680_newframe (struct usb_stv *stv680, int framenr) +{ + int errors = 0; + + while (stv680->streaming && (stv680->frame[framenr].grabstate == FRAME_READY || stv680->frame[framenr].grabstate == FRAME_GRABBING)) { + if (!stv680->frame[framenr].curpix) { + errors++; + } + wait_event_interruptible (stv680->wq, (stv680->scratch[stv680->scratch_use].state == BUFFER_READY)); + + if (stv680->nullpackets > STV680_MAX_NULLPACKETS) { + stv680->nullpackets = 0; + PDEBUG (2, "STV(i): too many null length packets, restarting capture"); + stv680_stop_stream (stv680); + stv680_start_stream (stv680); + } else { + if (stv680->scratch[stv680->scratch_use].state != BUFFER_READY) { + stv680->frame[framenr].grabstate = FRAME_ERROR; + PDEBUG (2, "STV(e): FRAME_ERROR in _newframe"); + return -EIO; + } + stv680->scratch[stv680->scratch_use].state = BUFFER_BUSY; + + bayer_unshuffle (stv680, &stv680->scratch[stv680->scratch_use]); + + stv680->scratch[stv680->scratch_use].state = BUFFER_UNUSED; + stv680->scratch_use++; + if (stv680->scratch_use >= STV680_NUMSCRATCH) + stv680->scratch_use = 0; + if (errors > STV680_MAX_ERRORS) { + errors = 0; + PDEBUG (2, "STV(i): too many errors, restarting capture"); + stv680_stop_stream (stv680); + stv680_start_stream (stv680); + } + } /* else */ + } /* while */ + return 0; +} + +/********************************************************************* + * Video4Linux + *********************************************************************/ + +static int stv_open (struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct usb_stv *stv680 = video_get_drvdata(dev); + int err = 0; + + /* we are called with the BKL held */ + stv680->user = 1; + err = stv_init (stv680); /* main initialization routine for camera */ + + if (err >= 0) { + stv680->fbuf = rvmalloc (stv680->maxframesize * STV680_NUMFRAMES); + if (!stv680->fbuf) { + PDEBUG (0, "STV(e): Could not rvmalloc frame bufer"); + err = -ENOMEM; + } + file->private_data = dev; + } + if (err) + stv680->user = 0; + + return err; +} + +static int stv_close (struct inode *inode, struct file *file) +{ + struct video_device *dev = file->private_data; + struct usb_stv *stv680 = video_get_drvdata(dev); + int i; + + for (i = 0; i < STV680_NUMFRAMES; i++) + stv680->frame[i].grabstate = FRAME_UNUSED; + if (stv680->streaming) + stv680_stop_stream (stv680); + + if ((i = stv_stop_video (stv680)) < 0) + PDEBUG (1, "STV(e): stop_video failed in stv_close"); + + rvfree (stv680->fbuf, stv680->maxframesize * STV680_NUMFRAMES); + stv680->user = 0; + + if (stv680->removed) { + kfree(stv680); + stv680 = NULL; + PDEBUG (0, "STV(i): device unregistered"); + } + file->private_data = NULL; + return 0; +} + +static int stv680_do_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vdev = file->private_data; + struct usb_stv *stv680 = video_get_drvdata(vdev); + + if (!stv680->udev) + return -EIO; + + switch (cmd) { + case VIDIOCGCAP:{ + struct video_capability *b = arg; + + strcpy (b->name, stv680->camera_name); + b->type = VID_TYPE_CAPTURE; + b->channels = 1; + b->audios = 0; + b->maxwidth = stv680->maxwidth; + b->maxheight = stv680->maxheight; + b->minwidth = stv680->maxwidth / 2; + b->minheight = stv680->maxheight / 2; + return 0; + } + case VIDIOCGCHAN:{ + struct video_channel *v = arg; + + if (v->channel != 0) + return -EINVAL; + v->flags = 0; + v->tuners = 0; + v->type = VIDEO_TYPE_CAMERA; + strcpy (v->name, "STV Camera"); + return 0; + } + case VIDIOCSCHAN:{ + struct video_channel *v = arg; + if (v->channel != 0) + return -EINVAL; + return 0; + } + case VIDIOCGPICT:{ + struct video_picture *p = arg; + + stv680_get_pict (stv680, p); + return 0; + } + case VIDIOCSPICT:{ + struct video_picture *p = arg; + + if (stv680_set_pict (stv680, p)) + return -EINVAL; + return 0; + } + case VIDIOCSWIN:{ + struct video_window *vw = arg; + + if (vw->flags) + return -EINVAL; + if (vw->clipcount) + return -EINVAL; + if (vw->width != stv680->vwidth) { + if (stv680_set_size (stv680, vw->width, vw->height)) { + PDEBUG (2, "STV(e): failed (from user) set size in VIDIOCSWIN"); + return -EINVAL; + } + } + return 0; + } + case VIDIOCGWIN:{ + struct video_window *vw = arg; + + vw->x = 0; /* FIXME */ + vw->y = 0; + vw->chromakey = 0; + vw->flags = 0; + vw->clipcount = 0; + vw->width = stv680->vwidth; + vw->height = stv680->vheight; + return 0; + } + case VIDIOCGMBUF:{ + struct video_mbuf *vm = arg; + int i; + + memset (vm, 0, sizeof (*vm)); + vm->size = STV680_NUMFRAMES * stv680->maxframesize; + vm->frames = STV680_NUMFRAMES; + for (i = 0; i < STV680_NUMFRAMES; i++) + vm->offsets[i] = stv680->maxframesize * i; + return 0; + } + case VIDIOCMCAPTURE:{ + struct video_mmap *vm = arg; + + if (vm->format != STV_VIDEO_PALETTE) { + PDEBUG (2, "STV(i): VIDIOCMCAPTURE vm.format (%i) != VIDEO_PALETTE (%i)", + vm->format, STV_VIDEO_PALETTE); + if ((vm->format == 3) && (swapRGB_on == 0)) { + PDEBUG (2, "STV(i): VIDIOCMCAPTURE swapRGB is (auto) ON"); + /* this may fix those apps (e.g., xawtv) that want BGR */ + swapRGB = 1; + } + return -EINVAL; + } + if (vm->frame >= STV680_NUMFRAMES) { + PDEBUG (2, "STV(e): VIDIOCMCAPTURE vm.frame > NUMFRAMES"); + return -EINVAL; + } + if ((stv680->frame[vm->frame].grabstate == FRAME_ERROR) + || (stv680->frame[vm->frame].grabstate == FRAME_GRABBING)) { + PDEBUG (2, "STV(e): VIDIOCMCAPTURE grabstate (%i) error", + stv680->frame[vm->frame].grabstate); + return -EBUSY; + } + /* Is this according to the v4l spec??? */ + if (stv680->vwidth != vm->width) { + if (stv680_set_size (stv680, vm->width, vm->height)) { + PDEBUG (2, "STV(e): VIDIOCMCAPTURE set_size failed"); + return -EINVAL; + } + } + stv680->frame[vm->frame].grabstate = FRAME_READY; + + if (!stv680->streaming) + stv680_start_stream (stv680); + + return 0; + } + case VIDIOCSYNC:{ + int *frame = arg; + int ret = 0; + + if (*frame < 0 || *frame >= STV680_NUMFRAMES) { + PDEBUG (2, "STV(e): Bad frame # in VIDIOCSYNC"); + return -EINVAL; + } + ret = stv680_newframe (stv680, *frame); + stv680->frame[*frame].grabstate = FRAME_UNUSED; + return ret; + } + case VIDIOCGFBUF:{ + struct video_buffer *vb = arg; + + memset (vb, 0, sizeof (*vb)); + return 0; + } + case VIDIOCKEY: + return 0; + case VIDIOCCAPTURE: + { + PDEBUG (2, "STV(e): VIDIOCCAPTURE failed"); + return -EINVAL; + } + case VIDIOCSFBUF: + case VIDIOCGTUNER: + case VIDIOCSTUNER: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; + } /* end switch */ + + return 0; +} + +static int stv680_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, stv680_do_ioctl); +} + +static int stv680_mmap (struct file *file, struct vm_area_struct *vma) +{ + struct video_device *dev = file->private_data; + struct usb_stv *stv680 = video_get_drvdata(dev); + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end-vma->vm_start; + unsigned long page, pos; + + mutex_lock(&stv680->lock); + + if (stv680->udev == NULL) { + mutex_unlock(&stv680->lock); + return -EIO; + } + if (size > (((STV680_NUMFRAMES * stv680->maxframesize) + PAGE_SIZE - 1) + & ~(PAGE_SIZE - 1))) { + mutex_unlock(&stv680->lock); + return -EINVAL; + } + pos = (unsigned long) stv680->fbuf; + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { + mutex_unlock(&stv680->lock); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + mutex_unlock(&stv680->lock); + + return 0; +} + +static ssize_t stv680_read (struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct video_device *dev = file->private_data; + unsigned long int realcount = count; + int ret = 0; + struct usb_stv *stv680 = video_get_drvdata(dev); + unsigned long int i; + + if (STV680_NUMFRAMES != 2) { + PDEBUG (0, "STV(e): STV680_NUMFRAMES needs to be 2!"); + return -1; + } + if (stv680->udev == NULL) + return -EIO; + if (realcount > (stv680->vwidth * stv680->vheight * 3)) + realcount = stv680->vwidth * stv680->vheight * 3; + + /* Shouldn't happen: */ + if (stv680->frame[0].grabstate == FRAME_GRABBING) { + PDEBUG (2, "STV(e): FRAME_GRABBING in stv680_read"); + return -EBUSY; + } + stv680->frame[0].grabstate = FRAME_READY; + stv680->frame[1].grabstate = FRAME_UNUSED; + stv680->curframe = 0; + + if (!stv680->streaming) + stv680_start_stream (stv680); + + if (!stv680->streaming) { + ret = stv680_newframe (stv680, 0); /* ret should = 0 */ + } + + ret = stv680_newframe (stv680, 0); + + if (!ret) { + if ((i = copy_to_user (buf, stv680->frame[0].data, realcount)) != 0) { + PDEBUG (2, "STV(e): copy_to_user frame 0 failed, ret count = %li", i); + return -EFAULT; + } + } else { + realcount = ret; + } + stv680->frame[0].grabstate = FRAME_UNUSED; + return realcount; +} /* stv680_read */ + +static struct file_operations stv680_fops = { + .owner = THIS_MODULE, + .open = stv_open, + .release = stv_close, + .read = stv680_read, + .mmap = stv680_mmap, + .ioctl = stv680_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; +static struct video_device stv680_template = { + .owner = THIS_MODULE, + .name = "STV0680 USB camera", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_SE401, + .fops = &stv680_fops, + .release = video_device_release, + .minor = -1, +}; + +static int stv680_probe (struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_host_interface *interface; + struct usb_stv *stv680 = NULL; + char *camera_name = NULL; + int retval = 0; + + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) { + PDEBUG (0, "STV(e): Number of Configurations != 1"); + return -ENODEV; + } + + interface = &intf->altsetting[0]; + /* Is it a STV680? */ + if ((le16_to_cpu(dev->descriptor.idVendor) == USB_PENCAM_VENDOR_ID) && + (le16_to_cpu(dev->descriptor.idProduct) == USB_PENCAM_PRODUCT_ID)) { + camera_name = "STV0680"; + PDEBUG (0, "STV(i): STV0680 camera found."); + } else if ((le16_to_cpu(dev->descriptor.idVendor) == USB_CREATIVEGOMINI_VENDOR_ID) && + (le16_to_cpu(dev->descriptor.idProduct) == USB_CREATIVEGOMINI_PRODUCT_ID)) { + camera_name = "Creative WebCam Go Mini"; + PDEBUG (0, "STV(i): Creative WebCam Go Mini found."); + } else { + PDEBUG (0, "STV(e): Vendor/Product ID do not match STV0680 or Creative WebCam Go Mini values."); + PDEBUG (0, "STV(e): Check that the STV0680 or Creative WebCam Go Mini camera is connected to the computer."); + retval = -ENODEV; + goto error; + } + /* We found one */ + if ((stv680 = kzalloc (sizeof (*stv680), GFP_KERNEL)) == NULL) { + PDEBUG (0, "STV(e): couldn't kmalloc stv680 struct."); + retval = -ENOMEM; + goto error; + } + + stv680->udev = dev; + stv680->camera_name = camera_name; + + stv680->vdev = video_device_alloc(); + if (!stv680->vdev) { + retval = -ENOMEM; + goto error; + } + memcpy(stv680->vdev, &stv680_template, sizeof(stv680_template)); + stv680->vdev->dev = &intf->dev; + video_set_drvdata(stv680->vdev, stv680); + + memcpy (stv680->vdev->name, stv680->camera_name, strlen (stv680->camera_name)); + init_waitqueue_head (&stv680->wq); + mutex_init (&stv680->lock); + wmb (); + + if (video_register_device (stv680->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { + PDEBUG (0, "STV(e): video_register_device failed"); + retval = -EIO; + goto error_vdev; + } + PDEBUG (0, "STV(i): registered new video device: video%d", stv680->vdev->minor); + + usb_set_intfdata (intf, stv680); + stv680_create_sysfs_files(stv680->vdev); + return 0; + +error_vdev: + video_device_release(stv680->vdev); +error: + kfree(stv680); + return retval; +} + +static inline void usb_stv680_remove_disconnected (struct usb_stv *stv680) +{ + int i; + + stv680->udev = NULL; + stv680->frame[0].grabstate = FRAME_ERROR; + stv680->frame[1].grabstate = FRAME_ERROR; + stv680->streaming = 0; + + wake_up_interruptible (&stv680->wq); + + for (i = 0; i < STV680_NUMSBUF; i++) + if (stv680->urb[i]) { + usb_kill_urb (stv680->urb[i]); + usb_free_urb (stv680->urb[i]); + stv680->urb[i] = NULL; + kfree(stv680->sbuf[i].data); + } + for (i = 0; i < STV680_NUMSCRATCH; i++) + kfree(stv680->scratch[i].data); + PDEBUG (0, "STV(i): %s disconnected", stv680->camera_name); + + /* Free the memory */ + kfree(stv680); +} + +static void stv680_disconnect (struct usb_interface *intf) +{ + struct usb_stv *stv680 = usb_get_intfdata (intf); + + usb_set_intfdata (intf, NULL); + + if (stv680) { + /* We don't want people trying to open up the device */ + if (stv680->vdev) { + stv680_remove_sysfs_files(stv680->vdev); + video_unregister_device(stv680->vdev); + stv680->vdev = NULL; + } + if (!stv680->user) { + usb_stv680_remove_disconnected (stv680); + } else { + stv680->removed = 1; + } + } +} + +static struct usb_driver stv680_driver = { + .name = "stv680", + .probe = stv680_probe, + .disconnect = stv680_disconnect, + .id_table = device_table +}; + +/******************************************************************** + * Module routines + ********************************************************************/ + +static int __init usb_stv680_init (void) +{ + if (usb_register (&stv680_driver) < 0) { + PDEBUG (0, "STV(e): Could not setup STV0680 driver"); + return -1; + } + PDEBUG (0, "STV(i): usb camera driver version %s registering", DRIVER_VERSION); + + info(DRIVER_DESC " " DRIVER_VERSION); + return 0; +} + +static void __exit usb_stv680_exit (void) +{ + usb_deregister (&stv680_driver); + PDEBUG (0, "STV(i): driver deregistered"); +} + +module_init (usb_stv680_init); +module_exit (usb_stv680_exit); diff --git a/linux/drivers/media/video/stv680.h b/linux/drivers/media/video/stv680.h new file mode 100644 index 000000000..a08f1b08a --- /dev/null +++ b/linux/drivers/media/video/stv680.h @@ -0,0 +1,227 @@ +/**************************************************************************** + * + * Filename: stv680.h + * + * Description: + * This is a USB driver for STV0680 based usb video cameras. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************************/ + +/* size of usb transfers */ +#define STV680_PACKETSIZE 4096 + +/* number of queued bulk transfers to use, may have problems if > 1 */ +#define STV680_NUMSBUF 1 + +/* number of frames supported by the v4l part */ +#define STV680_NUMFRAMES 2 + +/* scratch buffers for passing data to the decoders: 2 or 4 are good */ +#define STV680_NUMSCRATCH 2 + +/* number of nul sized packets to receive before kicking the camera */ +#define STV680_MAX_NULLPACKETS 200 + +/* number of decoding errors before kicking the camera */ +#define STV680_MAX_ERRORS 100 + +#define USB_PENCAM_VENDOR_ID 0x0553 +#define USB_PENCAM_PRODUCT_ID 0x0202 + +#define USB_CREATIVEGOMINI_VENDOR_ID 0x041e +#define USB_CREATIVEGOMINI_PRODUCT_ID 0x4007 + +#define PENCAM_TIMEOUT 1000 +/* fmt 4 */ +#define STV_VIDEO_PALETTE VIDEO_PALETTE_RGB24 + +static struct usb_device_id device_table[] = { + {USB_DEVICE (USB_PENCAM_VENDOR_ID, USB_PENCAM_PRODUCT_ID)}, + {USB_DEVICE (USB_CREATIVEGOMINI_VENDOR_ID, USB_CREATIVEGOMINI_PRODUCT_ID)}, + {} +}; +MODULE_DEVICE_TABLE (usb, device_table); + +struct stv680_sbuf { + unsigned char *data; +}; + +enum { + FRAME_UNUSED, /* Unused (no MCAPTURE) */ + FRAME_READY, /* Ready to start grabbing */ + FRAME_GRABBING, /* In the process of being grabbed into */ + FRAME_DONE, /* Finished grabbing, but not been synced yet */ + FRAME_ERROR, /* Something bad happened while processing */ +}; + +enum { + BUFFER_UNUSED, + BUFFER_READY, + BUFFER_BUSY, + BUFFER_DONE, +}; + +/* raw camera data <- sbuf (urb transfer buf) */ +struct stv680_scratch { + unsigned char *data; + volatile int state; + int offset; + int length; +}; + +/* processed data for display ends up here, after bayer */ +struct stv680_frame { + unsigned char *data; /* Frame buffer */ + volatile int grabstate; /* State of grabbing */ + unsigned char *curline; + int curlinepix; + int curpix; +}; + +/* this is almost the video structure uvd_t, with extra parameters for stv */ +struct usb_stv { + struct video_device *vdev; + + struct usb_device *udev; + + unsigned char bulk_in_endpointAddr; /* __u8 the address of the bulk in endpoint */ + char *camera_name; + + unsigned int VideoMode; /* 0x0100 = VGA, 0x0000 = CIF, 0x0300 = QVGA */ + int SupportedModes; + int CIF; + int VGA; + int QVGA; + int cwidth; /* camera width */ + int cheight; /* camera height */ + int maxwidth; /* max video width */ + int maxheight; /* max video height */ + int vwidth; /* current width for video window */ + int vheight; /* current height for video window */ + unsigned long int rawbufsize; + unsigned long int maxframesize; /* rawbufsize * 3 for RGB */ + + int origGain; + int origMode; /* original camera mode */ + + struct mutex lock; /* to lock the structure */ + int user; /* user count for exclusive use */ + int removed; /* device disconnected */ + int streaming; /* Are we streaming video? */ + char *fbuf; /* Videodev buffer area */ + struct urb *urb[STV680_NUMSBUF]; /* # of queued bulk transfers */ + int curframe; /* Current receiving frame */ + struct stv680_frame frame[STV680_NUMFRAMES]; /* # frames supported by v4l part */ + int readcount; + int framecount; + int error; + int dropped; + int scratch_next; + int scratch_use; + int scratch_overflow; + struct stv680_scratch scratch[STV680_NUMSCRATCH]; /* for decoders */ + struct stv680_sbuf sbuf[STV680_NUMSBUF]; + + unsigned int brightness; + unsigned int chgbright; + unsigned int whiteness; + unsigned int colour; + unsigned int contrast; + unsigned int hue; + unsigned int palette; + unsigned int depth; /* rgb24 in bits */ + + wait_queue_head_t wq; /* Processes waiting */ + + int nullpackets; +}; + + +static const unsigned char red[256] = { + 0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 25, 30, 35, 38, 42, + 44, 47, 50, 53, 54, 57, 59, 61, 63, 65, 67, 69, + 71, 71, 73, 75, 77, 78, 80, 81, 82, 84, 85, 87, + 88, 89, 90, 91, 93, 94, 95, 97, 98, 98, 99, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 125, 126, 127, 128, 129, 129, 130, 131, 132, 133, 134, + 134, 135, 135, 136, 137, 138, 139, 140, 140, 141, 142, 143, + 143, 143, 144, 145, 146, 147, 147, 148, 149, 150, 150, 151, + 152, 152, 152, 153, 154, 154, 155, 156, 157, 157, 158, 159, + 159, 160, 161, 161, 161, 162, 163, 163, 164, 165, 165, 166, + 167, 167, 168, 168, 169, 170, 170, 170, 171, 171, 172, 173, + 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 179, 179, + 180, 180, 181, 181, 182, 183, 183, 184, 184, 185, 185, 186, + 187, 187, 188, 188, 188, 188, 189, 190, 190, 191, 191, 192, + 192, 193, 193, 194, 195, 195, 196, 196, 197, 197, 197, 197, + 198, 198, 199, 199, 200, 201, 201, 202, 202, 203, 203, 204, + 204, 205, 205, 206, 206, 206, 206, 207, 207, 208, 208, 209, + 209, 210, 210, 211, 211, 212, 212, 213, 213, 214, 214, 215, + 215, 215, 215, 216, 216, 217, 217, 218, 218, 218, 219, 219, + 220, 220, 221, 221 +}; + +static const unsigned char green[256] = { + 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 28, 34, 39, 43, 47, + 50, 53, 56, 59, 61, 64, 66, 68, 71, 73, 75, 77, + 79, 80, 82, 84, 86, 87, 89, 91, 92, 94, 95, 97, + 98, 100, 101, 102, 104, 105, 106, 108, 109, 110, 111, 113, + 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149, + 150, 151, 151, 152, 153, 154, 155, 156, 156, 157, 158, 159, + 160, 160, 161, 162, 163, 164, 164, 165, 166, 167, 167, 168, + 169, 170, 170, 171, 172, 172, 173, 174, 175, 175, 176, 177, + 177, 178, 179, 179, 180, 181, 182, 182, 183, 184, 184, 185, + 186, 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, + 193, 194, 194, 195, 196, 196, 197, 198, 198, 199, 199, 200, + 201, 201, 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, + 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, + 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, + 221, 221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, + 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, + 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, + 239, 240, 240, 241, 241, 242, 242, 243, 243, 243, 244, 244, + 245, 245, 246, 246 +}; + +static const unsigned char blue[256] = { + 0, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 30, 37, 42, 47, 51, + 55, 58, 61, 64, 67, 70, 72, 74, 78, 80, 82, 84, + 86, 88, 90, 92, 94, 95, 97, 100, 101, 103, 104, 106, + 107, 110, 111, 112, 114, 115, 116, 118, 119, 121, 122, 124, + 125, 126, 127, 128, 129, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 154, 155, 156, 157, 158, 158, 159, 160, 161, 162, 163, + 165, 166, 166, 167, 168, 169, 170, 171, 171, 172, 173, 174, + 176, 176, 177, 178, 179, 180, 180, 181, 182, 183, 183, 184, + 185, 187, 187, 188, 189, 189, 190, 191, 192, 192, 193, 194, + 194, 195, 196, 196, 198, 199, 200, 200, 201, 202, 202, 203, + 204, 204, 205, 205, 206, 207, 207, 209, 210, 210, 211, 212, + 212, 213, 213, 214, 215, 215, 216, 217, 217, 218, 218, 220, + 221, 221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, + 228, 228, 229, 229, 231, 231, 232, 233, 233, 234, 234, 235, + 235, 236, 236, 237, 238, 238, 239, 239, 240, 240, 242, 242, + 243, 243, 244, 244, 245, 246, 246, 247, 247, 248, 248, 249, + 249, 250, 250, 251, 251, 253, 253, 254, 254, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255 +}; diff --git a/linux/drivers/media/video/tuner-3036.c b/linux/drivers/media/video/tuner-3036.c new file mode 100644 index 000000000..545f54eaa --- /dev/null +++ b/linux/drivers/media/video/tuner-3036.c @@ -0,0 +1,214 @@ +/* + * Driver for Philips SAB3036 "CITAC" tuner control chip. + * + * Author: Phil Blundell + * + * The SAB3036 is just about different enough from the chips that + * tuner.c copes with to make it not worth the effort to crowbar + * the support into that file. So instead we have a separate driver. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "compat.h" +#include + +#include + +static int debug; /* insmod parameter */ +static int this_adap; + +static struct i2c_client client_template; + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x60, 0x61, I2C_CLIENT_END }; +static unsigned short ignore = I2C_CLIENT_END; + +static struct i2c_client_address_data addr_data = { + .normal_i2c = normal_i2c, + .probe = &ignore, + .ignore = &ignore, +}; + +/* ---------------------------------------------------------------------- */ + +static unsigned char +tuner_getstatus (struct i2c_client *c) +{ + unsigned char byte; + if (i2c_master_recv(c, &byte, 1) != 1) + printk(KERN_ERR "tuner-3036: I/O error.\n"); + return byte; +} + +#define TUNER_FL 0x80 + +static int +tuner_islocked (struct i2c_client *c) +{ + return (tuner_getstatus(c) & TUNER_FL); +} + +/* ---------------------------------------------------------------------- */ + +static void +set_tv_freq(struct i2c_client *c, int freq) +{ + u16 div = ((freq * 20) / 16); + unsigned long give_up = jiffies + HZ; + unsigned char buffer[2]; + + if (debug) + printk(KERN_DEBUG "tuner: setting frequency %dMHz, divisor %x\n", freq / 16, div); + + /* Select high tuning current */ + buffer[0] = 0x29; + buffer[1] = 0x3e; + + if (i2c_master_send(c, buffer, 2) != 2) + printk("tuner: i2c i/o error 1\n"); + + buffer[0] = 0x80 | ((div>>8) & 0x7f); + buffer[1] = div & 0xff; + + if (i2c_master_send(c, buffer, 2) != 2) + printk("tuner: i2c i/o error 2\n"); + + while (!tuner_islocked(c) && time_before(jiffies, give_up)) + schedule(); + + if (!tuner_islocked(c)) + printk(KERN_WARNING "tuner: failed to achieve PLL lock\n"); + + /* Select low tuning current and engage AFC */ + buffer[0] = 0x29; + buffer[1] = 0xb2; + + if (i2c_master_send(c, buffer, 2) != 2) + printk("tuner: i2c i/o error 3\n"); + + if (debug) + printk(KERN_DEBUG "tuner: status %02x\n", tuner_getstatus(c)); +} + +/* ---------------------------------------------------------------------- */ + +static int +tuner_attach(struct i2c_adapter *adap, int addr, int kind) +{ + static unsigned char buffer[] = { 0x29, 0x32, 0x2a, 0, 0x2b, 0 }; + + struct i2c_client *client; + + if (this_adap > 0) + return -1; + this_adap++; + + client_template.adapter = adap; + client_template.addr = addr; + + client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == NULL) + return -ENOMEM; + memcpy(client, &client_template, sizeof(struct i2c_client)); + + printk("tuner: SAB3036 found, status %02x\n", tuner_getstatus(client)); + + i2c_attach_client(client); + + if (i2c_master_send(client, buffer, 2) != 2) + printk("tuner: i2c i/o error 1\n"); + if (i2c_master_send(client, buffer+2, 2) != 2) + printk("tuner: i2c i/o error 2\n"); + if (i2c_master_send(client, buffer+4, 2) != 2) + printk("tuner: i2c i/o error 3\n"); + return 0; +} + +static int +tuner_detach(struct i2c_client *c) +{ + return 0; +} + +static int +tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + int *iarg = (int*)arg; + + switch (cmd) + { + case VIDIOCSFREQ: + set_tv_freq(client, *iarg); + break; + + default: + return -EINVAL; + } + return 0; +} + +static int +tuner_probe(struct i2c_adapter *adap) +{ + this_adap = 0; + if (adap->id == I2C_HW_B_LP) + return i2c_probe(adap, &addr_data, tuner_attach); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver +i2c_driver_tuner = +{ + .driver = { + .name = "sab3036", + }, + .id = I2C_DRIVERID_SAB3036, + .attach_adapter = tuner_probe, + .detach_client = tuner_detach, + .command = tuner_command +}; + +static struct i2c_client client_template = +{ + .driver = &i2c_driver_tuner, + .name = "SAB3036", +}; + +static int __init +tuner3036_init(void) +{ + return i2c_add_driver(&i2c_driver_tuner); +} + +static void __exit +tuner3036_exit(void) +{ + i2c_del_driver(&i2c_driver_tuner); +} + +MODULE_DESCRIPTION("SAB3036 tuner driver"); +MODULE_AUTHOR("Philip Blundell "); +MODULE_LICENSE("GPL"); + +module_param(debug, int, 0); +MODULE_PARM_DESC(debug,"Enable debugging output"); + +module_init(tuner3036_init); +module_exit(tuner3036_exit); diff --git a/linux/drivers/media/video/videocodec.c b/linux/drivers/media/video/videocodec.c new file mode 100644 index 000000000..8f271de57 --- /dev/null +++ b/linux/drivers/media/video/videocodec.c @@ -0,0 +1,481 @@ +/* + * VIDEO MOTION CODECs internal API for video devices + * + * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's + * bound to a master device. + * + * (c) 2002 Wolfgang Scherr + * + * $Id: videocodec.c,v 1.1.2.8 2003/03/29 07:16:04 rbultje Exp $ + * + * ------------------------------------------------------------------------ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * ------------------------------------------------------------------------ + */ + +#define VIDEOCODEC_VERSION "v0.2" + +#include +#include +#include +#include +#include + +// kernel config is here (procfs flag) +#include + +#ifdef CONFIG_PROC_FS +#include +#include +#endif + +#include "videocodec.h" + +static int debug = 0; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-4)"); + +#define dprintk(num, format, args...) \ + do { \ + if (debug >= num) \ + printk(format, ##args); \ + } while (0) + +struct attached_list { + struct videocodec *codec; + struct attached_list *next; +}; + +struct codec_list { + const struct videocodec *codec; + int attached; + struct attached_list *list; + struct codec_list *next; +}; + +static struct codec_list *codeclist_top = NULL; + +/* ================================================= */ +/* function prototypes of the master/slave interface */ +/* ================================================= */ + +struct videocodec * +videocodec_attach (struct videocodec_master *master) +{ + struct codec_list *h = codeclist_top; + struct attached_list *a, *ptr; + struct videocodec *codec; + int res; + + if (!master) { + dprintk(1, KERN_ERR "videocodec_attach: no data\n"); + return NULL; + } + + dprintk(2, + "videocodec_attach: '%s', type: %x, flags %lx, magic %lx\n", + master->name, master->type, master->flags, master->magic); + + if (!h) { + dprintk(1, + KERN_ERR + "videocodec_attach: no device available\n"); + return NULL; + } + + while (h) { + // attach only if the slave has at least the flags + // expected by the master + if ((master->flags & h->codec->flags) == master->flags) { + dprintk(4, "videocodec_attach: try '%s'\n", + h->codec->name); + + if (!try_module_get(h->codec->owner)) + return NULL; + + codec = + kmalloc(sizeof(struct videocodec), GFP_KERNEL); + if (!codec) { + dprintk(1, + KERN_ERR + "videocodec_attach: no mem\n"); + goto out_module_put; + } + memcpy(codec, h->codec, sizeof(struct videocodec)); + + snprintf(codec->name, sizeof(codec->name), + "%s[%d]", codec->name, h->attached); + codec->master_data = master; + res = codec->setup(codec); + if (res == 0) { + dprintk(3, "videocodec_attach '%s'\n", + codec->name); + ptr = kzalloc(sizeof(struct attached_list), GFP_KERNEL); + if (!ptr) { + dprintk(1, + KERN_ERR + "videocodec_attach: no memory\n"); + goto out_kfree; + } + ptr->codec = codec; + + a = h->list; + if (!a) { + h->list = ptr; + dprintk(4, + "videocodec: first element\n"); + } else { + while (a->next) + a = a->next; // find end + a->next = ptr; + dprintk(4, + "videocodec: in after '%s'\n", + h->codec->name); + } + + h->attached += 1; + return codec; + } else { + kfree(codec); + } + } + h = h->next; + } + + dprintk(1, KERN_ERR "videocodec_attach: no codec found!\n"); + return NULL; + + out_module_put: + module_put(h->codec->owner); + out_kfree: + kfree(codec); + return NULL; +} + +int +videocodec_detach (struct videocodec *codec) +{ + struct codec_list *h = codeclist_top; + struct attached_list *a, *prev; + int res; + + if (!codec) { + dprintk(1, KERN_ERR "videocodec_detach: no data\n"); + return -EINVAL; + } + + dprintk(2, + "videocodec_detach: '%s', type: %x, flags %lx, magic %lx\n", + codec->name, codec->type, codec->flags, codec->magic); + + if (!h) { + dprintk(1, + KERN_ERR "videocodec_detach: no device left...\n"); + return -ENXIO; + } + + while (h) { + a = h->list; + prev = NULL; + while (a) { + if (codec == a->codec) { + res = a->codec->unset(a->codec); + if (res >= 0) { + dprintk(3, + "videocodec_detach: '%s'\n", + a->codec->name); + a->codec->master_data = NULL; + } else { + dprintk(1, + KERN_ERR + "videocodec_detach: '%s'\n", + a->codec->name); + a->codec->master_data = NULL; + } + if (prev == NULL) { + h->list = a->next; + dprintk(4, + "videocodec: delete first\n"); + } else { + prev->next = a->next; + dprintk(4, + "videocodec: delete middle\n"); + } + module_put(a->codec->owner); + kfree(a->codec); + kfree(a); + h->attached -= 1; + return 0; + } + prev = a; + a = a->next; + } + h = h->next; + } + + dprintk(1, KERN_ERR "videocodec_detach: given codec not found!\n"); + return -EINVAL; +} + +int +videocodec_register (const struct videocodec *codec) +{ + struct codec_list *ptr, *h = codeclist_top; + + if (!codec) { + dprintk(1, KERN_ERR "videocodec_register: no data!\n"); + return -EINVAL; + } + + dprintk(2, + "videocodec: register '%s', type: %x, flags %lx, magic %lx\n", + codec->name, codec->type, codec->flags, codec->magic); + + ptr = kzalloc(sizeof(struct codec_list), GFP_KERNEL); + if (!ptr) { + dprintk(1, KERN_ERR "videocodec_register: no memory\n"); + return -ENOMEM; + } + ptr->codec = codec; + + if (!h) { + codeclist_top = ptr; + dprintk(4, "videocodec: hooked in as first element\n"); + } else { + while (h->next) + h = h->next; // find the end + h->next = ptr; + dprintk(4, "videocodec: hooked in after '%s'\n", + h->codec->name); + } + + return 0; +} + +int +videocodec_unregister (const struct videocodec *codec) +{ + struct codec_list *prev = NULL, *h = codeclist_top; + + if (!codec) { + dprintk(1, KERN_ERR "videocodec_unregister: no data!\n"); + return -EINVAL; + } + + dprintk(2, + "videocodec: unregister '%s', type: %x, flags %lx, magic %lx\n", + codec->name, codec->type, codec->flags, codec->magic); + + if (!h) { + dprintk(1, + KERN_ERR + "videocodec_unregister: no device left...\n"); + return -ENXIO; + } + + while (h) { + if (codec == h->codec) { + if (h->attached) { + dprintk(1, + KERN_ERR + "videocodec: '%s' is used\n", + h->codec->name); + return -EBUSY; + } + dprintk(3, "videocodec: unregister '%s' is ok.\n", + h->codec->name); + if (prev == NULL) { + codeclist_top = h->next; + dprintk(4, + "videocodec: delete first element\n"); + } else { + prev->next = h->next; + dprintk(4, + "videocodec: delete middle element\n"); + } + kfree(h); + return 0; + } + prev = h; + h = h->next; + } + + dprintk(1, + KERN_ERR + "videocodec_unregister: given codec not found!\n"); + return -EINVAL; +} + +#ifdef CONFIG_PROC_FS +/* ============ */ +/* procfs stuff */ +/* ============ */ + +static char *videocodec_buf = NULL; +static int videocodec_bufsize = 0; + +static int +videocodec_build_table (void) +{ + struct codec_list *h = codeclist_top; + struct attached_list *a; + int i = 0, size; + + // sum up amount of slaves plus their attached masters + while (h) { + i += h->attached + 1; + h = h->next; + } +#define LINESIZE 100 + size = LINESIZE * (i + 1); + + dprintk(3, "videocodec_build table: %d entries, %d bytes\n", i, + size); + + kfree(videocodec_buf); + videocodec_buf = (char *) kmalloc(size, GFP_KERNEL); + + i = 0; + i += scnprintf(videocodec_buf + i, size - 1, + "lave or attached aster name type flags magic "); + i += scnprintf(videocodec_buf + i, size -i - 1, "(connected as)\n"); + + h = codeclist_top; + while (h) { + if (i > (size - LINESIZE)) + break; // security check + i += scnprintf(videocodec_buf + i, size -i -1, + "S %32s %04x %08lx %08lx (TEMPLATE)\n", + h->codec->name, h->codec->type, + h->codec->flags, h->codec->magic); + a = h->list; + while (a) { + if (i > (size - LINESIZE)) + break; // security check + i += scnprintf(videocodec_buf + i, size -i -1, + "M %32s %04x %08lx %08lx (%s)\n", + a->codec->master_data->name, + a->codec->master_data->type, + a->codec->master_data->flags, + a->codec->master_data->magic, + a->codec->name); + a = a->next; + } + h = h->next; + } + + return i; +} + +//The definition: +//typedef int (read_proc_t)(char *page, char **start, off_t off, +// int count, int *eof, void *data); + +static int +videocodec_info (char *buffer, + char **buffer_location, + off_t offset, + int buffer_length, + int *eof, + void *data) +{ + int size; + + dprintk(3, "videocodec_info: offset: %ld, len %d / size %d\n", + offset, buffer_length, videocodec_bufsize); + + if (offset == 0) { + videocodec_bufsize = videocodec_build_table(); + } + if ((offset < 0) || (offset >= videocodec_bufsize)) { + dprintk(4, + "videocodec_info: call delivers no result, return 0\n"); + *eof = 1; + return 0; + } + + if (buffer_length < (videocodec_bufsize - offset)) { + dprintk(4, "videocodec_info: %ld needed, %d got\n", + videocodec_bufsize - offset, buffer_length); + size = buffer_length; + } else { + dprintk(4, "videocodec_info: last reading of %ld bytes\n", + videocodec_bufsize - offset); + size = videocodec_bufsize - offset; + *eof = 1; + } + + memcpy(buffer, videocodec_buf + offset, size); + /* doesn't work... */ + /* copy_to_user(buffer, videocodec_buf+offset, size); */ + /* *buffer_location = videocodec_buf+offset; */ + + return size; +} +#endif + +/* ===================== */ +/* hook in driver module */ +/* ===================== */ +static int __init +videocodec_init (void) +{ +#ifdef CONFIG_PROC_FS + static struct proc_dir_entry *videocodec_proc_entry; +#endif + + printk(KERN_INFO "Linux video codec intermediate layer: %s\n", + VIDEOCODEC_VERSION); + +#ifdef CONFIG_PROC_FS + videocodec_buf = NULL; + videocodec_bufsize = 0; + + videocodec_proc_entry = create_proc_entry("videocodecs", 0, NULL); + if (videocodec_proc_entry) { + videocodec_proc_entry->read_proc = videocodec_info; + videocodec_proc_entry->write_proc = NULL; + videocodec_proc_entry->data = NULL; + videocodec_proc_entry->owner = THIS_MODULE; + } else { + dprintk(1, KERN_ERR "videocodec: can't init procfs.\n"); + } +#endif + return 0; +} + +static void __exit +videocodec_exit (void) +{ +#ifdef CONFIG_PROC_FS + remove_proc_entry("videocodecs", NULL); + kfree(videocodec_buf); +#endif +} + +EXPORT_SYMBOL(videocodec_attach); +EXPORT_SYMBOL(videocodec_detach); +EXPORT_SYMBOL(videocodec_register); +EXPORT_SYMBOL(videocodec_unregister); + +module_init(videocodec_init); +module_exit(videocodec_exit); + +MODULE_AUTHOR("Wolfgang Scherr "); +MODULE_DESCRIPTION("Intermediate API module for video codecs " + VIDEOCODEC_VERSION); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/videocodec.h b/linux/drivers/media/video/videocodec.h new file mode 100644 index 000000000..3f968eb7b --- /dev/null +++ b/linux/drivers/media/video/videocodec.h @@ -0,0 +1,359 @@ +/* + * VIDEO MOTION CODECs internal API for video devices + * + * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's + * bound to a master device. + * + * (c) 2002 Wolfgang Scherr + * + * $Id: videocodec.h,v 1.1.2.4 2003/01/14 21:15:03 rbultje Exp $ + * + * ------------------------------------------------------------------------ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * ------------------------------------------------------------------------ + */ + +/* =================== */ +/* general description */ +/* =================== */ + +/* Should ease the (re-)usage of drivers supporting cards with (different) + video codecs. The codecs register to this module their functionality, + and the processors (masters) can attach to them if they fit. + + The codecs are typically have a "strong" binding to their master - so I + don't think it makes sense to have a full blown interfacing as with e.g. + i2c. If you have an other opinion, let's discuss & implement it :-))) + + Usage: + + The slave has just to setup the videocodec structure and use two functions: + videocodec_register(codecdata); + videocodec_unregister(codecdata); + The best is just calling them at module (de-)initialisation. + + The master sets up the structure videocodec_master and calls: + codecdata=videocodec_attach(master_codecdata); + videocodec_detach(codecdata); + + The slave is called during attach/detach via functions setup previously + during register. At that time, the master_data pointer is set up + and the slave can access any io registers of the master device (in the case + the slave is bound to it). Otherwise it doesn't need this functions and + therfor they may not be initialized. + + The other fuctions are just for convenience, as they are for sure used by + most/all of the codecs. The last ones may be ommited, too. + + See the structure declaration below for more information and which data has + to be set up for the master and the slave. + + ---------------------------------------------------------------------------- + The master should have "knowledge" of the slave and vice versa. So the data + structures sent to/from slave via set_data/get_data set_image/get_image are + device dependent and vary between MJPEG/MPEG/WAVELET/... devices. (!!!!) + ---------------------------------------------------------------------------- +*/ + + +/* ========================================== */ +/* description of the videocodec_io structure */ +/* ========================================== */ + +/* + ==== master setup ==== + name -> name of the device structure for reference and debugging + master_data -> data ref. for the master (e.g. the zr36055,57,67) + readreg -> ref. to read-fn from register (setup by master, used by slave) + writereg -> ref. to write-fn to register (setup by master, used by slave) + this two functions do the lowlevel I/O job + + ==== slave functionality setup ==== + slave_data -> data ref. for the slave (e.g. the zr36050,60) + check -> fn-ref. checks availability of an device, returns -EIO on failure or + the type on success + this makes espcecially sense if a driver module supports more than + one codec which may be quite similar to access, nevertheless it + is good for a first functionality check + + -- main functions you always need for compression/decompression -- + + set_mode -> this fn-ref. resets the entire codec, and sets up the mode + with the last defined norm/size (or device default if not + available) - it returns 0 if the mode is possible + set_size -> this fn-ref. sets the norm and image size for + compression/decompression (returns 0 on success) + the norm param is defined in videodev.h (VIDEO_MODE_*) + + additional setup may be available, too - but the codec should work with + some default values even without this + + set_data -> sets device-specific data (tables, quality etc.) + get_data -> query device-specific data (tables, quality etc.) + + if the device delivers interrupts, they may be setup/handled here + setup_interrupt -> codec irq setup (not needed for 36050/60) + handle_interrupt -> codec irq handling (not needed for 36050/60) + + if the device delivers pictures, they may be handled here + put_image -> puts image data to the codec (not needed for 36050/60) + get_image -> gets image data from the codec (not needed for 36050/60) + the calls include frame numbers and flags (even/odd/...) + if needed and a flag which allows blocking until its ready +*/ + +/* ============== */ +/* user interface */ +/* ============== */ + +/* + Currently there is only a information display planned, as the layer + is not visible for the user space at all. + + Information is available via procfs. The current entry is "/proc/videocodecs" + but it makes sense to "hide" it in the /proc/video tree of v4l(2) --TODO--. + +A example for such an output is: + +lave or attached aster name type flags magic (connected as) +S zr36050 0002 0000d001 00000000 (TEMPLATE) +M zr36055[0] 0001 0000c001 00000000 (zr36050[0]) +M zr36055[1] 0001 0000c001 00000000 (zr36050[1]) + +*/ + + +/* =============================================== */ +/* special defines for the videocodec_io structure */ +/* =============================================== */ + +#ifndef __LINUX_VIDEOCODEC_H +#define __LINUX_VIDEOCODEC_H + +#include "compat.h" +#include + +//should be in videodev.h ??? (VID_DO_....) +#define CODEC_DO_COMPRESSION 0 +#define CODEC_DO_EXPANSION 1 + +/* this are the current codec flags I think they are needed */ +/* -> type value in structure */ +#define CODEC_FLAG_JPEG 0x00000001L // JPEG codec +#define CODEC_FLAG_MPEG 0x00000002L // MPEG1/2/4 codec +#define CODEC_FLAG_DIVX 0x00000004L // DIVX codec +#define CODEC_FLAG_WAVELET 0x00000008L // WAVELET codec + // room for other types + +#define CODEC_FLAG_MAGIC 0x00000800L // magic key must match +#define CODEC_FLAG_HARDWARE 0x00001000L // is a hardware codec +#define CODEC_FLAG_VFE 0x00002000L // has direct video frontend +#define CODEC_FLAG_ENCODER 0x00004000L // compression capability +#define CODEC_FLAG_DECODER 0x00008000L // decompression capability +#define CODEC_FLAG_NEEDIRQ 0x00010000L // needs irq handling +#define CODEC_FLAG_RDWRPIC 0x00020000L // handles picture I/O + +/* a list of modes, some are just examples (is there any HW?) */ +#define CODEC_MODE_BJPG 0x0001 // Baseline JPEG +#define CODEC_MODE_LJPG 0x0002 // Lossless JPEG +#define CODEC_MODE_MPEG1 0x0003 // MPEG 1 +#define CODEC_MODE_MPEG2 0x0004 // MPEG 2 +#define CODEC_MODE_MPEG4 0x0005 // MPEG 4 +#define CODEC_MODE_MSDIVX 0x0006 // MS DivX +#define CODEC_MODE_ODIVX 0x0007 // Open DivX +#define CODEC_MODE_WAVELET 0x0008 // Wavelet + +/* this are the current codec types I want to implement */ +/* -> type value in structure */ +#define CODEC_TYPE_NONE 0 +#define CODEC_TYPE_L64702 1 +#define CODEC_TYPE_ZR36050 2 +#define CODEC_TYPE_ZR36016 3 +#define CODEC_TYPE_ZR36060 4 + +/* the type of data may be enhanced by future implementations (data-fn.'s) */ +/* -> used in command */ +#define CODEC_G_STATUS 0x0000 /* codec status (query only) */ +#define CODEC_S_CODEC_MODE 0x0001 /* codec mode (baseline JPEG, MPEG1,... */ +#define CODEC_G_CODEC_MODE 0x8001 +#define CODEC_S_VFE 0x0002 /* additional video frontend setup */ +#define CODEC_G_VFE 0x8002 +#define CODEC_S_MMAP 0x0003 /* MMAP setup (if available) */ + +#define CODEC_S_JPEG_TDS_BYTE 0x0010 /* target data size in bytes */ +#define CODEC_G_JPEG_TDS_BYTE 0x8010 +#define CODEC_S_JPEG_SCALE 0x0011 /* scaling factor for quant. tables */ +#define CODEC_G_JPEG_SCALE 0x8011 +#define CODEC_S_JPEG_HDT_DATA 0x0018 /* huffman-tables */ +#define CODEC_G_JPEG_HDT_DATA 0x8018 +#define CODEC_S_JPEG_QDT_DATA 0x0019 /* quantizing-tables */ +#define CODEC_G_JPEG_QDT_DATA 0x8019 +#define CODEC_S_JPEG_APP_DATA 0x001A /* APP marker */ +#define CODEC_G_JPEG_APP_DATA 0x801A +#define CODEC_S_JPEG_COM_DATA 0x001B /* COM marker */ +#define CODEC_G_JPEG_COM_DATA 0x801B + +#define CODEC_S_PRIVATE 0x1000 /* "private" commands start here */ +#define CODEC_G_PRIVATE 0x9000 + +#define CODEC_G_FLAG 0x8000 /* this is how 'get' is detected */ + +/* types of transfer, directly user space or a kernel buffer (image-fn.'s) */ +/* -> used in get_image, put_image */ +#define CODEC_TRANSFER_KERNEL 0 /* use "memcopy" */ +#define CODEC_TRANSFER_USER 1 /* use "to/from_user" */ + + +/* ========================= */ +/* the structures itself ... */ +/* ========================= */ + +struct vfe_polarity { + int vsync_pol:1; + int hsync_pol:1; + int field_pol:1; + int blank_pol:1; + int subimg_pol:1; + int poe_pol:1; + int pvalid_pol:1; + int vclk_pol:1; +}; + +struct vfe_settings { + __u32 x, y; /* Offsets into image */ + __u32 width, height; /* Area to capture */ + __u16 decimation; /* Decimation divider */ + __u16 flags; /* Flags for capture */ +/* flags are the same as in struct video_capture - see videodev.h: +#define VIDEO_CAPTURE_ODD 0 +#define VIDEO_CAPTURE_EVEN 1 +*/ + __u16 quality; /* quality of the video */ +}; + +struct tvnorm { + u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart; +}; + +struct jpeg_com_marker { + int len; /* number of usable bytes in data */ + char data[60]; +}; + +struct jpeg_app_marker { + int appn; /* number app segment */ + int len; /* number of usable bytes in data */ + char data[60]; +}; + +struct videocodec { + struct module *owner; + /* -- filled in by slave device during register -- */ + char name[32]; + unsigned long magic; /* may be used for client<->master attaching */ + unsigned long flags; /* functionality flags */ + unsigned int type; /* codec type */ + + /* -- these is filled in later during master device attach -- */ + + struct videocodec_master *master_data; + + /* -- these are filled in by the slave device during register -- */ + + void *data; /* private slave data */ + + /* attach/detach client functions (indirect call) */ + int (*setup) (struct videocodec * codec); + int (*unset) (struct videocodec * codec); + + /* main functions, every client needs them for sure! */ + // set compression or decompression (or freeze, stop, standby, etc) + int (*set_mode) (struct videocodec * codec, + int mode); + // setup picture size and norm (for the codec's video frontend) + int (*set_video) (struct videocodec * codec, + struct tvnorm * norm, + struct vfe_settings * cap, + struct vfe_polarity * pol); + // other control commands, also mmap setup etc. + int (*control) (struct videocodec * codec, + int type, + int size, + void *data); + + /* additional setup/query/processing (may be NULL pointer) */ + // interrupt setup / handling (for irq's delivered by master) + int (*setup_interrupt) (struct videocodec * codec, + long mode); + int (*handle_interrupt) (struct videocodec * codec, + int source, + long flag); + // picture interface (if any) + long (*put_image) (struct videocodec * codec, + int tr_type, + int block, + long *fr_num, + long *flag, + long size, + void *buf); + long (*get_image) (struct videocodec * codec, + int tr_type, + int block, + long *fr_num, + long *flag, + long size, + void *buf); +}; + +struct videocodec_master { + /* -- filled in by master device for registration -- */ + char name[32]; + unsigned long magic; /* may be used for client<->master attaching */ + unsigned long flags; /* functionality flags */ + unsigned int type; /* master type */ + + void *data; /* private master data */ + + __u32(*readreg) (struct videocodec * codec, + __u16 reg); + void (*writereg) (struct videocodec * codec, + __u16 reg, + __u32 value); +}; + + +/* ================================================= */ +/* function prototypes of the master/slave interface */ +/* ================================================= */ + +/* attach and detach commands for the master */ +// * master structure needs to be kmalloc'ed before calling attach +// and free'd after calling detach +// * returns pointer on success, NULL on failure +extern struct videocodec *videocodec_attach(struct videocodec_master *); +// * 0 on success, <0 (errno) on failure +extern int videocodec_detach(struct videocodec *); + +/* register and unregister commands for the slaves */ +// * 0 on success, <0 (errno) on failure +extern int videocodec_register(const struct videocodec *); +// * 0 on success, <0 (errno) on failure +extern int videocodec_unregister(const struct videocodec *); + +/* the other calls are directly done via the videocodec structure! */ + +#endif /*ifndef __LINUX_VIDEOCODEC_H */ diff --git a/linux/drivers/media/video/vino.c b/linux/drivers/media/video/vino.c new file mode 100644 index 000000000..a8c101494 --- /dev/null +++ b/linux/drivers/media/video/vino.c @@ -0,0 +1,4680 @@ +/* + * Driver for the VINO (Video In No Out) system found in SGI Indys. + * + * This file is subject to the terms and conditions of the GNU General Public + * License version 2 as published by the Free Software Foundation. + * + * Copyright (C) 2004,2005 Mikael Nousiainen + * + * Based on the previous version of the driver for 2.4 kernels by: + * Copyright (C) 2003 Ladislav Michl + */ + +/* + * TODO: + * - remove "mark pages reserved-hacks" from memory allocation code + * and implement nopage() + * - check decimation, calculating and reporting image size when + * using decimation + * - implement read(), user mode buffers and overlay (?) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_KMOD +#include +#endif + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "vino.h" +#include "saa7191.h" +#include "indycam.h" + +/* Uncomment the following line to get lots and lots of (mostly useless) + * debug info. + * Note that the debug output also slows down the driver significantly */ +// #define VINO_DEBUG +// #define VINO_DEBUG_INT + +#define VINO_MODULE_VERSION "0.0.5" +#define VINO_VERSION_CODE KERNEL_VERSION(0, 0, 5) + +MODULE_DESCRIPTION("SGI VINO Video4Linux2 driver"); +MODULE_VERSION(VINO_MODULE_VERSION); +MODULE_AUTHOR("Mikael Nousiainen "); +MODULE_LICENSE("GPL"); + +#ifdef VINO_DEBUG +#define dprintk(x...) printk("VINO: " x); +#else +#define dprintk(x...) +#endif + +#define VINO_NO_CHANNEL 0 +#define VINO_CHANNEL_A 1 +#define VINO_CHANNEL_B 2 + +#define VINO_PAL_WIDTH 768 +#define VINO_PAL_HEIGHT 576 +#define VINO_NTSC_WIDTH 640 +#define VINO_NTSC_HEIGHT 480 + +#define VINO_MIN_WIDTH 32 +#define VINO_MIN_HEIGHT 32 + +#define VINO_CLIPPING_START_ODD_D1 1 +#define VINO_CLIPPING_START_ODD_PAL 15 +#define VINO_CLIPPING_START_ODD_NTSC 12 + +#define VINO_CLIPPING_START_EVEN_D1 2 +#define VINO_CLIPPING_START_EVEN_PAL 15 +#define VINO_CLIPPING_START_EVEN_NTSC 12 + +#define VINO_INPUT_CHANNEL_COUNT 3 + +/* the number is the index for vino_inputs */ +#define VINO_INPUT_NONE -1 +#define VINO_INPUT_COMPOSITE 0 +#define VINO_INPUT_SVIDEO 1 +#define VINO_INPUT_D1 2 + +#define VINO_PAGE_RATIO (PAGE_SIZE / VINO_PAGE_SIZE) + +#define VINO_FIFO_THRESHOLD_DEFAULT 16 + +#define VINO_FRAMEBUFFER_SIZE ((VINO_PAL_WIDTH \ + * VINO_PAL_HEIGHT * 4 \ + + 3 * PAGE_SIZE) & ~(PAGE_SIZE - 1)) + +#define VINO_FRAMEBUFFER_COUNT_MAX 8 + +#define VINO_FRAMEBUFFER_UNUSED 0 +#define VINO_FRAMEBUFFER_IN_USE 1 +#define VINO_FRAMEBUFFER_READY 2 + +#define VINO_QUEUE_ERROR -1 +#define VINO_QUEUE_MAGIC 0x20050125 + +#define VINO_MEMORY_NONE 0 +#define VINO_MEMORY_MMAP 1 +#define VINO_MEMORY_USERPTR 2 + +#define VINO_DUMMY_DESC_COUNT 4 +#define VINO_DESC_FETCH_DELAY 5 /* microseconds */ + +#define VINO_MAX_FRAME_SKIP_COUNT 128 + +/* the number is the index for vino_data_formats */ +#define VINO_DATA_FMT_NONE -1 +#define VINO_DATA_FMT_GREY 0 +#define VINO_DATA_FMT_RGB332 1 +#define VINO_DATA_FMT_RGB32 2 +#define VINO_DATA_FMT_YUV 3 + +#define VINO_DATA_FMT_COUNT 4 + +/* the number is the index for vino_data_norms */ +#define VINO_DATA_NORM_NONE -1 +#define VINO_DATA_NORM_NTSC 0 +#define VINO_DATA_NORM_PAL 1 +#define VINO_DATA_NORM_SECAM 2 +#define VINO_DATA_NORM_D1 3 +/* The following are special entries that can be used to + * autodetect the norm. */ +#define VINO_DATA_NORM_AUTO 0xfe +#define VINO_DATA_NORM_AUTO_EXT 0xff + +#define VINO_DATA_NORM_COUNT 4 + +/* Internal data structure definitions */ + +struct vino_input { + char *name; + v4l2_std_id std; +}; + +struct vino_clipping { + unsigned int left, right, top, bottom; +}; + +struct vino_data_format { + /* the description */ + char *description; + /* bytes per pixel */ + unsigned int bpp; + /* V4L2 fourcc code */ + __u32 pixelformat; + /* V4L2 colorspace (duh!) */ + enum v4l2_colorspace colorspace; +}; + +struct vino_data_norm { + char *description; + unsigned int width, height; + struct vino_clipping odd; + struct vino_clipping even; + + v4l2_std_id std; + unsigned int fps_min, fps_max; + __u32 framelines; +}; + +struct vino_descriptor_table { + /* the number of PAGE_SIZE sized pages in the buffer */ + unsigned int page_count; + /* virtual (kmalloc'd) pointers to the actual data + * (in PAGE_SIZE chunks, used with mmap streaming) */ + unsigned long *virtual; + + /* cpu address for the VINO descriptor table + * (contains DMA addresses, VINO_PAGE_SIZE chunks) */ + unsigned long *dma_cpu; + /* dma address for the VINO descriptor table + * (contains DMA addresses, VINO_PAGE_SIZE chunks) */ + dma_addr_t dma; +}; + +struct vino_framebuffer { + /* identifier nubmer */ + unsigned int id; + /* the length of the whole buffer */ + unsigned int size; + /* the length of actual data in buffer */ + unsigned int data_size; + /* the data format */ + unsigned int data_format; + /* the state of buffer data */ + unsigned int state; + /* is the buffer mapped in user space? */ + unsigned int map_count; + /* memory offset for mmap() */ + unsigned int offset; + /* frame counter */ + unsigned int frame_counter; + /* timestamp (written when image capture finishes) */ + struct timeval timestamp; + + struct vino_descriptor_table desc_table; + + spinlock_t state_lock; +}; + +struct vino_framebuffer_fifo { + unsigned int length; + + unsigned int used; + unsigned int head; + unsigned int tail; + + unsigned int data[VINO_FRAMEBUFFER_COUNT_MAX]; +}; + +struct vino_framebuffer_queue { + unsigned int magic; + + /* VINO_MEMORY_NONE, VINO_MEMORY_MMAP or VINO_MEMORY_USERPTR */ + unsigned int type; + unsigned int length; + + /* data field of in and out contain index numbers for buffer */ + struct vino_framebuffer_fifo in; + struct vino_framebuffer_fifo out; + + struct vino_framebuffer *buffer[VINO_FRAMEBUFFER_COUNT_MAX]; + + spinlock_t queue_lock; + struct mutex queue_mutex; + wait_queue_head_t frame_wait_queue; +}; + +struct vino_interrupt_data { + struct timeval timestamp; + unsigned int frame_counter; + unsigned int skip_count; + unsigned int skip; +}; + +struct vino_channel_settings { + unsigned int channel; + + int input; + unsigned int data_format; + unsigned int data_norm; + struct vino_clipping clipping; + unsigned int decimation; + unsigned int line_size; + unsigned int alpha; + unsigned int fps; + unsigned int framert_reg; + + unsigned int fifo_threshold; + + struct vino_framebuffer_queue fb_queue; + + /* number of the current field */ + unsigned int field; + + /* read in progress */ + int reading; + /* streaming is active */ + int streaming; + /* the driver is currently processing the queue */ + int capturing; + + struct mutex mutex; + spinlock_t capture_lock; + + unsigned int users; + + struct vino_interrupt_data int_data; + + /* V4L support */ + struct video_device *v4l_device; +}; + +struct vino_client { + /* the channel which owns this client: + * VINO_NO_CHANNEL, VINO_CHANNEL_A or VINO_CHANNEL_B */ + unsigned int owner; + struct i2c_client *driver; +}; + +struct vino_settings { + struct vino_channel_settings a; + struct vino_channel_settings b; + + struct vino_client decoder; + struct vino_client camera; + + /* a lock for vino register access */ + spinlock_t vino_lock; + /* a lock for channel input changes */ + spinlock_t input_lock; + + unsigned long dummy_page; + struct vino_descriptor_table dummy_desc_table; +}; + +/* Module parameters */ + +/* + * Using vino_pixel_conversion the ABGR32-format pixels supplied + * by the VINO chip can be converted to more common formats + * like RGBA32 (or probably RGB24 in the future). This way we + * can give out data that can be specified correctly with + * the V4L2-definitions. + * + * The pixel format is specified as RGBA32 when no conversion + * is used. + * + * Note that this only affects the 32-bit bit depth. + * + * Use non-zero value to enable conversion. + */ +static int vino_pixel_conversion = 0; + +module_param_named(pixelconv, vino_pixel_conversion, int, 0); + +MODULE_PARM_DESC(pixelconv, + "enable pixel conversion (non-zero value enables)"); + +/* Internal data structures */ + +static struct sgi_vino *vino; + +static struct vino_settings *vino_drvdata; + +static const char *vino_driver_name = "vino"; +static const char *vino_driver_description = "SGI VINO"; +static const char *vino_bus_name = "GIO64 bus"; +static const char *vino_v4l_device_name_a = "SGI VINO Channel A"; +static const char *vino_v4l_device_name_b = "SGI VINO Channel B"; + +static void vino_capture_tasklet(unsigned long channel); + +DECLARE_TASKLET(vino_tasklet_a, vino_capture_tasklet, VINO_CHANNEL_A); +DECLARE_TASKLET(vino_tasklet_b, vino_capture_tasklet, VINO_CHANNEL_B); + +static const struct vino_input vino_inputs[] = { + { + .name = "Composite", + .std = V4L2_STD_NTSC | V4L2_STD_PAL + | V4L2_STD_SECAM, + },{ + .name = "S-Video", + .std = V4L2_STD_NTSC | V4L2_STD_PAL + | V4L2_STD_SECAM, + },{ + .name = "D1/IndyCam", + .std = V4L2_STD_NTSC, + } +}; + +static const struct vino_data_format vino_data_formats[] = { + { + .description = "8-bit greyscale", + .bpp = 1, + .pixelformat = V4L2_PIX_FMT_GREY, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + },{ + .description = "8-bit dithered RGB 3-3-2", + .bpp = 1, + .pixelformat = V4L2_PIX_FMT_RGB332, + .colorspace = V4L2_COLORSPACE_SRGB, + },{ + .description = "32-bit RGB", + .bpp = 4, + .pixelformat = V4L2_PIX_FMT_RGB32, + .colorspace = V4L2_COLORSPACE_SRGB, + },{ + .description = "YUV 4:2:2", + .bpp = 2, + .pixelformat = V4L2_PIX_FMT_YUYV, // XXX: swapped? + .colorspace = V4L2_COLORSPACE_SMPTE170M, + } +}; + +static const struct vino_data_norm vino_data_norms[] = { + { + .description = "NTSC", + .std = V4L2_STD_NTSC, + .fps_min = 6, + .fps_max = 30, + .framelines = 525, + .width = VINO_NTSC_WIDTH, + .height = VINO_NTSC_HEIGHT, + .odd = { + .top = VINO_CLIPPING_START_ODD_NTSC, + .left = 0, + .bottom = VINO_CLIPPING_START_ODD_NTSC + + VINO_NTSC_HEIGHT / 2 - 1, + .right = VINO_NTSC_WIDTH, + }, + .even = { + .top = VINO_CLIPPING_START_EVEN_NTSC, + .left = 0, + .bottom = VINO_CLIPPING_START_EVEN_NTSC + + VINO_NTSC_HEIGHT / 2 - 1, + .right = VINO_NTSC_WIDTH, + }, + },{ + .description = "PAL", + .std = V4L2_STD_PAL, + .fps_min = 5, + .fps_max = 25, + .framelines = 625, + .width = VINO_PAL_WIDTH, + .height = VINO_PAL_HEIGHT, + .odd = { + .top = VINO_CLIPPING_START_ODD_PAL, + .left = 0, + .bottom = VINO_CLIPPING_START_ODD_PAL + + VINO_PAL_HEIGHT / 2 - 1, + .right = VINO_PAL_WIDTH, + }, + .even = { + .top = VINO_CLIPPING_START_EVEN_PAL, + .left = 0, + .bottom = VINO_CLIPPING_START_EVEN_PAL + + VINO_PAL_HEIGHT / 2 - 1, + .right = VINO_PAL_WIDTH, + }, + },{ + .description = "SECAM", + .std = V4L2_STD_SECAM, + .fps_min = 5, + .fps_max = 25, + .framelines = 625, + .width = VINO_PAL_WIDTH, + .height = VINO_PAL_HEIGHT, + .odd = { + .top = VINO_CLIPPING_START_ODD_PAL, + .left = 0, + .bottom = VINO_CLIPPING_START_ODD_PAL + + VINO_PAL_HEIGHT / 2 - 1, + .right = VINO_PAL_WIDTH, + }, + .even = { + .top = VINO_CLIPPING_START_EVEN_PAL, + .left = 0, + .bottom = VINO_CLIPPING_START_EVEN_PAL + + VINO_PAL_HEIGHT / 2 - 1, + .right = VINO_PAL_WIDTH, + }, + },{ + .description = "NTSC/D1", + .std = V4L2_STD_NTSC, + .fps_min = 6, + .fps_max = 30, + .framelines = 525, + .width = VINO_NTSC_WIDTH, + .height = VINO_NTSC_HEIGHT, + .odd = { + .top = VINO_CLIPPING_START_ODD_D1, + .left = 0, + .bottom = VINO_CLIPPING_START_ODD_D1 + + VINO_NTSC_HEIGHT / 2 - 1, + .right = VINO_NTSC_WIDTH, + }, + .even = { + .top = VINO_CLIPPING_START_EVEN_D1, + .left = 0, + .bottom = VINO_CLIPPING_START_EVEN_D1 + + VINO_NTSC_HEIGHT / 2 - 1, + .right = VINO_NTSC_WIDTH, + }, + } +}; + +#define VINO_INDYCAM_V4L2_CONTROL_COUNT 9 + +struct v4l2_queryctrl vino_indycam_v4l2_controls[] = { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Automatic Gain Control", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = INDYCAM_AGC_DEFAULT, + .flags = 0, + .reserved = { INDYCAM_CONTROL_AGC, 0 }, + },{ + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Automatic White Balance", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = INDYCAM_AWB_DEFAULT, + .flags = 0, + .reserved = { INDYCAM_CONTROL_AWB, 0 }, + },{ + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = INDYCAM_GAIN_MIN, + .maximum = INDYCAM_GAIN_MAX, + .step = 1, + .default_value = INDYCAM_GAIN_DEFAULT, + .flags = 0, + .reserved = { INDYCAM_CONTROL_GAIN, 0 }, + },{ + .id = V4L2_CID_PRIVATE_BASE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Saturation", + .minimum = INDYCAM_RED_SATURATION_MIN, + .maximum = INDYCAM_RED_SATURATION_MAX, + .step = 1, + .default_value = INDYCAM_RED_SATURATION_DEFAULT, + .flags = 0, + .reserved = { INDYCAM_CONTROL_RED_SATURATION, 0 }, + },{ + .id = V4L2_CID_PRIVATE_BASE + 1, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Saturation", + .minimum = INDYCAM_BLUE_SATURATION_MIN, + .maximum = INDYCAM_BLUE_SATURATION_MAX, + .step = 1, + .default_value = INDYCAM_BLUE_SATURATION_DEFAULT, + .flags = 0, + .reserved = { INDYCAM_CONTROL_BLUE_SATURATION, 0 }, + },{ + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = INDYCAM_RED_BALANCE_MIN, + .maximum = INDYCAM_RED_BALANCE_MAX, + .step = 1, + .default_value = INDYCAM_RED_BALANCE_DEFAULT, + .flags = 0, + .reserved = { INDYCAM_CONTROL_RED_BALANCE, 0 }, + },{ + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = INDYCAM_BLUE_BALANCE_MIN, + .maximum = INDYCAM_BLUE_BALANCE_MAX, + .step = 1, + .default_value = INDYCAM_BLUE_BALANCE_DEFAULT, + .flags = 0, + .reserved = { INDYCAM_CONTROL_BLUE_BALANCE, 0 }, + },{ + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Shutter Control", + .minimum = INDYCAM_SHUTTER_MIN, + .maximum = INDYCAM_SHUTTER_MAX, + .step = 1, + .default_value = INDYCAM_SHUTTER_DEFAULT, + .flags = 0, + .reserved = { INDYCAM_CONTROL_SHUTTER, 0 }, + },{ + .id = V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma", + .minimum = INDYCAM_GAMMA_MIN, + .maximum = INDYCAM_GAMMA_MAX, + .step = 1, + .default_value = INDYCAM_GAMMA_DEFAULT, + .flags = 0, + .reserved = { INDYCAM_CONTROL_GAMMA, 0 }, + } +}; + +#define VINO_SAA7191_V4L2_CONTROL_COUNT 9 + +struct v4l2_queryctrl vino_saa7191_v4l2_controls[] = { + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = SAA7191_HUE_MIN, + .maximum = SAA7191_HUE_MAX, + .step = 1, + .default_value = SAA7191_HUE_DEFAULT, + .flags = 0, + .reserved = { SAA7191_CONTROL_HUE, 0 }, + },{ + .id = V4L2_CID_PRIVATE_BASE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Luminance Bandpass", + .minimum = SAA7191_BANDPASS_MIN, + .maximum = SAA7191_BANDPASS_MAX, + .step = 1, + .default_value = SAA7191_BANDPASS_DEFAULT, + .flags = 0, + .reserved = { SAA7191_CONTROL_BANDPASS, 0 }, + },{ + .id = V4L2_CID_PRIVATE_BASE + 1, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Luminance Bandpass Weight", + .minimum = SAA7191_BANDPASS_WEIGHT_MIN, + .maximum = SAA7191_BANDPASS_WEIGHT_MAX, + .step = 1, + .default_value = SAA7191_BANDPASS_WEIGHT_DEFAULT, + .flags = 0, + .reserved = { SAA7191_CONTROL_BANDPASS_WEIGHT, 0 }, + },{ + .id = V4L2_CID_PRIVATE_BASE + 2, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "HF Luminance Coring", + .minimum = SAA7191_CORING_MIN, + .maximum = SAA7191_CORING_MAX, + .step = 1, + .default_value = SAA7191_CORING_DEFAULT, + .flags = 0, + .reserved = { SAA7191_CONTROL_CORING, 0 }, + },{ + .id = V4L2_CID_PRIVATE_BASE + 3, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Force Colour", + .minimum = SAA7191_FORCE_COLOUR_MIN, + .maximum = SAA7191_FORCE_COLOUR_MAX, + .step = 1, + .default_value = SAA7191_FORCE_COLOUR_DEFAULT, + .flags = 0, + .reserved = { SAA7191_CONTROL_FORCE_COLOUR, 0 }, + },{ + .id = V4L2_CID_PRIVATE_BASE + 4, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Chrominance Gain Control", + .minimum = SAA7191_CHROMA_GAIN_MIN, + .maximum = SAA7191_CHROMA_GAIN_MAX, + .step = 1, + .default_value = SAA7191_CHROMA_GAIN_DEFAULT, + .flags = 0, + .reserved = { SAA7191_CONTROL_CHROMA_GAIN, 0 }, + },{ + .id = V4L2_CID_PRIVATE_BASE + 5, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "VTR Time Constant", + .minimum = SAA7191_VTRC_MIN, + .maximum = SAA7191_VTRC_MAX, + .step = 1, + .default_value = SAA7191_VTRC_DEFAULT, + .flags = 0, + .reserved = { SAA7191_CONTROL_VTRC, 0 }, + },{ + .id = V4L2_CID_PRIVATE_BASE + 6, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Luminance Delay Compensation", + .minimum = SAA7191_LUMA_DELAY_MIN, + .maximum = SAA7191_LUMA_DELAY_MAX, + .step = 1, + .default_value = SAA7191_LUMA_DELAY_DEFAULT, + .flags = 0, + .reserved = { SAA7191_CONTROL_LUMA_DELAY, 0 }, + },{ + .id = V4L2_CID_PRIVATE_BASE + 7, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Vertical Noise Reduction", + .minimum = SAA7191_VNR_MIN, + .maximum = SAA7191_VNR_MAX, + .step = 1, + .default_value = SAA7191_VNR_DEFAULT, + .flags = 0, + .reserved = { SAA7191_CONTROL_VNR, 0 }, + } +}; + +/* VINO I2C bus functions */ + +unsigned i2c_vino_getctrl(void *data) +{ + return vino->i2c_control; +} + +void i2c_vino_setctrl(void *data, unsigned val) +{ + vino->i2c_control = val; +} + +unsigned i2c_vino_rdata(void *data) +{ + return vino->i2c_data; +} + +void i2c_vino_wdata(void *data, unsigned val) +{ + vino->i2c_data = val; +} + +static struct i2c_algo_sgi_data i2c_sgi_vino_data = +{ + .getctrl = &i2c_vino_getctrl, + .setctrl = &i2c_vino_setctrl, + .rdata = &i2c_vino_rdata, + .wdata = &i2c_vino_wdata, + .xfer_timeout = 200, + .ack_timeout = 1000, +}; + +/* + * There are two possible clients on VINO I2C bus, so we limit usage only + * to them. + */ +static int i2c_vino_client_reg(struct i2c_client *client) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + switch (client->driver->id) { + case I2C_DRIVERID_SAA7191: + if (vino_drvdata->decoder.driver) + ret = -EBUSY; + else + vino_drvdata->decoder.driver = client; + break; + case I2C_DRIVERID_INDYCAM: + if (vino_drvdata->camera.driver) + ret = -EBUSY; + else + vino_drvdata->camera.driver = client; + break; + default: + ret = -ENODEV; + } + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + return ret; +} + +static int i2c_vino_client_unreg(struct i2c_client *client) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + if (client == vino_drvdata->decoder.driver) { + if (vino_drvdata->decoder.owner != VINO_NO_CHANNEL) + ret = -EBUSY; + else + vino_drvdata->decoder.driver = NULL; + } else if (client == vino_drvdata->camera.driver) { + if (vino_drvdata->camera.owner != VINO_NO_CHANNEL) + ret = -EBUSY; + else + vino_drvdata->camera.driver = NULL; + } + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + return ret; +} + +static struct i2c_adapter vino_i2c_adapter = +{ + .name = "VINO I2C bus", + .id = I2C_HW_SGI_VINO, + .algo_data = &i2c_sgi_vino_data, + .client_register = &i2c_vino_client_reg, + .client_unregister = &i2c_vino_client_unreg, +}; + +static int vino_i2c_add_bus(void) +{ + return i2c_sgi_add_bus(&vino_i2c_adapter); +} + +static int vino_i2c_del_bus(void) +{ + return i2c_sgi_del_bus(&vino_i2c_adapter); +} + +static int i2c_camera_command(unsigned int cmd, void *arg) +{ + return vino_drvdata->camera.driver-> + driver->command(vino_drvdata->camera.driver, + cmd, arg); +} + +static int i2c_decoder_command(unsigned int cmd, void *arg) +{ + return vino_drvdata->decoder.driver-> + driver->command(vino_drvdata->decoder.driver, + cmd, arg); +} + +/* VINO framebuffer/DMA descriptor management */ + +static void vino_free_buffer_with_count(struct vino_framebuffer *fb, + unsigned int count) +{ + unsigned int i; + + dprintk("vino_free_buffer_with_count(): count = %d\n", count); + + for (i = 0; i < count; i++) { + ClearPageReserved(virt_to_page(fb->desc_table.virtual[i])); + dma_unmap_single(NULL, + fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i], + PAGE_SIZE, DMA_FROM_DEVICE); + free_page(fb->desc_table.virtual[i]); + } + + dma_free_coherent(NULL, + VINO_PAGE_RATIO * (fb->desc_table.page_count + 4) * + sizeof(dma_addr_t), (void *)fb->desc_table.dma_cpu, + fb->desc_table.dma); + kfree(fb->desc_table.virtual); + + memset(fb, 0, sizeof(struct vino_framebuffer)); +} + +static void vino_free_buffer(struct vino_framebuffer *fb) +{ + vino_free_buffer_with_count(fb, fb->desc_table.page_count); +} + +static int vino_allocate_buffer(struct vino_framebuffer *fb, + unsigned int size) +{ + unsigned int count, i, j; + int ret = 0; + + dprintk("vino_allocate_buffer():\n"); + + if (size < 1) + return -EINVAL; + + memset(fb, 0, sizeof(struct vino_framebuffer)); + + count = ((size / PAGE_SIZE) + 4) & ~3; + + dprintk("vino_allocate_buffer(): size = %d, count = %d\n", + size, count); + + /* allocate memory for table with virtual (page) addresses */ + fb->desc_table.virtual = (unsigned long *) + kmalloc(count * sizeof(unsigned long), GFP_KERNEL); + if (!fb->desc_table.virtual) + return -ENOMEM; + + /* allocate memory for table with dma addresses + * (has space for four extra descriptors) */ + fb->desc_table.dma_cpu = + dma_alloc_coherent(NULL, VINO_PAGE_RATIO * (count + 4) * + sizeof(dma_addr_t), &fb->desc_table.dma, + GFP_KERNEL | GFP_DMA); + if (!fb->desc_table.dma_cpu) { + ret = -ENOMEM; + goto out_free_virtual; + } + + /* allocate pages for the buffer and acquire the according + * dma addresses */ + for (i = 0; i < count; i++) { + dma_addr_t dma_data_addr; + + fb->desc_table.virtual[i] = + get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!fb->desc_table.virtual[i]) { + ret = -ENOBUFS; + break; + } + + dma_data_addr = + dma_map_single(NULL, + (void *)fb->desc_table.virtual[i], + PAGE_SIZE, DMA_FROM_DEVICE); + + for (j = 0; j < VINO_PAGE_RATIO; j++) { + fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i + j] = + dma_data_addr + VINO_PAGE_SIZE * j; + } + + SetPageReserved(virt_to_page(fb->desc_table.virtual[i])); + } + + /* page_count needs to be set anyway, because the descriptor table has + * been allocated according to this number */ + fb->desc_table.page_count = count; + + if (ret) { + /* the descriptor with index i doesn't contain + * a valid address yet */ + vino_free_buffer_with_count(fb, i); + return ret; + } + + //fb->size = size; + fb->size = count * PAGE_SIZE; + fb->data_format = VINO_DATA_FMT_NONE; + + /* set the dma stop-bit for the last (count+1)th descriptor */ + fb->desc_table.dma_cpu[VINO_PAGE_RATIO * count] = VINO_DESC_STOP; + return 0; + + out_free_virtual: + kfree(fb->desc_table.virtual); + return ret; +} + +#if 0 +/* user buffers not fully implemented yet */ +static int vino_prepare_user_buffer(struct vino_framebuffer *fb, + void *user, + unsigned int size) +{ + unsigned int count, i, j; + int ret = 0; + + dprintk("vino_prepare_user_buffer():\n"); + + if (size < 1) + return -EINVAL; + + memset(fb, 0, sizeof(struct vino_framebuffer)); + + count = ((size / PAGE_SIZE)) & ~3; + + dprintk("vino_prepare_user_buffer(): size = %d, count = %d\n", + size, count); + + /* allocate memory for table with virtual (page) addresses */ + fb->desc_table.virtual = (unsigned long *) + kmalloc(count * sizeof(unsigned long), GFP_KERNEL); + if (!fb->desc_table.virtual) + return -ENOMEM; + + /* allocate memory for table with dma addresses + * (has space for four extra descriptors) */ + fb->desc_table.dma_cpu = + dma_alloc_coherent(NULL, VINO_PAGE_RATIO * (count + 4) * + sizeof(dma_addr_t), &fb->desc_table.dma, + GFP_KERNEL | GFP_DMA); + if (!fb->desc_table.dma_cpu) { + ret = -ENOMEM; + goto out_free_virtual; + } + + /* allocate pages for the buffer and acquire the according + * dma addresses */ + for (i = 0; i < count; i++) { + dma_addr_t dma_data_addr; + + fb->desc_table.virtual[i] = + get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!fb->desc_table.virtual[i]) { + ret = -ENOBUFS; + break; + } + + dma_data_addr = + dma_map_single(NULL, + (void *)fb->desc_table.virtual[i], + PAGE_SIZE, DMA_FROM_DEVICE); + + for (j = 0; j < VINO_PAGE_RATIO; j++) { + fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i + j] = + dma_data_addr + VINO_PAGE_SIZE * j; + } + + SetPageReserved(virt_to_page(fb->desc_table.virtual[i])); + } + + /* page_count needs to be set anyway, because the descriptor table has + * been allocated according to this number */ + fb->desc_table.page_count = count; + + if (ret) { + /* the descriptor with index i doesn't contain + * a valid address yet */ + vino_free_buffer_with_count(fb, i); + return ret; + } + + //fb->size = size; + fb->size = count * PAGE_SIZE; + + /* set the dma stop-bit for the last (count+1)th descriptor */ + fb->desc_table.dma_cpu[VINO_PAGE_RATIO * count] = VINO_DESC_STOP; + return 0; + + out_free_virtual: + kfree(fb->desc_table.virtual); + return ret; +} +#endif + +static void vino_sync_buffer(struct vino_framebuffer *fb) +{ + int i; + + dprintk("vino_sync_buffer():\n"); + + for (i = 0; i < fb->desc_table.page_count; i++) + dma_sync_single(NULL, + fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i], + PAGE_SIZE, DMA_FROM_DEVICE); +} + +/* Framebuffer fifo functions (need to be locked externally) */ + +static inline void vino_fifo_init(struct vino_framebuffer_fifo *f, + unsigned int length) +{ + f->length = 0; + f->used = 0; + f->head = 0; + f->tail = 0; + + if (length > VINO_FRAMEBUFFER_COUNT_MAX) + length = VINO_FRAMEBUFFER_COUNT_MAX; + + f->length = length; +} + +/* returns true/false */ +static inline int vino_fifo_has_id(struct vino_framebuffer_fifo *f, + unsigned int id) +{ + unsigned int i; + + for (i = f->head; i == (f->tail - 1); i = (i + 1) % f->length) { + if (f->data[i] == id) + return 1; + } + + return 0; +} + +#if 0 +/* returns true/false */ +static inline int vino_fifo_full(struct vino_framebuffer_fifo *f) +{ + return (f->used == f->length); +} +#endif + +static inline unsigned int vino_fifo_get_used(struct vino_framebuffer_fifo *f) +{ + return f->used; +} + +static int vino_fifo_enqueue(struct vino_framebuffer_fifo *f, unsigned int id) +{ + if (id >= f->length) { + return VINO_QUEUE_ERROR; + } + + if (vino_fifo_has_id(f, id)) { + return VINO_QUEUE_ERROR; + } + + if (f->used < f->length) { + f->data[f->tail] = id; + f->tail = (f->tail + 1) % f->length; + f->used++; + } else { + return VINO_QUEUE_ERROR; + } + + return 0; +} + +static int vino_fifo_peek(struct vino_framebuffer_fifo *f, unsigned int *id) +{ + if (f->used > 0) { + *id = f->data[f->head]; + } else { + return VINO_QUEUE_ERROR; + } + + return 0; +} + +static int vino_fifo_dequeue(struct vino_framebuffer_fifo *f, unsigned int *id) +{ + if (f->used > 0) { + *id = f->data[f->head]; + f->head = (f->head + 1) % f->length; + f->used--; + } else { + return VINO_QUEUE_ERROR; + } + + return 0; +} + +/* Framebuffer queue functions */ + +/* execute with queue_lock locked */ +static void vino_queue_free_with_count(struct vino_framebuffer_queue *q, + unsigned int length) +{ + unsigned int i; + + q->length = 0; + memset(&q->in, 0, sizeof(struct vino_framebuffer_fifo)); + memset(&q->out, 0, sizeof(struct vino_framebuffer_fifo)); + for (i = 0; i < length; i++) { + dprintk("vino_queue_free_with_count(): freeing buffer %d\n", + i); + vino_free_buffer(q->buffer[i]); + kfree(q->buffer[i]); + } + + q->type = VINO_MEMORY_NONE; + q->magic = 0; +} + +static void vino_queue_free(struct vino_framebuffer_queue *q) +{ + dprintk("vino_queue_free():\n"); + + if (q->magic != VINO_QUEUE_MAGIC) + return; + if (q->type != VINO_MEMORY_MMAP) + return; + + mutex_lock(&q->queue_mutex); + + vino_queue_free_with_count(q, q->length); + + mutex_unlock(&q->queue_mutex); +} + +static int vino_queue_init(struct vino_framebuffer_queue *q, + unsigned int *length) +{ + unsigned int i; + int ret = 0; + + dprintk("vino_queue_init(): length = %d\n", *length); + + if (q->magic == VINO_QUEUE_MAGIC) { + dprintk("vino_queue_init(): queue already initialized!\n"); + return -EINVAL; + } + + if (q->type != VINO_MEMORY_NONE) { + dprintk("vino_queue_init(): queue already initialized!\n"); + return -EINVAL; + } + + if (*length < 1) + return -EINVAL; + + mutex_lock(&q->queue_mutex); + + if (*length > VINO_FRAMEBUFFER_COUNT_MAX) + *length = VINO_FRAMEBUFFER_COUNT_MAX; + + q->length = 0; + + for (i = 0; i < *length; i++) { + dprintk("vino_queue_init(): allocating buffer %d\n", i); + q->buffer[i] = kmalloc(sizeof(struct vino_framebuffer), + GFP_KERNEL); + if (!q->buffer[i]) { + dprintk("vino_queue_init(): kmalloc() failed\n"); + ret = -ENOMEM; + break; + } + + ret = vino_allocate_buffer(q->buffer[i], + VINO_FRAMEBUFFER_SIZE); + if (ret) { + kfree(q->buffer[i]); + dprintk("vino_queue_init(): " + "vino_allocate_buffer() failed\n"); + break; + } + + q->buffer[i]->id = i; + if (i > 0) { + q->buffer[i]->offset = q->buffer[i - 1]->offset + + q->buffer[i - 1]->size; + } else { + q->buffer[i]->offset = 0; + } + + spin_lock_init(&q->buffer[i]->state_lock); + + dprintk("vino_queue_init(): buffer = %d, offset = %d, " + "size = %d\n", i, q->buffer[i]->offset, + q->buffer[i]->size); + } + + if (ret) { + vino_queue_free_with_count(q, i); + *length = 0; + } else { + q->length = *length; + vino_fifo_init(&q->in, q->length); + vino_fifo_init(&q->out, q->length); + q->type = VINO_MEMORY_MMAP; + q->magic = VINO_QUEUE_MAGIC; + } + + mutex_unlock(&q->queue_mutex); + + return ret; +} + +static struct vino_framebuffer *vino_queue_add(struct + vino_framebuffer_queue *q, + unsigned int id) +{ + struct vino_framebuffer *ret = NULL; + unsigned int total; + unsigned long flags; + + dprintk("vino_queue_add(): id = %d\n", id); + + if (q->magic != VINO_QUEUE_MAGIC) { + return ret; + } + + spin_lock_irqsave(&q->queue_lock, flags); + + if (q->length == 0) + goto out; + + if (id >= q->length) + goto out; + + /* not needed?: if (vino_fifo_full(&q->out)) { + goto out; + }*/ + /* check that outgoing queue isn't already full + * (or that it won't become full) */ + total = vino_fifo_get_used(&q->in) + + vino_fifo_get_used(&q->out); + if (total >= q->length) + goto out; + + if (vino_fifo_enqueue(&q->in, id)) + goto out; + + ret = q->buffer[id]; + +out: + spin_unlock_irqrestore(&q->queue_lock, flags); + + return ret; +} + +static struct vino_framebuffer *vino_queue_transfer(struct + vino_framebuffer_queue *q) +{ + struct vino_framebuffer *ret = NULL; + struct vino_framebuffer *fb; + int id; + unsigned long flags; + + dprintk("vino_queue_transfer():\n"); + + if (q->magic != VINO_QUEUE_MAGIC) { + return ret; + } + + spin_lock_irqsave(&q->queue_lock, flags); + + if (q->length == 0) + goto out; + + // now this actually removes an entry from the incoming queue + if (vino_fifo_dequeue(&q->in, &id)) { + goto out; + } + + dprintk("vino_queue_transfer(): id = %d\n", id); + fb = q->buffer[id]; + + // we have already checked that the outgoing queue is not full, but... + if (vino_fifo_enqueue(&q->out, id)) { + printk(KERN_ERR "vino_queue_transfer(): " + "outgoing queue is full, this shouldn't happen!\n"); + goto out; + } + + ret = fb; +out: + spin_unlock_irqrestore(&q->queue_lock, flags); + + return ret; +} + +/* returns true/false */ +static int vino_queue_incoming_contains(struct vino_framebuffer_queue *q, + unsigned int id) +{ + int ret = 0; + unsigned long flags; + + if (q->magic != VINO_QUEUE_MAGIC) { + return ret; + } + + spin_lock_irqsave(&q->queue_lock, flags); + + if (q->length == 0) + goto out; + + ret = vino_fifo_has_id(&q->in, id); + +out: + spin_unlock_irqrestore(&q->queue_lock, flags); + + return ret; +} + +/* returns true/false */ +static int vino_queue_outgoing_contains(struct vino_framebuffer_queue *q, + unsigned int id) +{ + int ret = 0; + unsigned long flags; + + if (q->magic != VINO_QUEUE_MAGIC) { + return ret; + } + + spin_lock_irqsave(&q->queue_lock, flags); + + if (q->length == 0) + goto out; + + ret = vino_fifo_has_id(&q->out, id); + +out: + spin_unlock_irqrestore(&q->queue_lock, flags); + + return ret; +} + +static int vino_queue_get_incoming(struct vino_framebuffer_queue *q, + unsigned int *used) +{ + int ret = 0; + unsigned long flags; + + if (q->magic != VINO_QUEUE_MAGIC) { + return VINO_QUEUE_ERROR; + } + + spin_lock_irqsave(&q->queue_lock, flags); + + if (q->length == 0) { + ret = VINO_QUEUE_ERROR; + goto out; + } + + *used = vino_fifo_get_used(&q->in); + +out: + spin_unlock_irqrestore(&q->queue_lock, flags); + + return ret; +} + +static int vino_queue_get_outgoing(struct vino_framebuffer_queue *q, + unsigned int *used) +{ + int ret = 0; + unsigned long flags; + + if (q->magic != VINO_QUEUE_MAGIC) { + return VINO_QUEUE_ERROR; + } + + spin_lock_irqsave(&q->queue_lock, flags); + + if (q->length == 0) { + ret = VINO_QUEUE_ERROR; + goto out; + } + + *used = vino_fifo_get_used(&q->out); + +out: + spin_unlock_irqrestore(&q->queue_lock, flags); + + return ret; +} + +#if 0 +static int vino_queue_get_total(struct vino_framebuffer_queue *q, + unsigned int *total) +{ + int ret = 0; + unsigned long flags; + + if (q->magic != VINO_QUEUE_MAGIC) { + return VINO_QUEUE_ERROR; + } + + spin_lock_irqsave(&q->queue_lock, flags); + + if (q->length == 0) { + ret = VINO_QUEUE_ERROR; + goto out; + } + + *total = vino_fifo_get_used(&q->in) + + vino_fifo_get_used(&q->out); + +out: + spin_unlock_irqrestore(&q->queue_lock, flags); + + return ret; +} +#endif + +static struct vino_framebuffer *vino_queue_peek(struct + vino_framebuffer_queue *q, + unsigned int *id) +{ + struct vino_framebuffer *ret = NULL; + unsigned long flags; + + if (q->magic != VINO_QUEUE_MAGIC) { + return ret; + } + + spin_lock_irqsave(&q->queue_lock, flags); + + if (q->length == 0) + goto out; + + if (vino_fifo_peek(&q->in, id)) { + goto out; + } + + ret = q->buffer[*id]; +out: + spin_unlock_irqrestore(&q->queue_lock, flags); + + return ret; +} + +static struct vino_framebuffer *vino_queue_remove(struct + vino_framebuffer_queue *q, + unsigned int *id) +{ + struct vino_framebuffer *ret = NULL; + unsigned long flags; + dprintk("vino_queue_remove():\n"); + + if (q->magic != VINO_QUEUE_MAGIC) { + return ret; + } + + spin_lock_irqsave(&q->queue_lock, flags); + + if (q->length == 0) + goto out; + + if (vino_fifo_dequeue(&q->out, id)) { + goto out; + } + + dprintk("vino_queue_remove(): id = %d\n", *id); + ret = q->buffer[*id]; +out: + spin_unlock_irqrestore(&q->queue_lock, flags); + + return ret; +} + +static struct +vino_framebuffer *vino_queue_get_buffer(struct vino_framebuffer_queue *q, + unsigned int id) +{ + struct vino_framebuffer *ret = NULL; + unsigned long flags; + + if (q->magic != VINO_QUEUE_MAGIC) { + return ret; + } + + spin_lock_irqsave(&q->queue_lock, flags); + + if (q->length == 0) + goto out; + + if (id >= q->length) + goto out; + + ret = q->buffer[id]; + out: + spin_unlock_irqrestore(&q->queue_lock, flags); + + return ret; +} + +static unsigned int vino_queue_get_length(struct vino_framebuffer_queue *q) +{ + unsigned int length = 0; + unsigned long flags; + + if (q->magic != VINO_QUEUE_MAGIC) { + return length; + } + + spin_lock_irqsave(&q->queue_lock, flags); + length = q->length; + spin_unlock_irqrestore(&q->queue_lock, flags); + + return length; +} + +static int vino_queue_has_mapped_buffers(struct vino_framebuffer_queue *q) +{ + unsigned int i; + int ret = 0; + unsigned long flags; + + if (q->magic != VINO_QUEUE_MAGIC) { + return ret; + } + + spin_lock_irqsave(&q->queue_lock, flags); + for (i = 0; i < q->length; i++) { + if (q->buffer[i]->map_count > 0) { + ret = 1; + break; + } + } + spin_unlock_irqrestore(&q->queue_lock, flags); + + return ret; +} + +/* VINO functions */ + +/* execute with input_lock locked */ +static void vino_update_line_size(struct vino_channel_settings *vcs) +{ + unsigned int w = vcs->clipping.right - vcs->clipping.left; + unsigned int d = vcs->decimation; + unsigned int bpp = vino_data_formats[vcs->data_format].bpp; + unsigned int lsize; + + dprintk("update_line_size(): before: w = %d, d = %d, " + "line_size = %d\n", w, d, vcs->line_size); + + /* line size must be multiple of 8 bytes */ + lsize = (bpp * (w / d)) & ~7; + w = (lsize / bpp) * d; + + vcs->clipping.right = vcs->clipping.left + w; + vcs->line_size = lsize; + + dprintk("update_line_size(): after: w = %d, d = %d, " + "line_size = %d\n", w, d, vcs->line_size); +} + +/* execute with input_lock locked */ +static void vino_set_clipping(struct vino_channel_settings *vcs, + unsigned int x, unsigned int y, + unsigned int w, unsigned int h) +{ + unsigned int maxwidth, maxheight; + unsigned int d; + + maxwidth = vino_data_norms[vcs->data_norm].width; + maxheight = vino_data_norms[vcs->data_norm].height; + d = vcs->decimation; + + y &= ~1; /* odd/even fields */ + + if (x > maxwidth) { + x = 0; + } + if (y > maxheight) { + y = 0; + } + + if (((w / d) < VINO_MIN_WIDTH) + || ((h / d) < VINO_MIN_HEIGHT)) { + w = VINO_MIN_WIDTH * d; + h = VINO_MIN_HEIGHT * d; + } + + if ((x + w) > maxwidth) { + w = maxwidth - x; + if ((w / d) < VINO_MIN_WIDTH) + x = maxwidth - VINO_MIN_WIDTH * d; + } + if ((y + h) > maxheight) { + h = maxheight - y; + if ((h / d) < VINO_MIN_HEIGHT) + y = maxheight - VINO_MIN_HEIGHT * d; + } + + vcs->clipping.left = x; + vcs->clipping.top = y; + vcs->clipping.right = x + w; + vcs->clipping.bottom = y + h; + + vino_update_line_size(vcs); + + dprintk("clipping %d, %d, %d, %d / %d - %d\n", + vcs->clipping.left, vcs->clipping.top, vcs->clipping.right, + vcs->clipping.bottom, vcs->decimation, vcs->line_size); +} + +/* execute with input_lock locked */ +static inline void vino_set_default_clipping(struct vino_channel_settings *vcs) +{ + vino_set_clipping(vcs, 0, 0, vino_data_norms[vcs->data_norm].width, + vino_data_norms[vcs->data_norm].height); +} + +/* execute with input_lock locked */ +static void vino_set_scaling(struct vino_channel_settings *vcs, + unsigned int w, unsigned int h) +{ + unsigned int x, y, curw, curh, d; + + x = vcs->clipping.left; + y = vcs->clipping.top; + curw = vcs->clipping.right - vcs->clipping.left; + curh = vcs->clipping.bottom - vcs->clipping.top; + + d = max(curw / w, curh / h); + + dprintk("scaling w: %d, h: %d, curw: %d, curh: %d, d: %d\n", + w, h, curw, curh, d); + + if (d < 1) { + d = 1; + } else if (d > 8) { + d = 8; + } + + vcs->decimation = d; + vino_set_clipping(vcs, x, y, w * d, h * d); + + dprintk("scaling %d, %d, %d, %d / %d - %d\n", vcs->clipping.left, + vcs->clipping.top, vcs->clipping.right, vcs->clipping.bottom, + vcs->decimation, vcs->line_size); +} + +/* execute with input_lock locked */ +static inline void vino_set_default_scaling(struct vino_channel_settings *vcs) +{ + vino_set_scaling(vcs, vcs->clipping.right - vcs->clipping.left, + vcs->clipping.bottom - vcs->clipping.top); +} + +/* execute with input_lock locked */ +static void vino_set_framerate(struct vino_channel_settings *vcs, + unsigned int fps) +{ + unsigned int mask; + + switch (vcs->data_norm) { + case VINO_DATA_NORM_NTSC: + case VINO_DATA_NORM_D1: + fps = (unsigned int)(fps / 6) * 6; // FIXME: round! + + if (fps < vino_data_norms[vcs->data_norm].fps_min) + fps = vino_data_norms[vcs->data_norm].fps_min; + if (fps > vino_data_norms[vcs->data_norm].fps_max) + fps = vino_data_norms[vcs->data_norm].fps_max; + + switch (fps) { + case 6: + mask = 0x003; + break; + case 12: + mask = 0x0c3; + break; + case 18: + mask = 0x333; + break; + case 24: + mask = 0x3ff; + break; + case 30: + mask = 0xfff; + break; + default: + mask = VINO_FRAMERT_FULL; + } + vcs->framert_reg = VINO_FRAMERT_RT(mask); + break; + case VINO_DATA_NORM_PAL: + case VINO_DATA_NORM_SECAM: + fps = (unsigned int)(fps / 5) * 5; // FIXME: round! + + if (fps < vino_data_norms[vcs->data_norm].fps_min) + fps = vino_data_norms[vcs->data_norm].fps_min; + if (fps > vino_data_norms[vcs->data_norm].fps_max) + fps = vino_data_norms[vcs->data_norm].fps_max; + + switch (fps) { + case 5: + mask = 0x003; + break; + case 10: + mask = 0x0c3; + break; + case 15: + mask = 0x333; + break; + case 20: + mask = 0x0ff; + break; + case 25: + mask = 0x3ff; + break; + default: + mask = VINO_FRAMERT_FULL; + } + vcs->framert_reg = VINO_FRAMERT_RT(mask) | VINO_FRAMERT_PAL; + break; + } + + vcs->fps = fps; +} + +/* execute with input_lock locked */ +static inline void vino_set_default_framerate(struct + vino_channel_settings *vcs) +{ + vino_set_framerate(vcs, vino_data_norms[vcs->data_norm].fps_max); +} + +/* + * Prepare VINO for DMA transfer... + * (execute only with vino_lock and input_lock locked) + */ +static int vino_dma_setup(struct vino_channel_settings *vcs, + struct vino_framebuffer *fb) +{ + u32 ctrl, intr; + struct sgi_vino_channel *ch; + const struct vino_data_norm *norm; + + dprintk("vino_dma_setup():\n"); + + vcs->field = 0; + fb->frame_counter = 0; + + ch = (vcs->channel == VINO_CHANNEL_A) ? &vino->a : &vino->b; + norm = &vino_data_norms[vcs->data_norm]; + + ch->page_index = 0; + ch->line_count = 0; + + /* VINO line size register is set 8 bytes less than actual */ + ch->line_size = vcs->line_size - 8; + + /* let VINO know where to transfer data */ + ch->start_desc_tbl = fb->desc_table.dma; + ch->next_4_desc = fb->desc_table.dma; + + /* give vino time to fetch the first four descriptors, 5 usec + * should be more than enough time */ + udelay(VINO_DESC_FETCH_DELAY); + + dprintk("vino_dma_setup(): start desc = %08x, next 4 desc = %08x\n", + ch->start_desc_tbl, ch->next_4_desc); + + /* set the alpha register */ + ch->alpha = vcs->alpha; + + /* set clipping registers */ + ch->clip_start = VINO_CLIP_ODD(norm->odd.top + vcs->clipping.top / 2) | + VINO_CLIP_EVEN(norm->even.top + + vcs->clipping.top / 2) | + VINO_CLIP_X(vcs->clipping.left); + ch->clip_end = VINO_CLIP_ODD(norm->odd.top + + vcs->clipping.bottom / 2 - 1) | + VINO_CLIP_EVEN(norm->even.top + + vcs->clipping.bottom / 2 - 1) | + VINO_CLIP_X(vcs->clipping.right); + + /* set the size of actual content in the buffer (DECIMATION !) */ + fb->data_size = ((vcs->clipping.right - vcs->clipping.left) / + vcs->decimation) * + ((vcs->clipping.bottom - vcs->clipping.top) / + vcs->decimation) * + vino_data_formats[vcs->data_format].bpp; + + ch->frame_rate = vcs->framert_reg; + + ctrl = vino->control; + intr = vino->intr_status; + + if (vcs->channel == VINO_CHANNEL_A) { + /* All interrupt conditions for this channel was cleared + * so clear the interrupt status register and enable + * interrupts */ + intr &= ~VINO_INTSTAT_A; + ctrl |= VINO_CTRL_A_INT; + + /* enable synchronization */ + ctrl |= VINO_CTRL_A_SYNC_ENBL; + + /* enable frame assembly */ + ctrl |= VINO_CTRL_A_INTERLEAVE_ENBL; + + /* set decimation used */ + if (vcs->decimation < 2) + ctrl &= ~VINO_CTRL_A_DEC_ENBL; + else { + ctrl |= VINO_CTRL_A_DEC_ENBL; + ctrl &= ~VINO_CTRL_A_DEC_SCALE_MASK; + ctrl |= (vcs->decimation - 1) << + VINO_CTRL_A_DEC_SCALE_SHIFT; + } + + /* select input interface */ + if (vcs->input == VINO_INPUT_D1) + ctrl |= VINO_CTRL_A_SELECT; + else + ctrl &= ~VINO_CTRL_A_SELECT; + + /* palette */ + ctrl &= ~(VINO_CTRL_A_LUMA_ONLY | VINO_CTRL_A_RGB | + VINO_CTRL_A_DITHER); + } else { + intr &= ~VINO_INTSTAT_B; + ctrl |= VINO_CTRL_B_INT; + + ctrl |= VINO_CTRL_B_SYNC_ENBL; + ctrl |= VINO_CTRL_B_INTERLEAVE_ENBL; + + if (vcs->decimation < 2) + ctrl &= ~VINO_CTRL_B_DEC_ENBL; + else { + ctrl |= VINO_CTRL_B_DEC_ENBL; + ctrl &= ~VINO_CTRL_B_DEC_SCALE_MASK; + ctrl |= (vcs->decimation - 1) << + VINO_CTRL_B_DEC_SCALE_SHIFT; + + } + if (vcs->input == VINO_INPUT_D1) + ctrl |= VINO_CTRL_B_SELECT; + else + ctrl &= ~VINO_CTRL_B_SELECT; + + ctrl &= ~(VINO_CTRL_B_LUMA_ONLY | VINO_CTRL_B_RGB | + VINO_CTRL_B_DITHER); + } + + /* set palette */ + fb->data_format = vcs->data_format; + + switch (vcs->data_format) { + case VINO_DATA_FMT_GREY: + ctrl |= (vcs->channel == VINO_CHANNEL_A) ? + VINO_CTRL_A_LUMA_ONLY : VINO_CTRL_B_LUMA_ONLY; + break; + case VINO_DATA_FMT_RGB32: + ctrl |= (vcs->channel == VINO_CHANNEL_A) ? + VINO_CTRL_A_RGB : VINO_CTRL_B_RGB; + break; + case VINO_DATA_FMT_YUV: + /* nothing needs to be done */ + break; + case VINO_DATA_FMT_RGB332: + ctrl |= (vcs->channel == VINO_CHANNEL_A) ? + VINO_CTRL_A_RGB | VINO_CTRL_A_DITHER : + VINO_CTRL_B_RGB | VINO_CTRL_B_DITHER; + break; + } + + vino->intr_status = intr; + vino->control = ctrl; + + return 0; +} + +/* (execute only with vino_lock locked) */ +static inline void vino_dma_start(struct vino_channel_settings *vcs) +{ + u32 ctrl = vino->control; + + dprintk("vino_dma_start():\n"); + ctrl |= (vcs->channel == VINO_CHANNEL_A) ? + VINO_CTRL_A_DMA_ENBL : VINO_CTRL_B_DMA_ENBL; + vino->control = ctrl; +} + +/* (execute only with vino_lock locked) */ +static inline void vino_dma_stop(struct vino_channel_settings *vcs) +{ + u32 ctrl = vino->control; + + ctrl &= (vcs->channel == VINO_CHANNEL_A) ? + ~VINO_CTRL_A_DMA_ENBL : ~VINO_CTRL_B_DMA_ENBL; + ctrl &= (vcs->channel == VINO_CHANNEL_A) ? + ~VINO_CTRL_A_INT : ~VINO_CTRL_B_INT; + vino->control = ctrl; + dprintk("vino_dma_stop():\n"); +} + +/* + * Load dummy page to descriptor registers. This prevents generating of + * spurious interrupts. (execute only with vino_lock locked) + */ +static void vino_clear_interrupt(struct vino_channel_settings *vcs) +{ + struct sgi_vino_channel *ch; + + ch = (vcs->channel == VINO_CHANNEL_A) ? &vino->a : &vino->b; + + ch->page_index = 0; + ch->line_count = 0; + + ch->start_desc_tbl = vino_drvdata->dummy_desc_table.dma; + ch->next_4_desc = vino_drvdata->dummy_desc_table.dma; + + udelay(VINO_DESC_FETCH_DELAY); + dprintk("channel %c clear interrupt condition\n", + (vcs->channel == VINO_CHANNEL_A) ? 'A':'B'); +} + +static int vino_capture(struct vino_channel_settings *vcs, + struct vino_framebuffer *fb) +{ + int err = 0; + unsigned long flags, flags2; + + spin_lock_irqsave(&fb->state_lock, flags); + + if (fb->state == VINO_FRAMEBUFFER_IN_USE) + err = -EBUSY; + fb->state = VINO_FRAMEBUFFER_IN_USE; + + spin_unlock_irqrestore(&fb->state_lock, flags); + + if (err) + return err; + + spin_lock_irqsave(&vino_drvdata->vino_lock, flags); + spin_lock_irqsave(&vino_drvdata->input_lock, flags2); + + vino_dma_setup(vcs, fb); + vino_dma_start(vcs); + + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags2); + spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags); + + return err; +} + +static +struct vino_framebuffer *vino_capture_enqueue(struct + vino_channel_settings *vcs, + unsigned int index) +{ + struct vino_framebuffer *fb; + unsigned long flags; + + dprintk("vino_capture_enqueue():\n"); + + spin_lock_irqsave(&vcs->capture_lock, flags); + + fb = vino_queue_add(&vcs->fb_queue, index); + if (fb == NULL) { + dprintk("vino_capture_enqueue(): vino_queue_add() failed, " + "queue full?\n"); + goto out; + } +out: + spin_unlock_irqrestore(&vcs->capture_lock, flags); + + return fb; +} + +static int vino_capture_next(struct vino_channel_settings *vcs, int start) +{ + struct vino_framebuffer *fb; + unsigned int incoming, id; + int err = 0; + unsigned long flags; + + dprintk("vino_capture_next():\n"); + + spin_lock_irqsave(&vcs->capture_lock, flags); + + if (start) { + /* start capture only if capture isn't in progress already */ + if (vcs->capturing) { + spin_unlock_irqrestore(&vcs->capture_lock, flags); + return 0; + } + + } else { + /* capture next frame: + * stop capture if capturing is not set */ + if (!vcs->capturing) { + spin_unlock_irqrestore(&vcs->capture_lock, flags); + return 0; + } + } + + err = vino_queue_get_incoming(&vcs->fb_queue, &incoming); + if (err) { + dprintk("vino_capture_next(): vino_queue_get_incoming() " + "failed\n"); + err = -EINVAL; + goto out; + } + if (incoming == 0) { + dprintk("vino_capture_next(): no buffers available\n"); + goto out; + } + + fb = vino_queue_peek(&vcs->fb_queue, &id); + if (fb == NULL) { + dprintk("vino_capture_next(): vino_queue_peek() failed\n"); + err = -EINVAL; + goto out; + } + + if (start) { + vcs->capturing = 1; + } + + spin_unlock_irqrestore(&vcs->capture_lock, flags); + + err = vino_capture(vcs, fb); + + return err; + +out: + vcs->capturing = 0; + spin_unlock_irqrestore(&vcs->capture_lock, flags); + + return err; +} + +static inline int vino_is_capturing(struct vino_channel_settings *vcs) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&vcs->capture_lock, flags); + + ret = vcs->capturing; + + spin_unlock_irqrestore(&vcs->capture_lock, flags); + + return ret; +} + +/* waits until a frame is captured */ +static int vino_wait_for_frame(struct vino_channel_settings *vcs) +{ + wait_queue_t wait; + int err = 0; + + dprintk("vino_wait_for_frame():\n"); + + init_waitqueue_entry(&wait, current); + /* add ourselves into wait queue */ + add_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait); + /* and set current state */ + set_current_state(TASK_INTERRUPTIBLE); + + /* to ensure that schedule_timeout will return immediately + * if VINO interrupt was triggred meanwhile */ + schedule_timeout(HZ / 10); + + if (signal_pending(current)) + err = -EINTR; + + remove_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait); + + dprintk("vino_wait_for_frame(): waiting for frame %s\n", + err ? "failed" : "ok"); + + return err; +} + +/* the function assumes that PAGE_SIZE % 4 == 0 */ +static void vino_convert_to_rgba(struct vino_framebuffer *fb) { + unsigned char *pageptr; + unsigned int page, i; + unsigned char a; + + for (page = 0; page < fb->desc_table.page_count; page++) { + pageptr = (unsigned char *)fb->desc_table.virtual[page]; + + for (i = 0; i < PAGE_SIZE; i += 4) { + a = pageptr[0]; + pageptr[0] = pageptr[3]; + pageptr[1] = pageptr[2]; + pageptr[2] = pageptr[1]; + pageptr[3] = a; + pageptr += 4; + } + } +} + +/* checks if the buffer is in correct state and syncs data */ +static int vino_check_buffer(struct vino_channel_settings *vcs, + struct vino_framebuffer *fb) +{ + int err = 0; + unsigned long flags; + + dprintk("vino_check_buffer():\n"); + + spin_lock_irqsave(&fb->state_lock, flags); + switch (fb->state) { + case VINO_FRAMEBUFFER_IN_USE: + err = -EIO; + break; + case VINO_FRAMEBUFFER_READY: + vino_sync_buffer(fb); + fb->state = VINO_FRAMEBUFFER_UNUSED; + break; + default: + err = -EINVAL; + } + spin_unlock_irqrestore(&fb->state_lock, flags); + + if (!err) { + if (vino_pixel_conversion + && (fb->data_format == VINO_DATA_FMT_RGB32)) { + vino_convert_to_rgba(fb); + } + } else if (err && (err != -EINVAL)) { + dprintk("vino_check_buffer(): buffer not ready\n"); + + spin_lock_irqsave(&vino_drvdata->vino_lock, flags); + vino_dma_stop(vcs); + vino_clear_interrupt(vcs); + spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags); + } + + return err; +} + +/* forcefully terminates capture */ +static void vino_capture_stop(struct vino_channel_settings *vcs) +{ + unsigned int incoming = 0, outgoing = 0, id; + unsigned long flags, flags2; + + dprintk("vino_capture_stop():\n"); + + spin_lock_irqsave(&vcs->capture_lock, flags); + + /* unset capturing to stop queue processing */ + vcs->capturing = 0; + + spin_lock_irqsave(&vino_drvdata->vino_lock, flags2); + + vino_dma_stop(vcs); + vino_clear_interrupt(vcs); + + spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags2); + + /* remove all items from the queue */ + if (vino_queue_get_incoming(&vcs->fb_queue, &incoming)) { + dprintk("vino_capture_stop(): " + "vino_queue_get_incoming() failed\n"); + goto out; + } + while (incoming > 0) { + vino_queue_transfer(&vcs->fb_queue); + + if (vino_queue_get_incoming(&vcs->fb_queue, &incoming)) { + dprintk("vino_capture_stop(): " + "vino_queue_get_incoming() failed\n"); + goto out; + } + } + + if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) { + dprintk("vino_capture_stop(): " + "vino_queue_get_outgoing() failed\n"); + goto out; + } + while (outgoing > 0) { + vino_queue_remove(&vcs->fb_queue, &id); + + if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) { + dprintk("vino_capture_stop(): " + "vino_queue_get_outgoing() failed\n"); + goto out; + } + } + +out: + spin_unlock_irqrestore(&vcs->capture_lock, flags); +} + +#if 0 +static int vino_capture_failed(struct vino_channel_settings *vcs) +{ + struct vino_framebuffer *fb; + unsigned long flags; + unsigned int i; + int ret; + + dprintk("vino_capture_failed():\n"); + + spin_lock_irqsave(&vino_drvdata->vino_lock, flags); + + vino_dma_stop(vcs); + vino_clear_interrupt(vcs); + + spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags); + + ret = vino_queue_get_incoming(&vcs->fb_queue, &i); + if (ret == VINO_QUEUE_ERROR) { + dprintk("vino_queue_get_incoming() failed\n"); + return -EINVAL; + } + if (i == 0) { + /* no buffers to process */ + return 0; + } + + fb = vino_queue_peek(&vcs->fb_queue, &i); + if (fb == NULL) { + dprintk("vino_queue_peek() failed\n"); + return -EINVAL; + } + + spin_lock_irqsave(&fb->state_lock, flags); + if (fb->state == VINO_FRAMEBUFFER_IN_USE) { + fb->state = VINO_FRAMEBUFFER_UNUSED; + vino_queue_transfer(&vcs->fb_queue); + vino_queue_remove(&vcs->fb_queue, &i); + /* we should actually discard the newest frame, + * but who cares ... */ + } + spin_unlock_irqrestore(&fb->state_lock, flags); + + return 0; +} +#endif + +static void vino_skip_frame(struct vino_channel_settings *vcs) +{ + struct vino_framebuffer *fb; + unsigned long flags; + unsigned int id; + + spin_lock_irqsave(&vcs->capture_lock, flags); + fb = vino_queue_peek(&vcs->fb_queue, &id); + if (!fb) { + spin_unlock_irqrestore(&vcs->capture_lock, flags); + dprintk("vino_skip_frame(): vino_queue_peek() failed!\n"); + return; + } + spin_unlock_irqrestore(&vcs->capture_lock, flags); + + spin_lock_irqsave(&fb->state_lock, flags); + fb->state = VINO_FRAMEBUFFER_UNUSED; + spin_unlock_irqrestore(&fb->state_lock, flags); + + vino_capture_next(vcs, 0); +} + +static void vino_frame_done(struct vino_channel_settings *vcs) +{ + struct vino_framebuffer *fb; + unsigned long flags; + + spin_lock_irqsave(&vcs->capture_lock, flags); + fb = vino_queue_transfer(&vcs->fb_queue); + if (!fb) { + spin_unlock_irqrestore(&vcs->capture_lock, flags); + dprintk("vino_frame_done(): vino_queue_transfer() failed!\n"); + return; + } + spin_unlock_irqrestore(&vcs->capture_lock, flags); + + fb->frame_counter = vcs->int_data.frame_counter; + memcpy(&fb->timestamp, &vcs->int_data.timestamp, + sizeof(struct timeval)); + + spin_lock_irqsave(&fb->state_lock, flags); + if (fb->state == VINO_FRAMEBUFFER_IN_USE) + fb->state = VINO_FRAMEBUFFER_READY; + spin_unlock_irqrestore(&fb->state_lock, flags); + + wake_up(&vcs->fb_queue.frame_wait_queue); + + vino_capture_next(vcs, 0); +} + +static void vino_capture_tasklet(unsigned long channel) { + struct vino_channel_settings *vcs; + + vcs = (channel == VINO_CHANNEL_A) + ? &vino_drvdata->a : &vino_drvdata->b; + + if (vcs->int_data.skip) + vcs->int_data.skip_count++; + + if (vcs->int_data.skip && (vcs->int_data.skip_count + <= VINO_MAX_FRAME_SKIP_COUNT)) { + vino_skip_frame(vcs); + } else { + vcs->int_data.skip_count = 0; + vino_frame_done(vcs); + } +} + +static irqreturn_t vino_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 ctrl, intr; + unsigned int fc_a, fc_b; + int handled_a = 0, skip_a = 0, done_a = 0; + int handled_b = 0, skip_b = 0, done_b = 0; + +#ifdef VINO_DEBUG_INT + int loop = 0; + unsigned int line_count = vino->a.line_count, + page_index = vino->a.page_index, + field_counter = vino->a.field_counter, + start_desc_tbl = vino->a.start_desc_tbl, + next_4_desc = vino->a.next_4_desc; + unsigned int line_count_2, + page_index_2, + field_counter_2, + start_desc_tbl_2, + next_4_desc_2; +#endif + + spin_lock(&vino_drvdata->vino_lock); + + while ((intr = vino->intr_status)) { + fc_a = vino->a.field_counter >> 1; + fc_b = vino->b.field_counter >> 1; + + /* handle error-interrupts in some special way ? + * --> skips frames */ + if (intr & VINO_INTSTAT_A) { + if (intr & VINO_INTSTAT_A_EOF) { + vino_drvdata->a.field++; + if (vino_drvdata->a.field > 1) { + vino_dma_stop(&vino_drvdata->a); + vino_clear_interrupt(&vino_drvdata->a); + vino_drvdata->a.field = 0; + done_a = 1; + } else { + if (vino->a.page_index + != vino_drvdata->a.line_size) { + vino->a.line_count = 0; + vino->a.page_index = + vino_drvdata-> + a.line_size; + vino->a.next_4_desc = + vino->a.start_desc_tbl; + } + } + dprintk("channel A end-of-field " + "interrupt: %04x\n", intr); + } else { + vino_dma_stop(&vino_drvdata->a); + vino_clear_interrupt(&vino_drvdata->a); + vino_drvdata->a.field = 0; + skip_a = 1; + dprintk("channel A error interrupt: %04x\n", + intr); + } + +#ifdef VINO_DEBUG_INT + line_count_2 = vino->a.line_count; + page_index_2 = vino->a.page_index; + field_counter_2 = vino->a.field_counter; + start_desc_tbl_2 = vino->a.start_desc_tbl; + next_4_desc_2 = vino->a.next_4_desc; + + printk("intr = %04x, loop = %d, field = %d\n", + intr, loop, vino_drvdata->a.field); + printk("1- line count = %04d, page index = %04d, " + "start = %08x, next = %08x\n" + " fieldc = %d, framec = %d\n", + line_count, page_index, start_desc_tbl, + next_4_desc, field_counter, fc_a); + printk("12-line count = %04d, page index = %04d, " + " start = %08x, next = %08x\n", + line_count_2, page_index_2, start_desc_tbl_2, + next_4_desc_2); + + if (done_a) + printk("\n"); +#endif + } + + if (intr & VINO_INTSTAT_B) { + if (intr & VINO_INTSTAT_B_EOF) { + vino_drvdata->b.field++; + if (vino_drvdata->b.field > 1) { + vino_dma_stop(&vino_drvdata->b); + vino_clear_interrupt(&vino_drvdata->b); + vino_drvdata->b.field = 0; + done_b = 1; + } + dprintk("channel B end-of-field " + "interrupt: %04x\n", intr); + } else { + vino_dma_stop(&vino_drvdata->b); + vino_clear_interrupt(&vino_drvdata->b); + vino_drvdata->b.field = 0; + skip_b = 1; + dprintk("channel B error interrupt: %04x\n", + intr); + } + } + + /* Always remember to clear interrupt status. + * Disable VINO interrupts while we do this. */ + ctrl = vino->control; + vino->control = ctrl & ~(VINO_CTRL_A_INT | VINO_CTRL_B_INT); + vino->intr_status = ~intr; + vino->control = ctrl; + + spin_unlock(&vino_drvdata->vino_lock); + + if ((!handled_a) && (done_a || skip_a)) { + if (!skip_a) { + do_gettimeofday(&vino_drvdata-> + a.int_data.timestamp); + vino_drvdata->a.int_data.frame_counter = fc_a; + } + vino_drvdata->a.int_data.skip = skip_a; + + dprintk("channel A %s, interrupt: %d\n", + skip_a ? "skipping frame" : "frame done", + intr); + tasklet_hi_schedule(&vino_tasklet_a); + handled_a = 1; + } + + if ((!handled_b) && (done_b || skip_b)) { + if (!skip_b) { + do_gettimeofday(&vino_drvdata-> + b.int_data.timestamp); + vino_drvdata->b.int_data.frame_counter = fc_b; + } + vino_drvdata->b.int_data.skip = skip_b; + + dprintk("channel B %s, interrupt: %d\n", + skip_b ? "skipping frame" : "frame done", + intr); + tasklet_hi_schedule(&vino_tasklet_b); + handled_b = 1; + } + +#ifdef VINO_DEBUG_INT + loop++; +#endif + spin_lock(&vino_drvdata->vino_lock); + } + + spin_unlock(&vino_drvdata->vino_lock); + + return IRQ_HANDLED; +} + +/* VINO video input management */ + +static int vino_get_saa7191_input(int input) +{ + switch (input) { + case VINO_INPUT_COMPOSITE: + return SAA7191_INPUT_COMPOSITE; + case VINO_INPUT_SVIDEO: + return SAA7191_INPUT_SVIDEO; + default: + printk(KERN_ERR "VINO: vino_get_saa7191_input(): " + "invalid input!\n"); + return -1; + } +} + +static int vino_get_saa7191_norm(unsigned int data_norm) +{ + switch (data_norm) { + case VINO_DATA_NORM_AUTO: + return SAA7191_NORM_AUTO; + case VINO_DATA_NORM_AUTO_EXT: + return SAA7191_NORM_AUTO_EXT; + case VINO_DATA_NORM_PAL: + return SAA7191_NORM_PAL; + case VINO_DATA_NORM_NTSC: + return SAA7191_NORM_NTSC; + case VINO_DATA_NORM_SECAM: + return SAA7191_NORM_SECAM; + default: + printk(KERN_ERR "VINO: vino_get_saa7191_norm(): " + "invalid norm!\n"); + return -1; + } +} + +static int vino_get_from_saa7191_norm(int saa7191_norm) +{ + switch (saa7191_norm) { + case SAA7191_NORM_PAL: + return VINO_DATA_NORM_PAL; + case SAA7191_NORM_NTSC: + return VINO_DATA_NORM_NTSC; + case SAA7191_NORM_SECAM: + return VINO_DATA_NORM_SECAM; + default: + printk(KERN_ERR "VINO: vino_get_from_saa7191_norm(): " + "invalid norm!\n"); + return VINO_DATA_NORM_NONE; + } +} + +static int vino_saa7191_set_norm(unsigned int *data_norm) +{ + int saa7191_norm, new_data_norm; + int err = 0; + + saa7191_norm = vino_get_saa7191_norm(*data_norm); + + err = i2c_decoder_command(DECODER_SAA7191_SET_NORM, + &saa7191_norm); + if (err) + goto out; + + if ((*data_norm == VINO_DATA_NORM_AUTO) + || (*data_norm == VINO_DATA_NORM_AUTO_EXT)) { + struct saa7191_status status; + + err = i2c_decoder_command(DECODER_SAA7191_GET_STATUS, + &status); + if (err) + goto out; + + new_data_norm = + vino_get_from_saa7191_norm(status.norm); + if (new_data_norm == VINO_DATA_NORM_NONE) { + err = -EINVAL; + goto out; + } + + *data_norm = (unsigned int)new_data_norm; + } + +out: + return err; +} + +/* execute with input_lock locked */ +static int vino_is_input_owner(struct vino_channel_settings *vcs) +{ + switch(vcs->input) { + case VINO_INPUT_COMPOSITE: + case VINO_INPUT_SVIDEO: + return (vino_drvdata->decoder.owner == vcs->channel); + case VINO_INPUT_D1: + return (vino_drvdata->camera.owner == vcs->channel); + default: + return 0; + } +} + +static int vino_acquire_input(struct vino_channel_settings *vcs) +{ + unsigned long flags; + int ret = 0; + + dprintk("vino_acquire_input():\n"); + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + + /* First try D1 and then SAA7191 */ + if (vino_drvdata->camera.driver + && (vino_drvdata->camera.owner == VINO_NO_CHANNEL)) { + if (i2c_use_client(vino_drvdata->camera.driver)) { + ret = -ENODEV; + goto out; + } + + vino_drvdata->camera.owner = vcs->channel; + vcs->input = VINO_INPUT_D1; + vcs->data_norm = VINO_DATA_NORM_D1; + } else if (vino_drvdata->decoder.driver + && (vino_drvdata->decoder.owner == VINO_NO_CHANNEL)) { + int input, data_norm; + int saa7191_input; + + if (i2c_use_client(vino_drvdata->decoder.driver)) { + ret = -ENODEV; + goto out; + } + + input = VINO_INPUT_COMPOSITE; + + saa7191_input = vino_get_saa7191_input(input); + ret = i2c_decoder_command(DECODER_SET_INPUT, + &saa7191_input); + if (ret) { + ret = -EINVAL; + goto out; + } + + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + /* Don't hold spinlocks while auto-detecting norm + * as it may take a while... */ + + data_norm = VINO_DATA_NORM_AUTO_EXT; + + ret = vino_saa7191_set_norm(&data_norm); + if ((ret == -EBUSY) || (ret == -EAGAIN)) { + data_norm = VINO_DATA_NORM_PAL; + ret = vino_saa7191_set_norm(&data_norm); + } + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + + if (ret) { + ret = -EINVAL; + goto out; + } + + vino_drvdata->decoder.owner = vcs->channel; + + vcs->input = input; + vcs->data_norm = data_norm; + } else { + vcs->input = (vcs->channel == VINO_CHANNEL_A) ? + vino_drvdata->b.input : vino_drvdata->a.input; + vcs->data_norm = (vcs->channel == VINO_CHANNEL_A) ? + vino_drvdata->b.data_norm : vino_drvdata->a.data_norm; + } + + if (vcs->input == VINO_INPUT_NONE) { + ret = -ENODEV; + goto out; + } + + vino_set_default_clipping(vcs); + vino_set_default_scaling(vcs); + vino_set_default_framerate(vcs); + + dprintk("vino_acquire_input(): %s\n", vino_inputs[vcs->input].name); + +out: + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + return ret; +} + +static int vino_set_input(struct vino_channel_settings *vcs, int input) +{ + struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ? + &vino_drvdata->b : &vino_drvdata->a; + unsigned long flags; + int ret = 0; + + dprintk("vino_set_input():\n"); + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + + if (vcs->input == input) + goto out; + + switch (input) { + case VINO_INPUT_COMPOSITE: + case VINO_INPUT_SVIDEO: + if (!vino_drvdata->decoder.driver) { + ret = -EINVAL; + goto out; + } + + if (vino_drvdata->decoder.owner == VINO_NO_CHANNEL) { + if (i2c_use_client(vino_drvdata->decoder.driver)) { + ret = -ENODEV; + goto out; + } + vino_drvdata->decoder.owner = vcs->channel; + } + + if (vino_drvdata->decoder.owner == vcs->channel) { + int data_norm; + int saa7191_input; + + saa7191_input = vino_get_saa7191_input(input); + ret = i2c_decoder_command(DECODER_SET_INPUT, + &saa7191_input); + if (ret) { + vino_drvdata->decoder.owner = VINO_NO_CHANNEL; + ret = -EINVAL; + goto out; + } + + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + /* Don't hold spinlocks while auto-detecting norm + * as it may take a while... */ + + data_norm = VINO_DATA_NORM_AUTO_EXT; + + ret = vino_saa7191_set_norm(&data_norm); + if ((ret == -EBUSY) || (ret == -EAGAIN)) { + data_norm = VINO_DATA_NORM_PAL; + ret = vino_saa7191_set_norm(&data_norm); + } + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + + if (ret) { + vino_drvdata->decoder.owner = VINO_NO_CHANNEL; + ret = -EINVAL; + goto out; + } + + vcs->input = input; + vcs->data_norm = data_norm; + } else { + if (input != vcs2->input) { + ret = -EBUSY; + goto out; + } + + vcs->input = input; + vcs->data_norm = vcs2->data_norm; + } + + if (vino_drvdata->camera.owner == vcs->channel) { + /* Transfer the ownership or release the input */ + if (vcs2->input == VINO_INPUT_D1) { + vino_drvdata->camera.owner = vcs2->channel; + } else { + i2c_release_client(vino_drvdata-> + camera.driver); + vino_drvdata->camera.owner = VINO_NO_CHANNEL; + } + } + break; + case VINO_INPUT_D1: + if (!vino_drvdata->camera.driver) { + ret = -EINVAL; + goto out; + } + + if (vino_drvdata->camera.owner == VINO_NO_CHANNEL) { + if (i2c_use_client(vino_drvdata->camera.driver)) { + ret = -ENODEV; + goto out; + } + vino_drvdata->camera.owner = vcs->channel; + } + + if (vino_drvdata->decoder.owner == vcs->channel) { + /* Transfer the ownership or release the input */ + if ((vcs2->input == VINO_INPUT_COMPOSITE) || + (vcs2->input == VINO_INPUT_SVIDEO)) { + vino_drvdata->decoder.owner = vcs2->channel; + } else { + i2c_release_client(vino_drvdata-> + decoder.driver); + vino_drvdata->decoder.owner = VINO_NO_CHANNEL; + } + } + + vcs->input = input; + vcs->data_norm = VINO_DATA_NORM_D1; + break; + default: + ret = -EINVAL; + goto out; + } + + vino_set_default_clipping(vcs); + vino_set_default_scaling(vcs); + vino_set_default_framerate(vcs); + + dprintk("vino_set_input(): %s\n", vino_inputs[vcs->input].name); + +out: + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + return ret; +} + +static void vino_release_input(struct vino_channel_settings *vcs) +{ + struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ? + &vino_drvdata->b : &vino_drvdata->a; + unsigned long flags; + + dprintk("vino_release_input():\n"); + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + + /* Release ownership of the channel + * and if the other channel takes input from + * the same source, transfer the ownership */ + if (vino_drvdata->camera.owner == vcs->channel) { + if (vcs2->input == VINO_INPUT_D1) { + vino_drvdata->camera.owner = vcs2->channel; + } else { + i2c_release_client(vino_drvdata->camera.driver); + vino_drvdata->camera.owner = VINO_NO_CHANNEL; + } + } else if (vino_drvdata->decoder.owner == vcs->channel) { + if ((vcs2->input == VINO_INPUT_COMPOSITE) || + (vcs2->input == VINO_INPUT_SVIDEO)) { + vino_drvdata->decoder.owner = vcs2->channel; + } else { + i2c_release_client(vino_drvdata->decoder.driver); + vino_drvdata->decoder.owner = VINO_NO_CHANNEL; + } + } + vcs->input = VINO_INPUT_NONE; + + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); +} + +/* execute with input_lock locked */ +static int vino_set_data_norm(struct vino_channel_settings *vcs, + unsigned int data_norm, + unsigned long *flags) +{ + int err = 0; + + if (data_norm == vcs->data_norm) + return 0; + + switch (vcs->input) { + case VINO_INPUT_D1: + /* only one "norm" supported */ + if ((data_norm != VINO_DATA_NORM_D1) + && (data_norm != VINO_DATA_NORM_AUTO) + && (data_norm != VINO_DATA_NORM_AUTO_EXT)) + return -EINVAL; + break; + case VINO_INPUT_COMPOSITE: + case VINO_INPUT_SVIDEO: { + if ((data_norm != VINO_DATA_NORM_PAL) + && (data_norm != VINO_DATA_NORM_NTSC) + && (data_norm != VINO_DATA_NORM_SECAM) + && (data_norm != VINO_DATA_NORM_AUTO) + && (data_norm != VINO_DATA_NORM_AUTO_EXT)) + return -EINVAL; + + spin_unlock_irqrestore(&vino_drvdata->input_lock, *flags); + + /* Don't hold spinlocks while setting norm + * as it may take a while... */ + + err = vino_saa7191_set_norm(&data_norm); + + spin_lock_irqsave(&vino_drvdata->input_lock, *flags); + + if (err) + goto out; + + vcs->data_norm = data_norm; + + vino_set_default_clipping(vcs); + vino_set_default_scaling(vcs); + vino_set_default_framerate(vcs); + break; + } + default: + return -EINVAL; + } + +out: + return err; +} + +/* V4L2 helper functions */ + +static int vino_find_data_format(__u32 pixelformat) +{ + int i; + + for (i = 0; i < VINO_DATA_FMT_COUNT; i++) { + if (vino_data_formats[i].pixelformat == pixelformat) + return i; + } + + return VINO_DATA_FMT_NONE; +} + +static int vino_enum_data_norm(struct vino_channel_settings *vcs, __u32 index) +{ + int data_norm = VINO_DATA_NORM_NONE; + unsigned long flags; + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + switch(vcs->input) { + case VINO_INPUT_COMPOSITE: + case VINO_INPUT_SVIDEO: + if (index == 0) { + data_norm = VINO_DATA_NORM_PAL; + } else if (index == 1) { + data_norm = VINO_DATA_NORM_NTSC; + } else if (index == 2) { + data_norm = VINO_DATA_NORM_SECAM; + } + break; + case VINO_INPUT_D1: + if (index == 0) { + data_norm = VINO_DATA_NORM_D1; + } + break; + } + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + return data_norm; +} + +static int vino_enum_input(struct vino_channel_settings *vcs, __u32 index) +{ + int input = VINO_INPUT_NONE; + unsigned long flags; + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + if (vino_drvdata->decoder.driver && vino_drvdata->camera.driver) { + switch (index) { + case 0: + input = VINO_INPUT_COMPOSITE; + break; + case 1: + input = VINO_INPUT_SVIDEO; + break; + case 2: + input = VINO_INPUT_D1; + break; + } + } else if (vino_drvdata->decoder.driver) { + switch (index) { + case 0: + input = VINO_INPUT_COMPOSITE; + break; + case 1: + input = VINO_INPUT_SVIDEO; + break; + } + } else if (vino_drvdata->camera.driver) { + switch (index) { + case 0: + input = VINO_INPUT_D1; + break; + } + } + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + return input; +} + +/* execute with input_lock locked */ +static __u32 vino_find_input_index(struct vino_channel_settings *vcs) +{ + __u32 index = 0; + // FIXME: detect when no inputs available + + if (vino_drvdata->decoder.driver && vino_drvdata->camera.driver) { + switch (vcs->input) { + case VINO_INPUT_COMPOSITE: + index = 0; + break; + case VINO_INPUT_SVIDEO: + index = 1; + break; + case VINO_INPUT_D1: + index = 2; + break; + } + } else if (vino_drvdata->decoder.driver) { + switch (vcs->input) { + case VINO_INPUT_COMPOSITE: + index = 0; + break; + case VINO_INPUT_SVIDEO: + index = 1; + break; + } + } else if (vino_drvdata->camera.driver) { + switch (vcs->input) { + case VINO_INPUT_D1: + index = 0; + break; + } + } + + return index; +} + +/* V4L2 ioctls */ + +static void vino_v4l2_querycap(struct v4l2_capability *cap) +{ + memset(cap, 0, sizeof(struct v4l2_capability)); + + strcpy(cap->driver, vino_driver_name); + strcpy(cap->card, vino_driver_description); + strcpy(cap->bus_info, vino_bus_name); + cap->version = VINO_VERSION_CODE; + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING; + // V4L2_CAP_OVERLAY, V4L2_CAP_READWRITE +} + +static int vino_v4l2_enuminput(struct vino_channel_settings *vcs, + struct v4l2_input *i) +{ + __u32 index = i->index; + int input; + dprintk("requested index = %d\n", index); + + input = vino_enum_input(vcs, index); + if (input == VINO_INPUT_NONE) + return -EINVAL; + + memset(i, 0, sizeof(struct v4l2_input)); + + i->index = index; + i->type = V4L2_INPUT_TYPE_CAMERA; + i->std = vino_inputs[input].std; + strcpy(i->name, vino_inputs[input].name); + + if ((input == VINO_INPUT_COMPOSITE) + || (input == VINO_INPUT_SVIDEO)) { + struct saa7191_status status; + i2c_decoder_command(DECODER_SAA7191_GET_STATUS, &status); + i->status |= status.signal ? 0 : V4L2_IN_ST_NO_SIGNAL; + i->status |= status.color ? 0 : V4L2_IN_ST_NO_COLOR; + } + + return 0; +} + +static int vino_v4l2_g_input(struct vino_channel_settings *vcs, + unsigned int *i) +{ + __u32 index; + int input; + unsigned long flags; + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + input = vcs->input; + index = vino_find_input_index(vcs); + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + dprintk("input = %d\n", input); + + if (input == VINO_INPUT_NONE) { + return -EINVAL; + } + + *i = index; + + return 0; +} + +static int vino_v4l2_s_input(struct vino_channel_settings *vcs, + unsigned int *i) +{ + int input; + dprintk("requested input = %d\n", *i); + + input = vino_enum_input(vcs, *i); + if (input == VINO_INPUT_NONE) + return -EINVAL; + + return vino_set_input(vcs, input); +} + +static int vino_v4l2_enumstd(struct vino_channel_settings *vcs, + struct v4l2_standard *s) +{ + int index = s->index; + int data_norm; + + data_norm = vino_enum_data_norm(vcs, index); + dprintk("standard index = %d\n", index); + + if (data_norm == VINO_DATA_NORM_NONE) + return -EINVAL; + + dprintk("standard name = %s\n", + vino_data_norms[data_norm].description); + + memset(s, 0, sizeof(struct v4l2_standard)); + s->index = index; + + s->id = vino_data_norms[data_norm].std; + s->frameperiod.numerator = 1; + s->frameperiod.denominator = + vino_data_norms[data_norm].fps_max; + s->framelines = + vino_data_norms[data_norm].framelines; + strcpy(s->name, + vino_data_norms[data_norm].description); + + return 0; +} + +static int vino_v4l2_querystd(struct vino_channel_settings *vcs, + v4l2_std_id *std) +{ + unsigned long flags; + int err = 0; + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + + switch (vcs->input) { + case VINO_INPUT_D1: + *std = vino_inputs[vcs->input].std; + break; + case VINO_INPUT_COMPOSITE: + case VINO_INPUT_SVIDEO: { + struct saa7191_status status; + + i2c_decoder_command(DECODER_SAA7191_GET_STATUS, &status); + + if (status.signal) { + if (status.signal_60hz) { + *std = V4L2_STD_NTSC; + } else { + *std = V4L2_STD_PAL | V4L2_STD_SECAM; + } + } else { + *std = vino_inputs[vcs->input].std; + } + break; + } + default: + err = -EINVAL; + } + + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + return err; +} + +static int vino_v4l2_g_std(struct vino_channel_settings *vcs, + v4l2_std_id *std) +{ + unsigned long flags; + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + + *std = vino_data_norms[vcs->data_norm].std; + dprintk("current standard = %d\n", vcs->data_norm); + + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + return 0; +} + +static int vino_v4l2_s_std(struct vino_channel_settings *vcs, + v4l2_std_id *std) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + + if (!vino_is_input_owner(vcs)) { + ret = -EBUSY; + goto out; + } + + /* check if the standard is valid for the current input */ + if ((*std) & vino_inputs[vcs->input].std) { + dprintk("standard accepted\n"); + + /* change the video norm for SAA7191 + * and accept NTSC for D1 (do nothing) */ + + if (vcs->input == VINO_INPUT_D1) + goto out; + + if (((*std) & V4L2_STD_PAL) + && ((*std) & V4L2_STD_NTSC) + && ((*std) & V4L2_STD_SECAM)) { + ret = vino_set_data_norm(vcs, VINO_DATA_NORM_AUTO_EXT, + &flags); + } else if ((*std) & V4L2_STD_PAL) { + ret = vino_set_data_norm(vcs, VINO_DATA_NORM_PAL, + &flags); + } else if ((*std) & V4L2_STD_NTSC) { + ret = vino_set_data_norm(vcs, VINO_DATA_NORM_NTSC, + &flags); + } else if ((*std) & V4L2_STD_SECAM) { + ret = vino_set_data_norm(vcs, VINO_DATA_NORM_SECAM, + &flags); + } else { + ret = -EINVAL; + } + + if (ret) { + ret = -EINVAL; + } + } else { + ret = -EINVAL; + } + +out: + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + return ret; +} + +static int vino_v4l2_enum_fmt(struct vino_channel_settings *vcs, + struct v4l2_fmtdesc *fd) +{ + enum v4l2_buf_type type = fd->type; + int index = fd->index; + dprintk("format index = %d\n", index); + + switch (fd->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if ((fd->index < 0) || + (fd->index >= VINO_DATA_FMT_COUNT)) + return -EINVAL; + dprintk("format name = %s\n", + vino_data_formats[index].description); + + memset(fd, 0, sizeof(struct v4l2_fmtdesc)); + fd->index = index; + fd->type = type; + fd->pixelformat = vino_data_formats[index].pixelformat; + strcpy(fd->description, vino_data_formats[index].description); + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + default: + return -EINVAL; + } + + return 0; +} + +static int vino_v4l2_try_fmt(struct vino_channel_settings *vcs, + struct v4l2_format *f) +{ + struct vino_channel_settings tempvcs; + unsigned long flags; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: { + struct v4l2_pix_format *pf = &f->fmt.pix; + + dprintk("requested: w = %d, h = %d\n", + pf->width, pf->height); + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + memcpy(&tempvcs, vcs, sizeof(struct vino_channel_settings)); + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + tempvcs.data_format = vino_find_data_format(pf->pixelformat); + if (tempvcs.data_format == VINO_DATA_FMT_NONE) { + tempvcs.data_format = VINO_DATA_FMT_GREY; + pf->pixelformat = + vino_data_formats[tempvcs.data_format]. + pixelformat; + } + + /* data format must be set before clipping/scaling */ + vino_set_scaling(&tempvcs, pf->width, pf->height); + + dprintk("data format = %s\n", + vino_data_formats[tempvcs.data_format].description); + + pf->width = (tempvcs.clipping.right - tempvcs.clipping.left) / + tempvcs.decimation; + pf->height = (tempvcs.clipping.bottom - tempvcs.clipping.top) / + tempvcs.decimation; + + pf->field = V4L2_FIELD_INTERLACED; + pf->bytesperline = tempvcs.line_size; + pf->sizeimage = tempvcs.line_size * + (tempvcs.clipping.bottom - tempvcs.clipping.top) / + tempvcs.decimation; + pf->colorspace = + vino_data_formats[tempvcs.data_format].colorspace; + + pf->priv = 0; + break; + } + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + default: + return -EINVAL; + } + + return 0; +} + +static int vino_v4l2_g_fmt(struct vino_channel_settings *vcs, + struct v4l2_format *f) +{ + unsigned long flags; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: { + struct v4l2_pix_format *pf = &f->fmt.pix; + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + + pf->width = (vcs->clipping.right - vcs->clipping.left) / + vcs->decimation; + pf->height = (vcs->clipping.bottom - vcs->clipping.top) / + vcs->decimation; + pf->pixelformat = + vino_data_formats[vcs->data_format].pixelformat; + + pf->field = V4L2_FIELD_INTERLACED; + pf->bytesperline = vcs->line_size; + pf->sizeimage = vcs->line_size * + (vcs->clipping.bottom - vcs->clipping.top) / + vcs->decimation; + pf->colorspace = + vino_data_formats[vcs->data_format].colorspace; + + pf->priv = 0; + + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + break; + } + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + default: + return -EINVAL; + } + + return 0; +} + +static int vino_v4l2_s_fmt(struct vino_channel_settings *vcs, + struct v4l2_format *f) +{ + int data_format; + unsigned long flags; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: { + struct v4l2_pix_format *pf = &f->fmt.pix; + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + + data_format = vino_find_data_format(pf->pixelformat); + + if (data_format == VINO_DATA_FMT_NONE) { + vcs->data_format = VINO_DATA_FMT_GREY; + pf->pixelformat = + vino_data_formats[vcs->data_format]. + pixelformat; + } else { + vcs->data_format = data_format; + } + + /* data format must be set before clipping/scaling */ + vino_set_scaling(vcs, pf->width, pf->height); + + dprintk("data format = %s\n", + vino_data_formats[vcs->data_format].description); + + pf->width = vcs->clipping.right - vcs->clipping.left; + pf->height = vcs->clipping.bottom - vcs->clipping.top; + + pf->field = V4L2_FIELD_INTERLACED; + pf->bytesperline = vcs->line_size; + pf->sizeimage = vcs->line_size * + (vcs->clipping.bottom - vcs->clipping.top) / + vcs->decimation; + pf->colorspace = + vino_data_formats[vcs->data_format].colorspace; + + pf->priv = 0; + + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + break; + } + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + default: + return -EINVAL; + } + + return 0; +} + +static int vino_v4l2_cropcap(struct vino_channel_settings *vcs, + struct v4l2_cropcap *ccap) +{ + const struct vino_data_norm *norm; + unsigned long flags; + + switch (ccap->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + + norm = &vino_data_norms[vcs->data_norm]; + + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + ccap->bounds.left = 0; + ccap->bounds.top = 0; + ccap->bounds.width = norm->width; + ccap->bounds.height = norm->height; + memcpy(&ccap->defrect, &ccap->bounds, + sizeof(struct v4l2_rect)); + + ccap->pixelaspect.numerator = 1; + ccap->pixelaspect.denominator = 1; + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + default: + return -EINVAL; + } + + return 0; +} + +static int vino_v4l2_g_crop(struct vino_channel_settings *vcs, + struct v4l2_crop *c) +{ + unsigned long flags; + + switch (c->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + + c->c.left = vcs->clipping.left; + c->c.top = vcs->clipping.top; + c->c.width = vcs->clipping.right - vcs->clipping.left; + c->c.height = vcs->clipping.bottom - vcs->clipping.top; + + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + default: + return -EINVAL; + } + + return 0; +} + +static int vino_v4l2_s_crop(struct vino_channel_settings *vcs, + struct v4l2_crop *c) +{ + unsigned long flags; + + switch (c->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + + vino_set_clipping(vcs, c->c.left, c->c.top, + c->c.width, c->c.height); + + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + default: + return -EINVAL; + } + + return 0; +} + +static int vino_v4l2_g_parm(struct vino_channel_settings *vcs, + struct v4l2_streamparm *sp) +{ + unsigned long flags; + + switch (sp->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: { + struct v4l2_captureparm *cp = &sp->parm.capture; + memset(cp, 0, sizeof(struct v4l2_captureparm)); + + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = 1; + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + + cp->timeperframe.denominator = vcs->fps; + + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + // TODO: cp->readbuffers = xxx; + break; + } + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + default: + return -EINVAL; + } + + return 0; +} + +static int vino_v4l2_s_parm(struct vino_channel_settings *vcs, + struct v4l2_streamparm *sp) +{ + unsigned long flags; + + switch (sp->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: { + struct v4l2_captureparm *cp = &sp->parm.capture; + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + + if ((cp->timeperframe.numerator == 0) || + (cp->timeperframe.denominator == 0)) { + /* reset framerate */ + vino_set_default_framerate(vcs); + } else { + vino_set_framerate(vcs, cp->timeperframe.denominator / + cp->timeperframe.numerator); + } + + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + // TODO: set buffers according to cp->readbuffers + break; + } + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + default: + return -EINVAL; + } + + return 0; +} + +static int vino_v4l2_reqbufs(struct vino_channel_settings *vcs, + struct v4l2_requestbuffers *rb) +{ + if (vcs->reading) + return -EBUSY; + + switch (rb->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: { + // TODO: check queue type + if (rb->memory != V4L2_MEMORY_MMAP) { + dprintk("type not mmap\n"); + return -EINVAL; + } + + dprintk("count = %d\n", rb->count); + if (rb->count > 0) { + if (vino_is_capturing(vcs)) { + dprintk("busy, capturing\n"); + return -EBUSY; + } + + if (vino_queue_has_mapped_buffers(&vcs->fb_queue)) { + dprintk("busy, buffers still mapped\n"); + return -EBUSY; + } else { + vcs->streaming = 0; + vino_queue_free(&vcs->fb_queue); + vino_queue_init(&vcs->fb_queue, &rb->count); + } + } else { + vcs->streaming = 0; + vino_capture_stop(vcs); + vino_queue_free(&vcs->fb_queue); + } + break; + } + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + default: + return -EINVAL; + } + + return 0; +} + +static void vino_v4l2_get_buffer_status(struct vino_channel_settings *vcs, + struct vino_framebuffer *fb, + struct v4l2_buffer *b) +{ + if (vino_queue_outgoing_contains(&vcs->fb_queue, + fb->id)) { + b->flags &= ~V4L2_BUF_FLAG_QUEUED; + b->flags |= V4L2_BUF_FLAG_DONE; + } else if (vino_queue_incoming_contains(&vcs->fb_queue, + fb->id)) { + b->flags &= ~V4L2_BUF_FLAG_DONE; + b->flags |= V4L2_BUF_FLAG_QUEUED; + } else { + b->flags &= ~(V4L2_BUF_FLAG_DONE | + V4L2_BUF_FLAG_QUEUED); + } + + b->flags &= ~(V4L2_BUF_FLAG_TIMECODE); + + if (fb->map_count > 0) + b->flags |= V4L2_BUF_FLAG_MAPPED; + + b->index = fb->id; + b->memory = (vcs->fb_queue.type == VINO_MEMORY_MMAP) ? + V4L2_MEMORY_MMAP : V4L2_MEMORY_USERPTR; + b->m.offset = fb->offset; + b->bytesused = fb->data_size; + b->length = fb->size; + b->field = V4L2_FIELD_INTERLACED; + b->sequence = fb->frame_counter; + memcpy(&b->timestamp, &fb->timestamp, + sizeof(struct timeval)); + // b->input ? + + dprintk("buffer %d: length = %d, bytesused = %d, offset = %d\n", + fb->id, fb->size, fb->data_size, fb->offset); +} + +static int vino_v4l2_querybuf(struct vino_channel_settings *vcs, + struct v4l2_buffer *b) +{ + if (vcs->reading) + return -EBUSY; + + switch (b->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: { + struct vino_framebuffer *fb; + + // TODO: check queue type + if (b->index >= vino_queue_get_length(&vcs->fb_queue)) { + dprintk("invalid index = %d\n", + b->index); + return -EINVAL; + } + + fb = vino_queue_get_buffer(&vcs->fb_queue, + b->index); + if (fb == NULL) { + dprintk("vino_queue_get_buffer() failed"); + return -EINVAL; + } + + vino_v4l2_get_buffer_status(vcs, fb, b); + break; + } + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + default: + return -EINVAL; + } + + return 0; +} + +static int vino_v4l2_qbuf(struct vino_channel_settings *vcs, + struct v4l2_buffer *b) +{ + if (vcs->reading) + return -EBUSY; + + switch (b->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: { + struct vino_framebuffer *fb; + int ret; + + // TODO: check queue type + if (b->memory != V4L2_MEMORY_MMAP) { + dprintk("type not mmap\n"); + return -EINVAL; + } + + fb = vino_capture_enqueue(vcs, b->index); + if (fb == NULL) + return -EINVAL; + + vino_v4l2_get_buffer_status(vcs, fb, b); + + if (vcs->streaming) { + ret = vino_capture_next(vcs, 1); + if (ret) + return ret; + } + break; + } + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + default: + return -EINVAL; + } + + return 0; +} + +static int vino_v4l2_dqbuf(struct vino_channel_settings *vcs, + struct v4l2_buffer *b, + unsigned int nonblocking) +{ + if (vcs->reading) + return -EBUSY; + + switch (b->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: { + struct vino_framebuffer *fb; + unsigned int incoming, outgoing; + int err; + + // TODO: check queue type + + err = vino_queue_get_incoming(&vcs->fb_queue, &incoming); + if (err) { + dprintk("vino_queue_get_incoming() failed\n"); + return -EINVAL; + } + err = vino_queue_get_outgoing(&vcs->fb_queue, &outgoing); + if (err) { + dprintk("vino_queue_get_outgoing() failed\n"); + return -EINVAL; + } + + dprintk("incoming = %d, outgoing = %d\n", incoming, outgoing); + + if (outgoing == 0) { + if (incoming == 0) { + dprintk("no incoming or outgoing buffers\n"); + return -EINVAL; + } + if (nonblocking) { + dprintk("non-blocking I/O was selected and " + "there are no buffers to dequeue\n"); + return -EAGAIN; + } + + err = vino_wait_for_frame(vcs); + if (err) { + err = vino_wait_for_frame(vcs); + if (err) { + /* interrupted or + * no frames captured because + * of frame skipping */ + // vino_capture_failed(vcs); + return -EIO; + } + } + } + + fb = vino_queue_remove(&vcs->fb_queue, &b->index); + if (fb == NULL) { + dprintk("vino_queue_remove() failed\n"); + return -EINVAL; + } + + err = vino_check_buffer(vcs, fb); + + vino_v4l2_get_buffer_status(vcs, fb, b); + + if (err) + return -EIO; + + break; + } + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + default: + return -EINVAL; + } + + return 0; +} + +static int vino_v4l2_streamon(struct vino_channel_settings *vcs) +{ + unsigned int incoming; + int ret; + if (vcs->reading) + return -EBUSY; + + if (vcs->streaming) + return 0; + + // TODO: check queue type + + if (vino_queue_get_length(&vcs->fb_queue) < 1) { + dprintk("no buffers allocated\n"); + return -EINVAL; + } + + ret = vino_queue_get_incoming(&vcs->fb_queue, &incoming); + if (ret) { + dprintk("vino_queue_get_incoming() failed\n"); + return -EINVAL; + } + + vcs->streaming = 1; + + if (incoming > 0) { + ret = vino_capture_next(vcs, 1); + if (ret) { + vcs->streaming = 0; + + dprintk("couldn't start capture\n"); + return -EINVAL; + } + } + + return 0; +} + +static int vino_v4l2_streamoff(struct vino_channel_settings *vcs) +{ + if (vcs->reading) + return -EBUSY; + + if (!vcs->streaming) + return 0; + + vcs->streaming = 0; + vino_capture_stop(vcs); + + return 0; +} + +static int vino_v4l2_queryctrl(struct vino_channel_settings *vcs, + struct v4l2_queryctrl *queryctrl) +{ + unsigned long flags; + int i; + int err = 0; + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + + switch (vcs->input) { + case VINO_INPUT_D1: + for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) { + if (vino_indycam_v4l2_controls[i].id == + queryctrl->id) { + memcpy(queryctrl, + &vino_indycam_v4l2_controls[i], + sizeof(struct v4l2_queryctrl)); + queryctrl->reserved[0] = 0; + goto found; + } + } + + err = -EINVAL; + break; + case VINO_INPUT_COMPOSITE: + case VINO_INPUT_SVIDEO: + for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) { + if (vino_saa7191_v4l2_controls[i].id == + queryctrl->id) { + memcpy(queryctrl, + &vino_saa7191_v4l2_controls[i], + sizeof(struct v4l2_queryctrl)); + queryctrl->reserved[0] = 0; + goto found; + } + } + + err = -EINVAL; + break; + default: + err = -EINVAL; + } + + found: + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + return err; +} + +static int vino_v4l2_g_ctrl(struct vino_channel_settings *vcs, + struct v4l2_control *control) +{ + unsigned long flags; + int i; + int err = 0; + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + + switch (vcs->input) { + case VINO_INPUT_D1: { + struct indycam_control indycam_ctrl; + + for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) { + if (vino_indycam_v4l2_controls[i].id == + control->id) { + goto found1; + } + } + + err = -EINVAL; + goto out; + +found1: + indycam_ctrl.type = vino_indycam_v4l2_controls[i].reserved[0]; + + err = i2c_camera_command(DECODER_INDYCAM_GET_CONTROL, + &indycam_ctrl); + if (err) { + err = -EINVAL; + goto out; + } + + control->value = indycam_ctrl.value; + break; + } + case VINO_INPUT_COMPOSITE: + case VINO_INPUT_SVIDEO: { + struct saa7191_control saa7191_ctrl; + + for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) { + if (vino_saa7191_v4l2_controls[i].id == + control->id) { + goto found2; + } + } + + err = -EINVAL; + goto out; + +found2: + saa7191_ctrl.type = vino_saa7191_v4l2_controls[i].reserved[0]; + + err = i2c_decoder_command(DECODER_SAA7191_GET_CONTROL, + &saa7191_ctrl); + if (err) { + err = -EINVAL; + goto out; + } + + control->value = saa7191_ctrl.value; + break; + } + default: + err = -EINVAL; + } + +out: + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + return err; +} + +static int vino_v4l2_s_ctrl(struct vino_channel_settings *vcs, + struct v4l2_control *control) +{ + unsigned long flags; + int i; + int err = 0; + + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + + if (!vino_is_input_owner(vcs)) { + err = -EBUSY; + goto out; + } + + switch (vcs->input) { + case VINO_INPUT_D1: { + struct indycam_control indycam_ctrl; + + for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) { + if (vino_indycam_v4l2_controls[i].id == + control->id) { + if ((control->value >= + vino_indycam_v4l2_controls[i].minimum) + && (control->value <= + vino_indycam_v4l2_controls[i]. + maximum)) { + goto found1; + } else { + err = -ERANGE; + goto out; + } + } + } + + err = -EINVAL; + goto out; + +found1: + indycam_ctrl.type = vino_indycam_v4l2_controls[i].reserved[0]; + indycam_ctrl.value = control->value; + + err = i2c_camera_command(DECODER_INDYCAM_SET_CONTROL, + &indycam_ctrl); + if (err) + err = -EINVAL; + break; + } + case VINO_INPUT_COMPOSITE: + case VINO_INPUT_SVIDEO: { + struct saa7191_control saa7191_ctrl; + + for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) { + if (vino_saa7191_v4l2_controls[i].id == + control->id) { + if ((control->value >= + vino_saa7191_v4l2_controls[i].minimum) + && (control->value <= + vino_saa7191_v4l2_controls[i]. + maximum)) { + goto found2; + } else { + err = -ERANGE; + goto out; + } + } + } + err = -EINVAL; + goto out; + +found2: + saa7191_ctrl.type = vino_saa7191_v4l2_controls[i].reserved[0]; + saa7191_ctrl.value = control->value; + + err = i2c_decoder_command(DECODER_SAA7191_SET_CONTROL, + &saa7191_ctrl); + if (err) + err = -EINVAL; + break; + } + default: + err = -EINVAL; + } + +out: + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + + return err; +} + +/* File operations */ + +static int vino_open(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct vino_channel_settings *vcs = video_get_drvdata(dev); + int ret = 0; + dprintk("open(): channel = %c\n", + (vcs->channel == VINO_CHANNEL_A) ? 'A' : 'B'); + + mutex_lock(&vcs->mutex); + + if (vcs->users) { + dprintk("open(): driver busy\n"); + ret = -EBUSY; + goto out; + } + + ret = vino_acquire_input(vcs); + if (ret) { + dprintk("open(): vino_acquire_input() failed\n"); + goto out; + } + + vcs->users++; + + out: + mutex_unlock(&vcs->mutex); + + dprintk("open(): %s!\n", ret ? "failed" : "complete"); + + return ret; +} + +static int vino_close(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct vino_channel_settings *vcs = video_get_drvdata(dev); + dprintk("close():\n"); + + mutex_lock(&vcs->mutex); + + vcs->users--; + + if (!vcs->users) { + vino_release_input(vcs); + + /* stop DMA and free buffers */ + vino_capture_stop(vcs); + vino_queue_free(&vcs->fb_queue); + } + + mutex_unlock(&vcs->mutex); + + return 0; +} + +static void vino_vm_open(struct vm_area_struct *vma) +{ + struct vino_framebuffer *fb = vma->vm_private_data; + + fb->map_count++; + dprintk("vino_vm_open(): count = %d\n", fb->map_count); +} + +static void vino_vm_close(struct vm_area_struct *vma) +{ + struct vino_framebuffer *fb = vma->vm_private_data; + + fb->map_count--; + dprintk("vino_vm_close(): count = %d\n", fb->map_count); +} + +static struct vm_operations_struct vino_vm_ops = { + .open = vino_vm_open, + .close = vino_vm_close, +}; + +static int vino_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *dev = video_devdata(file); + struct vino_channel_settings *vcs = video_get_drvdata(dev); + + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + + struct vino_framebuffer *fb = NULL; + unsigned int i, length; + int ret = 0; + + dprintk("mmap():\n"); + + // TODO: reject mmap if already mapped + + if (mutex_lock_interruptible(&vcs->mutex)) + return -EINTR; + + if (vcs->reading) { + ret = -EBUSY; + goto out; + } + + // TODO: check queue type + + if (!(vma->vm_flags & VM_WRITE)) { + dprintk("mmap(): app bug: PROT_WRITE please\n"); + ret = -EINVAL; + goto out; + } + if (!(vma->vm_flags & VM_SHARED)) { + dprintk("mmap(): app bug: MAP_SHARED please\n"); + ret = -EINVAL; + goto out; + } + + /* find the correct buffer using offset */ + length = vino_queue_get_length(&vcs->fb_queue); + if (length == 0) { + dprintk("mmap(): queue not initialized\n"); + ret = -EINVAL; + goto out; + } + + for (i = 0; i < length; i++) { + fb = vino_queue_get_buffer(&vcs->fb_queue, i); + if (fb == NULL) { + dprintk("mmap(): vino_queue_get_buffer() failed\n"); + ret = -EINVAL; + goto out; + } + + if (fb->offset == offset) + goto found; + } + + dprintk("mmap(): invalid offset = %lu\n", offset); + ret = -EINVAL; + goto out; + +found: + dprintk("mmap(): buffer = %d\n", i); + + if (size > (fb->desc_table.page_count * PAGE_SIZE)) { + dprintk("mmap(): failed: size = %lu > %lu\n", + size, fb->desc_table.page_count * PAGE_SIZE); + ret = -EINVAL; + goto out; + } + + for (i = 0; i < fb->desc_table.page_count; i++) { + unsigned long pfn = + virt_to_phys((void *)fb->desc_table.virtual[i]) >> + PAGE_SHIFT; + + if (size < PAGE_SIZE) + break; + + // protection was: PAGE_READONLY + if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, + vma->vm_page_prot)) { + dprintk("mmap(): remap_pfn_range() failed\n"); + ret = -EAGAIN; + goto out; + } + + start += PAGE_SIZE; + size -= PAGE_SIZE; + } + + fb->map_count = 1; + + vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; + vma->vm_flags &= ~VM_IO; + vma->vm_private_data = fb; + vma->vm_file = file; + vma->vm_ops = &vino_vm_ops; + +out: + mutex_unlock(&vcs->mutex); + + return ret; +} + +static unsigned int vino_poll(struct file *file, poll_table *pt) +{ + struct video_device *dev = video_devdata(file); + struct vino_channel_settings *vcs = video_get_drvdata(dev); + unsigned int outgoing; + unsigned int ret = 0; + + // lock mutex (?) + // TODO: this has to be corrected for different read modes + + dprintk("poll():\n"); + + if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) { + dprintk("poll(): vino_queue_get_outgoing() failed\n"); + ret = POLLERR; + goto error; + } + if (outgoing > 0) + goto over; + + poll_wait(file, &vcs->fb_queue.frame_wait_queue, pt); + + if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) { + dprintk("poll(): vino_queue_get_outgoing() failed\n"); + ret = POLLERR; + goto error; + } + +over: + dprintk("poll(): data %savailable\n", + (outgoing > 0) ? "" : "not "); + + if (outgoing > 0) + ret = POLLIN | POLLRDNORM; + +error: + + return ret; +} + +static int vino_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct vino_channel_settings *vcs = video_get_drvdata(dev); + +#ifdef VINO_DEBUG + switch (_IOC_TYPE(cmd)) { + case 'v': + dprintk("ioctl(): V4L1 unsupported (0x%08x)\n", cmd); + break; + case 'V': + dprintk("ioctl(): V4L2 %s (0x%08x)\n", + v4l2_ioctl_names[_IOC_NR(cmd)], cmd); + break; + default: + dprintk("ioctl(): unsupported command 0x%08x\n", cmd); + } +#endif + + switch (cmd) { + /* V4L2 interface */ + case VIDIOC_QUERYCAP: { + vino_v4l2_querycap(arg); + break; + } + case VIDIOC_ENUMINPUT: { + return vino_v4l2_enuminput(vcs, arg); + } + case VIDIOC_G_INPUT: { + return vino_v4l2_g_input(vcs, arg); + } + case VIDIOC_S_INPUT: { + return vino_v4l2_s_input(vcs, arg); + } + case VIDIOC_ENUMSTD: { + return vino_v4l2_enumstd(vcs, arg); + } + case VIDIOC_QUERYSTD: { + return vino_v4l2_querystd(vcs, arg); + } + case VIDIOC_G_STD: { + return vino_v4l2_g_std(vcs, arg); + } + case VIDIOC_S_STD: { + return vino_v4l2_s_std(vcs, arg); + } + case VIDIOC_ENUM_FMT: { + return vino_v4l2_enum_fmt(vcs, arg); + } + case VIDIOC_TRY_FMT: { + return vino_v4l2_try_fmt(vcs, arg); + } + case VIDIOC_G_FMT: { + return vino_v4l2_g_fmt(vcs, arg); + } + case VIDIOC_S_FMT: { + return vino_v4l2_s_fmt(vcs, arg); + } + case VIDIOC_CROPCAP: { + return vino_v4l2_cropcap(vcs, arg); + } + case VIDIOC_G_CROP: { + return vino_v4l2_g_crop(vcs, arg); + } + case VIDIOC_S_CROP: { + return vino_v4l2_s_crop(vcs, arg); + } + case VIDIOC_G_PARM: { + return vino_v4l2_g_parm(vcs, arg); + } + case VIDIOC_S_PARM: { + return vino_v4l2_s_parm(vcs, arg); + } + case VIDIOC_REQBUFS: { + return vino_v4l2_reqbufs(vcs, arg); + } + case VIDIOC_QUERYBUF: { + return vino_v4l2_querybuf(vcs, arg); + } + case VIDIOC_QBUF: { + return vino_v4l2_qbuf(vcs, arg); + } + case VIDIOC_DQBUF: { + return vino_v4l2_dqbuf(vcs, arg, file->f_flags & O_NONBLOCK); + } + case VIDIOC_STREAMON: { + return vino_v4l2_streamon(vcs); + } + case VIDIOC_STREAMOFF: { + return vino_v4l2_streamoff(vcs); + } + case VIDIOC_QUERYCTRL: { + return vino_v4l2_queryctrl(vcs, arg); + } + case VIDIOC_G_CTRL: { + return vino_v4l2_g_ctrl(vcs, arg); + } + case VIDIOC_S_CTRL: { + return vino_v4l2_s_ctrl(vcs, arg); + } + default: + return -ENOIOCTLCMD; + } + + return 0; +} + +static int vino_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct video_device *dev = video_devdata(file); + struct vino_channel_settings *vcs = video_get_drvdata(dev); + int ret; + + if (mutex_lock_interruptible(&vcs->mutex)) + return -EINTR; + + ret = video_usercopy(inode, file, cmd, arg, vino_do_ioctl); + + mutex_unlock(&vcs->mutex); + + return ret; +} + +/* Initialization and cleanup */ + +// __initdata +static int vino_init_stage = 0; + +static struct file_operations vino_fops = { + .owner = THIS_MODULE, + .open = vino_open, + .release = vino_close, + .ioctl = vino_ioctl, + .mmap = vino_mmap, + .poll = vino_poll, + .llseek = no_llseek, +}; + +static struct video_device v4l_device_template = { + .name = "NOT SET", + //.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE | + // VID_TYPE_CLIPPING | VID_TYPE_SCALES, VID_TYPE_OVERLAY + .hardware = VID_HARDWARE_VINO, + .fops = &vino_fops, + .minor = -1, +}; + +static void vino_module_cleanup(int stage) +{ + switch(stage) { + case 10: + video_unregister_device(vino_drvdata->b.v4l_device); + vino_drvdata->b.v4l_device = NULL; + case 9: + video_unregister_device(vino_drvdata->a.v4l_device); + vino_drvdata->a.v4l_device = NULL; + case 8: + vino_i2c_del_bus(); + case 7: + free_irq(SGI_VINO_IRQ, NULL); + case 6: + if (vino_drvdata->b.v4l_device) { + video_device_release(vino_drvdata->b.v4l_device); + vino_drvdata->b.v4l_device = NULL; + } + case 5: + if (vino_drvdata->a.v4l_device) { + video_device_release(vino_drvdata->a.v4l_device); + vino_drvdata->a.v4l_device = NULL; + } + case 4: + /* all entries in dma_cpu dummy table have the same address */ + dma_unmap_single(NULL, + vino_drvdata->dummy_desc_table.dma_cpu[0], + PAGE_SIZE, DMA_FROM_DEVICE); + dma_free_coherent(NULL, VINO_DUMMY_DESC_COUNT + * sizeof(dma_addr_t), + (void *)vino_drvdata-> + dummy_desc_table.dma_cpu, + vino_drvdata->dummy_desc_table.dma); + case 3: + free_page(vino_drvdata->dummy_page); + case 2: + kfree(vino_drvdata); + case 1: + iounmap(vino); + case 0: + break; + default: + dprintk("vino_module_cleanup(): invalid cleanup stage = %d\n", + stage); + } +} + +static int vino_probe(void) +{ + unsigned long rev_id; + + if (ip22_is_fullhouse()) { + printk(KERN_ERR "VINO doesn't exist in IP22 Fullhouse\n"); + return -ENODEV; + } + + if (!(sgimc->systemid & SGIMC_SYSID_EPRESENT)) { + printk(KERN_ERR "VINO is not found (EISA BUS not present)\n"); + return -ENODEV; + } + + vino = (struct sgi_vino *)ioremap(VINO_BASE, sizeof(struct sgi_vino)); + if (!vino) { + printk(KERN_ERR "VINO: ioremap() failed\n"); + return -EIO; + } + vino_init_stage++; + + if (get_dbe(rev_id, &(vino->rev_id))) { + printk(KERN_ERR "Failed to read VINO revision register\n"); + vino_module_cleanup(vino_init_stage); + return -ENODEV; + } + + if (VINO_ID_VALUE(rev_id) != VINO_CHIP_ID) { + printk(KERN_ERR "Unknown VINO chip ID (Rev/ID: 0x%02lx)\n", + rev_id); + vino_module_cleanup(vino_init_stage); + return -ENODEV; + } + + printk(KERN_INFO "VINO revision %ld found\n", VINO_REV_NUM(rev_id)); + + return 0; +} + +static int vino_init(void) +{ + dma_addr_t dma_dummy_address; + int i; + + vino_drvdata = kzalloc(sizeof(struct vino_settings), GFP_KERNEL); + if (!vino_drvdata) { + vino_module_cleanup(vino_init_stage); + return -ENOMEM; + } + vino_init_stage++; + + /* create a dummy dma descriptor */ + vino_drvdata->dummy_page = get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!vino_drvdata->dummy_page) { + vino_module_cleanup(vino_init_stage); + return -ENOMEM; + } + vino_init_stage++; + + // TODO: use page_count in dummy_desc_table + + vino_drvdata->dummy_desc_table.dma_cpu = + dma_alloc_coherent(NULL, + VINO_DUMMY_DESC_COUNT * sizeof(dma_addr_t), + &vino_drvdata->dummy_desc_table.dma, + GFP_KERNEL | GFP_DMA); + if (!vino_drvdata->dummy_desc_table.dma_cpu) { + vino_module_cleanup(vino_init_stage); + return -ENOMEM; + } + vino_init_stage++; + + dma_dummy_address = dma_map_single(NULL, + (void *)vino_drvdata->dummy_page, + PAGE_SIZE, DMA_FROM_DEVICE); + for (i = 0; i < VINO_DUMMY_DESC_COUNT; i++) { + vino_drvdata->dummy_desc_table.dma_cpu[i] = dma_dummy_address; + } + + /* initialize VINO */ + + vino->control = 0; + vino->a.next_4_desc = vino_drvdata->dummy_desc_table.dma; + vino->b.next_4_desc = vino_drvdata->dummy_desc_table.dma; + udelay(VINO_DESC_FETCH_DELAY); + + vino->intr_status = 0; + + vino->a.fifo_thres = VINO_FIFO_THRESHOLD_DEFAULT; + vino->b.fifo_thres = VINO_FIFO_THRESHOLD_DEFAULT; + + return 0; +} + +static int vino_init_channel_settings(struct vino_channel_settings *vcs, + unsigned int channel, const char *name) +{ + vcs->channel = channel; + vcs->input = VINO_INPUT_NONE; + vcs->alpha = 0; + vcs->users = 0; + vcs->data_format = VINO_DATA_FMT_GREY; + vcs->data_norm = VINO_DATA_NORM_NTSC; + vcs->decimation = 1; + vino_set_default_clipping(vcs); + vino_set_default_framerate(vcs); + + vcs->capturing = 0; + + mutex_init(&vcs->mutex); + spin_lock_init(&vcs->capture_lock); + + mutex_init(&vcs->fb_queue.queue_mutex); + spin_lock_init(&vcs->fb_queue.queue_lock); + init_waitqueue_head(&vcs->fb_queue.frame_wait_queue); + + vcs->v4l_device = video_device_alloc(); + if (!vcs->v4l_device) { + vino_module_cleanup(vino_init_stage); + return -ENOMEM; + } + vino_init_stage++; + + memcpy(vcs->v4l_device, &v4l_device_template, + sizeof(struct video_device)); + strcpy(vcs->v4l_device->name, name); + vcs->v4l_device->release = video_device_release; + + video_set_drvdata(vcs->v4l_device, vcs); + + return 0; +} + +static int __init vino_module_init(void) +{ + int ret; + + printk(KERN_INFO "SGI VINO driver version %s\n", + VINO_MODULE_VERSION); + + ret = vino_probe(); + if (ret) + return ret; + + ret = vino_init(); + if (ret) + return ret; + + /* initialize data structures */ + + spin_lock_init(&vino_drvdata->vino_lock); + spin_lock_init(&vino_drvdata->input_lock); + + ret = vino_init_channel_settings(&vino_drvdata->a, VINO_CHANNEL_A, + vino_v4l_device_name_a); + if (ret) + return ret; + + ret = vino_init_channel_settings(&vino_drvdata->b, VINO_CHANNEL_B, + vino_v4l_device_name_b); + if (ret) + return ret; + + /* initialize hardware and register V4L devices */ + + ret = request_irq(SGI_VINO_IRQ, vino_interrupt, 0, + vino_driver_description, NULL); + if (ret) { + printk(KERN_ERR "VINO: requesting IRQ %02d failed\n", + SGI_VINO_IRQ); + vino_module_cleanup(vino_init_stage); + return -EAGAIN; + } + vino_init_stage++; + + ret = vino_i2c_add_bus(); + if (ret) { + printk(KERN_ERR "VINO I2C bus registration failed\n"); + vino_module_cleanup(vino_init_stage); + return ret; + } + vino_init_stage++; + + ret = video_register_device(vino_drvdata->a.v4l_device, + VFL_TYPE_GRABBER, -1); + if (ret < 0) { + printk(KERN_ERR "VINO channel A Video4Linux-device " + "registration failed\n"); + vino_module_cleanup(vino_init_stage); + return -EINVAL; + } + vino_init_stage++; + + ret = video_register_device(vino_drvdata->b.v4l_device, + VFL_TYPE_GRABBER, -1); + if (ret < 0) { + printk(KERN_ERR "VINO channel B Video4Linux-device " + "registration failed\n"); + vino_module_cleanup(vino_init_stage); + return -EINVAL; + } + vino_init_stage++; + +#if defined(CONFIG_KMOD) && defined(MODULE) + request_module("saa7191"); + request_module("indycam"); +#endif + + dprintk("init complete!\n"); + + return 0; +} + +static void __exit vino_module_exit(void) +{ + dprintk("exiting, stage = %d ...\n", vino_init_stage); + vino_module_cleanup(vino_init_stage); + dprintk("cleanup complete, exit!\n"); +} + +module_init(vino_module_init); +module_exit(vino_module_exit); diff --git a/linux/drivers/media/video/vino.h b/linux/drivers/media/video/vino.h new file mode 100644 index 000000000..de2d615ae --- /dev/null +++ b/linux/drivers/media/video/vino.h @@ -0,0 +1,138 @@ +/* + * Driver for the VINO (Video In No Out) system found in SGI Indys. + * + * This file is subject to the terms and conditions of the GNU General Public + * License version 2 as published by the Free Software Foundation. + * + * Copyright (C) 1999 Ulf Karlsson + * Copyright (C) 2003 Ladislav Michl + */ + +#ifndef _VINO_H_ +#define _VINO_H_ + +#define VINO_BASE 0x00080000 /* Vino is in the EISA address space, + * but it is not an EISA bus card */ +#define VINO_PAGE_SIZE 4096 + +struct sgi_vino_channel { + u32 _pad_alpha; + volatile u32 alpha; + +#define VINO_CLIP_X(x) ((x) & 0x3ff) /* bits 0:9 */ +#define VINO_CLIP_ODD(x) (((x) & 0x1ff) << 10) /* bits 10:18 */ +#define VINO_CLIP_EVEN(x) (((x) & 0x1ff) << 19) /* bits 19:27 */ + u32 _pad_clip_start; + volatile u32 clip_start; + u32 _pad_clip_end; + volatile u32 clip_end; + +#define VINO_FRAMERT_FULL 0xfff +#define VINO_FRAMERT_PAL (1<<0) /* 0=NTSC 1=PAL */ +#define VINO_FRAMERT_RT(x) (((x) & 0xfff) << 1) /* bits 1:12 */ + u32 _pad_frame_rate; + volatile u32 frame_rate; + + u32 _pad_field_counter; + volatile u32 field_counter; + u32 _pad_line_size; + volatile u32 line_size; + u32 _pad_line_count; + volatile u32 line_count; + u32 _pad_page_index; + volatile u32 page_index; + u32 _pad_next_4_desc; + volatile u32 next_4_desc; + u32 _pad_start_desc_tbl; + volatile u32 start_desc_tbl; + +#define VINO_DESC_JUMP (1<<30) +#define VINO_DESC_STOP (1<<31) +#define VINO_DESC_VALID (1<<32) + u32 _pad_desc_0; + volatile u32 desc_0; + u32 _pad_desc_1; + volatile u32 desc_1; + u32 _pad_desc_2; + volatile u32 desc_2; + u32 _pad_Bdesc_3; + volatile u32 desc_3; + + u32 _pad_fifo_thres; + volatile u32 fifo_thres; + u32 _pad_fifo_read; + volatile u32 fifo_read; + u32 _pad_fifo_write; + volatile u32 fifo_write; +}; + +struct sgi_vino { +#define VINO_CHIP_ID 0xb +#define VINO_REV_NUM(x) ((x) & 0x0f) +#define VINO_ID_VALUE(x) (((x) & 0xf0) >> 4) + u32 _pad_rev_id; + volatile u32 rev_id; + +#define VINO_CTRL_LITTLE_ENDIAN (1<<0) +#define VINO_CTRL_A_EOF_INT (1<<1) /* Field transferred int */ +#define VINO_CTRL_A_FIFO_INT (1<<2) /* FIFO overflow int */ +#define VINO_CTRL_A_EOD_INT (1<<3) /* End of desc table int */ +#define VINO_CTRL_A_INT (VINO_CTRL_A_EOF_INT | \ + VINO_CTRL_A_FIFO_INT | \ + VINO_CTRL_A_EOD_INT) +#define VINO_CTRL_B_EOF_INT (1<<4) /* Field transferred int */ +#define VINO_CTRL_B_FIFO_INT (1<<5) /* FIFO overflow int */ +#define VINO_CTRL_B_EOD_INT (1<<6) /* End of desc table int */ +#define VINO_CTRL_B_INT (VINO_CTRL_B_EOF_INT | \ + VINO_CTRL_B_FIFO_INT | \ + VINO_CTRL_B_EOD_INT) +#define VINO_CTRL_A_DMA_ENBL (1<<7) +#define VINO_CTRL_A_INTERLEAVE_ENBL (1<<8) +#define VINO_CTRL_A_SYNC_ENBL (1<<9) +#define VINO_CTRL_A_SELECT (1<<10) /* 1=D1 0=Philips */ +#define VINO_CTRL_A_RGB (1<<11) /* 1=RGB 0=YUV */ +#define VINO_CTRL_A_LUMA_ONLY (1<<12) +#define VINO_CTRL_A_DEC_ENBL (1<<13) /* Decimation */ +#define VINO_CTRL_A_DEC_SCALE_MASK 0x1c000 /* bits 14:17 */ +#define VINO_CTRL_A_DEC_SCALE_SHIFT (14) +#define VINO_CTRL_A_DEC_HOR_ONLY (1<<17) /* Horizontal only */ +#define VINO_CTRL_A_DITHER (1<<18) /* 24 -> 8 bit dither */ +#define VINO_CTRL_B_DMA_ENBL (1<<19) +#define VINO_CTRL_B_INTERLEAVE_ENBL (1<<20) +#define VINO_CTRL_B_SYNC_ENBL (1<<21) +#define VINO_CTRL_B_SELECT (1<<22) /* 1=D1 0=Philips */ +#define VINO_CTRL_B_RGB (1<<23) /* 1=RGB 0=YUV */ +#define VINO_CTRL_B_LUMA_ONLY (1<<24) +#define VINO_CTRL_B_DEC_ENBL (1<<25) /* Decimation */ +#define VINO_CTRL_B_DEC_SCALE_MASK 0x1c000000 /* bits 26:28 */ +#define VINO_CTRL_B_DEC_SCALE_SHIFT (26) +#define VINO_CTRL_B_DEC_HOR_ONLY (1<<29) /* Decimation horizontal only */ +#define VINO_CTRL_B_DITHER (1<<30) /* ChanB 24 -> 8 bit dither */ + u32 _pad_control; + volatile u32 control; + +#define VINO_INTSTAT_A_EOF (1<<0) /* Field transferred int */ +#define VINO_INTSTAT_A_FIFO (1<<1) /* FIFO overflow int */ +#define VINO_INTSTAT_A_EOD (1<<2) /* End of desc table int */ +#define VINO_INTSTAT_A (VINO_INTSTAT_A_EOF | \ + VINO_INTSTAT_A_FIFO | \ + VINO_INTSTAT_A_EOD) +#define VINO_INTSTAT_B_EOF (1<<3) /* Field transferred int */ +#define VINO_INTSTAT_B_FIFO (1<<4) /* FIFO overflow int */ +#define VINO_INTSTAT_B_EOD (1<<5) /* End of desc table int */ +#define VINO_INTSTAT_B (VINO_INTSTAT_B_EOF | \ + VINO_INTSTAT_B_FIFO | \ + VINO_INTSTAT_B_EOD) + u32 _pad_intr_status; + volatile u32 intr_status; + + u32 _pad_i2c_control; + volatile u32 i2c_control; + u32 _pad_i2c_data; + volatile u32 i2c_data; + + struct sgi_vino_channel a; + struct sgi_vino_channel b; +}; + +#endif diff --git a/linux/drivers/media/video/vpx3220.c b/linux/drivers/media/video/vpx3220.c new file mode 100644 index 000000000..a34b47326 --- /dev/null +++ b/linux/drivers/media/video/vpx3220.c @@ -0,0 +1,749 @@ +/* + * vpx3220a, vpx3216b & vpx3214c video decoder driver version 0.0.1 + * + * Copyright (C) 2001 Laurent Pinchart + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#define I2C_NAME(x) (x)->name + +#include "compat.h" +#include +#include + +#define I2C_VPX3220 0x86 +#define VPX3220_DEBUG KERN_DEBUG "vpx3220: " + +static int debug = 0; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define dprintk(num, format, args...) \ + do { \ + if (debug >= num) \ + printk(format, ##args); \ + } while (0) + +#define VPX_TIMEOUT_COUNT 10 + +/* ----------------------------------------------------------------------- */ + +struct vpx3220 { + unsigned char reg[255]; + + int norm; + int input; + int enable; + int bright; + int contrast; + int hue; + int sat; +}; + +static char *inputs[] = { "internal", "composite", "svideo" }; + +/* ----------------------------------------------------------------------- */ +static inline int +vpx3220_write (struct i2c_client *client, + u8 reg, + u8 value) +{ + struct vpx3220 *decoder = i2c_get_clientdata(client); + + decoder->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int +vpx3220_read (struct i2c_client *client, + u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int +vpx3220_fp_status (struct i2c_client *client) +{ + unsigned char status; + unsigned int i; + + for (i = 0; i < VPX_TIMEOUT_COUNT; i++) { + status = vpx3220_read(client, 0x29); + + if (!(status & 4)) + return 0; + + udelay(10); + + if (need_resched()) + cond_resched(); + } + + return -1; +} + +static int +vpx3220_fp_write (struct i2c_client *client, + u8 fpaddr, + u16 data) +{ + /* Write the 16-bit address to the FPWR register */ + if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) { + dprintk(1, VPX3220_DEBUG "%s: failed\n", __func__); + return -1; + } + + if (vpx3220_fp_status(client) < 0) + return -1; + + /* Write the 16-bit data to the FPDAT register */ + if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) { + dprintk(1, VPX3220_DEBUG "%s: failed\n", __func__); + return -1; + } + + return 0; +} + +static u16 +vpx3220_fp_read (struct i2c_client *client, + u16 fpaddr) +{ + s16 data; + + /* Write the 16-bit address to the FPRD register */ + if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) { + dprintk(1, VPX3220_DEBUG "%s: failed\n", __func__); + return -1; + } + + if (vpx3220_fp_status(client) < 0) + return -1; + + /* Read the 16-bit data from the FPDAT register */ + data = i2c_smbus_read_word_data(client, 0x28); + if (data == -1) { + dprintk(1, VPX3220_DEBUG "%s: failed\n", __func__); + return -1; + } + + return swab16(data); +} + +static int +vpx3220_write_block (struct i2c_client *client, + const u8 *data, + unsigned int len) +{ + u8 reg; + int ret = -1; + + while (len >= 2) { + reg = *data++; + if ((ret = + vpx3220_write(client, reg, *data++)) < 0) + break; + len -= 2; + } + + return ret; +} + +static int +vpx3220_write_fp_block (struct i2c_client *client, + const u16 *data, + unsigned int len) +{ + u8 reg; + int ret = 0; + + while (len > 1) { + reg = *data++; + ret |= vpx3220_fp_write(client, reg, *data++); + len -= 2; + } + + return ret; +} + +/* ---------------------------------------------------------------------- */ + +static const unsigned short init_ntsc[] = { + 0x1c, 0x00, /* NTSC tint angle */ + 0x88, 17, /* Window 1 vertical */ + 0x89, 240, /* Vertical lines in */ + 0x8a, 240, /* Vertical lines out */ + 0x8b, 000, /* Horizontal begin */ + 0x8c, 640, /* Horizontal length */ + 0x8d, 640, /* Number of pixels */ + 0x8f, 0xc00, /* Disable window 2 */ + 0xf0, 0x73, /* 13.5 MHz transport, Forced + * mode, latch windows */ + 0xf2, 0x13, /* NTSC M, composite input */ + 0xe7, 0x1e1, /* Enable vertical standard + * locking @ 240 lines */ +}; + +static const unsigned short init_pal[] = { + 0x88, 23, /* Window 1 vertical begin */ + 0x89, 288, /* Vertical lines in (16 lines + * skipped by the VFE) */ + 0x8a, 288, /* Vertical lines out (16 lines + * skipped by the VFE) */ + 0x8b, 16, /* Horizontal begin */ + 0x8c, 768, /* Horizontal length */ + 0x8d, 784, /* Number of pixels + * Must be >= Horizontal begin + Horizontal length */ + 0x8f, 0xc00, /* Disable window 2 */ + 0xf0, 0x77, /* 13.5 MHz transport, Forced + * mode, latch windows */ + 0xf2, 0x3d1, /* PAL B,G,H,I, composite input */ + 0xe7, 0x241, /* PAL/SECAM set to 288 lines */ +}; + +static const unsigned short init_secam[] = { + 0x88, 23, /* Window 1 vertical begin */ + 0x89, 288, /* Vertical lines in (16 lines + * skipped by the VFE) */ + 0x8a, 288, /* Vertical lines out (16 lines + * skipped by the VFE) */ + 0x8b, 16, /* Horizontal begin */ + 0x8c, 768, /* Horizontal length */ + 0x8d, 784, /* Number of pixels + * Must be >= Horizontal begin + Horizontal length */ + 0x8f, 0xc00, /* Disable window 2 */ + 0xf0, 0x77, /* 13.5 MHz transport, Forced + * mode, latch windows */ + 0xf2, 0x3d5, /* SECAM, composite input */ + 0xe7, 0x241, /* PAL/SECAM set to 288 lines */ +}; + +static const unsigned char init_common[] = { + 0xf2, 0x00, /* Disable all outputs */ + 0x33, 0x0d, /* Luma : VIN2, Chroma : CIN + * (clamp off) */ + 0xd8, 0xa8, /* HREF/VREF active high, VREF + * pulse = 2, Odd/Even flag */ + 0x20, 0x03, /* IF compensation 0dB/oct */ + 0xe0, 0xff, /* Open up all comparators */ + 0xe1, 0x00, + 0xe2, 0x7f, + 0xe3, 0x80, + 0xe4, 0x7f, + 0xe5, 0x80, + 0xe6, 0x00, /* Brightness set to 0 */ + 0xe7, 0xe0, /* Contrast to 1.0, noise shaping + * 10 to 8 2-bit error diffusion */ + 0xe8, 0xf8, /* YUV422, CbCr binary offset, + * ... (p.32) */ + 0xea, 0x18, /* LLC2 connected, output FIFO + * reset with VACTintern */ + 0xf0, 0x8a, /* Half full level to 10, bus + * shuffler [7:0, 23:16, 15:8] */ + 0xf1, 0x18, /* Single clock, sync mode, no + * FE delay, no HLEN counter */ + 0xf8, 0x12, /* Port A, PIXCLK, HF# & FE# + * strength to 2 */ + 0xf9, 0x24, /* Port B, HREF, VREF, PREF & + * ALPHA strength to 4 */ +}; + +static const unsigned short init_fp[] = { + 0x59, 0, + 0xa0, 2070, /* ACC reference */ + 0xa3, 0, + 0xa4, 0, + 0xa8, 30, + 0xb2, 768, + 0xbe, 27, + 0x58, 0, + 0x26, 0, + 0x4b, 0x298, /* PLL gain */ +}; + +static void +vpx3220_dump_i2c (struct i2c_client *client) +{ + int len = sizeof(init_common); + const unsigned char *data = init_common; + + while (len > 1) { + dprintk(1, + KERN_DEBUG "vpx3216b i2c reg 0x%02x data 0x%02x\n", + *data, vpx3220_read(client, *data)); + data += 2; + len -= 2; + } +} + +static int +vpx3220_command (struct i2c_client *client, + unsigned int cmd, + void *arg) +{ + struct vpx3220 *decoder = i2c_get_clientdata(client); + + switch (cmd) { + case 0: + { + vpx3220_write_block(client, init_common, + sizeof(init_common)); + vpx3220_write_fp_block(client, init_fp, + sizeof(init_fp) >> 1); + switch (decoder->norm) { + + case VIDEO_MODE_NTSC: + vpx3220_write_fp_block(client, init_ntsc, + sizeof(init_ntsc) >> 1); + break; + + case VIDEO_MODE_PAL: + vpx3220_write_fp_block(client, init_pal, + sizeof(init_pal) >> 1); + break; + case VIDEO_MODE_SECAM: + vpx3220_write_fp_block(client, init_secam, + sizeof(init_secam) >> 1); + break; + default: + vpx3220_write_fp_block(client, init_pal, + sizeof(init_pal) >> 1); + break; + } + } + break; + + case DECODER_DUMP: + { + vpx3220_dump_i2c(client); + } + break; + + case DECODER_GET_CAPABILITIES: + { + struct video_decoder_capability *cap = arg; + + dprintk(1, KERN_DEBUG "%s: DECODER_GET_CAPABILITIES\n", + I2C_NAME(client)); + + cap->flags = VIDEO_DECODER_PAL | + VIDEO_DECODER_NTSC | + VIDEO_DECODER_SECAM | + VIDEO_DECODER_AUTO | + VIDEO_DECODER_CCIR; + cap->inputs = 3; + cap->outputs = 1; + } + break; + + case DECODER_GET_STATUS: + { + int res = 0, status; + + dprintk(1, KERN_INFO "%s: DECODER_GET_STATUS\n", + I2C_NAME(client)); + + status = vpx3220_fp_read(client, 0x0f3); + + dprintk(1, KERN_INFO "%s: status: 0x%04x\n", I2C_NAME(client), + status); + + if (status < 0) + return status; + + if ((status & 0x20) == 0) { + res |= DECODER_STATUS_GOOD | DECODER_STATUS_COLOR; + + switch (status & 0x18) { + + case 0x00: + case 0x10: + case 0x14: + case 0x18: + res |= DECODER_STATUS_PAL; + break; + + case 0x08: + res |= DECODER_STATUS_SECAM; + break; + + case 0x04: + case 0x0c: + case 0x1c: + res |= DECODER_STATUS_NTSC; + break; + } + } + + *(int *) arg = res; + } + break; + + case DECODER_SET_NORM: + { + int *iarg = arg, data; + int temp_input; + + /* Here we back up the input selection because it gets + overwritten when we fill the registers with the + choosen video norm */ + temp_input = vpx3220_fp_read(client, 0xf2); + + dprintk(1, KERN_DEBUG "%s: DECODER_SET_NORM %d\n", + I2C_NAME(client), *iarg); + switch (*iarg) { + + case VIDEO_MODE_NTSC: + vpx3220_write_fp_block(client, init_ntsc, + sizeof(init_ntsc) >> 1); + dprintk(1, KERN_INFO "%s: norm switched to NTSC\n", + I2C_NAME(client)); + break; + + case VIDEO_MODE_PAL: + vpx3220_write_fp_block(client, init_pal, + sizeof(init_pal) >> 1); + dprintk(1, KERN_INFO "%s: norm switched to PAL\n", + I2C_NAME(client)); + break; + + case VIDEO_MODE_SECAM: + vpx3220_write_fp_block(client, init_secam, + sizeof(init_secam) >> 1); + dprintk(1, KERN_INFO "%s: norm switched to SECAM\n", + I2C_NAME(client)); + break; + + case VIDEO_MODE_AUTO: + /* FIXME This is only preliminary support */ + data = vpx3220_fp_read(client, 0xf2) & 0x20; + vpx3220_fp_write(client, 0xf2, 0x00c0 | data); + dprintk(1, KERN_INFO "%s: norm switched to Auto\n", + I2C_NAME(client)); + break; + + default: + return -EINVAL; + + } + decoder->norm = *iarg; + + /* And here we set the backed up video input again */ + vpx3220_fp_write(client, 0xf2, temp_input | 0x0010); + udelay(10); + } + break; + + case DECODER_SET_INPUT: + { + int *iarg = arg, data; + + /* RJ: *iarg = 0: ST8 (PCTV) input + *iarg = 1: COMPOSITE input + *iarg = 2: SVHS input */ + + const int input[3][2] = { + {0x0c, 0}, + {0x0d, 0}, + {0x0e, 1} + }; + + if (*iarg < 0 || *iarg > 2) + return -EINVAL; + + dprintk(1, KERN_INFO "%s: input switched to %s\n", + I2C_NAME(client), inputs[*iarg]); + + vpx3220_write(client, 0x33, input[*iarg][0]); + + data = vpx3220_fp_read(client, 0xf2) & ~(0x0020); + if (data < 0) + return data; + /* 0x0010 is required to latch the setting */ + vpx3220_fp_write(client, 0xf2, + data | (input[*iarg][1] << 5) | 0x0010); + + udelay(10); + } + break; + + case DECODER_SET_OUTPUT: + { + int *iarg = arg; + + /* not much choice of outputs */ + if (*iarg != 0) { + return -EINVAL; + } + } + break; + + case DECODER_ENABLE_OUTPUT: + { + int *iarg = arg; + + dprintk(1, KERN_DEBUG "%s: DECODER_ENABLE_OUTPUT %d\n", + I2C_NAME(client), *iarg); + + vpx3220_write(client, 0xf2, (*iarg ? 0x1b : 0x00)); + } + break; + + case DECODER_SET_PICTURE: + { + struct video_picture *pic = arg; + + if (decoder->bright != pic->brightness) { + /* We want -128 to 128 we get 0-65535 */ + decoder->bright = pic->brightness; + vpx3220_write(client, 0xe6, + (decoder->bright - 32768) >> 8); + } + if (decoder->contrast != pic->contrast) { + /* We want 0 to 64 we get 0-65535 */ + /* Bit 7 and 8 is for noise shaping */ + decoder->contrast = pic->contrast; + vpx3220_write(client, 0xe7, + (decoder->contrast >> 10) + 192); + } + if (decoder->sat != pic->colour) { + /* We want 0 to 4096 we get 0-65535 */ + decoder->sat = pic->colour; + vpx3220_fp_write(client, 0xa0, + decoder->sat >> 4); + } + if (decoder->hue != pic->hue) { + /* We want -512 to 512 we get 0-65535 */ + decoder->hue = pic->hue; + vpx3220_fp_write(client, 0x1c, + ((decoder->hue - 32768) >> 6) & 0xFFF); + } + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int +vpx3220_init_client (struct i2c_client *client) +{ + vpx3220_write_block(client, init_common, sizeof(init_common)); + vpx3220_write_fp_block(client, init_fp, sizeof(init_fp) >> 1); + /* Default to PAL */ + vpx3220_write_fp_block(client, init_pal, sizeof(init_pal) >> 1); + + return 0; +} + +/* ----------------------------------------------------------------------- + * Client managment code + */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ +static unsigned short normal_i2c[] = + { I2C_VPX3220 >> 1, (I2C_VPX3220 >> 1) + 4, + I2C_CLIENT_END +}; + +static unsigned short ignore = I2C_CLIENT_END; + +static struct i2c_client_address_data addr_data = { + .normal_i2c = normal_i2c, + .probe = &ignore, + .ignore = &ignore, +}; + +static struct i2c_driver vpx3220_i2c_driver; + +static int +vpx3220_detach_client (struct i2c_client *client) +{ + struct vpx3220 *decoder = i2c_get_clientdata(client); + int err; + + err = i2c_detach_client(client); + if (err) { + return err; + } + + kfree(decoder); + kfree(client); + + return 0; +} + +static int +vpx3220_detect_client (struct i2c_adapter *adapter, + int address, + int kind) +{ + int err; + struct i2c_client *client; + struct vpx3220 *decoder; + + dprintk(1, VPX3220_DEBUG "%s\n", __func__); + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality + (adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == NULL) { + return -ENOMEM; + } + + client->addr = address; + client->adapter = adapter; + client->driver = &vpx3220_i2c_driver; + + /* Check for manufacture ID and part number */ + if (kind < 0) { + u8 id; + u16 pn; + + id = vpx3220_read(client, 0x00); + if (id != 0xec) { + dprintk(1, + KERN_INFO + "vpx3220_attach: Wrong manufacturer ID (0x%02x)\n", + id); + kfree(client); + return 0; + } + + pn = (vpx3220_read(client, 0x02) << 8) + + vpx3220_read(client, 0x01); + switch (pn) { + case 0x4680: + strlcpy(I2C_NAME(client), "vpx3220a", + sizeof(I2C_NAME(client))); + break; + case 0x4260: + strlcpy(I2C_NAME(client), "vpx3216b", + sizeof(I2C_NAME(client))); + break; + case 0x4280: + strlcpy(I2C_NAME(client), "vpx3214c", + sizeof(I2C_NAME(client))); + break; + default: + dprintk(1, + KERN_INFO + "%s: Wrong part number (0x%04x)\n", + __func__, pn); + kfree(client); + return 0; + } + } else { + strlcpy(I2C_NAME(client), "forced vpx32xx", + sizeof(I2C_NAME(client))); + } + + decoder = kzalloc(sizeof(struct vpx3220), GFP_KERNEL); + if (decoder == NULL) { + kfree(client); + return -ENOMEM; + } + decoder->norm = VIDEO_MODE_PAL; + decoder->input = 0; + decoder->enable = 1; + decoder->bright = 32768; + decoder->contrast = 32768; + decoder->hue = 32768; + decoder->sat = 32768; + i2c_set_clientdata(client, decoder); + + err = i2c_attach_client(client); + if (err) { + kfree(client); + kfree(decoder); + return err; + } + + dprintk(1, KERN_INFO "%s: vpx32xx client found at address 0x%02x\n", + I2C_NAME(client), client->addr << 1); + + vpx3220_init_client(client); + + return 0; +} + +static int +vpx3220_attach_adapter (struct i2c_adapter *adapter) +{ + int ret; + + ret = i2c_probe(adapter, &addr_data, &vpx3220_detect_client); + dprintk(1, VPX3220_DEBUG "%s: i2c_probe returned %d\n", + __func__, ret); + return ret; +} + +/* ----------------------------------------------------------------------- + * Driver initialization and cleanup code + */ + +static struct i2c_driver vpx3220_i2c_driver = { + .driver = { + .name = "vpx3220", + }, + + .id = I2C_DRIVERID_VPX3220, + + .attach_adapter = vpx3220_attach_adapter, + .detach_client = vpx3220_detach_client, + .command = vpx3220_command, +}; + +static int __init +vpx3220_init (void) +{ + return i2c_add_driver(&vpx3220_i2c_driver); +} + +static void __exit +vpx3220_cleanup (void) +{ + i2c_del_driver(&vpx3220_i2c_driver); +} + +module_init(vpx3220_init); +module_exit(vpx3220_cleanup); + +MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video encoder driver"); +MODULE_AUTHOR("Laurent Pinchart"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/w9966.c b/linux/drivers/media/video/w9966.c new file mode 100644 index 000000000..5aea29c69 --- /dev/null +++ b/linux/drivers/media/video/w9966.c @@ -0,0 +1,986 @@ +/* + Winbond w9966cf Webcam parport driver. + + Version 0.32 + + Copyright (C) 2001 Jakob Kemi + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + Supported devices: + *Lifeview FlyCam Supra (using the Philips saa7111a chip) + + Does any other model using the w9966 interface chip exist ? + + Todo: + + *Add a working EPP mode, since DMA ECP read isn't implemented + in the parport drivers. (That's why it's so sloow) + + *Add support for other ccd-control chips than the saa7111 + please send me feedback on what kind of chips you have. + + *Add proper probing. I don't know what's wrong with the IEEE1284 + parport drivers but (IEEE1284_MODE_NIBBLE|IEEE1284_DEVICE_ID) + and nibble read seems to be broken for some peripherals. + + *Add probing for onboard SRAM, port directions etc. (if possible) + + *Add support for the hardware compressed modes (maybe using v4l2) + + *Fix better support for the capture window (no skewed images, v4l + interface to capt. window) + + *Probably some bugs that I don't know of + + Please support me by sending feedback! + + Changes: + + Alan Cox: Removed RGB mode for kernel merge, added THIS_MODULE + and owner support for newer module locks +*/ + +#include +#include +#include +#include "compat.h" +#include +#include + +//#define DEBUG // Undef me for production + +#ifdef DEBUG +#define DPRINTF(x, a...) printk(KERN_DEBUG "W9966: %s(): "x, __FUNCTION__ , ##a) +#else +#define DPRINTF(x...) +#endif + +/* + * Defines, simple typedefs etc. + */ + +#define W9966_DRIVERNAME "W9966CF Webcam" +#define W9966_MAXCAMS 4 // Maximum number of cameras +#define W9966_RBUFFER 2048 // Read buffer (must be an even number) +#define W9966_SRAMSIZE 131072 // 128kb +#define W9966_SRAMID 0x02 // check w9966cf.pdf + +// Empirically determined window limits +#define W9966_WND_MIN_X 16 +#define W9966_WND_MIN_Y 14 +#define W9966_WND_MAX_X 705 +#define W9966_WND_MAX_Y 253 +#define W9966_WND_MAX_W (W9966_WND_MAX_X - W9966_WND_MIN_X) +#define W9966_WND_MAX_H (W9966_WND_MAX_Y - W9966_WND_MIN_Y) + +// Keep track of our current state +#define W9966_STATE_PDEV 0x01 +#define W9966_STATE_CLAIMED 0x02 +#define W9966_STATE_VDEV 0x04 + +#define W9966_I2C_W_ID 0x48 +#define W9966_I2C_R_ID 0x49 +#define W9966_I2C_R_DATA 0x08 +#define W9966_I2C_R_CLOCK 0x04 +#define W9966_I2C_W_DATA 0x02 +#define W9966_I2C_W_CLOCK 0x01 + +struct w9966_dev { + unsigned char dev_state; + unsigned char i2c_state; + unsigned short ppmode; + struct parport* pport; + struct pardevice* pdev; + struct video_device vdev; + unsigned short width; + unsigned short height; + unsigned char brightness; + signed char contrast; + signed char color; + signed char hue; +}; + +/* + * Module specific properties + */ + +MODULE_AUTHOR("Jakob Kemi "); +MODULE_DESCRIPTION("Winbond w9966cf WebCam driver (0.32)"); +MODULE_LICENSE("GPL"); + + +#ifdef MODULE +static const char* pardev[] = {[0 ... W9966_MAXCAMS] = ""}; +#else +static const char* pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"}; +#endif +module_param_array(pardev, charp, NULL, 0); +MODULE_PARM_DESC(pardev, "pardev: where to search for\n\ +\teach camera. 'aggressive' means brute-force search.\n\ +\tEg: >pardev=parport3,aggressive,parport2,parport1< would assign\n\ +\tcam 1 to parport3 and search every parport for cam 2 etc..."); + +static int parmode = 0; +module_param(parmode, int, 0); +MODULE_PARM_DESC(parmode, "parmode: transfer mode (0=auto, 1=ecp, 2=epp"); + +static int video_nr = -1; +module_param(video_nr, int, 0); + +/* + * Private data + */ + +static struct w9966_dev w9966_cams[W9966_MAXCAMS]; + +/* + * Private function declares + */ + +static inline void w9966_setState(struct w9966_dev* cam, int mask, int val); +static inline int w9966_getState(struct w9966_dev* cam, int mask, int val); +static inline void w9966_pdev_claim(struct w9966_dev *vdev); +static inline void w9966_pdev_release(struct w9966_dev *vdev); + +static int w9966_rReg(struct w9966_dev* cam, int reg); +static int w9966_wReg(struct w9966_dev* cam, int reg, int data); +#if 0 +static int w9966_rReg_i2c(struct w9966_dev* cam, int reg); +#endif +static int w9966_wReg_i2c(struct w9966_dev* cam, int reg, int data); +static int w9966_findlen(int near, int size, int maxlen); +static int w9966_calcscale(int size, int min, int max, int* beg, int* end, unsigned char* factor); +static int w9966_setup(struct w9966_dev* cam, int x1, int y1, int x2, int y2, int w, int h); + +static int w9966_init(struct w9966_dev* cam, struct parport* port); +static void w9966_term(struct w9966_dev* cam); + +static inline void w9966_i2c_setsda(struct w9966_dev* cam, int state); +static inline int w9966_i2c_setscl(struct w9966_dev* cam, int state); +static inline int w9966_i2c_getsda(struct w9966_dev* cam); +static inline int w9966_i2c_getscl(struct w9966_dev* cam); +static int w9966_i2c_wbyte(struct w9966_dev* cam, int data); +#if 0 +static int w9966_i2c_rbyte(struct w9966_dev* cam); +#endif + +static int w9966_v4l_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static ssize_t w9966_v4l_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos); + +static struct file_operations w9966_fops = { + .owner = THIS_MODULE, + .open = video_exclusive_open, + .release = video_exclusive_release, + .ioctl = w9966_v4l_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .read = w9966_v4l_read, + .llseek = no_llseek, +}; +static struct video_device w9966_template = { + .owner = THIS_MODULE, + .name = W9966_DRIVERNAME, + .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES, + .hardware = VID_HARDWARE_W9966, + .fops = &w9966_fops, +}; + +/* + * Private function defines + */ + + +// Set camera phase flags, so we know what to uninit when terminating +static inline void w9966_setState(struct w9966_dev* cam, int mask, int val) +{ + cam->dev_state = (cam->dev_state & ~mask) ^ val; +} + +// Get camera phase flags +static inline int w9966_getState(struct w9966_dev* cam, int mask, int val) +{ + return ((cam->dev_state & mask) == val); +} + +// Claim parport for ourself +static inline void w9966_pdev_claim(struct w9966_dev* cam) +{ + if (w9966_getState(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED)) + return; + parport_claim_or_block(cam->pdev); + w9966_setState(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED); +} + +// Release parport for others to use +static inline void w9966_pdev_release(struct w9966_dev* cam) +{ + if (w9966_getState(cam, W9966_STATE_CLAIMED, 0)) + return; + parport_release(cam->pdev); + w9966_setState(cam, W9966_STATE_CLAIMED, 0); +} + +// Read register from W9966 interface-chip +// Expects a claimed pdev +// -1 on error, else register data (byte) +static int w9966_rReg(struct w9966_dev* cam, int reg) +{ + // ECP, read, regtransfer, REG, REG, REG, REG, REG + const unsigned char addr = 0x80 | (reg & 0x1f); + unsigned char val; + + if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0) + return -1; + if (parport_write(cam->pport, &addr, 1) != 1) + return -1; + if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0) + return -1; + if (parport_read(cam->pport, &val, 1) != 1) + return -1; + + return val; +} + +// Write register to W9966 interface-chip +// Expects a claimed pdev +// -1 on error +static int w9966_wReg(struct w9966_dev* cam, int reg, int data) +{ + // ECP, write, regtransfer, REG, REG, REG, REG, REG + const unsigned char addr = 0xc0 | (reg & 0x1f); + const unsigned char val = data; + + if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0) + return -1; + if (parport_write(cam->pport, &addr, 1) != 1) + return -1; + if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0) + return -1; + if (parport_write(cam->pport, &val, 1) != 1) + return -1; + + return 0; +} + +// Initialize camera device. Setup all internal flags, set a +// default video mode, setup ccd-chip, register v4l device etc.. +// Also used for 'probing' of hardware. +// -1 on error +static int w9966_init(struct w9966_dev* cam, struct parport* port) +{ + if (cam->dev_state != 0) + return -1; + + cam->pport = port; + cam->brightness = 128; + cam->contrast = 64; + cam->color = 64; + cam->hue = 0; + +// Select requested transfer mode + switch(parmode) + { + default: // Auto-detect (priority: hw-ecp, hw-epp, sw-ecp) + case 0: + if (port->modes & PARPORT_MODE_ECP) + cam->ppmode = IEEE1284_MODE_ECP; + else if (port->modes & PARPORT_MODE_EPP) + cam->ppmode = IEEE1284_MODE_EPP; + else + cam->ppmode = IEEE1284_MODE_ECP; + break; + case 1: // hw- or sw-ecp + cam->ppmode = IEEE1284_MODE_ECP; + break; + case 2: // hw- or sw-epp + cam->ppmode = IEEE1284_MODE_EPP; + break; + } + +// Tell the parport driver that we exists + cam->pdev = parport_register_device(port, "w9966", NULL, NULL, NULL, 0, NULL); + if (cam->pdev == NULL) { + DPRINTF("parport_register_device() failed\n"); + return -1; + } + w9966_setState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV); + + w9966_pdev_claim(cam); + +// Setup a default capture mode + if (w9966_setup(cam, 0, 0, 1023, 1023, 200, 160) != 0) { + DPRINTF("w9966_setup() failed.\n"); + return -1; + } + + w9966_pdev_release(cam); + +// Fill in the video_device struct and register us to v4l + memcpy(&cam->vdev, &w9966_template, sizeof(struct video_device)); + cam->vdev.priv = cam; + + if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) == -1) + return -1; + + w9966_setState(cam, W9966_STATE_VDEV, W9966_STATE_VDEV); + + // All ok + printk( + "w9966cf: Found and initialized a webcam on %s.\n", + cam->pport->name + ); + return 0; +} + + +// Terminate everything gracefully +static void w9966_term(struct w9966_dev* cam) +{ +// Unregister from v4l + if (w9966_getState(cam, W9966_STATE_VDEV, W9966_STATE_VDEV)) { + video_unregister_device(&cam->vdev); + w9966_setState(cam, W9966_STATE_VDEV, 0); + } + +// Terminate from IEEE1284 mode and release pdev block + if (w9966_getState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) { + w9966_pdev_claim(cam); + parport_negotiate(cam->pport, IEEE1284_MODE_COMPAT); + w9966_pdev_release(cam); + } + +// Unregister from parport + if (w9966_getState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) { + parport_unregister_device(cam->pdev); + w9966_setState(cam, W9966_STATE_PDEV, 0); + } +} + + +// Find a good length for capture window (used both for W and H) +// A bit ugly but pretty functional. The capture length +// have to match the downscale +static int w9966_findlen(int near, int size, int maxlen) +{ + int bestlen = size; + int besterr = abs(near - bestlen); + int len; + + for(len = size+1;len < maxlen;len++) + { + int err; + if ( ((64*size) %len) != 0) + continue; + + err = abs(near - len); + + // Only continue as long as we keep getting better values + if (err > besterr) + break; + + besterr = err; + bestlen = len; + } + + return bestlen; +} + +// Modify capture window (if necessary) +// and calculate downscaling +// Return -1 on error +static int w9966_calcscale(int size, int min, int max, int* beg, int* end, unsigned char* factor) +{ + int maxlen = max - min; + int len = *end - *beg + 1; + int newlen = w9966_findlen(len, size, maxlen); + int err = newlen - len; + + // Check for bad format + if (newlen > maxlen || newlen < size) + return -1; + + // Set factor (6 bit fixed) + *factor = (64*size) / newlen; + if (*factor == 64) + *factor = 0x00; // downscale is disabled + else + *factor |= 0x80; // set downscale-enable bit + + // Modify old beginning and end + *beg -= err / 2; + *end += err - (err / 2); + + // Move window if outside borders + if (*beg < min) { + *end += min - *beg; + *beg += min - *beg; + } + if (*end > max) { + *beg -= *end - max; + *end -= *end - max; + } + + return 0; +} + +// Setup the cameras capture window etc. +// Expects a claimed pdev +// return -1 on error +static int w9966_setup(struct w9966_dev* cam, int x1, int y1, int x2, int y2, int w, int h) +{ + unsigned int i; + unsigned int enh_s, enh_e; + unsigned char scale_x, scale_y; + unsigned char regs[0x1c]; + unsigned char saa7111_regs[] = { + 0x21, 0x00, 0xd8, 0x23, 0x00, 0x80, 0x80, 0x00, + 0x88, 0x10, 0x80, 0x40, 0x40, 0x00, 0x01, 0x00, + 0x48, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x71, 0xe7, 0x00, 0x00, 0xc0 + }; + + + if (w*h*2 > W9966_SRAMSIZE) + { + DPRINTF("capture window exceeds SRAM size!.\n"); + w = 200; h = 160; // Pick default values + } + + w &= ~0x1; + if (w < 2) w = 2; + if (h < 1) h = 1; + if (w > W9966_WND_MAX_W) w = W9966_WND_MAX_W; + if (h > W9966_WND_MAX_H) h = W9966_WND_MAX_H; + + cam->width = w; + cam->height = h; + + enh_s = 0; + enh_e = w*h*2; + +// Modify capture window if necessary and calculate downscaling + if ( + w9966_calcscale(w, W9966_WND_MIN_X, W9966_WND_MAX_X, &x1, &x2, &scale_x) != 0 || + w9966_calcscale(h, W9966_WND_MIN_Y, W9966_WND_MAX_Y, &y1, &y2, &scale_y) != 0 + ) return -1; + + DPRINTF( + "%dx%d, x: %d<->%d, y: %d<->%d, sx: %d/64, sy: %d/64.\n", + w, h, x1, x2, y1, y2, scale_x&~0x80, scale_y&~0x80 + ); + +// Setup registers + regs[0x00] = 0x00; // Set normal operation + regs[0x01] = 0x18; // Capture mode + regs[0x02] = scale_y; // V-scaling + regs[0x03] = scale_x; // H-scaling + + // Capture window + regs[0x04] = (x1 & 0x0ff); // X-start (8 low bits) + regs[0x05] = (x1 & 0x300)>>8; // X-start (2 high bits) + regs[0x06] = (y1 & 0x0ff); // Y-start (8 low bits) + regs[0x07] = (y1 & 0x300)>>8; // Y-start (2 high bits) + regs[0x08] = (x2 & 0x0ff); // X-end (8 low bits) + regs[0x09] = (x2 & 0x300)>>8; // X-end (2 high bits) + regs[0x0a] = (y2 & 0x0ff); // Y-end (8 low bits) + + regs[0x0c] = W9966_SRAMID; // SRAM-banks (1x 128kb) + + // Enhancement layer + regs[0x0d] = (enh_s& 0x000ff); // Enh. start (0-7) + regs[0x0e] = (enh_s& 0x0ff00)>>8; // Enh. start (8-15) + regs[0x0f] = (enh_s& 0x70000)>>16; // Enh. start (16-17/18??) + regs[0x10] = (enh_e& 0x000ff); // Enh. end (0-7) + regs[0x11] = (enh_e& 0x0ff00)>>8; // Enh. end (8-15) + regs[0x12] = (enh_e& 0x70000)>>16; // Enh. end (16-17/18??) + + // Misc + regs[0x13] = 0x40; // VEE control (raw 4:2:2) + regs[0x17] = 0x00; // ??? + regs[0x18] = cam->i2c_state = 0x00; // Serial bus + regs[0x19] = 0xff; // I/O port direction control + regs[0x1a] = 0xff; // I/O port data register + regs[0x1b] = 0x10; // ??? + + // SAA7111 chip settings + saa7111_regs[0x0a] = cam->brightness; + saa7111_regs[0x0b] = cam->contrast; + saa7111_regs[0x0c] = cam->color; + saa7111_regs[0x0d] = cam->hue; + +// Reset (ECP-fifo & serial-bus) + if (w9966_wReg(cam, 0x00, 0x03) == -1) + return -1; + +// Write regs to w9966cf chip + for (i = 0; i < 0x1c; i++) + if (w9966_wReg(cam, i, regs[i]) == -1) + return -1; + +// Write regs to saa7111 chip + for (i = 0; i < 0x20; i++) + if (w9966_wReg_i2c(cam, i, saa7111_regs[i]) == -1) + return -1; + + return 0; +} + +/* + * Ugly and primitive i2c protocol functions + */ + +// Sets the data line on the i2c bus. +// Expects a claimed pdev. +static inline void w9966_i2c_setsda(struct w9966_dev* cam, int state) +{ + if (state) + cam->i2c_state |= W9966_I2C_W_DATA; + else + cam->i2c_state &= ~W9966_I2C_W_DATA; + + w9966_wReg(cam, 0x18, cam->i2c_state); + udelay(5); +} + +// Get peripheral clock line +// Expects a claimed pdev. +static inline int w9966_i2c_getscl(struct w9966_dev* cam) +{ + const unsigned char state = w9966_rReg(cam, 0x18); + return ((state & W9966_I2C_R_CLOCK) > 0); +} + +// Sets the clock line on the i2c bus. +// Expects a claimed pdev. -1 on error +static inline int w9966_i2c_setscl(struct w9966_dev* cam, int state) +{ + unsigned long timeout; + + if (state) + cam->i2c_state |= W9966_I2C_W_CLOCK; + else + cam->i2c_state &= ~W9966_I2C_W_CLOCK; + + w9966_wReg(cam, 0x18, cam->i2c_state); + udelay(5); + + // we go to high, we also expect the peripheral to ack. + if (state) { + timeout = jiffies + 100; + while (!w9966_i2c_getscl(cam)) { + if (time_after(jiffies, timeout)) + return -1; + } + } + return 0; +} + +// Get peripheral data line +// Expects a claimed pdev. +static inline int w9966_i2c_getsda(struct w9966_dev* cam) +{ + const unsigned char state = w9966_rReg(cam, 0x18); + return ((state & W9966_I2C_R_DATA) > 0); +} + +// Write a byte with ack to the i2c bus. +// Expects a claimed pdev. -1 on error +static int w9966_i2c_wbyte(struct w9966_dev* cam, int data) +{ + int i; + for (i = 7; i >= 0; i--) + { + w9966_i2c_setsda(cam, (data >> i) & 0x01); + + if (w9966_i2c_setscl(cam, 1) == -1) + return -1; + w9966_i2c_setscl(cam, 0); + } + + w9966_i2c_setsda(cam, 1); + + if (w9966_i2c_setscl(cam, 1) == -1) + return -1; + w9966_i2c_setscl(cam, 0); + + return 0; +} + +// Read a data byte with ack from the i2c-bus +// Expects a claimed pdev. -1 on error +#if 0 +static int w9966_i2c_rbyte(struct w9966_dev* cam) +{ + unsigned char data = 0x00; + int i; + + w9966_i2c_setsda(cam, 1); + + for (i = 0; i < 8; i++) + { + if (w9966_i2c_setscl(cam, 1) == -1) + return -1; + data = data << 1; + if (w9966_i2c_getsda(cam)) + data |= 0x01; + + w9966_i2c_setscl(cam, 0); + } + return data; +} +#endif + +// Read a register from the i2c device. +// Expects claimed pdev. -1 on error +#if 0 +static int w9966_rReg_i2c(struct w9966_dev* cam, int reg) +{ + int data; + + w9966_i2c_setsda(cam, 0); + w9966_i2c_setscl(cam, 0); + + if ( + w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 || + w9966_i2c_wbyte(cam, reg) == -1 + ) + return -1; + + w9966_i2c_setsda(cam, 1); + if (w9966_i2c_setscl(cam, 1) == -1) + return -1; + w9966_i2c_setsda(cam, 0); + w9966_i2c_setscl(cam, 0); + + if ( + w9966_i2c_wbyte(cam, W9966_I2C_R_ID) == -1 || + (data = w9966_i2c_rbyte(cam)) == -1 + ) + return -1; + + w9966_i2c_setsda(cam, 0); + + if (w9966_i2c_setscl(cam, 1) == -1) + return -1; + w9966_i2c_setsda(cam, 1); + + return data; +} +#endif + +// Write a register to the i2c device. +// Expects claimed pdev. -1 on error +static int w9966_wReg_i2c(struct w9966_dev* cam, int reg, int data) +{ + w9966_i2c_setsda(cam, 0); + w9966_i2c_setscl(cam, 0); + + if ( + w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 || + w9966_i2c_wbyte(cam, reg) == -1 || + w9966_i2c_wbyte(cam, data) == -1 + ) + return -1; + + w9966_i2c_setsda(cam, 0); + if (w9966_i2c_setscl(cam, 1) == -1) + return -1; + + w9966_i2c_setsda(cam, 1); + + return 0; +} + +/* + * Video4linux interfacing + */ + +static int w9966_v4l_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct w9966_dev *cam = vdev->priv; + + switch(cmd) + { + case VIDIOCGCAP: + { + static struct video_capability vcap = { + .name = W9966_DRIVERNAME, + .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES, + .channels = 1, + .maxwidth = W9966_WND_MAX_W, + .maxheight = W9966_WND_MAX_H, + .minwidth = 2, + .minheight = 1, + }; + struct video_capability *cap = arg; + *cap = vcap; + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel *vch = arg; + if(vch->channel != 0) // We only support one channel (#0) + return -EINVAL; + memset(vch,0,sizeof(*vch)); + strcpy(vch->name, "CCD-input"); + vch->type = VIDEO_TYPE_CAMERA; + return 0; + } + case VIDIOCSCHAN: + { + struct video_channel *vch = arg; + if(vch->channel != 0) + return -EINVAL; + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *vtune = arg; + if(vtune->tuner != 0) + return -EINVAL; + strcpy(vtune->name, "no tuner"); + vtune->rangelow = 0; + vtune->rangehigh = 0; + vtune->flags = VIDEO_TUNER_NORM; + vtune->mode = VIDEO_MODE_AUTO; + vtune->signal = 0xffff; + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *vtune = arg; + if (vtune->tuner != 0) + return -EINVAL; + if (vtune->mode != VIDEO_MODE_AUTO) + return -EINVAL; + return 0; + } + case VIDIOCGPICT: + { + struct video_picture vpic = { + cam->brightness << 8, // brightness + (cam->hue + 128) << 8, // hue + cam->color << 9, // color + cam->contrast << 9, // contrast + 0x8000, // whiteness + 16, VIDEO_PALETTE_YUV422// bpp, palette format + }; + struct video_picture *pic = arg; + *pic = vpic; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture *vpic = arg; + if (vpic->depth != 16 || vpic->palette != VIDEO_PALETTE_YUV422) + return -EINVAL; + + cam->brightness = vpic->brightness >> 8; + cam->hue = (vpic->hue >> 8) - 128; + cam->color = vpic->colour >> 9; + cam->contrast = vpic->contrast >> 9; + + w9966_pdev_claim(cam); + + if ( + w9966_wReg_i2c(cam, 0x0a, cam->brightness) == -1 || + w9966_wReg_i2c(cam, 0x0b, cam->contrast) == -1 || + w9966_wReg_i2c(cam, 0x0c, cam->color) == -1 || + w9966_wReg_i2c(cam, 0x0d, cam->hue) == -1 + ) { + w9966_pdev_release(cam); + return -EIO; + } + + w9966_pdev_release(cam); + return 0; + } + case VIDIOCSWIN: + { + int ret; + struct video_window *vwin = arg; + + if (vwin->flags != 0) + return -EINVAL; + if (vwin->clipcount != 0) + return -EINVAL; + if (vwin->width < 2 || vwin->width > W9966_WND_MAX_W) + return -EINVAL; + if (vwin->height < 1 || vwin->height > W9966_WND_MAX_H) + return -EINVAL; + + // Update camera regs + w9966_pdev_claim(cam); + ret = w9966_setup(cam, 0, 0, 1023, 1023, vwin->width, vwin->height); + w9966_pdev_release(cam); + + if (ret != 0) { + DPRINTF("VIDIOCSWIN: w9966_setup() failed.\n"); + return -EIO; + } + + return 0; + } + case VIDIOCGWIN: + { + struct video_window *vwin = arg; + memset(vwin, 0, sizeof(*vwin)); + vwin->width = cam->width; + vwin->height = cam->height; + return 0; + } + // Unimplemented + case VIDIOCCAPTURE: + case VIDIOCGFBUF: + case VIDIOCSFBUF: + case VIDIOCKEY: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int w9966_v4l_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, w9966_v4l_do_ioctl); +} + +// Capture data +static ssize_t w9966_v4l_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct video_device *vdev = video_devdata(file); + struct w9966_dev *cam = vdev->priv; + unsigned char addr = 0xa0; // ECP, read, CCD-transfer, 00000 + unsigned char __user *dest = (unsigned char __user *)buf; + unsigned long dleft = count; + unsigned char *tbuf; + + // Why would anyone want more than this?? + if (count > cam->width * cam->height * 2) + return -EINVAL; + + w9966_pdev_claim(cam); + w9966_wReg(cam, 0x00, 0x02); // Reset ECP-FIFO buffer + w9966_wReg(cam, 0x00, 0x00); // Return to normal operation + w9966_wReg(cam, 0x01, 0x98); // Enable capture + + // write special capture-addr and negotiate into data transfer + if ( + (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_ADDR) != 0 )|| + (parport_write(cam->pport, &addr, 1) != 1 )|| + (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_DATA) != 0 ) + ) { + w9966_pdev_release(cam); + return -EFAULT; + } + + tbuf = kmalloc(W9966_RBUFFER, GFP_KERNEL); + if (tbuf == NULL) { + count = -ENOMEM; + goto out; + } + + while(dleft > 0) + { + unsigned long tsize = (dleft > W9966_RBUFFER) ? W9966_RBUFFER : dleft; + + if (parport_read(cam->pport, tbuf, tsize) < tsize) { + count = -EFAULT; + goto out; + } + if (copy_to_user(dest, tbuf, tsize) != 0) { + count = -EFAULT; + goto out; + } + dest += tsize; + dleft -= tsize; + } + + w9966_wReg(cam, 0x01, 0x18); // Disable capture + +out: + kfree(tbuf); + w9966_pdev_release(cam); + + return count; +} + + +// Called once for every parport on init +static void w9966_attach(struct parport *port) +{ + int i; + + for (i = 0; i < W9966_MAXCAMS; i++) + { + if (w9966_cams[i].dev_state != 0) // Cam is already assigned + continue; + if ( + strcmp(pardev[i], "aggressive") == 0 || + strcmp(pardev[i], port->name) == 0 + ) { + if (w9966_init(&w9966_cams[i], port) != 0) + w9966_term(&w9966_cams[i]); + break; // return + } + } +} + +// Called once for every parport on termination +static void w9966_detach(struct parport *port) +{ + int i; + for (i = 0; i < W9966_MAXCAMS; i++) + if (w9966_cams[i].dev_state != 0 && w9966_cams[i].pport == port) + w9966_term(&w9966_cams[i]); +} + + +static struct parport_driver w9966_ppd = { + .name = W9966_DRIVERNAME, + .attach = w9966_attach, + .detach = w9966_detach, +}; + +// Module entry point +static int __init w9966_mod_init(void) +{ + int i; + for (i = 0; i < W9966_MAXCAMS; i++) + w9966_cams[i].dev_state = 0; + + return parport_register_driver(&w9966_ppd); +} + +// Module cleanup +static void __exit w9966_mod_term(void) +{ + parport_unregister_driver(&w9966_ppd); +} + +module_init(w9966_mod_init); +module_exit(w9966_mod_term); diff --git a/linux/drivers/media/video/w9968cf.c b/linux/drivers/media/video/w9968cf.c new file mode 100644 index 000000000..20f211b55 --- /dev/null +++ b/linux/drivers/media/video/w9968cf.c @@ -0,0 +1,3691 @@ +/*************************************************************************** + * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. * + * * + * Copyright (C) 2002-2004 by Luca Risolia * + * * + * - Memory management code from bttv driver by Ralph Metzler, * + * Marcus Metzler and Gerd Knorr. * + * - I2C interface to kernel, high-level image sensor control routines and * + * some symbolic names from OV511 driver by Mark W. McClelland. * + * - Low-level I2C fast write function by Piotr Czerczak. * + * - Low-level I2C read function by Frederic Jouault. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "w9968cf.h" +#include "w9968cf_decoder.h" + +static struct w9968cf_vpp_t* w9968cf_vpp; +static DECLARE_WAIT_QUEUE_HEAD(w9968cf_vppmod_wait); + +static LIST_HEAD(w9968cf_dev_list); /* head of V4L registered cameras list */ +static DEFINE_MUTEX(w9968cf_devlist_mutex); /* semaphore for list traversal */ + +static DECLARE_RWSEM(w9968cf_disconnect); /* prevent races with open() */ + + +/**************************************************************************** + * Module macros and parameters * + ****************************************************************************/ + +MODULE_DEVICE_TABLE(usb, winbond_id_table); + +MODULE_AUTHOR(W9968CF_MODULE_AUTHOR" "W9968CF_AUTHOR_EMAIL); +MODULE_DESCRIPTION(W9968CF_MODULE_NAME); +MODULE_VERSION(W9968CF_MODULE_VERSION); +MODULE_LICENSE(W9968CF_MODULE_LICENSE); +MODULE_SUPPORTED_DEVICE("Video"); + +static int ovmod_load = W9968CF_OVMOD_LOAD; +static unsigned short simcams = W9968CF_SIMCAMS; +static short video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /*-1=first free*/ +static unsigned int packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_PACKET_SIZE}; +static unsigned short max_buffers[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_BUFFERS}; +static int double_buffer[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_DOUBLE_BUFFER}; +static int clamping[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLAMPING}; +static unsigned short filter_type[]= {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_FILTER_TYPE}; +static int largeview[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LARGEVIEW}; +static unsigned short decompression[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_DECOMPRESSION}; +static int upscaling[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_UPSCALING}; +static unsigned short force_palette[] = {[0 ... W9968CF_MAX_DEVICES-1] = 0}; +static int force_rgb[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FORCE_RGB}; +static int autobright[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOBRIGHT}; +static int autoexp[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOEXP}; +static unsigned short lightfreq[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_LIGHTFREQ}; +static int bandingfilter[] = {[0 ... W9968CF_MAX_DEVICES-1]= + W9968CF_BANDINGFILTER}; +static short clockdiv[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLOCKDIV}; +static int backlight[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BACKLIGHT}; +static int mirror[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_MIRROR}; +static int monochrome[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_MONOCHROME}; +static unsigned int brightness[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_BRIGHTNESS}; +static unsigned int hue[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_HUE}; +static unsigned int colour[]={[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_COLOUR}; +static unsigned int contrast[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_CONTRAST}; +static unsigned int whiteness[] = {[0 ... W9968CF_MAX_DEVICES-1] = + W9968CF_WHITENESS}; +#ifdef W9968CF_DEBUG +static unsigned short debug = W9968CF_DEBUG_LEVEL; +static int specific_debug = W9968CF_SPECIFIC_DEBUG; +#endif + +static unsigned int param_nv[24]; /* number of values per parameter */ + +#ifdef CONFIG_KMOD +module_param(ovmod_load, bool, 0644); +#endif +module_param(simcams, ushort, 0644); +module_param_array(video_nr, short, ¶m_nv[0], 0444); +module_param_array(packet_size, uint, ¶m_nv[1], 0444); +module_param_array(max_buffers, ushort, ¶m_nv[2], 0444); +module_param_array(double_buffer, bool, ¶m_nv[3], 0444); +module_param_array(clamping, bool, ¶m_nv[4], 0444); +module_param_array(filter_type, ushort, ¶m_nv[5], 0444); +module_param_array(largeview, bool, ¶m_nv[6], 0444); +module_param_array(decompression, ushort, ¶m_nv[7], 0444); +module_param_array(upscaling, bool, ¶m_nv[8], 0444); +module_param_array(force_palette, ushort, ¶m_nv[9], 0444); +module_param_array(force_rgb, ushort, ¶m_nv[10], 0444); +module_param_array(autobright, bool, ¶m_nv[11], 0444); +module_param_array(autoexp, bool, ¶m_nv[12], 0444); +module_param_array(lightfreq, ushort, ¶m_nv[13], 0444); +module_param_array(bandingfilter, bool, ¶m_nv[14], 0444); +module_param_array(clockdiv, short, ¶m_nv[15], 0444); +module_param_array(backlight, bool, ¶m_nv[16], 0444); +module_param_array(mirror, bool, ¶m_nv[17], 0444); +module_param_array(monochrome, bool, ¶m_nv[18], 0444); +module_param_array(brightness, uint, ¶m_nv[19], 0444); +module_param_array(hue, uint, ¶m_nv[20], 0444); +module_param_array(colour, uint, ¶m_nv[21], 0444); +module_param_array(contrast, uint, ¶m_nv[22], 0444); +module_param_array(whiteness, uint, ¶m_nv[23], 0444); +#ifdef W9968CF_DEBUG +module_param(debug, ushort, 0644); +module_param(specific_debug, bool, 0644); +#endif + +#ifdef CONFIG_KMOD +MODULE_PARM_DESC(ovmod_load, + "\n<0|1> Automatic 'ovcamchip' module loading." + "\n0 disabled, 1 enabled." + "\nIf enabled,'insmod' searches for the required 'ovcamchip'" + "\nmodule in the system, according to its configuration, and" + "\nattempts to load that module automatically. This action is" + "\nperformed once as soon as the 'w9968cf' module is loaded" + "\ninto memory." + "\nDefault value is "__MODULE_STRING(W9968CF_OVMOD_LOAD)"." + "\n"); +#endif +MODULE_PARM_DESC(simcams, + "\n Number of cameras allowed to stream simultaneously." + "\nn may vary from 0 to " + __MODULE_STRING(W9968CF_MAX_DEVICES)"." + "\nDefault value is "__MODULE_STRING(W9968CF_SIMCAMS)"." + "\n"); +MODULE_PARM_DESC(video_nr, + "\n<-1|n[,...]> Specify V4L minor mode number." + "\n -1 = use next available (default)" + "\n n = use minor number n (integer >= 0)" + "\nYou can specify up to "__MODULE_STRING(W9968CF_MAX_DEVICES) + " cameras this way." + "\nFor example:" + "\nvideo_nr=-1,2,-1 would assign minor number 2 to" + "\nthe second camera and use auto for the first" + "\none and for every other camera." + "\n"); +MODULE_PARM_DESC(packet_size, + "\n Specify the maximum data payload" + "\nsize in bytes for alternate settings, for each device." + "\nn is scaled between 63 and 1023 " + "(default is "__MODULE_STRING(W9968CF_PACKET_SIZE)")." + "\n"); +MODULE_PARM_DESC(max_buffers, + "\n For advanced users." + "\nSpecify the maximum number of video frame buffers" + "\nto allocate for each device, from 2 to " + __MODULE_STRING(W9968CF_MAX_BUFFERS) + ". (default is "__MODULE_STRING(W9968CF_BUFFERS)")." + "\n"); +MODULE_PARM_DESC(double_buffer, + "\n<0|1[,...]> " + "Hardware double buffering: 0 disabled, 1 enabled." + "\nIt should be enabled if you want smooth video output: if" + "\nyou obtain out of sync. video, disable it, or try to" + "\ndecrease the 'clockdiv' module parameter value." + "\nDefault value is "__MODULE_STRING(W9968CF_DOUBLE_BUFFER) + " for every device." + "\n"); +MODULE_PARM_DESC(clamping, + "\n<0|1[,...]> Video data clamping: 0 disabled, 1 enabled." + "\nDefault value is "__MODULE_STRING(W9968CF_CLAMPING) + " for every device." + "\n"); +MODULE_PARM_DESC(filter_type, + "\n<0|1|2[,...]> Video filter type." + "\n0 none, 1 (1-2-1) 3-tap filter, " + "2 (2-3-6-3-2) 5-tap filter." + "\nDefault value is "__MODULE_STRING(W9968CF_FILTER_TYPE) + " for every device." + "\nThe filter is used to reduce noise and aliasing artifacts" + "\nproduced by the CCD or CMOS image sensor, and the scaling" + " process." + "\n"); +MODULE_PARM_DESC(largeview, + "\n<0|1[,...]> Large view: 0 disabled, 1 enabled." + "\nDefault value is "__MODULE_STRING(W9968CF_LARGEVIEW) + " for every device." + "\n"); +MODULE_PARM_DESC(upscaling, + "\n<0|1[,...]> Software scaling (for non-compressed video):" + "\n0 disabled, 1 enabled." + "\nDisable it if you have a slow CPU or you don't have" + " enough memory." + "\nDefault value is "__MODULE_STRING(W9968CF_UPSCALING) + " for every device." + "\nIf 'w9968cf-vpp' is not present, this parameter is" + " set to 0." + "\n"); +MODULE_PARM_DESC(decompression, + "\n<0|1|2[,...]> Software video decompression:" + "\n- 0 disables decompression (doesn't allow formats needing" + " decompression)" + "\n- 1 forces decompression (allows formats needing" + " decompression only);" + "\n- 2 allows any permitted formats." + "\nFormats supporting compressed video are YUV422P and" + " YUV420P/YUV420 " + "\nin any resolutions where both width and height are " + "a multiple of 16." + "\nDefault value is "__MODULE_STRING(W9968CF_DECOMPRESSION) + " for every device." + "\nIf 'w9968cf-vpp' is not present, forcing decompression is " + "\nnot allowed; in this case this parameter is set to 2." + "\n"); +MODULE_PARM_DESC(force_palette, + "\n<0" + "|" __MODULE_STRING(VIDEO_PALETTE_UYVY) + "|" __MODULE_STRING(VIDEO_PALETTE_YUV420) + "|" __MODULE_STRING(VIDEO_PALETTE_YUV422P) + "|" __MODULE_STRING(VIDEO_PALETTE_YUV420P) + "|" __MODULE_STRING(VIDEO_PALETTE_YUYV) + "|" __MODULE_STRING(VIDEO_PALETTE_YUV422) + "|" __MODULE_STRING(VIDEO_PALETTE_GREY) + "|" __MODULE_STRING(VIDEO_PALETTE_RGB555) + "|" __MODULE_STRING(VIDEO_PALETTE_RGB565) + "|" __MODULE_STRING(VIDEO_PALETTE_RGB24) + "|" __MODULE_STRING(VIDEO_PALETTE_RGB32) + "[,...]>" + " Force picture palette." + "\nIn order:" + "\n- 0 allows any of the following formats:" + "\n- UYVY 16 bpp - Original video, compression disabled" + "\n- YUV420 12 bpp - Original video, compression enabled" + "\n- YUV422P 16 bpp - Original video, compression enabled" + "\n- YUV420P 12 bpp - Original video, compression enabled" + "\n- YUVY 16 bpp - Software conversion from UYVY" + "\n- YUV422 16 bpp - Software conversion from UYVY" + "\n- GREY 8 bpp - Software conversion from UYVY" + "\n- RGB555 16 bpp - Software conversion from UYVY" + "\n- RGB565 16 bpp - Software conversion from UYVY" + "\n- RGB24 24 bpp - Software conversion from UYVY" + "\n- RGB32 32 bpp - Software conversion from UYVY" + "\nWhen not 0, this parameter will override 'decompression'." + "\nDefault value is 0 for every device." + "\nInitial palette is " + __MODULE_STRING(W9968CF_PALETTE_DECOMP_ON)"." + "\nIf 'w9968cf-vpp' is not present, this parameter is" + " set to 9 (UYVY)." + "\n"); +MODULE_PARM_DESC(force_rgb, + "\n<0|1[,...]> Read RGB video data instead of BGR:" + "\n 1 = use RGB component ordering." + "\n 0 = use BGR component ordering." + "\nThis parameter has effect when using RGBX palettes only." + "\nDefault value is "__MODULE_STRING(W9968CF_FORCE_RGB) + " for every device." + "\n"); +MODULE_PARM_DESC(autobright, + "\n<0|1[,...]> Image sensor automatically changes brightness:" + "\n 0 = no, 1 = yes" + "\nDefault value is "__MODULE_STRING(W9968CF_AUTOBRIGHT) + " for every device." + "\n"); +MODULE_PARM_DESC(autoexp, + "\n<0|1[,...]> Image sensor automatically changes exposure:" + "\n 0 = no, 1 = yes" + "\nDefault value is "__MODULE_STRING(W9968CF_AUTOEXP) + " for every device." + "\n"); +MODULE_PARM_DESC(lightfreq, + "\n<50|60[,...]> Light frequency in Hz:" + "\n 50 for European and Asian lighting," + " 60 for American lighting." + "\nDefault value is "__MODULE_STRING(W9968CF_LIGHTFREQ) + " for every device." + "\n"); +MODULE_PARM_DESC(bandingfilter, + "\n<0|1[,...]> Banding filter to reduce effects of" + " fluorescent lighting:" + "\n 0 disabled, 1 enabled." + "\nThis filter tries to reduce the pattern of horizontal" + "\nlight/dark bands caused by some (usually fluorescent)" + " lighting." + "\nDefault value is "__MODULE_STRING(W9968CF_BANDINGFILTER) + " for every device." + "\n"); +MODULE_PARM_DESC(clockdiv, + "\n<-1|n[,...]> " + "Force pixel clock divisor to a specific value (for experts):" + "\n n may vary from 0 to 127." + "\n -1 for automatic value." + "\nSee also the 'double_buffer' module parameter." + "\nDefault value is "__MODULE_STRING(W9968CF_CLOCKDIV) + " for every device." + "\n"); +MODULE_PARM_DESC(backlight, + "\n<0|1[,...]> Objects are lit from behind:" + "\n 0 = no, 1 = yes" + "\nDefault value is "__MODULE_STRING(W9968CF_BACKLIGHT) + " for every device." + "\n"); +MODULE_PARM_DESC(mirror, + "\n<0|1[,...]> Reverse image horizontally:" + "\n 0 = no, 1 = yes" + "\nDefault value is "__MODULE_STRING(W9968CF_MIRROR) + " for every device." + "\n"); +MODULE_PARM_DESC(monochrome, + "\n<0|1[,...]> Use image sensor as monochrome sensor:" + "\n 0 = no, 1 = yes" + "\nNot all the sensors support monochrome color." + "\nDefault value is "__MODULE_STRING(W9968CF_MONOCHROME) + " for every device." + "\n"); +MODULE_PARM_DESC(brightness, + "\n Set picture brightness (0-65535)." + "\nDefault value is "__MODULE_STRING(W9968CF_BRIGHTNESS) + " for every device." + "\nThis parameter has no effect if 'autobright' is enabled." + "\n"); +MODULE_PARM_DESC(hue, + "\n Set picture hue (0-65535)." + "\nDefault value is "__MODULE_STRING(W9968CF_HUE) + " for every device." + "\n"); +MODULE_PARM_DESC(colour, + "\n Set picture saturation (0-65535)." + "\nDefault value is "__MODULE_STRING(W9968CF_COLOUR) + " for every device." + "\n"); +MODULE_PARM_DESC(contrast, + "\n Set picture contrast (0-65535)." + "\nDefault value is "__MODULE_STRING(W9968CF_CONTRAST) + " for every device." + "\n"); +MODULE_PARM_DESC(whiteness, + "\n Set picture whiteness (0-65535)." + "\nDefault value is "__MODULE_STRING(W9968CF_WHITENESS) + " for every device." + "\n"); +#ifdef W9968CF_DEBUG +MODULE_PARM_DESC(debug, + "\n Debugging information level, from 0 to 6:" + "\n0 = none (use carefully)" + "\n1 = critical errors" + "\n2 = significant informations" + "\n3 = configuration or general messages" + "\n4 = warnings" + "\n5 = called functions" + "\n6 = function internals" + "\nLevel 5 and 6 are useful for testing only, when only " + "one device is used." + "\nDefault value is "__MODULE_STRING(W9968CF_DEBUG_LEVEL)"." + "\n"); +MODULE_PARM_DESC(specific_debug, + "\n<0|1> Enable or disable specific debugging messages:" + "\n0 = print messages concerning every level" + " <= 'debug' level." + "\n1 = print messages concerning the level" + " indicated by 'debug'." + "\nDefault value is " + __MODULE_STRING(W9968CF_SPECIFIC_DEBUG)"." + "\n"); +#endif /* W9968CF_DEBUG */ + + + +/**************************************************************************** + * Some prototypes * + ****************************************************************************/ + +/* Video4linux interface */ +static struct file_operations w9968cf_fops; +static int w9968cf_open(struct inode*, struct file*); +static int w9968cf_release(struct inode*, struct file*); +static int w9968cf_mmap(struct file*, struct vm_area_struct*); +static int w9968cf_ioctl(struct inode*, struct file*, unsigned, unsigned long); +static ssize_t w9968cf_read(struct file*, char __user *, size_t, loff_t*); +static int w9968cf_v4l_ioctl(struct inode*, struct file*, unsigned int, + void __user *); + +/* USB-specific */ +static int w9968cf_start_transfer(struct w9968cf_device*); +static int w9968cf_stop_transfer(struct w9968cf_device*); +static int w9968cf_write_reg(struct w9968cf_device*, u16 value, u16 index); +static int w9968cf_read_reg(struct w9968cf_device*, u16 index); +static int w9968cf_write_fsb(struct w9968cf_device*, u16* data); +static int w9968cf_write_sb(struct w9968cf_device*, u16 value); +static int w9968cf_read_sb(struct w9968cf_device*); +static int w9968cf_upload_quantizationtables(struct w9968cf_device*); +static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs); + +/* Low-level I2C (SMBus) I/O */ +static int w9968cf_smbus_start(struct w9968cf_device*); +static int w9968cf_smbus_stop(struct w9968cf_device*); +static int w9968cf_smbus_write_byte(struct w9968cf_device*, u8 v); +static int w9968cf_smbus_read_byte(struct w9968cf_device*, u8* v); +static int w9968cf_smbus_write_ack(struct w9968cf_device*); +static int w9968cf_smbus_read_ack(struct w9968cf_device*); +static int w9968cf_smbus_refresh_bus(struct w9968cf_device*); +static int w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam, + u16 address, u8* value); +static int w9968cf_i2c_adap_read_byte_data(struct w9968cf_device*, u16 address, + u8 subaddress, u8* value); +static int w9968cf_i2c_adap_write_byte(struct w9968cf_device*, + u16 address, u8 subaddress); +static int w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device*, + u16 address, u8 subaddress, + u8 value); + +/* I2C interface to kernel */ +static int w9968cf_i2c_init(struct w9968cf_device*); +static int w9968cf_i2c_smbus_xfer(struct i2c_adapter*, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data*); +static u32 w9968cf_i2c_func(struct i2c_adapter*); +static int w9968cf_i2c_attach_inform(struct i2c_client*); +static int w9968cf_i2c_detach_inform(struct i2c_client*); +static int w9968cf_i2c_control(struct i2c_adapter*, unsigned int cmd, + unsigned long arg); + +/* Memory management */ +static void* rvmalloc(unsigned long size); +static void rvfree(void *mem, unsigned long size); +static void w9968cf_deallocate_memory(struct w9968cf_device*); +static int w9968cf_allocate_memory(struct w9968cf_device*); + +/* High-level image sensor control functions */ +static int w9968cf_sensor_set_control(struct w9968cf_device*,int cid,int val); +static int w9968cf_sensor_get_control(struct w9968cf_device*,int cid,int *val); +static int w9968cf_sensor_cmd(struct w9968cf_device*, + unsigned int cmd, void *arg); +static int w9968cf_sensor_init(struct w9968cf_device*); +static int w9968cf_sensor_update_settings(struct w9968cf_device*); +static int w9968cf_sensor_get_picture(struct w9968cf_device*); +static int w9968cf_sensor_update_picture(struct w9968cf_device*, + struct video_picture pict); + +/* Other helper functions */ +static void w9968cf_configure_camera(struct w9968cf_device*,struct usb_device*, + enum w9968cf_model_id, + const unsigned short dev_nr); +static void w9968cf_adjust_configuration(struct w9968cf_device*); +static int w9968cf_turn_on_led(struct w9968cf_device*); +static int w9968cf_init_chip(struct w9968cf_device*); +static inline u16 w9968cf_valid_palette(u16 palette); +static inline u16 w9968cf_valid_depth(u16 palette); +static inline u8 w9968cf_need_decompression(u16 palette); +static int w9968cf_set_picture(struct w9968cf_device*, struct video_picture); +static int w9968cf_set_window(struct w9968cf_device*, struct video_window); +static int w9968cf_postprocess_frame(struct w9968cf_device*, + struct w9968cf_frame_t*); +static int w9968cf_adjust_window_size(struct w9968cf_device*, u16* w, u16* h); +static void w9968cf_init_framelist(struct w9968cf_device*); +static void w9968cf_push_frame(struct w9968cf_device*, u8 f_num); +static void w9968cf_pop_frame(struct w9968cf_device*,struct w9968cf_frame_t**); +static void w9968cf_release_resources(struct w9968cf_device*); + + + +/**************************************************************************** + * Symbolic names * + ****************************************************************************/ + +/* Used to represent a list of values and their respective symbolic names */ +struct w9968cf_symbolic_list { + const int num; + const char *name; +}; + +/*-------------------------------------------------------------------------- + Returns the name of the matching element in the symbolic_list array. The + end of the list must be marked with an element that has a NULL name. + --------------------------------------------------------------------------*/ +static inline const char * +symbolic(struct w9968cf_symbolic_list list[], const int num) +{ + int i; + + for (i = 0; list[i].name != NULL; i++) + if (list[i].num == num) + return (list[i].name); + + return "Unknown"; +} + +static struct w9968cf_symbolic_list camlist[] = { + { W9968CF_MOD_GENERIC, "W996[87]CF JPEG USB Dual Mode Camera" }, + { W9968CF_MOD_CLVBWGP, "Creative Labs Video Blaster WebCam Go Plus" }, + + /* Other cameras (having the same descriptors as Generic W996[87]CF) */ + { W9968CF_MOD_ADPVDMA, "Aroma Digi Pen VGA Dual Mode ADG-5000" }, + { W9986CF_MOD_AAU, "AVerMedia AVerTV USB" }, + { W9968CF_MOD_CLVBWG, "Creative Labs Video Blaster WebCam Go" }, + { W9968CF_MOD_LL, "Lebon LDC-035A" }, + { W9968CF_MOD_EEEMC, "Ezonics EZ-802 EZMega Cam" }, + { W9968CF_MOD_OOE, "OmniVision OV8610-EDE" }, + { W9968CF_MOD_ODPVDMPC, "OPCOM Digi Pen VGA Dual Mode Pen Camera" }, + { W9968CF_MOD_PDPII, "Pretec Digi Pen-II" }, + { W9968CF_MOD_PDP480, "Pretec DigiPen-480" }, + + { -1, NULL } +}; + +static struct w9968cf_symbolic_list senlist[] = { + { CC_OV76BE, "OV76BE" }, + { CC_OV7610, "OV7610" }, + { CC_OV7620, "OV7620" }, + { CC_OV7620AE, "OV7620AE" }, + { CC_OV6620, "OV6620" }, + { CC_OV6630, "OV6630" }, + { CC_OV6630AE, "OV6630AE" }, + { CC_OV6630AF, "OV6630AF" }, + { -1, NULL } +}; + +/* Video4Linux1 palettes */ +static struct w9968cf_symbolic_list v4l1_plist[] = { + { VIDEO_PALETTE_GREY, "GREY" }, + { VIDEO_PALETTE_HI240, "HI240" }, + { VIDEO_PALETTE_RGB565, "RGB565" }, + { VIDEO_PALETTE_RGB24, "RGB24" }, + { VIDEO_PALETTE_RGB32, "RGB32" }, + { VIDEO_PALETTE_RGB555, "RGB555" }, + { VIDEO_PALETTE_YUV422, "YUV422" }, + { VIDEO_PALETTE_YUYV, "YUYV" }, + { VIDEO_PALETTE_UYVY, "UYVY" }, + { VIDEO_PALETTE_YUV420, "YUV420" }, + { VIDEO_PALETTE_YUV411, "YUV411" }, + { VIDEO_PALETTE_RAW, "RAW" }, + { VIDEO_PALETTE_YUV422P, "YUV422P" }, + { VIDEO_PALETTE_YUV411P, "YUV411P" }, + { VIDEO_PALETTE_YUV420P, "YUV420P" }, + { VIDEO_PALETTE_YUV410P, "YUV410P" }, + { -1, NULL } +}; + +/* Decoder error codes: */ +static struct w9968cf_symbolic_list decoder_errlist[] = { + { W9968CF_DEC_ERR_CORRUPTED_DATA, "Corrupted data" }, + { W9968CF_DEC_ERR_BUF_OVERFLOW, "Buffer overflow" }, + { W9968CF_DEC_ERR_NO_SOI, "SOI marker not found" }, + { W9968CF_DEC_ERR_NO_SOF0, "SOF0 marker not found" }, + { W9968CF_DEC_ERR_NO_SOS, "SOS marker not found" }, + { W9968CF_DEC_ERR_NO_EOI, "EOI marker not found" }, + { -1, NULL } +}; + +/* URB error codes: */ +static struct w9968cf_symbolic_list urb_errlist[] = { + { -ENOMEM, "No memory for allocation of internal structures" }, + { -ENOSPC, "The host controller's bandwidth is already consumed" }, + { -ENOENT, "URB was canceled by unlink_urb" }, + { -EXDEV, "ISO transfer only partially completed" }, + { -EAGAIN, "Too match scheduled for the future" }, + { -ENXIO, "URB already queued" }, + { -EFBIG, "Too much ISO frames requested" }, + { -ENOSR, "Buffer error (overrun)" }, + { -EPIPE, "Specified endpoint is stalled (device not responding)"}, + { -EOVERFLOW, "Babble (bad cable?)" }, + { -EPROTO, "Bit-stuff error (bad cable?)" }, + { -EILSEQ, "CRC/Timeout" }, + { -ETIMEDOUT, "NAK (device does not respond)" }, + { -1, NULL } +}; + + + +/**************************************************************************** + * Memory management functions * + ****************************************************************************/ +static void* rvmalloc(unsigned long size) +{ + void* mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + + +static void rvfree(void* mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + + +/*-------------------------------------------------------------------------- + Deallocate previously allocated memory. + --------------------------------------------------------------------------*/ +static void w9968cf_deallocate_memory(struct w9968cf_device* cam) +{ + u8 i; + + /* Free the isochronous transfer buffers */ + for (i = 0; i < W9968CF_URBS; i++) { + kfree(cam->transfer_buffer[i]); + cam->transfer_buffer[i] = NULL; + } + + /* Free temporary frame buffer */ + if (cam->frame_tmp.buffer) { + rvfree(cam->frame_tmp.buffer, cam->frame_tmp.size); + cam->frame_tmp.buffer = NULL; + } + + /* Free helper buffer */ + if (cam->frame_vpp.buffer) { + rvfree(cam->frame_vpp.buffer, cam->frame_vpp.size); + cam->frame_vpp.buffer = NULL; + } + + /* Free video frame buffers */ + if (cam->frame[0].buffer) { + rvfree(cam->frame[0].buffer, cam->nbuffers*cam->frame[0].size); + cam->frame[0].buffer = NULL; + } + + cam->nbuffers = 0; + + DBG(5, "Memory successfully deallocated") +} + + +/*-------------------------------------------------------------------------- + Allocate memory buffers for USB transfers and video frames. + This function is called by open() only. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_allocate_memory(struct w9968cf_device* cam) +{ + const u16 p_size = wMaxPacketSize[cam->altsetting-1]; + void* buff = NULL; + unsigned long hw_bufsize, vpp_bufsize; + u8 i, bpp; + + /* NOTE: Deallocation is done elsewhere in case of error */ + + /* Calculate the max amount of raw data per frame from the device */ + hw_bufsize = cam->maxwidth*cam->maxheight*2; + + /* Calculate the max buf. size needed for post-processing routines */ + bpp = (w9968cf_vpp) ? 4 : 2; + if (cam->upscaling) + vpp_bufsize = max(W9968CF_MAX_WIDTH*W9968CF_MAX_HEIGHT*bpp, + cam->maxwidth*cam->maxheight*bpp); + else + vpp_bufsize = cam->maxwidth*cam->maxheight*bpp; + + /* Allocate memory for the isochronous transfer buffers */ + for (i = 0; i < W9968CF_URBS; i++) { + if (!(cam->transfer_buffer[i] = + kzalloc(W9968CF_ISO_PACKETS*p_size, GFP_KERNEL))) { + DBG(1, "Couldn't allocate memory for the isochronous " + "transfer buffers (%u bytes)", + p_size * W9968CF_ISO_PACKETS) + return -ENOMEM; + } + } + + /* Allocate memory for the temporary frame buffer */ + if (!(cam->frame_tmp.buffer = rvmalloc(hw_bufsize))) { + DBG(1, "Couldn't allocate memory for the temporary " + "video frame buffer (%lu bytes)", hw_bufsize) + return -ENOMEM; + } + cam->frame_tmp.size = hw_bufsize; + cam->frame_tmp.number = -1; + + /* Allocate memory for the helper buffer */ + if (w9968cf_vpp) { + if (!(cam->frame_vpp.buffer = rvmalloc(vpp_bufsize))) { + DBG(1, "Couldn't allocate memory for the helper buffer" + " (%lu bytes)", vpp_bufsize) + return -ENOMEM; + } + cam->frame_vpp.size = vpp_bufsize; + } else + cam->frame_vpp.buffer = NULL; + + /* Allocate memory for video frame buffers */ + cam->nbuffers = cam->max_buffers; + while (cam->nbuffers >= 2) { + if ((buff = rvmalloc(cam->nbuffers * vpp_bufsize))) + break; + else + cam->nbuffers--; + } + + if (!buff) { + DBG(1, "Couldn't allocate memory for the video frame buffers") + cam->nbuffers = 0; + return -ENOMEM; + } + + if (cam->nbuffers != cam->max_buffers) + DBG(2, "Couldn't allocate memory for %u video frame buffers. " + "Only memory for %u buffers has been allocated", + cam->max_buffers, cam->nbuffers) + + for (i = 0; i < cam->nbuffers; i++) { + cam->frame[i].buffer = buff + i*vpp_bufsize; + cam->frame[i].size = vpp_bufsize; + cam->frame[i].number = i; + /* Circular list */ + if (i != cam->nbuffers-1) + cam->frame[i].next = &cam->frame[i+1]; + else + cam->frame[i].next = &cam->frame[0]; + cam->frame[i].status = F_UNUSED; + } + + DBG(5, "Memory successfully allocated") + return 0; +} + + + +/**************************************************************************** + * USB-specific functions * + ****************************************************************************/ + +/*-------------------------------------------------------------------------- + This is an handler function which is called after the URBs are completed. + It collects multiple data packets coming from the camera by putting them + into frame buffers: one or more zero data length data packets are used to + mark the end of a video frame; the first non-zero data packet is the start + of the next video frame; if an error is encountered in a packet, the entire + video frame is discarded and grabbed again. + If there are no requested frames in the FIFO list, packets are collected into + a temporary buffer. + --------------------------------------------------------------------------*/ +static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs) +{ + struct w9968cf_device* cam = (struct w9968cf_device*)urb->context; + struct w9968cf_frame_t** f; + unsigned int len, status; + void* pos; + u8 i; + int err = 0; + + if ((!cam->streaming) || cam->disconnected) { + DBG(4, "Got interrupt, but not streaming") + return; + } + + /* "(*f)" will be used instead of "cam->frame_current" */ + f = &cam->frame_current; + + /* If a frame has been requested and we are grabbing into + the temporary frame, we'll switch to that requested frame */ + if ((*f) == &cam->frame_tmp && *cam->requested_frame) { + if (cam->frame_tmp.status == F_GRABBING) { + w9968cf_pop_frame(cam, &cam->frame_current); + (*f)->status = F_GRABBING; + (*f)->length = cam->frame_tmp.length; + memcpy((*f)->buffer, cam->frame_tmp.buffer, + (*f)->length); + DBG(6, "Switched from temp. frame to frame #%d", + (*f)->number) + } + } + + for (i = 0; i < urb->number_of_packets; i++) { + len = urb->iso_frame_desc[i].actual_length; + status = urb->iso_frame_desc[i].status; + pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; + + if (status && len != 0) { + DBG(4, "URB failed, error in data packet " + "(error #%u, %s)", + status, symbolic(urb_errlist, status)) + (*f)->status = F_ERROR; + continue; + } + + if (len) { /* start of frame */ + + if ((*f)->status == F_UNUSED) { + (*f)->status = F_GRABBING; + (*f)->length = 0; + } + + /* Buffer overflows shouldn't happen, however...*/ + if ((*f)->length + len > (*f)->size) { + DBG(4, "Buffer overflow: bad data packets") + (*f)->status = F_ERROR; + } + + if ((*f)->status == F_GRABBING) { + memcpy((*f)->buffer + (*f)->length, pos, len); + (*f)->length += len; + } + + } else if ((*f)->status == F_GRABBING) { /* end of frame */ + + DBG(6, "Frame #%d successfully grabbed", (*f)->number) + + if (cam->vpp_flag & VPP_DECOMPRESSION) { + err = w9968cf_vpp->check_headers((*f)->buffer, + (*f)->length); + if (err) { + DBG(4, "Skip corrupted frame: %s", + symbolic(decoder_errlist, err)) + (*f)->status = F_UNUSED; + continue; /* grab this frame again */ + } + } + + (*f)->status = F_READY; + (*f)->queued = 0; + + /* Take a pointer to the new frame from the FIFO list. + If the list is empty,we'll use the temporary frame*/ + if (*cam->requested_frame) + w9968cf_pop_frame(cam, &cam->frame_current); + else { + cam->frame_current = &cam->frame_tmp; + (*f)->status = F_UNUSED; + } + + } else if ((*f)->status == F_ERROR) + (*f)->status = F_UNUSED; /* grab it again */ + + PDBGG("Frame length %lu | pack.#%u | pack.len. %u | state %d", + (unsigned long)(*f)->length, i, len, (*f)->status) + + } /* end for */ + + /* Resubmit this URB */ + urb->dev = cam->usbdev; + urb->status = 0; + spin_lock(&cam->urb_lock); + if (cam->streaming) + if ((err = usb_submit_urb(urb, GFP_ATOMIC))) { + cam->misconfigured = 1; + DBG(1, "Couldn't resubmit the URB: error %d, %s", + err, symbolic(urb_errlist, err)) + } + spin_unlock(&cam->urb_lock); + + /* Wake up the user process */ + wake_up_interruptible(&cam->wait_queue); +} + + +/*--------------------------------------------------------------------------- + Setup the URB structures for the isochronous transfer. + Submit the URBs so that the data transfer begins. + Return 0 on success, a negative number otherwise. + ---------------------------------------------------------------------------*/ +static int w9968cf_start_transfer(struct w9968cf_device* cam) +{ + struct usb_device *udev = cam->usbdev; + struct urb* urb; + const u16 p_size = wMaxPacketSize[cam->altsetting-1]; + u16 w, h, d; + int vidcapt; + u32 t_size; + int err = 0; + s8 i, j; + + for (i = 0; i < W9968CF_URBS; i++) { + urb = usb_alloc_urb(W9968CF_ISO_PACKETS, GFP_KERNEL); + cam->urb[i] = urb; + if (!urb) { + for (j = 0; j < i; j++) + usb_free_urb(cam->urb[j]); + DBG(1, "Couldn't allocate the URB structures") + return -ENOMEM; + } + + urb->dev = udev; + urb->context = (void*)cam; + urb->pipe = usb_rcvisocpipe(udev, 1); + urb->transfer_flags = URB_ISO_ASAP; + urb->number_of_packets = W9968CF_ISO_PACKETS; + urb->complete = w9968cf_urb_complete; + urb->transfer_buffer = cam->transfer_buffer[i]; + urb->transfer_buffer_length = p_size*W9968CF_ISO_PACKETS; + urb->interval = 1; + for (j = 0; j < W9968CF_ISO_PACKETS; j++) { + urb->iso_frame_desc[j].offset = p_size*j; + urb->iso_frame_desc[j].length = p_size; + } + } + + /* Transfer size per frame, in WORD ! */ + d = cam->hw_depth; + w = cam->hw_width; + h = cam->hw_height; + + t_size = (w*h*d)/16; + + err = w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */ + err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */ + + /* Transfer size */ + err += w9968cf_write_reg(cam, t_size & 0xffff, 0x3d); /* low bits */ + err += w9968cf_write_reg(cam, t_size >> 16, 0x3e); /* high bits */ + + if (cam->vpp_flag & VPP_DECOMPRESSION) + err += w9968cf_upload_quantizationtables(cam); + + vidcapt = w9968cf_read_reg(cam, 0x16); /* read picture settings */ + err += w9968cf_write_reg(cam, vidcapt|0x8000, 0x16); /* capt. enable */ + + err += usb_set_interface(udev, 0, cam->altsetting); + err += w9968cf_write_reg(cam, 0x8a05, 0x3c); /* USB FIFO enable */ + + if (err || (vidcapt < 0)) { + for (i = 0; i < W9968CF_URBS; i++) + usb_free_urb(cam->urb[i]); + DBG(1, "Couldn't tell the camera to start the data transfer") + return err; + } + + w9968cf_init_framelist(cam); + + /* Begin to grab into the temporary buffer */ + cam->frame_tmp.status = F_UNUSED; + cam->frame_tmp.queued = 0; + cam->frame_current = &cam->frame_tmp; + + if (!(cam->vpp_flag & VPP_DECOMPRESSION)) + DBG(5, "Isochronous transfer size: %lu bytes/frame", + (unsigned long)t_size*2) + + DBG(5, "Starting the isochronous transfer...") + + cam->streaming = 1; + + /* Submit the URBs */ + for (i = 0; i < W9968CF_URBS; i++) { + err = usb_submit_urb(cam->urb[i], GFP_KERNEL); + if (err) { + cam->streaming = 0; + for (j = i-1; j >= 0; j--) { + usb_kill_urb(cam->urb[j]); + usb_free_urb(cam->urb[j]); + } + DBG(1, "Couldn't send a transfer request to the " + "USB core (error #%d, %s)", err, + symbolic(urb_errlist, err)) + return err; + } + } + + return 0; +} + + +/*-------------------------------------------------------------------------- + Stop the isochronous transfer and set alternate setting to 0 (0Mb/s). + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_stop_transfer(struct w9968cf_device* cam) +{ + struct usb_device *udev = cam->usbdev; + unsigned long lock_flags; + int err = 0; + s8 i; + + if (!cam->streaming) + return 0; + + /* This avoids race conditions with usb_submit_urb() + in the URB completition handler */ + spin_lock_irqsave(&cam->urb_lock, lock_flags); + cam->streaming = 0; + spin_unlock_irqrestore(&cam->urb_lock, lock_flags); + + for (i = W9968CF_URBS-1; i >= 0; i--) + if (cam->urb[i]) { + usb_kill_urb(cam->urb[i]); + usb_free_urb(cam->urb[i]); + cam->urb[i] = NULL; + } + + if (cam->disconnected) + goto exit; + + err = w9968cf_write_reg(cam, 0x0a05, 0x3c); /* stop USB transfer */ + err += usb_set_interface(udev, 0, 0); /* 0 Mb/s */ + err += w9968cf_write_reg(cam, 0x0000, 0x39); /* disable JPEG encoder */ + err += w9968cf_write_reg(cam, 0x0000, 0x16); /* stop video capture */ + + if (err) { + DBG(2, "Failed to tell the camera to stop the isochronous " + "transfer. However this is not a critical error.") + return -EIO; + } + +exit: + DBG(5, "Isochronous transfer stopped") + return 0; +} + + +/*-------------------------------------------------------------------------- + Write a W9968CF register. + Return 0 on success, -1 otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_write_reg(struct w9968cf_device* cam, u16 value, u16 index) +{ + struct usb_device* udev = cam->usbdev; + int res; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + value, index, NULL, 0, W9968CF_USB_CTRL_TIMEOUT); + + if (res < 0) + DBG(4, "Failed to write a register " + "(value 0x%04X, index 0x%02X, error #%d, %s)", + value, index, res, symbolic(urb_errlist, res)) + + return (res >= 0) ? 0 : -1; +} + + +/*-------------------------------------------------------------------------- + Read a W9968CF register. + Return the register value on success, -1 otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_read_reg(struct w9968cf_device* cam, u16 index) +{ + struct usb_device* udev = cam->usbdev; + u16* buff = cam->control_buffer; + int res; + + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 1, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, buff, 2, W9968CF_USB_CTRL_TIMEOUT); + + if (res < 0) + DBG(4, "Failed to read a register " + "(index 0x%02X, error #%d, %s)", + index, res, symbolic(urb_errlist, res)) + + return (res >= 0) ? (int)(*buff) : -1; +} + + +/*-------------------------------------------------------------------------- + Write 64-bit data to the fast serial bus registers. + Return 0 on success, -1 otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_write_fsb(struct w9968cf_device* cam, u16* data) +{ + struct usb_device* udev = cam->usbdev; + u16 value; + int res; + + value = *data++; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + value, 0x06, data, 6, W9968CF_USB_CTRL_TIMEOUT); + + if (res < 0) + DBG(4, "Failed to write the FSB registers " + "(error #%d, %s)", res, symbolic(urb_errlist, res)) + + return (res >= 0) ? 0 : -1; +} + + +/*-------------------------------------------------------------------------- + Write data to the serial bus control register. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_write_sb(struct w9968cf_device* cam, u16 value) +{ + int err = 0; + + err = w9968cf_write_reg(cam, value, 0x01); + udelay(W9968CF_I2C_BUS_DELAY); + + return err; +} + + +/*-------------------------------------------------------------------------- + Read data from the serial bus control register. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_read_sb(struct w9968cf_device* cam) +{ + int v = 0; + + v = w9968cf_read_reg(cam, 0x01); + udelay(W9968CF_I2C_BUS_DELAY); + + return v; +} + + +/*-------------------------------------------------------------------------- + Upload quantization tables for the JPEG compression. + This function is called by w9968cf_start_transfer(). + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_upload_quantizationtables(struct w9968cf_device* cam) +{ + u16 a, b; + int err = 0, i, j; + + err += w9968cf_write_reg(cam, 0x0010, 0x39); /* JPEG clock enable */ + + for (i = 0, j = 0; i < 32; i++, j += 2) { + a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j+1]) << 8); + b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j+1]) << 8); + err += w9968cf_write_reg(cam, a, 0x40+i); + err += w9968cf_write_reg(cam, b, 0x60+i); + } + err += w9968cf_write_reg(cam, 0x0012, 0x39); /* JPEG encoder enable */ + + return err; +} + + + +/**************************************************************************** + * Low-level I2C I/O functions. * + * The adapter supports the following I2C transfer functions: * + * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only) * + * i2c_adap_read_byte_data() * + * i2c_adap_read_byte() * + ****************************************************************************/ + +static int w9968cf_smbus_start(struct w9968cf_device* cam) +{ + int err = 0; + + err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */ + err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */ + + return err; +} + + +static int w9968cf_smbus_stop(struct w9968cf_device* cam) +{ + int err = 0; + + err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */ + err += w9968cf_write_sb(cam, 0x0013); /* SDE=1, SDA=1, SCL=1 */ + + return err; +} + + +static int w9968cf_smbus_write_byte(struct w9968cf_device* cam, u8 v) +{ + u8 bit; + int err = 0, sda; + + for (bit = 0 ; bit < 8 ; bit++) { + sda = (v & 0x80) ? 2 : 0; + v <<= 1; + /* SDE=1, SDA=sda, SCL=0 */ + err += w9968cf_write_sb(cam, 0x10 | sda); + /* SDE=1, SDA=sda, SCL=1 */ + err += w9968cf_write_sb(cam, 0x11 | sda); + /* SDE=1, SDA=sda, SCL=0 */ + err += w9968cf_write_sb(cam, 0x10 | sda); + } + + return err; +} + + +static int w9968cf_smbus_read_byte(struct w9968cf_device* cam, u8* v) +{ + u8 bit; + int err = 0; + + *v = 0; + for (bit = 0 ; bit < 8 ; bit++) { + *v <<= 1; + err += w9968cf_write_sb(cam, 0x0013); + *v |= (w9968cf_read_sb(cam) & 0x0008) ? 1 : 0; + err += w9968cf_write_sb(cam, 0x0012); + } + + return err; +} + + +static int w9968cf_smbus_write_ack(struct w9968cf_device* cam) +{ + int err = 0; + + err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */ + err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */ + err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */ + + return err; +} + + +static int w9968cf_smbus_read_ack(struct w9968cf_device* cam) +{ + int err = 0, sda; + + err += w9968cf_write_sb(cam, 0x0013); /* SDE=1, SDA=1, SCL=1 */ + sda = (w9968cf_read_sb(cam) & 0x08) ? 1 : 0; /* sda = SDA */ + err += w9968cf_write_sb(cam, 0x0012); /* SDE=1, SDA=1, SCL=0 */ + if (sda < 0) + err += sda; + if (sda == 1) { + DBG(6, "Couldn't receive the ACK") + err += -1; + } + + return err; +} + + +/* This seems to refresh the communication through the serial bus */ +static int w9968cf_smbus_refresh_bus(struct w9968cf_device* cam) +{ + int err = 0, j; + + for (j = 1; j <= 10; j++) { + err = w9968cf_write_reg(cam, 0x0020, 0x01); + err += w9968cf_write_reg(cam, 0x0000, 0x01); + if (err) + break; + } + + return err; +} + + +/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */ +static int +w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device* cam, + u16 address, u8 subaddress,u8 value) +{ + u16* data = cam->data_buffer; + int err = 0; + + err += w9968cf_smbus_refresh_bus(cam); + + /* Enable SBUS outputs */ + err += w9968cf_write_sb(cam, 0x0020); + + data[0] = 0x082f | ((address & 0x80) ? 0x1500 : 0x0); + data[0] |= (address & 0x40) ? 0x4000 : 0x0; + data[1] = 0x2082 | ((address & 0x40) ? 0x0005 : 0x0); + data[1] |= (address & 0x20) ? 0x0150 : 0x0; + data[1] |= (address & 0x10) ? 0x5400 : 0x0; + data[2] = 0x8208 | ((address & 0x08) ? 0x0015 : 0x0); + data[2] |= (address & 0x04) ? 0x0540 : 0x0; + data[2] |= (address & 0x02) ? 0x5000 : 0x0; + data[3] = 0x1d20 | ((address & 0x02) ? 0x0001 : 0x0); + data[3] |= (address & 0x01) ? 0x0054 : 0x0; + + err += w9968cf_write_fsb(cam, data); + + data[0] = 0x8208 | ((subaddress & 0x80) ? 0x0015 : 0x0); + data[0] |= (subaddress & 0x40) ? 0x0540 : 0x0; + data[0] |= (subaddress & 0x20) ? 0x5000 : 0x0; + data[1] = 0x0820 | ((subaddress & 0x20) ? 0x0001 : 0x0); + data[1] |= (subaddress & 0x10) ? 0x0054 : 0x0; + data[1] |= (subaddress & 0x08) ? 0x1500 : 0x0; + data[1] |= (subaddress & 0x04) ? 0x4000 : 0x0; + data[2] = 0x2082 | ((subaddress & 0x04) ? 0x0005 : 0x0); + data[2] |= (subaddress & 0x02) ? 0x0150 : 0x0; + data[2] |= (subaddress & 0x01) ? 0x5400 : 0x0; + data[3] = 0x001d; + + err += w9968cf_write_fsb(cam, data); + + data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0); + data[0] |= (value & 0x40) ? 0x0540 : 0x0; + data[0] |= (value & 0x20) ? 0x5000 : 0x0; + data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0); + data[1] |= (value & 0x10) ? 0x0054 : 0x0; + data[1] |= (value & 0x08) ? 0x1500 : 0x0; + data[1] |= (value & 0x04) ? 0x4000 : 0x0; + data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0); + data[2] |= (value & 0x02) ? 0x0150 : 0x0; + data[2] |= (value & 0x01) ? 0x5400 : 0x0; + data[3] = 0xfe1d; + + err += w9968cf_write_fsb(cam, data); + + /* Disable SBUS outputs */ + err += w9968cf_write_sb(cam, 0x0000); + + if (!err) + DBG(5, "I2C write byte data done, addr.0x%04X, subaddr.0x%02X " + "value 0x%02X", address, subaddress, value) + else + DBG(5, "I2C write byte data failed, addr.0x%04X, " + "subaddr.0x%02X, value 0x%02X", + address, subaddress, value) + + return err; +} + + +/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */ +static int +w9968cf_i2c_adap_read_byte_data(struct w9968cf_device* cam, + u16 address, u8 subaddress, + u8* value) +{ + int err = 0; + + /* Serial data enable */ + err += w9968cf_write_sb(cam, 0x0013); /* don't change ! */ + + err += w9968cf_smbus_start(cam); + err += w9968cf_smbus_write_byte(cam, address); + err += w9968cf_smbus_read_ack(cam); + err += w9968cf_smbus_write_byte(cam, subaddress); + err += w9968cf_smbus_read_ack(cam); + err += w9968cf_smbus_stop(cam); + err += w9968cf_smbus_start(cam); + err += w9968cf_smbus_write_byte(cam, address + 1); + err += w9968cf_smbus_read_ack(cam); + err += w9968cf_smbus_read_byte(cam, value); + err += w9968cf_smbus_write_ack(cam); + err += w9968cf_smbus_stop(cam); + + /* Serial data disable */ + err += w9968cf_write_sb(cam, 0x0000); + + if (!err) + DBG(5, "I2C read byte data done, addr.0x%04X, " + "subaddr.0x%02X, value 0x%02X", + address, subaddress, *value) + else + DBG(5, "I2C read byte data failed, addr.0x%04X, " + "subaddr.0x%02X, wrong value 0x%02X", + address, subaddress, *value) + + return err; +} + + +/* SMBus protocol: S Addr+1 Rd [A] [Value] NA P */ +static int +w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam, + u16 address, u8* value) +{ + int err = 0; + + /* Serial data enable */ + err += w9968cf_write_sb(cam, 0x0013); + + err += w9968cf_smbus_start(cam); + err += w9968cf_smbus_write_byte(cam, address + 1); + err += w9968cf_smbus_read_ack(cam); + err += w9968cf_smbus_read_byte(cam, value); + err += w9968cf_smbus_write_ack(cam); + err += w9968cf_smbus_stop(cam); + + /* Serial data disable */ + err += w9968cf_write_sb(cam, 0x0000); + + if (!err) + DBG(5, "I2C read byte done, addr.0x%04X, " + "value 0x%02X", address, *value) + else + DBG(5, "I2C read byte failed, addr.0x%04X, " + "wrong value 0x%02X", address, *value) + + return err; +} + + +/* SMBus protocol: S Addr Wr [A] Value [A] P */ +static int +w9968cf_i2c_adap_write_byte(struct w9968cf_device* cam, + u16 address, u8 value) +{ + DBG(4, "i2c_write_byte() is an unsupported transfer mode") + return -EINVAL; +} + + + +/**************************************************************************** + * I2C interface to kernel * + ****************************************************************************/ + +static int +w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + struct w9968cf_device* cam = i2c_get_adapdata(adapter); + u8 i; + int err = 0; + + switch (addr) { + case OV6xx0_SID: + case OV7xx0_SID: + break; + default: + DBG(4, "Rejected slave ID 0x%04X", addr) + return -EINVAL; + } + + if (size == I2C_SMBUS_BYTE) { + /* Why addr <<= 1? See OVXXX0_SID defines in ovcamchip.h */ + addr <<= 1; + + if (read_write == I2C_SMBUS_WRITE) + err = w9968cf_i2c_adap_write_byte(cam, addr, command); + else if (read_write == I2C_SMBUS_READ) + err = w9968cf_i2c_adap_read_byte(cam,addr,&data->byte); + + } else if (size == I2C_SMBUS_BYTE_DATA) { + addr <<= 1; + + if (read_write == I2C_SMBUS_WRITE) + err = w9968cf_i2c_adap_fastwrite_byte_data(cam, addr, + command, data->byte); + else if (read_write == I2C_SMBUS_READ) { + for (i = 1; i <= W9968CF_I2C_RW_RETRIES; i++) { + err = w9968cf_i2c_adap_read_byte_data(cam,addr, + command, &data->byte); + if (err) { + if (w9968cf_smbus_refresh_bus(cam)) { + err = -EIO; + break; + } + } else + break; + } + + } else + return -EINVAL; + + } else { + DBG(4, "Unsupported I2C transfer mode (%d)", size) + return -EINVAL; + } + + return err; +} + + +static u32 w9968cf_i2c_func(struct i2c_adapter* adap) +{ + return I2C_FUNC_SMBUS_READ_BYTE | + I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA; +} + + +static int w9968cf_i2c_attach_inform(struct i2c_client* client) +{ + struct w9968cf_device* cam = i2c_get_adapdata(client->adapter); + int id = client->driver->id, err = 0; + + if (id == I2C_DRIVERID_OVCAMCHIP) { + cam->sensor_client = client; + err = w9968cf_sensor_init(cam); + if (err) { + cam->sensor_client = NULL; + return err; + } + } else { + DBG(4, "Rejected client [%s] with driver [%s]", + client->name, client->driver->driver.name) + return -EINVAL; + } + + DBG(5, "I2C attach client [%s] with driver [%s]", + client->name, client->driver->driver.name) + + return 0; +} + + +static int w9968cf_i2c_detach_inform(struct i2c_client* client) +{ + struct w9968cf_device* cam = i2c_get_adapdata(client->adapter); + + if (cam->sensor_client == client) + cam->sensor_client = NULL; + + DBG(5, "I2C detach client [%s]", client->name) + + return 0; +} + + +static int +w9968cf_i2c_control(struct i2c_adapter* adapter, unsigned int cmd, + unsigned long arg) +{ + return 0; +} + + +static int w9968cf_i2c_init(struct w9968cf_device* cam) +{ + int err = 0; + + static struct i2c_algorithm algo = { + .smbus_xfer = w9968cf_i2c_smbus_xfer, + .algo_control = w9968cf_i2c_control, + .functionality = w9968cf_i2c_func, + }; + + static struct i2c_adapter adap = { + .id = I2C_HW_SMBUS_W9968CF, + .class = I2C_CLASS_CAM_DIGITAL, + .owner = THIS_MODULE, + .client_register = w9968cf_i2c_attach_inform, + .client_unregister = w9968cf_i2c_detach_inform, + .algo = &algo, + }; + + memcpy(&cam->i2c_adapter, &adap, sizeof(struct i2c_adapter)); + strcpy(cam->i2c_adapter.name, "w9968cf"); + i2c_set_adapdata(&cam->i2c_adapter, cam); + + DBG(6, "Registering I2C adapter with kernel...") + + err = i2c_add_adapter(&cam->i2c_adapter); + if (err) + DBG(1, "Failed to register the I2C adapter") + else + DBG(5, "I2C adapter registered") + + return err; +} + + + +/**************************************************************************** + * Helper functions * + ****************************************************************************/ + +/*-------------------------------------------------------------------------- + Turn on the LED on some webcams. A beep should be heard too. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_turn_on_led(struct w9968cf_device* cam) +{ + int err = 0; + + err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power-down */ + err += w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */ + err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */ + err += w9968cf_write_reg(cam, 0x0010, 0x01); /* serial bus, SDS high */ + err += w9968cf_write_reg(cam, 0x0000, 0x01); /* serial bus, SDS low */ + err += w9968cf_write_reg(cam, 0x0010, 0x01); /* ..high 'beep-beep' */ + + if (err) + DBG(2, "Couldn't turn on the LED") + + DBG(5, "LED turned on") + + return err; +} + + +/*-------------------------------------------------------------------------- + Write some registers for the device initialization. + This function is called once on open(). + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_init_chip(struct w9968cf_device* cam) +{ + unsigned long hw_bufsize = cam->maxwidth*cam->maxheight*2, + y0 = 0x0000, + u0 = y0 + hw_bufsize/2, + v0 = u0 + hw_bufsize/4, + y1 = v0 + hw_bufsize/4, + u1 = y1 + hw_bufsize/2, + v1 = u1 + hw_bufsize/4; + int err = 0; + + err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power off */ + err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* power on */ + + err += w9968cf_write_reg(cam, 0x405d, 0x03); /* DRAM timings */ + err += w9968cf_write_reg(cam, 0x0030, 0x04); /* SDRAM timings */ + + err += w9968cf_write_reg(cam, y0 & 0xffff, 0x20); /* Y buf.0, low */ + err += w9968cf_write_reg(cam, y0 >> 16, 0x21); /* Y buf.0, high */ + err += w9968cf_write_reg(cam, u0 & 0xffff, 0x24); /* U buf.0, low */ + err += w9968cf_write_reg(cam, u0 >> 16, 0x25); /* U buf.0, high */ + err += w9968cf_write_reg(cam, v0 & 0xffff, 0x28); /* V buf.0, low */ + err += w9968cf_write_reg(cam, v0 >> 16, 0x29); /* V buf.0, high */ + + err += w9968cf_write_reg(cam, y1 & 0xffff, 0x22); /* Y buf.1, low */ + err += w9968cf_write_reg(cam, y1 >> 16, 0x23); /* Y buf.1, high */ + err += w9968cf_write_reg(cam, u1 & 0xffff, 0x26); /* U buf.1, low */ + err += w9968cf_write_reg(cam, u1 >> 16, 0x27); /* U buf.1, high */ + err += w9968cf_write_reg(cam, v1 & 0xffff, 0x2a); /* V buf.1, low */ + err += w9968cf_write_reg(cam, v1 >> 16, 0x2b); /* V buf.1, high */ + + err += w9968cf_write_reg(cam, y1 & 0xffff, 0x32); /* JPEG buf 0 low */ + err += w9968cf_write_reg(cam, y1 >> 16, 0x33); /* JPEG buf 0 high */ + + err += w9968cf_write_reg(cam, y1 & 0xffff, 0x34); /* JPEG buf 1 low */ + err += w9968cf_write_reg(cam, y1 >> 16, 0x35); /* JPEG bug 1 high */ + + err += w9968cf_write_reg(cam, 0x0000, 0x36);/* JPEG restart interval */ + err += w9968cf_write_reg(cam, 0x0804, 0x37);/*JPEG VLE FIFO threshold*/ + err += w9968cf_write_reg(cam, 0x0000, 0x38);/* disable hw up-scaling */ + err += w9968cf_write_reg(cam, 0x0000, 0x3f); /* JPEG/MCTL test data */ + + err += w9968cf_set_picture(cam, cam->picture); /* this before */ + err += w9968cf_set_window(cam, cam->window); + + if (err) + DBG(1, "Chip initialization failed") + else + DBG(5, "Chip successfully initialized") + + return err; +} + + +/*-------------------------------------------------------------------------- + Return non-zero if the palette is supported, 0 otherwise. + --------------------------------------------------------------------------*/ +static inline u16 w9968cf_valid_palette(u16 palette) +{ + u8 i = 0; + while (w9968cf_formatlist[i].palette != 0) { + if (palette == w9968cf_formatlist[i].palette) + return palette; + i++; + } + return 0; +} + + +/*-------------------------------------------------------------------------- + Return the depth corresponding to the given palette. + Palette _must_ be supported ! + --------------------------------------------------------------------------*/ +static inline u16 w9968cf_valid_depth(u16 palette) +{ + u8 i=0; + while (w9968cf_formatlist[i].palette != palette) + i++; + + return w9968cf_formatlist[i].depth; +} + + +/*-------------------------------------------------------------------------- + Return non-zero if the format requires decompression, 0 otherwise. + --------------------------------------------------------------------------*/ +static inline u8 w9968cf_need_decompression(u16 palette) +{ + u8 i = 0; + while (w9968cf_formatlist[i].palette != 0) { + if (palette == w9968cf_formatlist[i].palette) + return w9968cf_formatlist[i].compression; + i++; + } + return 0; +} + + +/*-------------------------------------------------------------------------- + Change the picture settings of the camera. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int +w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict) +{ + u16 fmt, hw_depth, hw_palette, reg_v = 0x0000; + int err = 0; + + /* Make sure we are using a valid depth */ + pict.depth = w9968cf_valid_depth(pict.palette); + + fmt = pict.palette; + + hw_depth = pict.depth; /* depth used by the winbond chip */ + hw_palette = pict.palette; /* palette used by the winbond chip */ + + /* VS & HS polarities */ + reg_v = (cam->vs_polarity << 12) | (cam->hs_polarity << 11); + + switch (fmt) + { + case VIDEO_PALETTE_UYVY: + reg_v |= 0x0000; + cam->vpp_flag = VPP_NONE; + break; + case VIDEO_PALETTE_YUV422P: + reg_v |= 0x0002; + cam->vpp_flag = VPP_DECOMPRESSION; + break; + case VIDEO_PALETTE_YUV420: + case VIDEO_PALETTE_YUV420P: + reg_v |= 0x0003; + cam->vpp_flag = VPP_DECOMPRESSION; + break; + case VIDEO_PALETTE_YUYV: + case VIDEO_PALETTE_YUV422: + reg_v |= 0x0000; + cam->vpp_flag = VPP_SWAP_YUV_BYTES; + hw_palette = VIDEO_PALETTE_UYVY; + break; + /* Original video is used instead of RGBX palettes. + Software conversion later. */ + case VIDEO_PALETTE_GREY: + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_RGB32: + reg_v |= 0x0000; /* UYVY 16 bit is used */ + hw_depth = 16; + hw_palette = VIDEO_PALETTE_UYVY; + cam->vpp_flag = VPP_UYVY_TO_RGBX; + break; + } + + /* NOTE: due to memory issues, it is better to disable the hardware + double buffering during compression */ + if (cam->double_buffer && !(cam->vpp_flag & VPP_DECOMPRESSION)) + reg_v |= 0x0080; + + if (cam->clamping) + reg_v |= 0x0020; + + if (cam->filter_type == 1) + reg_v |= 0x0008; + else if (cam->filter_type == 2) + reg_v |= 0x000c; + + if ((err = w9968cf_write_reg(cam, reg_v, 0x16))) + goto error; + + if ((err = w9968cf_sensor_update_picture(cam, pict))) + goto error; + + /* If all went well, update the device data structure */ + memcpy(&cam->picture, &pict, sizeof(pict)); + cam->hw_depth = hw_depth; + cam->hw_palette = hw_palette; + + /* Settings changed, so we clear the frame buffers */ + memset(cam->frame[0].buffer, 0, cam->nbuffers*cam->frame[0].size); + + DBG(4, "Palette is %s, depth is %u bpp", + symbolic(v4l1_plist, pict.palette), pict.depth) + + return 0; + +error: + DBG(1, "Failed to change picture settings") + return err; +} + + +/*-------------------------------------------------------------------------- + Change the capture area size of the camera. + This function _must_ be called _after_ w9968cf_set_picture(). + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int +w9968cf_set_window(struct w9968cf_device* cam, struct video_window win) +{ + u16 x, y, w, h, scx, scy, cw, ch, ax, ay; + unsigned long fw, fh; + struct ovcamchip_window s_win; + int err = 0; + + /* Work around to avoid FP arithmetics */ + #define __SC(x) ((x) << 10) + #define __UNSC(x) ((x) >> 10) + + /* Make sure we are using a supported resolution */ + if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width, + (u16*)&win.height))) + goto error; + + /* Scaling factors */ + fw = __SC(win.width) / cam->maxwidth; + fh = __SC(win.height) / cam->maxheight; + + /* Set up the width and height values used by the chip */ + if ((win.width > cam->maxwidth) || (win.height > cam->maxheight)) { + cam->vpp_flag |= VPP_UPSCALE; + /* Calculate largest w,h mantaining the same w/h ratio */ + w = (fw >= fh) ? cam->maxwidth : __SC(win.width)/fh; + h = (fw >= fh) ? __SC(win.height)/fw : cam->maxheight; + if (w < cam->minwidth) /* just in case */ + w = cam->minwidth; + if (h < cam->minheight) /* just in case */ + h = cam->minheight; + } else { + cam->vpp_flag &= ~VPP_UPSCALE; + w = win.width; + h = win.height; + } + + /* x,y offsets of the cropped area */ + scx = cam->start_cropx; + scy = cam->start_cropy; + + /* Calculate cropped area manteining the right w/h ratio */ + if (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE)) { + cw = (fw >= fh) ? cam->maxwidth : __SC(win.width)/fh; + ch = (fw >= fh) ? __SC(win.height)/fw : cam->maxheight; + } else { + cw = w; + ch = h; + } + + /* Setup the window of the sensor */ + s_win.format = VIDEO_PALETTE_UYVY; + s_win.width = cam->maxwidth; + s_win.height = cam->maxheight; + s_win.quarter = 0; /* full progressive video */ + + /* Center it */ + s_win.x = (s_win.width - cw) / 2; + s_win.y = (s_win.height - ch) / 2; + + /* Clock divisor */ + if (cam->clockdiv >= 0) + s_win.clockdiv = cam->clockdiv; /* manual override */ + else + switch (cam->sensor) { + case CC_OV6620: + s_win.clockdiv = 0; + break; + case CC_OV6630: + s_win.clockdiv = 0; + break; + case CC_OV76BE: + case CC_OV7610: + case CC_OV7620: + s_win.clockdiv = 0; + break; + default: + s_win.clockdiv = W9968CF_DEF_CLOCKDIVISOR; + } + + /* We have to scale win.x and win.y offsets */ + if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE)) + || (cam->vpp_flag & VPP_UPSCALE) ) { + ax = __SC(win.x)/fw; + ay = __SC(win.y)/fh; + } else { + ax = win.x; + ay = win.y; + } + + if ((ax + cw) > cam->maxwidth) + ax = cam->maxwidth - cw; + + if ((ay + ch) > cam->maxheight) + ay = cam->maxheight - ch; + + /* Adjust win.x, win.y */ + if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE)) + || (cam->vpp_flag & VPP_UPSCALE) ) { + win.x = __UNSC(ax*fw); + win.y = __UNSC(ay*fh); + } else { + win.x = ax; + win.y = ay; + } + + /* Offsets used by the chip */ + x = ax + s_win.x; + y = ay + s_win.y; + + /* Go ! */ + if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_MODE, &s_win))) + goto error; + + err += w9968cf_write_reg(cam, scx + x, 0x10); + err += w9968cf_write_reg(cam, scy + y, 0x11); + err += w9968cf_write_reg(cam, scx + x + cw, 0x12); + err += w9968cf_write_reg(cam, scy + y + ch, 0x13); + err += w9968cf_write_reg(cam, w, 0x14); + err += w9968cf_write_reg(cam, h, 0x15); + + /* JPEG width & height */ + err += w9968cf_write_reg(cam, w, 0x30); + err += w9968cf_write_reg(cam, h, 0x31); + + /* Y & UV frame buffer strides (in WORD) */ + if (cam->vpp_flag & VPP_DECOMPRESSION) { + err += w9968cf_write_reg(cam, w/2, 0x2c); + err += w9968cf_write_reg(cam, w/4, 0x2d); + } else + err += w9968cf_write_reg(cam, w, 0x2c); + + if (err) + goto error; + + /* If all went well, update the device data structure */ + memcpy(&cam->window, &win, sizeof(win)); + cam->hw_width = w; + cam->hw_height = h; + + /* Settings changed, so we clear the frame buffers */ + memset(cam->frame[0].buffer, 0, cam->nbuffers*cam->frame[0].size); + + DBG(4, "The capture area is %dx%d, Offset (x,y)=(%u,%u)", + win.width, win.height, win.x, win.y) + + PDBGG("x=%u ,y=%u, w=%u, h=%u, ax=%u, ay=%u, s_win.x=%u, s_win.y=%u, " + "cw=%u, ch=%u, win.x=%u, win.y=%u, win.width=%u, win.height=%u", + x, y, w, h, ax, ay, s_win.x, s_win.y, cw, ch, win.x, win.y, + win.width, win.height) + + return 0; + +error: + DBG(1, "Failed to change the capture area size") + return err; +} + + +/*-------------------------------------------------------------------------- + Adjust the asked values for window width and height. + Return 0 on success, -1 otherwise. + --------------------------------------------------------------------------*/ +static int +w9968cf_adjust_window_size(struct w9968cf_device* cam, u16* width, u16* height) +{ + u16 maxw, maxh; + + if ((*width < cam->minwidth) || (*height < cam->minheight)) + return -ERANGE; + + maxw = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) && + w9968cf_vpp ? max((u16)W9968CF_MAX_WIDTH, cam->maxwidth) + : cam->maxwidth; + maxh = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) && + w9968cf_vpp ? max((u16)W9968CF_MAX_HEIGHT, cam->maxheight) + : cam->maxheight; + + if (*width > maxw) + *width = maxw; + if (*height > maxh) + *height = maxh; + + if (cam->vpp_flag & VPP_DECOMPRESSION) { + *width &= ~15L; /* multiple of 16 */ + *height &= ~15L; + } + + PDBGG("Window size adjusted w=%u, h=%u ", *width, *height) + + return 0; +} + + +/*-------------------------------------------------------------------------- + Initialize the FIFO list of requested frames. + --------------------------------------------------------------------------*/ +static void w9968cf_init_framelist(struct w9968cf_device* cam) +{ + u8 i; + + for (i = 0; i < cam->nbuffers; i++) { + cam->requested_frame[i] = NULL; + cam->frame[i].queued = 0; + cam->frame[i].status = F_UNUSED; + } +} + + +/*-------------------------------------------------------------------------- + Add a frame in the FIFO list of requested frames. + This function is called in process context. + --------------------------------------------------------------------------*/ +static void w9968cf_push_frame(struct w9968cf_device* cam, u8 f_num) +{ + u8 f; + unsigned long lock_flags; + + spin_lock_irqsave(&cam->flist_lock, lock_flags); + + for (f=0; cam->requested_frame[f] != NULL; f++); + cam->requested_frame[f] = &cam->frame[f_num]; + cam->frame[f_num].queued = 1; + cam->frame[f_num].status = F_UNUSED; /* clear the status */ + + spin_unlock_irqrestore(&cam->flist_lock, lock_flags); + + DBG(6, "Frame #%u pushed into the FIFO list. Position %u", f_num, f) +} + + +/*-------------------------------------------------------------------------- + Read, store and remove the first pointer in the FIFO list of requested + frames. This function is called in interrupt context. + --------------------------------------------------------------------------*/ +static void +w9968cf_pop_frame(struct w9968cf_device* cam, struct w9968cf_frame_t** framep) +{ + u8 i; + + spin_lock(&cam->flist_lock); + + *framep = cam->requested_frame[0]; + + /* Shift the list of pointers */ + for (i = 0; i < cam->nbuffers-1; i++) + cam->requested_frame[i] = cam->requested_frame[i+1]; + cam->requested_frame[i] = NULL; + + spin_unlock(&cam->flist_lock); + + DBG(6,"Popped frame #%d from the list", (*framep)->number) +} + + +/*-------------------------------------------------------------------------- + High-level video post-processing routine on grabbed frames. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int +w9968cf_postprocess_frame(struct w9968cf_device* cam, + struct w9968cf_frame_t* fr) +{ + void *pIn = fr->buffer, *pOut = cam->frame_vpp.buffer, *tmp; + u16 w = cam->window.width, + h = cam->window.height, + d = cam->picture.depth, + fmt = cam->picture.palette, + rgb = cam->force_rgb, + hw_w = cam->hw_width, + hw_h = cam->hw_height, + hw_d = cam->hw_depth; + int err = 0; + + #define _PSWAP(pIn, pOut) {tmp = (pIn); (pIn) = (pOut); (pOut) = tmp;} + + if (cam->vpp_flag & VPP_DECOMPRESSION) { + memcpy(pOut, pIn, fr->length); + _PSWAP(pIn, pOut) + err = w9968cf_vpp->decode(pIn, fr->length, hw_w, hw_h, pOut); + PDBGG("Compressed frame length: %lu",(unsigned long)fr->length) + fr->length = (hw_w*hw_h*hw_d)/8; + _PSWAP(pIn, pOut) + if (err) { + DBG(4, "An error occurred while decoding the frame: " + "%s", symbolic(decoder_errlist, err)) + return err; + } else + DBG(6, "Frame decoded") + } + + if (cam->vpp_flag & VPP_SWAP_YUV_BYTES) { + w9968cf_vpp->swap_yuvbytes(pIn, fr->length); + DBG(6, "Original UYVY component ordering changed") + } + + if (cam->vpp_flag & VPP_UPSCALE) { + w9968cf_vpp->scale_up(pIn, pOut, hw_w, hw_h, hw_d, w, h); + fr->length = (w*h*hw_d)/8; + _PSWAP(pIn, pOut) + DBG(6, "Vertical up-scaling done: %u,%u,%ubpp->%u,%u", + hw_w, hw_h, hw_d, w, h) + } + + if (cam->vpp_flag & VPP_UYVY_TO_RGBX) { + w9968cf_vpp->uyvy_to_rgbx(pIn, fr->length, pOut, fmt, rgb); + fr->length = (w*h*d)/8; + _PSWAP(pIn, pOut) + DBG(6, "UYVY-16bit to %s conversion done", + symbolic(v4l1_plist, fmt)) + } + + if (pOut == fr->buffer) + memcpy(fr->buffer, cam->frame_vpp.buffer, fr->length); + + return 0; +} + + + +/**************************************************************************** + * Image sensor control routines * + ****************************************************************************/ + +static int +w9968cf_sensor_set_control(struct w9968cf_device* cam, int cid, int val) +{ + struct ovcamchip_control ctl; + int err; + + ctl.id = cid; + ctl.value = val; + + err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_CTRL, &ctl); + + return err; +} + + +static int +w9968cf_sensor_get_control(struct w9968cf_device* cam, int cid, int* val) +{ + struct ovcamchip_control ctl; + int err; + + ctl.id = cid; + + err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_G_CTRL, &ctl); + if (!err) + *val = ctl.value; + + return err; +} + + +static int +w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void* arg) +{ + struct i2c_client* c = cam->sensor_client; + int rc = 0; + + if (!c || !c->driver || !c->driver->command) + return -EINVAL; + + rc = c->driver->command(c, cmd, arg); + /* The I2C driver returns -EPERM on non-supported controls */ + return (rc < 0 && rc != -EPERM) ? rc : 0; +} + + +/*-------------------------------------------------------------------------- + Update some settings of the image sensor. + Returns: 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_sensor_update_settings(struct w9968cf_device* cam) +{ + int err = 0; + + /* Auto brightness */ + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOBRIGHT, + cam->auto_brt); + if (err) + return err; + + /* Auto exposure */ + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOEXP, + cam->auto_exp); + if (err) + return err; + + /* Banding filter */ + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BANDFILT, + cam->bandfilt); + if (err) + return err; + + /* Light frequency */ + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_FREQ, + cam->lightfreq); + if (err) + return err; + + /* Back light */ + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BACKLIGHT, + cam->backlight); + if (err) + return err; + + /* Mirror */ + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_MIRROR, + cam->mirror); + if (err) + return err; + + return 0; +} + + +/*-------------------------------------------------------------------------- + Get some current picture settings from the image sensor and update the + internal 'picture' structure of the camera. + Returns: 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_sensor_get_picture(struct w9968cf_device* cam) +{ + int err, v; + + err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_CONT, &v); + if (err) + return err; + cam->picture.contrast = v; + + err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_BRIGHT, &v); + if (err) + return err; + cam->picture.brightness = v; + + err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_SAT, &v); + if (err) + return err; + cam->picture.colour = v; + + err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_HUE, &v); + if (err) + return err; + cam->picture.hue = v; + + DBG(5, "Got picture settings from the image sensor") + + PDBGG("Brightness, contrast, hue, colour, whiteness are " + "%u,%u,%u,%u,%u", cam->picture.brightness,cam->picture.contrast, + cam->picture.hue, cam->picture.colour, cam->picture.whiteness) + + return 0; +} + + +/*-------------------------------------------------------------------------- + Update picture settings of the image sensor. + Returns: 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int +w9968cf_sensor_update_picture(struct w9968cf_device* cam, + struct video_picture pict) +{ + int err = 0; + + if ((!cam->sensor_initialized) + || pict.contrast != cam->picture.contrast) { + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_CONT, + pict.contrast); + if (err) + goto fail; + DBG(4, "Contrast changed from %u to %u", + cam->picture.contrast, pict.contrast) + cam->picture.contrast = pict.contrast; + } + + if (((!cam->sensor_initialized) || + pict.brightness != cam->picture.brightness) && (!cam->auto_brt)) { + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BRIGHT, + pict.brightness); + if (err) + goto fail; + DBG(4, "Brightness changed from %u to %u", + cam->picture.brightness, pict.brightness) + cam->picture.brightness = pict.brightness; + } + + if ((!cam->sensor_initialized) || pict.colour != cam->picture.colour) { + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_SAT, + pict.colour); + if (err) + goto fail; + DBG(4, "Colour changed from %u to %u", + cam->picture.colour, pict.colour) + cam->picture.colour = pict.colour; + } + + if ((!cam->sensor_initialized) || pict.hue != cam->picture.hue) { + err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_HUE, + pict.hue); + if (err) + goto fail; + DBG(4, "Hue changed from %u to %u", + cam->picture.hue, pict.hue) + cam->picture.hue = pict.hue; + } + + return 0; + +fail: + DBG(4, "Failed to change sensor picture setting") + return err; +} + + + +/**************************************************************************** + * Camera configuration * + ****************************************************************************/ + +/*-------------------------------------------------------------------------- + This function is called when a supported image sensor is detected. + Return 0 if the initialization succeeds, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_sensor_init(struct w9968cf_device* cam) +{ + int err = 0; + + if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_INITIALIZE, + &cam->monochrome))) + goto error; + + if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_Q_SUBTYPE, + &cam->sensor))) + goto error; + + /* NOTE: Make sure width and height are a multiple of 16 */ + switch (cam->sensor_client->addr) { + case OV6xx0_SID: + cam->maxwidth = 352; + cam->maxheight = 288; + cam->minwidth = 64; + cam->minheight = 48; + break; + case OV7xx0_SID: + cam->maxwidth = 640; + cam->maxheight = 480; + cam->minwidth = 64; + cam->minheight = 48; + break; + default: + DBG(1, "Not supported image sensor detected for %s", + symbolic(camlist, cam->id)) + return -EINVAL; + } + + /* These values depend on the ones in the ovxxx0.c sources */ + switch (cam->sensor) { + case CC_OV7620: + cam->start_cropx = 287; + cam->start_cropy = 35; + /* Seems to work around a bug in the image sensor */ + cam->vs_polarity = 1; + cam->hs_polarity = 1; + break; + default: + cam->start_cropx = 320; + cam->start_cropy = 35; + cam->vs_polarity = 1; + cam->hs_polarity = 0; + } + + if ((err = w9968cf_sensor_update_settings(cam))) + goto error; + + if ((err = w9968cf_sensor_update_picture(cam, cam->picture))) + goto error; + + cam->sensor_initialized = 1; + + DBG(2, "%s image sensor initialized", symbolic(senlist, cam->sensor)) + return 0; + +error: + cam->sensor_initialized = 0; + cam->sensor = CC_UNKNOWN; + DBG(1, "Image sensor initialization failed for %s (/dev/video%d). " + "Try to detach and attach this device again", + symbolic(camlist, cam->id), cam->v4ldev->minor) + return err; +} + + +/*-------------------------------------------------------------------------- + Fill some basic fields in the main device data structure. + This function is called once on w9968cf_usb_probe() for each recognized + camera. + --------------------------------------------------------------------------*/ +static void +w9968cf_configure_camera(struct w9968cf_device* cam, + struct usb_device* udev, + enum w9968cf_model_id mod_id, + const unsigned short dev_nr) +{ + mutex_init(&cam->fileop_mutex); + init_waitqueue_head(&cam->open); + spin_lock_init(&cam->urb_lock); + spin_lock_init(&cam->flist_lock); + + cam->users = 0; + cam->disconnected = 0; + cam->id = mod_id; + cam->sensor = CC_UNKNOWN; + cam->sensor_initialized = 0; + + /* Calculate the alternate setting number (from 1 to 16) + according to the 'packet_size' module parameter */ + if (packet_size[dev_nr] < W9968CF_MIN_PACKET_SIZE) + packet_size[dev_nr] = W9968CF_MIN_PACKET_SIZE; + for (cam->altsetting = 1; + packet_size[dev_nr] < wMaxPacketSize[cam->altsetting-1]; + cam->altsetting++); + + cam->max_buffers = (max_buffers[dev_nr] < 2 || + max_buffers[dev_nr] > W9968CF_MAX_BUFFERS) + ? W9968CF_BUFFERS : (u8)max_buffers[dev_nr]; + + cam->double_buffer = (double_buffer[dev_nr] == 0 || + double_buffer[dev_nr] == 1) + ? (u8)double_buffer[dev_nr]:W9968CF_DOUBLE_BUFFER; + + cam->clamping = (clamping[dev_nr] == 0 || clamping[dev_nr] == 1) + ? (u8)clamping[dev_nr] : W9968CF_CLAMPING; + + cam->filter_type = (filter_type[dev_nr] == 0 || + filter_type[dev_nr] == 1 || + filter_type[dev_nr] == 2) + ? (u8)filter_type[dev_nr] : W9968CF_FILTER_TYPE; + + cam->capture = 1; + + cam->largeview = (largeview[dev_nr] == 0 || largeview[dev_nr] == 1) + ? (u8)largeview[dev_nr] : W9968CF_LARGEVIEW; + + cam->decompression = (decompression[dev_nr] == 0 || + decompression[dev_nr] == 1 || + decompression[dev_nr] == 2) + ? (u8)decompression[dev_nr]:W9968CF_DECOMPRESSION; + + cam->upscaling = (upscaling[dev_nr] == 0 || + upscaling[dev_nr] == 1) + ? (u8)upscaling[dev_nr] : W9968CF_UPSCALING; + + cam->auto_brt = (autobright[dev_nr] == 0 || autobright[dev_nr] == 1) + ? (u8)autobright[dev_nr] : W9968CF_AUTOBRIGHT; + + cam->auto_exp = (autoexp[dev_nr] == 0 || autoexp[dev_nr] == 1) + ? (u8)autoexp[dev_nr] : W9968CF_AUTOEXP; + + cam->lightfreq = (lightfreq[dev_nr] == 50 || lightfreq[dev_nr] == 60) + ? (u8)lightfreq[dev_nr] : W9968CF_LIGHTFREQ; + + cam->bandfilt = (bandingfilter[dev_nr] == 0 || + bandingfilter[dev_nr] == 1) + ? (u8)bandingfilter[dev_nr] : W9968CF_BANDINGFILTER; + + cam->backlight = (backlight[dev_nr] == 0 || backlight[dev_nr] == 1) + ? (u8)backlight[dev_nr] : W9968CF_BACKLIGHT; + + cam->clockdiv = (clockdiv[dev_nr] == -1 || clockdiv[dev_nr] >= 0) + ? (s8)clockdiv[dev_nr] : W9968CF_CLOCKDIV; + + cam->mirror = (mirror[dev_nr] == 0 || mirror[dev_nr] == 1) + ? (u8)mirror[dev_nr] : W9968CF_MIRROR; + + cam->monochrome = (monochrome[dev_nr] == 0 || monochrome[dev_nr] == 1) + ? monochrome[dev_nr] : W9968CF_MONOCHROME; + + cam->picture.brightness = (u16)brightness[dev_nr]; + cam->picture.hue = (u16)hue[dev_nr]; + cam->picture.colour = (u16)colour[dev_nr]; + cam->picture.contrast = (u16)contrast[dev_nr]; + cam->picture.whiteness = (u16)whiteness[dev_nr]; + if (w9968cf_valid_palette((u16)force_palette[dev_nr])) { + cam->picture.palette = (u16)force_palette[dev_nr]; + cam->force_palette = 1; + } else { + cam->force_palette = 0; + if (cam->decompression == 0) + cam->picture.palette = W9968CF_PALETTE_DECOMP_OFF; + else if (cam->decompression == 1) + cam->picture.palette = W9968CF_PALETTE_DECOMP_FORCE; + else + cam->picture.palette = W9968CF_PALETTE_DECOMP_ON; + } + cam->picture.depth = w9968cf_valid_depth(cam->picture.palette); + + cam->force_rgb = (force_rgb[dev_nr] == 0 || force_rgb[dev_nr] == 1) + ? (u8)force_rgb[dev_nr] : W9968CF_FORCE_RGB; + + cam->window.x = 0; + cam->window.y = 0; + cam->window.width = W9968CF_WIDTH; + cam->window.height = W9968CF_HEIGHT; + cam->window.chromakey = 0; + cam->window.clipcount = 0; + cam->window.flags = 0; + + DBG(3, "%s configured with settings #%u:", + symbolic(camlist, cam->id), dev_nr) + + DBG(3, "- Data packet size for USB isochrnous transfer: %u bytes", + wMaxPacketSize[cam->altsetting-1]) + + DBG(3, "- Number of requested video frame buffers: %u", + cam->max_buffers) + + if (cam->double_buffer) + DBG(3, "- Hardware double buffering enabled") + else + DBG(3, "- Hardware double buffering disabled") + + if (cam->filter_type == 0) + DBG(3, "- Video filtering disabled") + else if (cam->filter_type == 1) + DBG(3, "- Video filtering enabled: type 1-2-1") + else if (cam->filter_type == 2) + DBG(3, "- Video filtering enabled: type 2-3-6-3-2") + + if (cam->clamping) + DBG(3, "- Video data clamping (CCIR-601 format) enabled") + else + DBG(3, "- Video data clamping (CCIR-601 format) disabled") + + if (cam->largeview) + DBG(3, "- Large view enabled") + else + DBG(3, "- Large view disabled") + + if ((cam->decompression) == 0 && (!cam->force_palette)) + DBG(3, "- Decompression disabled") + else if ((cam->decompression) == 1 && (!cam->force_palette)) + DBG(3, "- Decompression forced") + else if ((cam->decompression) == 2 && (!cam->force_palette)) + DBG(3, "- Decompression allowed") + + if (cam->upscaling) + DBG(3, "- Software image scaling enabled") + else + DBG(3, "- Software image scaling disabled") + + if (cam->force_palette) + DBG(3, "- Image palette forced to %s", + symbolic(v4l1_plist, cam->picture.palette)) + + if (cam->force_rgb) + DBG(3, "- RGB component ordering will be used instead of BGR") + + if (cam->auto_brt) + DBG(3, "- Auto brightness enabled") + else + DBG(3, "- Auto brightness disabled") + + if (cam->auto_exp) + DBG(3, "- Auto exposure enabled") + else + DBG(3, "- Auto exposure disabled") + + if (cam->backlight) + DBG(3, "- Backlight exposure algorithm enabled") + else + DBG(3, "- Backlight exposure algorithm disabled") + + if (cam->mirror) + DBG(3, "- Mirror enabled") + else + DBG(3, "- Mirror disabled") + + if (cam->bandfilt) + DBG(3, "- Banding filter enabled") + else + DBG(3, "- Banding filter disabled") + + DBG(3, "- Power lighting frequency: %u", cam->lightfreq) + + if (cam->clockdiv == -1) + DBG(3, "- Automatic clock divisor enabled") + else + DBG(3, "- Clock divisor: %d", cam->clockdiv) + + if (cam->monochrome) + DBG(3, "- Image sensor used as monochrome") + else + DBG(3, "- Image sensor not used as monochrome") +} + + +/*-------------------------------------------------------------------------- + If the video post-processing module is not loaded, some parameters + must be overridden. + --------------------------------------------------------------------------*/ +static void w9968cf_adjust_configuration(struct w9968cf_device* cam) +{ + if (!w9968cf_vpp) { + if (cam->decompression == 1) { + cam->decompression = 2; + DBG(2, "Video post-processing module not found: " + "'decompression' parameter forced to 2") + } + if (cam->upscaling) { + cam->upscaling = 0; + DBG(2, "Video post-processing module not found: " + "'upscaling' parameter forced to 0") + } + if (cam->picture.palette != VIDEO_PALETTE_UYVY) { + cam->force_palette = 0; + DBG(2, "Video post-processing module not found: " + "'force_palette' parameter forced to 0") + } + cam->picture.palette = VIDEO_PALETTE_UYVY; + cam->picture.depth = w9968cf_valid_depth(cam->picture.palette); + } +} + + +/*-------------------------------------------------------------------------- + Release the resources used by the driver. + This function is called on disconnect + (or on close if deallocation has been deferred) + --------------------------------------------------------------------------*/ +static void w9968cf_release_resources(struct w9968cf_device* cam) +{ + mutex_lock(&w9968cf_devlist_mutex); + + DBG(2, "V4L device deregistered: /dev/video%d", cam->v4ldev->minor) + + video_unregister_device(cam->v4ldev); + list_del(&cam->v4llist); + i2c_del_adapter(&cam->i2c_adapter); + w9968cf_deallocate_memory(cam); + kfree(cam->control_buffer); + kfree(cam->data_buffer); + + mutex_unlock(&w9968cf_devlist_mutex); +} + + + +/**************************************************************************** + * Video4Linux interface * + ****************************************************************************/ + +static int w9968cf_open(struct inode* inode, struct file* filp) +{ + struct w9968cf_device* cam; + int err; + + /* This the only safe way to prevent race conditions with disconnect */ + if (!down_read_trylock(&w9968cf_disconnect)) + return -ERESTARTSYS; + + cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); + + mutex_lock(&cam->dev_mutex); + + if (cam->sensor == CC_UNKNOWN) { + DBG(2, "No supported image sensor has been detected by the " + "'ovcamchip' module for the %s (/dev/video%d). Make " + "sure it is loaded *before* (re)connecting the camera.", + symbolic(camlist, cam->id), cam->v4ldev->minor) + mutex_unlock(&cam->dev_mutex); + up_read(&w9968cf_disconnect); + return -ENODEV; + } + + if (cam->users) { + DBG(2, "%s (/dev/video%d) has been already occupied by '%s'", + symbolic(camlist, cam->id),cam->v4ldev->minor,cam->command) + if ((filp->f_flags & O_NONBLOCK)||(filp->f_flags & O_NDELAY)) { + mutex_unlock(&cam->dev_mutex); + up_read(&w9968cf_disconnect); + return -EWOULDBLOCK; + } + mutex_unlock(&cam->dev_mutex); + err = wait_event_interruptible_exclusive(cam->open, + cam->disconnected || + !cam->users); + if (err) { + up_read(&w9968cf_disconnect); + return err; + } + if (cam->disconnected) { + up_read(&w9968cf_disconnect); + return -ENODEV; + } + mutex_lock(&cam->dev_mutex); + } + + DBG(5, "Opening '%s', /dev/video%d ...", + symbolic(camlist, cam->id), cam->v4ldev->minor) + + cam->streaming = 0; + cam->misconfigured = 0; + + w9968cf_adjust_configuration(cam); + + if ((err = w9968cf_allocate_memory(cam))) + goto deallocate_memory; + + if ((err = w9968cf_init_chip(cam))) + goto deallocate_memory; + + if ((err = w9968cf_start_transfer(cam))) + goto deallocate_memory; + + filp->private_data = cam; + + cam->users++; + strcpy(cam->command, current->comm); + + init_waitqueue_head(&cam->wait_queue); + + DBG(5, "Video device is open") + + mutex_unlock(&cam->dev_mutex); + up_read(&w9968cf_disconnect); + + return 0; + +deallocate_memory: + w9968cf_deallocate_memory(cam); + DBG(2, "Failed to open the video device") + mutex_unlock(&cam->dev_mutex); + up_read(&w9968cf_disconnect); + return err; +} + + +static int w9968cf_release(struct inode* inode, struct file* filp) +{ + struct w9968cf_device* cam; + + cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); + + mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */ + + w9968cf_stop_transfer(cam); + + if (cam->disconnected) { + w9968cf_release_resources(cam); + mutex_unlock(&cam->dev_mutex); + kfree(cam); + return 0; + } + + cam->users--; + w9968cf_deallocate_memory(cam); + wake_up_interruptible_nr(&cam->open, 1); + + DBG(5, "Video device closed") + mutex_unlock(&cam->dev_mutex); + return 0; +} + + +static ssize_t +w9968cf_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) +{ + struct w9968cf_device* cam; + struct w9968cf_frame_t* fr; + int err = 0; + + cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); + + if (filp->f_flags & O_NONBLOCK) + return -EWOULDBLOCK; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->disconnected) { + DBG(2, "Device not present") + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->misconfigured) { + DBG(2, "The camera is misconfigured. Close and open it again.") + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + if (!cam->frame[0].queued) + w9968cf_push_frame(cam, 0); + + if (!cam->frame[1].queued) + w9968cf_push_frame(cam, 1); + + err = wait_event_interruptible(cam->wait_queue, + cam->frame[0].status == F_READY || + cam->frame[1].status == F_READY || + cam->disconnected); + if (err) { + mutex_unlock(&cam->fileop_mutex); + return err; + } + if (cam->disconnected) { + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + fr = (cam->frame[0].status == F_READY) ? &cam->frame[0]:&cam->frame[1]; + + if (w9968cf_vpp) + w9968cf_postprocess_frame(cam, fr); + + if (count > fr->length) + count = fr->length; + + if (copy_to_user(buf, fr->buffer, count)) { + fr->status = F_UNUSED; + mutex_unlock(&cam->fileop_mutex); + return -EFAULT; + } + *f_pos += count; + + fr->status = F_UNUSED; + + DBG(5, "%zu bytes read", count) + + mutex_unlock(&cam->fileop_mutex); + return count; +} + + +static int w9968cf_mmap(struct file* filp, struct vm_area_struct *vma) +{ + struct w9968cf_device* cam = (struct w9968cf_device*) + video_get_drvdata(video_devdata(filp)); + unsigned long vsize = vma->vm_end - vma->vm_start, + psize = cam->nbuffers * cam->frame[0].size, + start = vma->vm_start, + pos = (unsigned long)cam->frame[0].buffer, + page; + + if (cam->disconnected) { + DBG(2, "Device not present") + return -ENODEV; + } + + if (cam->misconfigured) { + DBG(2, "The camera is misconfigured. Close and open it again") + return -EIO; + } + + PDBGG("mmapping %lu bytes...", vsize) + + if (vsize > psize - (vma->vm_pgoff << PAGE_SHIFT)) + return -EINVAL; + + while (vsize > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page + vma->vm_pgoff, + PAGE_SIZE, vma->vm_page_prot)) + return -EAGAIN; + start += PAGE_SIZE; + pos += PAGE_SIZE; + vsize -= PAGE_SIZE; + } + + DBG(5, "mmap method successfully called") + return 0; +} + + +static int +w9968cf_ioctl(struct inode* inode, struct file* filp, + unsigned int cmd, unsigned long arg) +{ + struct w9968cf_device* cam; + int err; + + cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->disconnected) { + DBG(2, "Device not present") + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->misconfigured) { + DBG(2, "The camera is misconfigured. Close and open it again.") + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + err = w9968cf_v4l_ioctl(inode, filp, cmd, (void __user *)arg); + + mutex_unlock(&cam->fileop_mutex); + return err; +} + + +static int w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, + unsigned int cmd, void __user * arg) +{ + struct w9968cf_device* cam; + const char* v4l1_ioctls[] = { + "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", + "GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF", + "SFBUF", "KEY", "GFREQ", "SFREQ", "GAUDIO", "SAUDIO", + "SYNC", "MCAPTURE", "GMBUF", "GUNIT", "GCAPTURE", "SCAPTURE", + "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", "SMICROCODE", + "GVBIFMT", "SVBIFMT" + }; + + #define V4L1_IOCTL(cmd) \ + ((_IOC_NR((cmd)) < ARRAY_SIZE(v4l1_ioctls)) ? \ + v4l1_ioctls[_IOC_NR((cmd))] : "?") + + cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); + + switch (cmd) { + + case VIDIOCGCAP: /* get video capability */ + { + struct video_capability cap = { + .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES, + .channels = 1, + .audios = 0, + .minwidth = cam->minwidth, + .minheight = cam->minheight, + }; + sprintf(cap.name, "W996[87]CF USB Camera #%d", + cam->v4ldev->minor); + cap.maxwidth = (cam->upscaling && w9968cf_vpp) + ? max((u16)W9968CF_MAX_WIDTH, cam->maxwidth) + : cam->maxwidth; + cap.maxheight = (cam->upscaling && w9968cf_vpp) + ? max((u16)W9968CF_MAX_HEIGHT, cam->maxheight) + : cam->maxheight; + + if (copy_to_user(arg, &cap, sizeof(cap))) + return -EFAULT; + + DBG(5, "VIDIOCGCAP successfully called") + return 0; + } + + case VIDIOCGCHAN: /* get video channel informations */ + { + struct video_channel chan; + if (copy_from_user(&chan, arg, sizeof(chan))) + return -EFAULT; + + if (chan.channel != 0) + return -EINVAL; + + strcpy(chan.name, "Camera"); + chan.tuners = 0; + chan.flags = 0; + chan.type = VIDEO_TYPE_CAMERA; + chan.norm = VIDEO_MODE_AUTO; + + if (copy_to_user(arg, &chan, sizeof(chan))) + return -EFAULT; + + DBG(5, "VIDIOCGCHAN successfully called") + return 0; + } + + case VIDIOCSCHAN: /* set active channel */ + { + struct video_channel chan; + + if (copy_from_user(&chan, arg, sizeof(chan))) + return -EFAULT; + + if (chan.channel != 0) + return -EINVAL; + + DBG(5, "VIDIOCSCHAN successfully called") + return 0; + } + + case VIDIOCGPICT: /* get image properties of the picture */ + { + if (w9968cf_sensor_get_picture(cam)) + return -EIO; + + if (copy_to_user(arg, &cam->picture, sizeof(cam->picture))) + return -EFAULT; + + DBG(5, "VIDIOCGPICT successfully called") + return 0; + } + + case VIDIOCSPICT: /* change picture settings */ + { + struct video_picture pict; + int err = 0; + + if (copy_from_user(&pict, arg, sizeof(pict))) + return -EFAULT; + + if ( (cam->force_palette || !w9968cf_vpp) + && pict.palette != cam->picture.palette ) { + DBG(4, "Palette %s rejected: only %s is allowed", + symbolic(v4l1_plist, pict.palette), + symbolic(v4l1_plist, cam->picture.palette)) + return -EINVAL; + } + + if (!w9968cf_valid_palette(pict.palette)) { + DBG(4, "Palette %s not supported. VIDIOCSPICT failed", + symbolic(v4l1_plist, pict.palette)) + return -EINVAL; + } + + if (!cam->force_palette) { + if (cam->decompression == 0) { + if (w9968cf_need_decompression(pict.palette)) { + DBG(4, "Decompression disabled: palette %s is not " + "allowed. VIDIOCSPICT failed", + symbolic(v4l1_plist, pict.palette)) + return -EINVAL; + } + } else if (cam->decompression == 1) { + if (!w9968cf_need_decompression(pict.palette)) { + DBG(4, "Decompression forced: palette %s is not " + "allowed. VIDIOCSPICT failed", + symbolic(v4l1_plist, pict.palette)) + return -EINVAL; + } + } + } + + if (pict.depth != w9968cf_valid_depth(pict.palette)) { + DBG(4, "Requested depth %u bpp is not valid for %s " + "palette: ignored and changed to %u bpp", + pict.depth, symbolic(v4l1_plist, pict.palette), + w9968cf_valid_depth(pict.palette)) + pict.depth = w9968cf_valid_depth(pict.palette); + } + + if (pict.palette != cam->picture.palette) { + if(*cam->requested_frame + || cam->frame_current->queued) { + err = wait_event_interruptible + ( cam->wait_queue, + cam->disconnected || + (!*cam->requested_frame && + !cam->frame_current->queued) ); + if (err) + return err; + if (cam->disconnected) + return -ENODEV; + } + + if (w9968cf_stop_transfer(cam)) + goto ioctl_fail; + + if (w9968cf_set_picture(cam, pict)) + goto ioctl_fail; + + if (w9968cf_start_transfer(cam)) + goto ioctl_fail; + + } else if (w9968cf_sensor_update_picture(cam, pict)) + return -EIO; + + + DBG(5, "VIDIOCSPICT successfully called") + return 0; + } + + case VIDIOCSWIN: /* set capture area */ + { + struct video_window win; + int err = 0; + + if (copy_from_user(&win, arg, sizeof(win))) + return -EFAULT; + + DBG(6, "VIDIOCSWIN called: clipcount=%d, flags=%u, " + "x=%u, y=%u, %ux%u", win.clipcount, win.flags, + win.x, win.y, win.width, win.height) + + if (win.clipcount != 0 || win.flags != 0) + return -EINVAL; + + if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width, + (u16*)&win.height))) { + DBG(4, "Resolution not supported (%ux%u). " + "VIDIOCSWIN failed", win.width, win.height) + return err; + } + + if (win.x != cam->window.x || + win.y != cam->window.y || + win.width != cam->window.width || + win.height != cam->window.height) { + if(*cam->requested_frame + || cam->frame_current->queued) { + err = wait_event_interruptible + ( cam->wait_queue, + cam->disconnected || + (!*cam->requested_frame && + !cam->frame_current->queued) ); + if (err) + return err; + if (cam->disconnected) + return -ENODEV; + } + + if (w9968cf_stop_transfer(cam)) + goto ioctl_fail; + + /* This _must_ be called before set_window() */ + if (w9968cf_set_picture(cam, cam->picture)) + goto ioctl_fail; + + if (w9968cf_set_window(cam, win)) + goto ioctl_fail; + + if (w9968cf_start_transfer(cam)) + goto ioctl_fail; + } + + DBG(5, "VIDIOCSWIN successfully called. ") + return 0; + } + + case VIDIOCGWIN: /* get current window properties */ + { + if (copy_to_user(arg,&cam->window,sizeof(struct video_window))) + return -EFAULT; + + DBG(5, "VIDIOCGWIN successfully called") + return 0; + } + + case VIDIOCGMBUF: /* request for memory (mapped) buffer */ + { + struct video_mbuf mbuf; + u8 i; + + mbuf.size = cam->nbuffers * cam->frame[0].size; + mbuf.frames = cam->nbuffers; + for (i = 0; i < cam->nbuffers; i++) + mbuf.offsets[i] = (unsigned long)cam->frame[i].buffer - + (unsigned long)cam->frame[0].buffer; + + if (copy_to_user(arg, &mbuf, sizeof(mbuf))) + return -EFAULT; + + DBG(5, "VIDIOCGMBUF successfully called") + return 0; + } + + case VIDIOCMCAPTURE: /* start the capture to a frame */ + { + struct video_mmap mmap; + struct w9968cf_frame_t* fr; + int err = 0; + + if (copy_from_user(&mmap, arg, sizeof(mmap))) + return -EFAULT; + + DBG(6, "VIDIOCMCAPTURE called: frame #%u, format=%s, %dx%d", + mmap.frame, symbolic(v4l1_plist, mmap.format), + mmap.width, mmap.height) + + if (mmap.frame >= cam->nbuffers) { + DBG(4, "Invalid frame number (%u). " + "VIDIOCMCAPTURE failed", mmap.frame) + return -EINVAL; + } + + if (mmap.format!=cam->picture.palette && + (cam->force_palette || !w9968cf_vpp)) { + DBG(4, "Palette %s rejected: only %s is allowed", + symbolic(v4l1_plist, mmap.format), + symbolic(v4l1_plist, cam->picture.palette)) + return -EINVAL; + } + + if (!w9968cf_valid_palette(mmap.format)) { + DBG(4, "Palette %s not supported. " + "VIDIOCMCAPTURE failed", + symbolic(v4l1_plist, mmap.format)) + return -EINVAL; + } + + if (!cam->force_palette) { + if (cam->decompression == 0) { + if (w9968cf_need_decompression(mmap.format)) { + DBG(4, "Decompression disabled: palette %s is not " + "allowed. VIDIOCSPICT failed", + symbolic(v4l1_plist, mmap.format)) + return -EINVAL; + } + } else if (cam->decompression == 1) { + if (!w9968cf_need_decompression(mmap.format)) { + DBG(4, "Decompression forced: palette %s is not " + "allowed. VIDIOCSPICT failed", + symbolic(v4l1_plist, mmap.format)) + return -EINVAL; + } + } + } + + if ((err = w9968cf_adjust_window_size(cam, (u16*)&mmap.width, + (u16*)&mmap.height))) { + DBG(4, "Resolution not supported (%dx%d). " + "VIDIOCMCAPTURE failed", + mmap.width, mmap.height) + return err; + } + + fr = &cam->frame[mmap.frame]; + + if (mmap.width != cam->window.width || + mmap.height != cam->window.height || + mmap.format != cam->picture.palette) { + + struct video_window win; + struct video_picture pict; + + if(*cam->requested_frame + || cam->frame_current->queued) { + DBG(6, "VIDIOCMCAPTURE. Change settings for " + "frame #%u: %dx%d, format %s. Wait...", + mmap.frame, mmap.width, mmap.height, + symbolic(v4l1_plist, mmap.format)) + err = wait_event_interruptible + ( cam->wait_queue, + cam->disconnected || + (!*cam->requested_frame && + !cam->frame_current->queued) ); + if (err) + return err; + if (cam->disconnected) + return -ENODEV; + } + + memcpy(&win, &cam->window, sizeof(win)); + memcpy(&pict, &cam->picture, sizeof(pict)); + win.width = mmap.width; + win.height = mmap.height; + pict.palette = mmap.format; + + if (w9968cf_stop_transfer(cam)) + goto ioctl_fail; + + /* This before set_window */ + if (w9968cf_set_picture(cam, pict)) + goto ioctl_fail; + + if (w9968cf_set_window(cam, win)) + goto ioctl_fail; + + if (w9968cf_start_transfer(cam)) + goto ioctl_fail; + + } else if (fr->queued) { + + DBG(6, "Wait until frame #%u is free", mmap.frame) + + err = wait_event_interruptible(cam->wait_queue, + cam->disconnected || + (!fr->queued)); + if (err) + return err; + if (cam->disconnected) + return -ENODEV; + } + + w9968cf_push_frame(cam, mmap.frame); + DBG(5, "VIDIOCMCAPTURE(%u): successfully called", mmap.frame) + return 0; + } + + case VIDIOCSYNC: /* wait until the capture of a frame is finished */ + { + unsigned int f_num; + struct w9968cf_frame_t* fr; + int err = 0; + + if (copy_from_user(&f_num, arg, sizeof(f_num))) + return -EFAULT; + + if (f_num >= cam->nbuffers) { + DBG(4, "Invalid frame number (%u). " + "VIDIOCMCAPTURE failed", f_num) + return -EINVAL; + } + + DBG(6, "VIDIOCSYNC called for frame #%u", f_num) + + fr = &cam->frame[f_num]; + + switch (fr->status) { + case F_UNUSED: + if (!fr->queued) { + DBG(4, "VIDIOSYNC: Frame #%u not requested!", + f_num) + return -EFAULT; + } + case F_ERROR: + case F_GRABBING: + err = wait_event_interruptible(cam->wait_queue, + (fr->status == F_READY) + || cam->disconnected); + if (err) + return err; + if (cam->disconnected) + return -ENODEV; + break; + case F_READY: + break; + } + + if (w9968cf_vpp) + w9968cf_postprocess_frame(cam, fr); + + fr->status = F_UNUSED; + + DBG(5, "VIDIOCSYNC(%u) successfully called", f_num) + return 0; + } + + case VIDIOCGUNIT:/* report the unit numbers of the associated devices*/ + { + struct video_unit unit = { + .video = cam->v4ldev->minor, + .vbi = VIDEO_NO_UNIT, + .radio = VIDEO_NO_UNIT, + .audio = VIDEO_NO_UNIT, + .teletext = VIDEO_NO_UNIT, + }; + + if (copy_to_user(arg, &unit, sizeof(unit))) + return -EFAULT; + + DBG(5, "VIDIOCGUNIT successfully called") + return 0; + } + + case VIDIOCKEY: + return 0; + + case VIDIOCGFBUF: + { + if (clear_user(arg, sizeof(struct video_buffer))) + return -EFAULT; + + DBG(5, "VIDIOCGFBUF successfully called") + return 0; + } + + case VIDIOCGTUNER: + { + struct video_tuner tuner; + if (copy_from_user(&tuner, arg, sizeof(tuner))) + return -EFAULT; + + if (tuner.tuner != 0) + return -EINVAL; + + strcpy(tuner.name, "no_tuner"); + tuner.rangelow = 0; + tuner.rangehigh = 0; + tuner.flags = VIDEO_TUNER_NORM; + tuner.mode = VIDEO_MODE_AUTO; + tuner.signal = 0xffff; + + if (copy_to_user(arg, &tuner, sizeof(tuner))) + return -EFAULT; + + DBG(5, "VIDIOCGTUNER successfully called") + return 0; + } + + case VIDIOCSTUNER: + { + struct video_tuner tuner; + if (copy_from_user(&tuner, arg, sizeof(tuner))) + return -EFAULT; + + if (tuner.tuner != 0) + return -EINVAL; + + if (tuner.mode != VIDEO_MODE_AUTO) + return -EINVAL; + + DBG(5, "VIDIOCSTUNER successfully called") + return 0; + } + + case VIDIOCSFBUF: + case VIDIOCCAPTURE: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + case VIDIOCSPLAYMODE: + case VIDIOCSWRITEMODE: + case VIDIOCGPLAYINFO: + case VIDIOCSMICROCODE: + case VIDIOCGVBIFMT: + case VIDIOCSVBIFMT: + DBG(4, "Unsupported V4L1 IOCtl: VIDIOC%s " + "(type 0x%01X, " + "n. 0x%01X, " + "dir. 0x%01X, " + "size 0x%02X)", + V4L1_IOCTL(cmd), + _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd)) + + return -EINVAL; + + default: + DBG(4, "Invalid V4L1 IOCtl: VIDIOC%s " + "type 0x%01X, " + "n. 0x%01X, " + "dir. 0x%01X, " + "size 0x%02X", + V4L1_IOCTL(cmd), + _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd)) + + return -ENOIOCTLCMD; + + } /* end of switch */ + +ioctl_fail: + cam->misconfigured = 1; + DBG(1, "VIDIOC%s failed because of hardware problems. " + "To use the camera, close and open it again.", V4L1_IOCTL(cmd)) + return -EFAULT; +} + + +static struct file_operations w9968cf_fops = { + .owner = THIS_MODULE, + .open = w9968cf_open, + .release = w9968cf_release, + .read = w9968cf_read, + .ioctl = w9968cf_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .mmap = w9968cf_mmap, + .llseek = no_llseek, +}; + + + +/**************************************************************************** + * USB probe and V4L registration, disconnect and id_table[] definition * + ****************************************************************************/ + +static int +w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct w9968cf_device* cam; + int err = 0; + enum w9968cf_model_id mod_id; + struct list_head* ptr; + u8 sc = 0; /* number of simultaneous cameras */ + static unsigned short dev_nr = 0; /* we are handling device number n */ + + if (le16_to_cpu(udev->descriptor.idVendor) == winbond_id_table[0].idVendor && + le16_to_cpu(udev->descriptor.idProduct) == winbond_id_table[0].idProduct) + mod_id = W9968CF_MOD_CLVBWGP; /* see camlist[] table */ + else if (le16_to_cpu(udev->descriptor.idVendor) == winbond_id_table[1].idVendor && + le16_to_cpu(udev->descriptor.idProduct) == winbond_id_table[1].idProduct) + mod_id = W9968CF_MOD_GENERIC; /* see camlist[] table */ + else + return -ENODEV; + + cam = (struct w9968cf_device*) + kzalloc(sizeof(struct w9968cf_device), GFP_KERNEL); + if (!cam) + return -ENOMEM; + + mutex_init(&cam->dev_mutex); + mutex_lock(&cam->dev_mutex); + + cam->usbdev = udev; + /* NOTE: a local copy is used to avoid possible race conditions */ + memcpy(&cam->dev, &udev->dev, sizeof(struct device)); + + DBG(2, "%s detected", symbolic(camlist, mod_id)) + + if (simcams > W9968CF_MAX_DEVICES) + simcams = W9968CF_SIMCAMS; + + /* How many cameras are connected ? */ + mutex_lock(&w9968cf_devlist_mutex); + list_for_each(ptr, &w9968cf_dev_list) + sc++; + mutex_unlock(&w9968cf_devlist_mutex); + + if (sc >= simcams) { + DBG(2, "Device rejected: too many connected cameras " + "(max. %u)", simcams) + err = -EPERM; + goto fail; + } + + + /* Allocate 2 bytes of memory for camera control USB transfers */ + if (!(cam->control_buffer = kzalloc(2, GFP_KERNEL))) { + DBG(1,"Couldn't allocate memory for camera control transfers") + err = -ENOMEM; + goto fail; + } + + /* Allocate 8 bytes of memory for USB data transfers to the FSB */ + if (!(cam->data_buffer = kzalloc(8, GFP_KERNEL))) { + DBG(1, "Couldn't allocate memory for data " + "transfers to the FSB") + err = -ENOMEM; + goto fail; + } + + /* Register the V4L device */ + cam->v4ldev = video_device_alloc(); + if (!cam->v4ldev) { + DBG(1, "Could not allocate memory for a V4L structure") + err = -ENOMEM; + goto fail; + } + + strcpy(cam->v4ldev->name, symbolic(camlist, mod_id)); + cam->v4ldev->owner = THIS_MODULE; + cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; + cam->v4ldev->hardware = VID_HARDWARE_W9968CF; + cam->v4ldev->fops = &w9968cf_fops; + cam->v4ldev->minor = video_nr[dev_nr]; + cam->v4ldev->release = video_device_release; + video_set_drvdata(cam->v4ldev, cam); + cam->v4ldev->dev = &cam->dev; + + err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, + video_nr[dev_nr]); + if (err) { + DBG(1, "V4L device registration failed") + if (err == -ENFILE && video_nr[dev_nr] == -1) + DBG(2, "Couldn't find a free /dev/videoX node") + video_nr[dev_nr] = -1; + dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0; + goto fail; + } + + DBG(2, "V4L device registered as /dev/video%d", cam->v4ldev->minor) + + /* Set some basic constants */ + w9968cf_configure_camera(cam, udev, mod_id, dev_nr); + + /* Add a new entry into the list of V4L registered devices */ + mutex_lock(&w9968cf_devlist_mutex); + list_add(&cam->v4llist, &w9968cf_dev_list); + mutex_unlock(&w9968cf_devlist_mutex); + dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0; + + w9968cf_turn_on_led(cam); + + w9968cf_i2c_init(cam); + + usb_set_intfdata(intf, cam); + mutex_unlock(&cam->dev_mutex); + return 0; + +fail: /* Free unused memory */ + kfree(cam->control_buffer); + kfree(cam->data_buffer); + if (cam->v4ldev) + video_device_release(cam->v4ldev); + mutex_unlock(&cam->dev_mutex); + kfree(cam); + return err; +} + + +static void w9968cf_usb_disconnect(struct usb_interface* intf) +{ + struct w9968cf_device* cam = + (struct w9968cf_device*)usb_get_intfdata(intf); + + down_write(&w9968cf_disconnect); + + if (cam) { + /* Prevent concurrent accesses to data */ + mutex_lock(&cam->dev_mutex); + + cam->disconnected = 1; + + DBG(2, "Disconnecting %s...", symbolic(camlist, cam->id)) + + wake_up_interruptible_all(&cam->open); + + if (cam->users) { + DBG(2, "The device is open (/dev/video%d)! " + "Process name: %s. Deregistration and memory " + "deallocation are deferred on close.", + cam->v4ldev->minor, cam->command) + cam->misconfigured = 1; + w9968cf_stop_transfer(cam); + wake_up_interruptible(&cam->wait_queue); + } else + w9968cf_release_resources(cam); + + mutex_unlock(&cam->dev_mutex); + + if (!cam->users) + kfree(cam); + } + + up_write(&w9968cf_disconnect); +} + + +static struct usb_driver w9968cf_usb_driver = { + .name = "w9968cf", + .id_table = winbond_id_table, + .probe = w9968cf_usb_probe, + .disconnect = w9968cf_usb_disconnect, +}; + + + +/**************************************************************************** + * Module init, exit and intermodule communication * + ****************************************************************************/ + +static int __init w9968cf_module_init(void) +{ + int err; + + KDBG(2, W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION) + KDBG(3, W9968CF_MODULE_AUTHOR) + + if (ovmod_load) + request_module("ovcamchip"); + + if ((err = usb_register(&w9968cf_usb_driver))) + return err; + + return 0; +} + + +static void __exit w9968cf_module_exit(void) +{ + /* w9968cf_usb_disconnect() will be called */ + usb_deregister(&w9968cf_usb_driver); + + KDBG(2, W9968CF_MODULE_NAME" deregistered") +} + + +module_init(w9968cf_module_init); +module_exit(w9968cf_module_exit); + diff --git a/linux/drivers/media/video/w9968cf.h b/linux/drivers/media/video/w9968cf.h new file mode 100644 index 000000000..a89013fa3 --- /dev/null +++ b/linux/drivers/media/video/w9968cf.h @@ -0,0 +1,331 @@ +/*************************************************************************** + * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. * + * * + * Copyright (C) 2002-2004 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _W9968CF_H_ +#define _W9968CF_H_ + +#include "compat.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "w9968cf_vpp.h" + + +/**************************************************************************** + * Default values * + ****************************************************************************/ + +#define W9968CF_OVMOD_LOAD 1 /* automatic 'ovcamchip' module loading */ +#define W9968CF_VPPMOD_LOAD 1 /* automatic 'w9968cf-vpp' module loading */ + +/* Comment/uncomment the following line to enable/disable debugging messages */ +#define W9968CF_DEBUG + +/* These have effect only if W9968CF_DEBUG is defined */ +#define W9968CF_DEBUG_LEVEL 2 /* from 0 to 6. 0 for no debug informations */ +#define W9968CF_SPECIFIC_DEBUG 0 /* 0 or 1 */ + +#define W9968CF_MAX_DEVICES 32 +#define W9968CF_SIMCAMS W9968CF_MAX_DEVICES /* simultaneous cameras */ + +#define W9968CF_MAX_BUFFERS 32 +#define W9968CF_BUFFERS 2 /* n. of frame buffers from 2 to MAX_BUFFERS */ + +/* Maximum data payload sizes in bytes for alternate settings */ +static const u16 wMaxPacketSize[] = {1023, 959, 895, 831, 767, 703, 639, 575, + 511, 447, 383, 319, 255, 191, 127, 63}; +#define W9968CF_PACKET_SIZE 1023 /* according to wMaxPacketSizes[] */ +#define W9968CF_MIN_PACKET_SIZE 63 /* minimum value */ +#define W9968CF_ISO_PACKETS 5 /* n.of packets for isochronous transfers */ +#define W9968CF_USB_CTRL_TIMEOUT 1000 /* timeout (ms) for usb control commands */ +#define W9968CF_URBS 2 /* n. of scheduled URBs for ISO transfer */ + +#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */ +#define W9968CF_I2C_RW_RETRIES 15 /* number of max I2C r/w retries */ + +/* Available video formats */ +struct w9968cf_format { + const u16 palette; + const u16 depth; + const u8 compression; +}; + +static const struct w9968cf_format w9968cf_formatlist[] = { + { VIDEO_PALETTE_UYVY, 16, 0 }, /* original video */ + { VIDEO_PALETTE_YUV422P, 16, 1 }, /* with JPEG compression */ + { VIDEO_PALETTE_YUV420P, 12, 1 }, /* with JPEG compression */ + { VIDEO_PALETTE_YUV420, 12, 1 }, /* same as YUV420P */ + { VIDEO_PALETTE_YUYV, 16, 0 }, /* software conversion */ + { VIDEO_PALETTE_YUV422, 16, 0 }, /* software conversion */ + { VIDEO_PALETTE_GREY, 8, 0 }, /* software conversion */ + { VIDEO_PALETTE_RGB555, 16, 0 }, /* software conversion */ + { VIDEO_PALETTE_RGB565, 16, 0 }, /* software conversion */ + { VIDEO_PALETTE_RGB24, 24, 0 }, /* software conversion */ + { VIDEO_PALETTE_RGB32, 32, 0 }, /* software conversion */ + { 0, 0, 0 } /* 0 is a terminating entry */ +}; + +#define W9968CF_DECOMPRESSION 2 /* decomp:0=disable,1=force,2=any formats */ +#define W9968CF_PALETTE_DECOMP_OFF VIDEO_PALETTE_UYVY /* when decomp=0 */ +#define W9968CF_PALETTE_DECOMP_FORCE VIDEO_PALETTE_YUV420P /* when decomp=1 */ +#define W9968CF_PALETTE_DECOMP_ON VIDEO_PALETTE_UYVY /* when decomp=2 */ + +#define W9968CF_FORCE_RGB 0 /* read RGB instead of BGR, yes=1/no=0 */ + +#define W9968CF_MAX_WIDTH 800 /* Has effect if up-scaling is on */ +#define W9968CF_MAX_HEIGHT 600 /* Has effect if up-scaling is on */ +#define W9968CF_WIDTH 320 /* from 128 to 352, multiple of 16 */ +#define W9968CF_HEIGHT 240 /* from 96 to 288, multiple of 16 */ + +#define W9968CF_CLAMPING 0 /* 0 disable, 1 enable video data clamping */ +#define W9968CF_FILTER_TYPE 0 /* 0 disable 1 (1-2-1), 2 (2-3-6-3-2) */ +#define W9968CF_DOUBLE_BUFFER 1 /* 0 disable, 1 enable double buffer */ +#define W9968CF_LARGEVIEW 1 /* 0 disable, 1 enable */ +#define W9968CF_UPSCALING 0 /* 0 disable, 1 enable */ + +#define W9968CF_MONOCHROME 0 /* 0 not monochrome, 1 monochrome sensor */ +#define W9968CF_BRIGHTNESS 31000 /* from 0 to 65535 */ +#define W9968CF_HUE 32768 /* from 0 to 65535 */ +#define W9968CF_COLOUR 32768 /* from 0 to 65535 */ +#define W9968CF_CONTRAST 50000 /* from 0 to 65535 */ +#define W9968CF_WHITENESS 32768 /* from 0 to 65535 */ + +#define W9968CF_AUTOBRIGHT 0 /* 0 disable, 1 enable automatic brightness */ +#define W9968CF_AUTOEXP 1 /* 0 disable, 1 enable automatic exposure */ +#define W9968CF_LIGHTFREQ 50 /* light frequency. 50Hz (Europe) or 60Hz */ +#define W9968CF_BANDINGFILTER 0 /* 0 disable, 1 enable banding filter */ +#define W9968CF_BACKLIGHT 0 /* 0 or 1, 1=object is lit from behind */ +#define W9968CF_MIRROR 0 /* 0 or 1 [don't] reverse image horizontally*/ + +#define W9968CF_CLOCKDIV -1 /* -1 = automatic clock divisor */ +#define W9968CF_DEF_CLOCKDIVISOR 0 /* default sensor clock divisor value */ + + +/**************************************************************************** + * Globals * + ****************************************************************************/ + +#define W9968CF_MODULE_NAME "V4L driver for W996[87]CF JPEG USB " \ + "Dual Mode Camera Chip" +#define W9968CF_MODULE_VERSION "1:1.33-basic" +#define W9968CF_MODULE_AUTHOR "(C) 2002-2004 Luca Risolia" +#define W9968CF_AUTHOR_EMAIL "" +#define W9968CF_MODULE_LICENSE "GPL" + +static const struct usb_device_id winbond_id_table[] = { + { + /* Creative Labs Video Blaster WebCam Go Plus */ + USB_DEVICE(0x041e, 0x4003), + .driver_info = (unsigned long)"w9968cf", + }, + { + /* Generic W996[87]CF JPEG USB Dual Mode Camera */ + USB_DEVICE(0x1046, 0x9967), + .driver_info = (unsigned long)"w9968cf", + }, + { } /* terminating entry */ +}; + +/* W996[87]CF camera models, internal ids: */ +enum w9968cf_model_id { + W9968CF_MOD_GENERIC = 1, /* Generic W996[87]CF based device */ + W9968CF_MOD_CLVBWGP = 11,/*Creative Labs Video Blaster WebCam Go Plus*/ + W9968CF_MOD_ADPVDMA = 21, /* Aroma Digi Pen VGA Dual Mode ADG-5000 */ + W9986CF_MOD_AAU = 31, /* AVerMedia AVerTV USB */ + W9968CF_MOD_CLVBWG = 34, /* Creative Labs Video Blaster WebCam Go */ + W9968CF_MOD_LL = 37, /* Lebon LDC-035A */ + W9968CF_MOD_EEEMC = 40, /* Ezonics EZ-802 EZMega Cam */ + W9968CF_MOD_OOE = 42, /* OmniVision OV8610-EDE */ + W9968CF_MOD_ODPVDMPC = 43,/* OPCOM Digi Pen VGA Dual Mode Pen Camera */ + W9968CF_MOD_PDPII = 46, /* Pretec Digi Pen-II */ + W9968CF_MOD_PDP480 = 49, /* Pretec DigiPen-480 */ +}; + +enum w9968cf_frame_status { + F_READY, /* finished grabbing & ready to be read/synced */ + F_GRABBING, /* in the process of being grabbed into */ + F_ERROR, /* something bad happened while processing */ + F_UNUSED /* unused (no VIDIOCMCAPTURE) */ +}; + +struct w9968cf_frame_t { + void* buffer; + unsigned long size; + u32 length; + int number; + enum w9968cf_frame_status status; + struct w9968cf_frame_t* next; + u8 queued; +}; + +enum w9968cf_vpp_flag { + VPP_NONE = 0x00, + VPP_UPSCALE = 0x01, + VPP_SWAP_YUV_BYTES = 0x02, + VPP_DECOMPRESSION = 0x04, + VPP_UYVY_TO_RGBX = 0x08, +}; + +/* Main device driver structure */ +struct w9968cf_device { + struct device dev; /* device structure */ + + enum w9968cf_model_id id; /* private device identifier */ + + struct video_device* v4ldev; /* -> V4L structure */ + struct list_head v4llist; /* entry of the list of V4L cameras */ + + struct usb_device* usbdev; /* -> main USB structure */ + struct urb* urb[W9968CF_URBS]; /* -> USB request block structs */ + void* transfer_buffer[W9968CF_URBS]; /* -> ISO transfer buffers */ + u16* control_buffer; /* -> buffer for control req.*/ + u16* data_buffer; /* -> data to send to the FSB */ + + struct w9968cf_frame_t frame[W9968CF_MAX_BUFFERS]; + struct w9968cf_frame_t frame_tmp; /* temporary frame */ + struct w9968cf_frame_t frame_vpp; /* helper frame.*/ + struct w9968cf_frame_t* frame_current; /* -> frame being grabbed */ + struct w9968cf_frame_t* requested_frame[W9968CF_MAX_BUFFERS]; + + u8 max_buffers, /* number of requested buffers */ + force_palette, /* yes=1/no=0 */ + force_rgb, /* read RGB instead of BGR, yes=1, no=0 */ + double_buffer, /* hardware double buffering yes=1/no=0 */ + clamping, /* video data clamping yes=1/no=0 */ + filter_type, /* 0=disabled, 1=3 tap, 2=5 tap filter */ + capture, /* 0=disabled, 1=enabled */ + largeview, /* 0=disabled, 1=enabled */ + decompression, /* 0=disabled, 1=forced, 2=allowed */ + upscaling; /* software image scaling, 0=enabled, 1=disabled */ + + struct video_picture picture; /* current picture settings */ + struct video_window window; /* current window settings */ + + u16 hw_depth, /* depth (used by the chip) */ + hw_palette, /* palette (used by the chip) */ + hw_width, /* width (used by the chip) */ + hw_height, /* height (used by the chip) */ + hs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */ + vs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */ + start_cropx, /* pixels from HS inactive edge to 1st cropped pixel*/ + start_cropy; /* pixels from VS inactive edge to 1st cropped pixel*/ + + enum w9968cf_vpp_flag vpp_flag; /* post-processing routines in use */ + + u8 nbuffers, /* number of allocated frame buffers */ + altsetting, /* camera alternate setting */ + disconnected, /* flag: yes=1, no=0 */ + misconfigured, /* flag: yes=1, no=0 */ + users, /* flag: number of users holding the device */ + streaming; /* flag: yes=1, no=0 */ + + u8 sensor_initialized; /* flag: yes=1, no=0 */ + + /* Determined by the image sensor type: */ + int sensor, /* type of image sensor chip (CC_*) */ + monochrome; /* image sensor is (probably) monochrome */ + u16 maxwidth, /* maximum width supported by the image sensor */ + maxheight, /* maximum height supported by the image sensor */ + minwidth, /* minimum width supported by the image sensor */ + minheight; /* minimum height supported by the image sensor */ + u8 auto_brt, /* auto brightness enabled flag */ + auto_exp, /* auto exposure enabled flag */ + backlight, /* backlight exposure algorithm flag */ + mirror, /* image is reversed horizontally */ + lightfreq, /* power (lighting) frequency */ + bandfilt; /* banding filter enabled flag */ + s8 clockdiv; /* clock divisor */ + + /* I2C interface to kernel */ + struct i2c_adapter i2c_adapter; + struct i2c_client* sensor_client; + + /* Locks */ + struct mutex dev_mutex, /* for probe, disconnect,open and close */ + fileop_mutex; /* for read and ioctl */ + spinlock_t urb_lock, /* for submit_urb() and unlink_urb() */ + flist_lock; /* for requested frame list accesses */ + wait_queue_head_t open, wait_queue; + + char command[16]; /* name of the program holding the device */ +}; + + +/**************************************************************************** + * Macros for debugging * + ****************************************************************************/ + +#undef DBG +#undef KDBG +#ifdef W9968CF_DEBUG +/* For device specific debugging messages */ +# define DBG(level, fmt, args...) \ +{ \ + if ( ((specific_debug) && (debug == (level))) || \ + ((!specific_debug) && (debug >= (level))) ) { \ + if ((level) == 1) \ + dev_err(&cam->dev, fmt "\n", ## args); \ + else if ((level) == 2 || (level) == 3) \ + dev_info(&cam->dev, fmt "\n", ## args); \ + else if ((level) == 4) \ + dev_warn(&cam->dev, fmt "\n", ## args); \ + else if ((level) >= 5) \ + dev_info(&cam->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args); \ + } \ +} +/* For generic kernel (not device specific) messages */ +# define KDBG(level, fmt, args...) \ +{ \ + if ( ((specific_debug) && (debug == (level))) || \ + ((!specific_debug) && (debug >= (level))) ) { \ + if ((level) >= 1 && (level) <= 4) \ + pr_info("w9968cf: " fmt "\n", ## args); \ + else if ((level) >= 5) \ + pr_debug("w9968cf: [%s:%d] " fmt "\n", __FUNCTION__, \ + __LINE__ , ## args); \ + } \ +} +#else + /* Not debugging: nothing */ +# define DBG(level, fmt, args...) do {;} while(0); +# define KDBG(level, fmt, args...) do {;} while(0); +#endif + +#undef PDBG +#define PDBG(fmt, args...) \ +dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args); + +#undef PDBGG +#define PDBGG(fmt, args...) do {;} while(0); /* nothing: it's a placeholder */ + +#endif /* _W9968CF_H_ */ diff --git a/linux/drivers/media/video/w9968cf_decoder.h b/linux/drivers/media/video/w9968cf_decoder.h new file mode 100644 index 000000000..59decbfc5 --- /dev/null +++ b/linux/drivers/media/video/w9968cf_decoder.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * Video decoder for the W996[87]CF driver for Linux. * + * * + * Copyright (C) 2003 2004 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _W9968CF_DECODER_H_ +#define _W9968CF_DECODER_H_ + +/* Comment/uncomment this for high/low quality of compressed video */ +#define W9968CF_DEC_FAST_LOWQUALITY_VIDEO + +#ifdef W9968CF_DEC_FAST_LOWQUALITY_VIDEO +static const unsigned char Y_QUANTABLE[64] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 +}; + +static const unsigned char UV_QUANTABLE[64] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; +#else +static const unsigned char Y_QUANTABLE[64] = { + 8, 5, 5, 8, 12, 20, 25, 30, + 6, 6, 7, 9, 13, 29, 30, 27, + 7, 6, 8, 12, 20, 28, 34, 28, + 7, 8, 11, 14, 25, 43, 40, 31, + 9, 11, 18, 28, 34, 54, 51, 38, + 12, 17, 27, 32, 40, 52, 56, 46, + 24, 32, 39, 43, 51, 60, 60, 50, + 36, 46, 47, 49, 56, 50, 51, 49 +}; + +static const unsigned char UV_QUANTABLE[64] = { + 8, 9, 12, 23, 49, 49, 49, 49, + 9, 10, 13, 33, 49, 49, 49, 49, + 12, 13, 28, 49, 49, 49, 49, 49, + 23, 33, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49 +}; +#endif + +#define W9968CF_DEC_ERR_CORRUPTED_DATA -1 +#define W9968CF_DEC_ERR_BUF_OVERFLOW -2 +#define W9968CF_DEC_ERR_NO_SOI -3 +#define W9968CF_DEC_ERR_NO_SOF0 -4 +#define W9968CF_DEC_ERR_NO_SOS -5 +#define W9968CF_DEC_ERR_NO_EOI -6 + +extern void w9968cf_init_decoder(void); +extern int w9968cf_check_headers(const unsigned char* Pin, + const unsigned long BUF_SIZE); +extern int w9968cf_decode(const char* Pin, const unsigned long BUF_SIZE, + const unsigned W, const unsigned H, char* Pout); + +#endif /* _W9968CF_DECODER_H_ */ diff --git a/linux/drivers/media/video/w9968cf_vpp.h b/linux/drivers/media/video/w9968cf_vpp.h new file mode 100644 index 000000000..88c9b6c0c --- /dev/null +++ b/linux/drivers/media/video/w9968cf_vpp.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Interface for video post-processing functions for the W996[87]CF driver * + * for Linux. * + * * + * Copyright (C) 2002-2004 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _W9968CF_VPP_H_ +#define _W9968CF_VPP_H_ + +#include +#include + +struct w9968cf_vpp_t { + struct module* owner; + int (*check_headers)(const unsigned char*, const unsigned long); + int (*decode)(const char*, const unsigned long, const unsigned, + const unsigned, char*); + void (*swap_yuvbytes)(void*, unsigned long); + void (*uyvy_to_rgbx)(u8*, unsigned long, u8*, u16, u8); + void (*scale_up)(u8*, u8*, u16, u16, u16, u16, u16); + + u8 busy; /* read-only flag: module is/is not in use */ +}; + +#endif /* _W9968CF_VPP_H_ */ diff --git a/linux/drivers/media/video/zoran.h b/linux/drivers/media/video/zoran.h new file mode 100644 index 000000000..0166f555a --- /dev/null +++ b/linux/drivers/media/video/zoran.h @@ -0,0 +1,515 @@ +/* + * zoran - Iomega Buz driver + * + * Copyright (C) 1999 Rainer Johanni + * + * based on + * + * zoran.0.0.3 Copyright (C) 1998 Dave Perks + * + * and + * + * bttv - Bt848 frame grabber driver + * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) + * & Marcus Metzler (mocm@thp.uni-koeln.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _BUZ_H_ +#define _BUZ_H_ + +struct zoran_requestbuffers { + unsigned long count; /* Number of buffers for MJPEG grabbing */ + unsigned long size; /* Size PER BUFFER in bytes */ +}; + +struct zoran_sync { + unsigned long frame; /* number of buffer that has been free'd */ + unsigned long length; /* number of code bytes in buffer (capture only) */ + unsigned long seq; /* frame sequence number */ + struct timeval timestamp; /* timestamp */ +}; + +struct zoran_status { + int input; /* Input channel, has to be set prior to BUZIOC_G_STATUS */ + int signal; /* Returned: 1 if valid video signal detected */ + int norm; /* Returned: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */ + int color; /* Returned: 1 if color signal detected */ +}; + +struct zoran_params { + + /* The following parameters can only be queried */ + + int major_version; /* Major version number of driver */ + int minor_version; /* Minor version number of driver */ + + /* Main control parameters */ + + int input; /* Input channel: 0 = Composite, 1 = S-VHS */ + int norm; /* Norm: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */ + int decimation; /* decimation of captured video, + * enlargement of video played back. + * Valid values are 1, 2, 4 or 0. + * 0 is a special value where the user + * has full control over video scaling */ + + /* The following parameters only have to be set if decimation==0, + * for other values of decimation they provide the data how the image is captured */ + + int HorDcm; /* Horizontal decimation: 1, 2 or 4 */ + int VerDcm; /* Vertical decimation: 1 or 2 */ + int TmpDcm; /* Temporal decimation: 1 or 2, + * if TmpDcm==2 in capture every second frame is dropped, + * in playback every frame is played twice */ + int field_per_buff; /* Number of fields per buffer: 1 or 2 */ + int img_x; /* start of image in x direction */ + int img_y; /* start of image in y direction */ + int img_width; /* image width BEFORE decimation, + * must be a multiple of HorDcm*16 */ + int img_height; /* image height BEFORE decimation, + * must be a multiple of VerDcm*8 */ + + /* --- End of parameters for decimation==0 only --- */ + + /* JPEG control parameters */ + + int quality; /* Measure for quality of compressed images. + * Scales linearly with the size of the compressed images. + * Must be beetween 0 and 100, 100 is a compression + * ratio of 1:4 */ + + int odd_even; /* Which field should come first ??? */ + + int APPn; /* Number of APP segment to be written, must be 0..15 */ + int APP_len; /* Length of data in JPEG APPn segment */ + char APP_data[60]; /* Data in the JPEG APPn segment. */ + + int COM_len; /* Length of data in JPEG COM segment */ + char COM_data[60]; /* Data in JPEG COM segment */ + + unsigned long jpeg_markers; /* Which markers should go into the JPEG output. + * Unless you exactly know what you do, leave them untouched. + * Inluding less markers will make the resulting code + * smaller, but there will be fewer aplications + * which can read it. + * The presence of the APP and COM marker is + * influenced by APP0_len and COM_len ONLY! */ +#define JPEG_MARKER_DHT (1<<3) /* Define Huffman Tables */ +#define JPEG_MARKER_DQT (1<<4) /* Define Quantization Tables */ +#define JPEG_MARKER_DRI (1<<5) /* Define Restart Interval */ +#define JPEG_MARKER_COM (1<<6) /* Comment segment */ +#define JPEG_MARKER_APP (1<<7) /* App segment, driver will allways use APP0 */ + + int VFIFO_FB; /* Flag for enabling Video Fifo Feedback. + * If this flag is turned on and JPEG decompressing + * is going to the screen, the decompress process + * is stopped every time the Video Fifo is full. + * This enables a smooth decompress to the screen + * but the video output signal will get scrambled */ + + /* Misc */ + + char reserved[312]; /* Makes 512 bytes for this structure */ +}; + +/* +Private IOCTL to set up for displaying MJPEG +*/ +#define BUZIOC_G_PARAMS _IOR ('v', BASE_VIDIOCPRIVATE+0, struct zoran_params) +#define BUZIOC_S_PARAMS _IOWR('v', BASE_VIDIOCPRIVATE+1, struct zoran_params) +#define BUZIOC_REQBUFS _IOWR('v', BASE_VIDIOCPRIVATE+2, struct zoran_requestbuffers) +#define BUZIOC_QBUF_CAPT _IOW ('v', BASE_VIDIOCPRIVATE+3, int) +#define BUZIOC_QBUF_PLAY _IOW ('v', BASE_VIDIOCPRIVATE+4, int) +#define BUZIOC_SYNC _IOR ('v', BASE_VIDIOCPRIVATE+5, struct zoran_sync) +#define BUZIOC_G_STATUS _IOWR('v', BASE_VIDIOCPRIVATE+6, struct zoran_status) + + +#ifdef __KERNEL__ + +#define MAJOR_VERSION 0 /* driver major version */ +#define MINOR_VERSION 9 /* driver minor version */ +#define RELEASE_VERSION 5 /* release version */ + +#define ZORAN_NAME "ZORAN" /* name of the device */ + +#define ZR_DEVNAME(zr) ((zr)->name) + +#define BUZ_MAX_WIDTH (zr->timing->Wa) +#define BUZ_MAX_HEIGHT (zr->timing->Ha) +#define BUZ_MIN_WIDTH 32 /* never display less than 32 pixels */ +#define BUZ_MIN_HEIGHT 24 /* never display less than 24 rows */ + +#define BUZ_NUM_STAT_COM 4 +#define BUZ_MASK_STAT_COM 3 + +#define BUZ_MAX_FRAME 256 /* Must be a power of 2 */ +#define BUZ_MASK_FRAME 255 /* Must be BUZ_MAX_FRAME-1 */ + +#define BUZ_MAX_INPUT 8 + +#if VIDEO_MAX_FRAME <= 32 +# define V4L_MAX_FRAME 32 +#elif VIDEO_MAX_FRAME <= 64 +# define V4L_MAX_FRAME 64 +#else +# error "Too many video frame buffers to handle" +#endif +#define V4L_MASK_FRAME (V4L_MAX_FRAME - 1) + +#define MAX_KMALLOC_MEM (128*1024) + +#include "zr36057.h" + +enum card_type { + UNKNOWN = -1, + + /* Pinnacle/Miro */ + DC10_old, /* DC30 like */ + DC10_new, /* DC10plus like */ + DC10plus, + DC30, + DC30plus, + + /* Linux Media Labs */ + LML33, + LML33R10, + + /* Iomega */ + BUZ, + + /* total number of cards */ + NUM_CARDS +}; + +enum zoran_codec_mode { + BUZ_MODE_IDLE, /* nothing going on */ + BUZ_MODE_MOTION_COMPRESS, /* grabbing frames */ + BUZ_MODE_MOTION_DECOMPRESS, /* playing frames */ + BUZ_MODE_STILL_COMPRESS, /* still frame conversion */ + BUZ_MODE_STILL_DECOMPRESS /* still frame conversion */ +}; + +enum zoran_buffer_state { + BUZ_STATE_USER, /* buffer is owned by application */ + BUZ_STATE_PEND, /* buffer is queued in pend[] ready to feed to I/O */ + BUZ_STATE_DMA, /* buffer is queued in dma[] for I/O */ + BUZ_STATE_DONE /* buffer is ready to return to application */ +}; + +enum zoran_map_mode { + ZORAN_MAP_MODE_RAW, + ZORAN_MAP_MODE_JPG_REC, +#define ZORAN_MAP_MODE_JPG ZORAN_MAP_MODE_JPG_REC + ZORAN_MAP_MODE_JPG_PLAY, +}; + +enum gpio_type { + GPIO_JPEG_SLEEP = 0, + GPIO_JPEG_RESET, + GPIO_JPEG_FRAME, + GPIO_VID_DIR, + GPIO_VID_EN, + GPIO_VID_RESET, + GPIO_CLK_SEL1, + GPIO_CLK_SEL2, + GPIO_MAX, +}; + +enum gpcs_type { + GPCS_JPEG_RESET = 0, + GPCS_JPEG_START, + GPCS_MAX, +}; + +struct zoran_format { + char *name; + int palette; + __u32 fourcc; + int colorspace; + int depth; + __u32 flags; +}; +/* flags */ +#define ZORAN_FORMAT_COMPRESSED 1<<0 +#define ZORAN_FORMAT_OVERLAY 1<<1 +#define ZORAN_FORMAT_CAPTURE 1<<2 +#define ZORAN_FORMAT_PLAYBACK 1<<3 + +/* overlay-settings */ +struct zoran_overlay_settings { + int is_set; + int x, y, width, height; /* position */ + int clipcount; /* position and number of clips */ + const struct zoran_format *format; /* overlay format */ +}; + +/* v4l-capture settings */ +struct zoran_v4l_settings { + int width, height, bytesperline; /* capture size */ + const struct zoran_format *format; /* capture format */ +}; + +/* whoops, this one is undeclared if !v4l2 */ +#ifndef HAVE_V4L2 +struct v4l2_jpegcompression { + int quality; + int APPn; + int APP_len; + char APP_data[60]; + int COM_len; + char COM_data[60]; + __u32 jpeg_markers; + __u8 reserved[116]; +}; +#endif + +/* jpg-capture/-playback settings */ +struct zoran_jpg_settings { + int decimation; /* this bit is used to set everything to default */ + int HorDcm, VerDcm, TmpDcm; /* capture decimation settings (TmpDcm=1 means both fields) */ + int field_per_buff, odd_even; /* field-settings (odd_even=1 (+TmpDcm=1) means top-field-first) */ + int img_x, img_y, img_width, img_height; /* crop settings (subframe capture) */ + struct v4l2_jpegcompression jpg_comp; /* JPEG-specific capture settings */ +}; + +struct zoran_mapping { + struct file *file; + int count; +}; + +struct zoran_jpg_buffer { + struct zoran_mapping *map; + u32 *frag_tab; /* addresses of frag table */ + u32 frag_tab_bus; /* same value cached to save time in ISR */ + enum zoran_buffer_state state; /* non-zero if corresponding buffer is in use in grab queue */ + struct zoran_sync bs; /* DONE: info to return to application */ +}; + +struct zoran_v4l_buffer { + struct zoran_mapping *map; + char *fbuffer; /* virtual address of frame buffer */ + unsigned long fbuffer_phys; /* physical address of frame buffer */ + unsigned long fbuffer_bus; /* bus address of frame buffer */ + enum zoran_buffer_state state; /* state: unused/pending/done */ + struct zoran_sync bs; /* DONE: info to return to application */ +}; + +enum zoran_lock_activity { + ZORAN_FREE, /* free for use */ + ZORAN_ACTIVE, /* active but unlocked */ + ZORAN_LOCKED, /* locked */ +}; + +/* buffer collections */ +struct zoran_jpg_struct { + enum zoran_lock_activity active; /* feature currently in use? */ + struct zoran_jpg_buffer buffer[BUZ_MAX_FRAME]; /* buffers */ + int num_buffers, buffer_size; + u8 allocated; /* Flag if buffers are allocated */ + u8 ready_to_be_freed; /* hack - see zoran_driver.c */ + u8 need_contiguous; /* Flag if contiguous buffers are needed */ +}; + +struct zoran_v4l_struct { + enum zoran_lock_activity active; /* feature currently in use? */ + struct zoran_v4l_buffer buffer[VIDEO_MAX_FRAME]; /* buffers */ + int num_buffers, buffer_size; + u8 allocated; /* Flag if buffers are allocated */ + u8 ready_to_be_freed; /* hack - see zoran_driver.c */ +}; + +struct zoran; + +/* zoran_fh contains per-open() settings */ +struct zoran_fh { + struct zoran *zr; + + enum zoran_map_mode map_mode; /* Flag which bufferset will map by next mmap() */ + + struct zoran_overlay_settings overlay_settings; + u32 *overlay_mask; /* overlay mask */ + enum zoran_lock_activity overlay_active; /* feature currently in use? */ + + struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */ + struct zoran_v4l_struct v4l_buffers; /* V4L buffers' info */ + + struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */ + struct zoran_jpg_struct jpg_buffers; /* MJPEG buffers' info */ +}; + +struct card_info { + enum card_type type; + char name[32]; + u16 i2c_decoder, i2c_encoder; /* I2C types */ + u16 video_vfe, video_codec; /* videocodec types */ + u16 audio_chip; /* audio type */ + u16 vendor_id, device_id; /* subsystem vendor/device ID */ + + int inputs; /* number of video inputs */ + struct input { + int muxsel; + char name[32]; + } input[BUZ_MAX_INPUT]; + + int norms; + struct tvnorm *tvn[3]; /* supported TV norms */ + + u32 jpeg_int; /* JPEG interrupt */ + u32 vsync_int; /* VSYNC interrupt */ + s8 gpio[GPIO_MAX]; + u8 gpcs[GPCS_MAX]; + + struct vfe_polarity vfe_pol; + u8 gpio_pol[GPIO_MAX]; + + /* is the /GWS line conected? */ + u8 gws_not_connected; + + void (*init) (struct zoran * zr); +}; + +struct zoran { + struct video_device *video_dev; + + struct i2c_adapter i2c_adapter; /* */ + struct i2c_algo_bit_data i2c_algo; /* */ + u32 i2cbr; + + struct i2c_client *decoder; /* video decoder i2c client */ + struct i2c_client *encoder; /* video encoder i2c client */ + + struct videocodec *codec; /* video codec */ + struct videocodec *vfe; /* video front end */ + + struct mutex resource_lock; /* prevent evil stuff */ + + u8 initialized; /* flag if zoran has been correctly initalized */ + int user; /* number of current users */ + struct card_info card; + struct tvnorm *timing; + + unsigned short id; /* number of this device */ + char name[32]; /* name of this device */ + struct pci_dev *pci_dev; /* PCI device */ + unsigned char revision; /* revision of zr36057 */ + unsigned int zr36057_adr; /* bus address of IO mem returned by PCI BIOS */ + unsigned char __iomem *zr36057_mem;/* pointer to mapped IO memory */ + + spinlock_t spinlock; /* Spinlock */ + + /* Video for Linux parameters */ + int input, norm; /* card's norm and input - norm=VIDEO_MODE_* */ + int hue, saturation, contrast, brightness; /* Current picture params */ + struct video_buffer buffer; /* Current buffer params */ + struct zoran_overlay_settings overlay_settings; + u32 *overlay_mask; /* overlay mask */ + enum zoran_lock_activity overlay_active; /* feature currently in use? */ + + wait_queue_head_t v4l_capq; + + int v4l_overlay_active; /* Overlay grab is activated */ + int v4l_memgrab_active; /* Memory grab is activated */ + + int v4l_grab_frame; /* Frame number being currently grabbed */ +#define NO_GRAB_ACTIVE (-1) + unsigned long v4l_grab_seq; /* Number of frames grabbed */ + struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */ + + /* V4L grab queue of frames pending */ + unsigned long v4l_pend_head; + unsigned long v4l_pend_tail; + unsigned long v4l_sync_tail; + int v4l_pend[V4L_MAX_FRAME]; + struct zoran_v4l_struct v4l_buffers; /* V4L buffers' info */ + + /* Buz MJPEG parameters */ + enum zoran_codec_mode codec_mode; /* status of codec */ + struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */ + + wait_queue_head_t jpg_capq; /* wait here for grab to finish */ + + /* grab queue counts/indices, mask with BUZ_MASK_STAT_COM before using as index */ + /* (dma_head - dma_tail) is number active in DMA, must be <= BUZ_NUM_STAT_COM */ + /* (value & BUZ_MASK_STAT_COM) corresponds to index in stat_com table */ + unsigned long jpg_que_head; /* Index where to put next buffer which is queued */ + unsigned long jpg_dma_head; /* Index of next buffer which goes into stat_com */ + unsigned long jpg_dma_tail; /* Index of last buffer in stat_com */ + unsigned long jpg_que_tail; /* Index of last buffer in queue */ + unsigned long jpg_seq_num; /* count of frames since grab/play started */ + unsigned long jpg_err_seq; /* last seq_num before error */ + unsigned long jpg_err_shift; + unsigned long jpg_queued_num; /* count of frames queued since grab/play started */ + + /* zr36057's code buffer table */ + u32 *stat_com; /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */ + + /* (value & BUZ_MASK_FRAME) corresponds to index in pend[] queue */ + int jpg_pend[BUZ_MAX_FRAME]; + + /* array indexed by frame number */ + struct zoran_jpg_struct jpg_buffers; /* MJPEG buffers' info */ + + /* Additional stuff for testing */ +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *zoran_proc; +#else + void *zoran_proc; +#endif + int testing; + int jpeg_error; + int intr_counter_GIRQ1; + int intr_counter_GIRQ0; + int intr_counter_CodRepIRQ; + int intr_counter_JPEGRepIRQ; + int field_counter; + int IRQ1_in; + int IRQ1_out; + int JPEG_in; + int JPEG_out; + int JPEG_0; + int JPEG_1; + int END_event_missed; + int JPEG_missed; + int JPEG_error; + int num_errors; + int JPEG_max_missed; + int JPEG_min_missed; + + u32 last_isr; + unsigned long frame_num; + + wait_queue_head_t test_q; +}; + +/*The following should be done in more portable way. It depends on define + of _ALPHA_BUZ in the Makefile.*/ + +#ifdef _ALPHA_BUZ +#define btwrite(dat,adr) writel((dat), zr->zr36057_adr+(adr)) +#define btread(adr) readl(zr->zr36057_adr+(adr)) +#else +#define btwrite(dat,adr) writel((dat), zr->zr36057_mem+(adr)) +#define btread(adr) readl(zr->zr36057_mem+(adr)) +#endif + +#define btand(dat,adr) btwrite((dat) & btread(adr), adr) +#define btor(dat,adr) btwrite((dat) | btread(adr), adr) +#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) + +#endif /* __kernel__ */ + +#endif diff --git a/linux/drivers/media/video/zoran_card.c b/linux/drivers/media/video/zoran_card.c new file mode 100644 index 000000000..54c8b2a2c --- /dev/null +++ b/linux/drivers/media/video/zoran_card.c @@ -0,0 +1,1578 @@ +/* + * Zoran zr36057/zr36067 PCI controller driver, for the + * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux + * Media Labs LML33/LML33R10. + * + * This part handles card-specific data and detection + * + * Copyright (C) 2000 Serguei Miridonov + * + * Currently maintained by: + * Ronald Bultje + * Laurent Pinchart + * Mailinglist + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "compat.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "videocodec.h" +#include "zoran.h" +#include "zoran_card.h" +#include "zoran_device.h" +#include "zoran_procfs.h" + +#define I2C_NAME(x) (x)->name + +extern const struct zoran_format zoran_formats[]; + +static int card[BUZ_MAX] = { -1, -1, -1, -1 }; +module_param_array(card, int, NULL, 0); +MODULE_PARM_DESC(card, "The type of card"); + +static int encoder[BUZ_MAX] = { -1, -1, -1, -1 }; +module_param_array(encoder, int, NULL, 0); +MODULE_PARM_DESC(encoder, "i2c TV encoder"); + +static int decoder[BUZ_MAX] = { -1, -1, -1, -1 }; +module_param_array(decoder, int, NULL, 0); +MODULE_PARM_DESC(decoder, "i2c TV decoder"); + +/* + The video mem address of the video card. + The driver has a little database for some videocards + to determine it from there. If your video card is not in there + you have either to give it to the driver as a parameter + or set in in a VIDIOCSFBUF ioctl + */ + +static unsigned long vidmem = 0; /* Video memory base address */ +module_param(vidmem, ulong, 0); + +/* + Default input and video norm at startup of the driver. +*/ + +static int default_input = 0; /* 0=Composite, 1=S-Video */ +module_param(default_input, int, 0); +MODULE_PARM_DESC(default_input, + "Default input (0=Composite, 1=S-Video, 2=Internal)"); + +static int default_norm = 0; /* 0=PAL, 1=NTSC 2=SECAM */ +module_param(default_norm, int, 0); +MODULE_PARM_DESC(default_norm, "Default norm (0=PAL, 1=NTSC, 2=SECAM)"); + +static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ +module_param(video_nr, int, 0); +MODULE_PARM_DESC(video_nr, "video device number"); + +/* + Number and size of grab buffers for Video 4 Linux + The vast majority of applications should not need more than 2, + the very popular BTTV driver actually does ONLY have 2. + Time sensitive applications might need more, the maximum + is VIDEO_MAX_FRAME (defined in ). + + The size is set so that the maximum possible request + can be satisfied. Decrease it, if bigphys_area alloc'd + memory is low. If you don't have the bigphys_area patch, + set it to 128 KB. Will you allow only to grab small + images with V4L, but that's better than nothing. + + v4l_bufsize has to be given in KB ! + +*/ + +int v4l_nbufs = 2; +int v4l_bufsize = 128; /* Everybody should be able to work with this setting */ +module_param(v4l_nbufs, int, 0); +MODULE_PARM_DESC(v4l_nbufs, "Maximum number of V4L buffers to use"); +module_param(v4l_bufsize, int, 0); +MODULE_PARM_DESC(v4l_bufsize, "Maximum size per V4L buffer (in kB)"); + +int jpg_nbufs = 32; +int jpg_bufsize = 512; /* max size for 100% quality full-PAL frame */ +module_param(jpg_nbufs, int, 0); +MODULE_PARM_DESC(jpg_nbufs, "Maximum number of JPG buffers to use"); +module_param(jpg_bufsize, int, 0); +MODULE_PARM_DESC(jpg_bufsize, "Maximum size per JPG buffer (in kB)"); + +int pass_through = 0; /* 1=Pass through TV signal when device is not used */ + /* 0=Show color bar when device is not used (LML33: only if lml33dpath=1) */ +module_param(pass_through, int, 0); +MODULE_PARM_DESC(pass_through, + "Pass TV signal through to TV-out when idling"); + +static int debug = 1; +int *zr_debug = &debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-4)"); + +MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver"); +MODULE_AUTHOR("Serguei Miridonov"); +MODULE_LICENSE("GPL"); + +static struct pci_device_id zr36067_pci_tbl[] = { + {PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36057, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0} +}; +MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl); + +#define dprintk(num, format, args...) \ + do { \ + if (*zr_debug >= num) \ + printk(format, ##args); \ + } while (0) + +int zoran_num; /* number of Buzs in use */ +struct zoran zoran[BUZ_MAX]; + +/* videocodec bus functions ZR36060 */ +static u32 +zr36060_read (struct videocodec *codec, + u16 reg) +{ + struct zoran *zr = (struct zoran *) codec->master_data->data; + __u32 data; + + if (post_office_wait(zr) + || post_office_write(zr, 0, 1, reg >> 8) + || post_office_write(zr, 0, 2, reg & 0xff)) { + return -1; + } + + data = post_office_read(zr, 0, 3) & 0xff; + return data; +} + +static void +zr36060_write (struct videocodec *codec, + u16 reg, + u32 val) +{ + struct zoran *zr = (struct zoran *) codec->master_data->data; + + if (post_office_wait(zr) + || post_office_write(zr, 0, 1, reg >> 8) + || post_office_write(zr, 0, 2, reg & 0xff)) { + return; + } + + post_office_write(zr, 0, 3, val & 0xff); +} + +/* videocodec bus functions ZR36050 */ +static u32 +zr36050_read (struct videocodec *codec, + u16 reg) +{ + struct zoran *zr = (struct zoran *) codec->master_data->data; + __u32 data; + + if (post_office_wait(zr) + || post_office_write(zr, 1, 0, reg >> 2)) { // reg. HIGHBYTES + return -1; + } + + data = post_office_read(zr, 0, reg & 0x03) & 0xff; // reg. LOWBYTES + read + return data; +} + +static void +zr36050_write (struct videocodec *codec, + u16 reg, + u32 val) +{ + struct zoran *zr = (struct zoran *) codec->master_data->data; + + if (post_office_wait(zr) + || post_office_write(zr, 1, 0, reg >> 2)) { // reg. HIGHBYTES + return; + } + + post_office_write(zr, 0, reg & 0x03, val & 0xff); // reg. LOWBYTES + wr. data +} + +/* videocodec bus functions ZR36016 */ +static u32 +zr36016_read (struct videocodec *codec, + u16 reg) +{ + struct zoran *zr = (struct zoran *) codec->master_data->data; + __u32 data; + + if (post_office_wait(zr)) { + return -1; + } + + data = post_office_read(zr, 2, reg & 0x03) & 0xff; // read + return data; +} + +/* hack for in zoran_device.c */ +void +zr36016_write (struct videocodec *codec, + u16 reg, + u32 val) +{ + struct zoran *zr = (struct zoran *) codec->master_data->data; + + if (post_office_wait(zr)) { + return; + } + + post_office_write(zr, 2, reg & 0x03, val & 0x0ff); // wr. data +} + +/* + * Board specific information + */ + +static void +dc10_init (struct zoran *zr) +{ + dprintk(3, KERN_DEBUG "%s: dc10_init()\n", ZR_DEVNAME(zr)); + + /* Pixel clock selection */ + GPIO(zr, 4, 0); + GPIO(zr, 5, 1); + /* Enable the video bus sync signals */ + GPIO(zr, 7, 0); +} + +static void +dc10plus_init (struct zoran *zr) +{ + dprintk(3, KERN_DEBUG "%s: dc10plus_init()\n", ZR_DEVNAME(zr)); +} + +static void +buz_init (struct zoran *zr) +{ + dprintk(3, KERN_DEBUG "%s: buz_init()\n", ZR_DEVNAME(zr)); + + /* some stuff from Iomega */ + pci_write_config_dword(zr->pci_dev, 0xfc, 0x90680f15); + pci_write_config_dword(zr->pci_dev, 0x0c, 0x00012020); + pci_write_config_dword(zr->pci_dev, 0xe8, 0xc0200000); +} + +static void +lml33_init (struct zoran *zr) +{ + dprintk(3, KERN_DEBUG "%s: lml33_init()\n", ZR_DEVNAME(zr)); + + GPIO(zr, 2, 1); // Set Composite input/output +} + +static char * +i2cid_to_modulename (u16 i2c_id) +{ + char *name = NULL; + + switch (i2c_id) { + case I2C_DRIVERID_SAA7110: + name = "saa7110"; + break; + case I2C_DRIVERID_SAA7111A: + name = "saa7111"; + break; + case I2C_DRIVERID_SAA7114: + name = "saa7114"; + break; + case I2C_DRIVERID_SAA7185B: + name = "saa7185"; + break; + case I2C_DRIVERID_ADV7170: + name = "adv7170"; + break; + case I2C_DRIVERID_ADV7175: + name = "adv7175"; + break; + case I2C_DRIVERID_BT819: + name = "bt819"; + break; + case I2C_DRIVERID_BT856: + name = "bt856"; + break; + case I2C_DRIVERID_VPX3220: + name = "vpx3220"; + break; +/* case I2C_DRIVERID_VPX3224: + name = "vpx3224"; + break; + case I2C_DRIVERID_MSE3000: + name = "mse3000"; + break;*/ + default: + break; + } + + return name; +} + +static char * +codecid_to_modulename (u16 codecid) +{ + char *name = NULL; + + switch (codecid) { + case CODEC_TYPE_ZR36060: + name = "zr36060"; + break; + case CODEC_TYPE_ZR36050: + name = "zr36050"; + break; + case CODEC_TYPE_ZR36016: + name = "zr36016"; + break; + default: + break; + } + + return name; +} + +// struct tvnorm { +// u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart; +// }; + +static struct tvnorm f50sqpixel = { 944, 768, 83, 880, 625, 576, 16 }; +static struct tvnorm f60sqpixel = { 780, 640, 51, 716, 525, 480, 12 }; +static struct tvnorm f50ccir601 = { 864, 720, 75, 804, 625, 576, 18 }; +static struct tvnorm f60ccir601 = { 858, 720, 57, 788, 525, 480, 16 }; + +static struct tvnorm f50ccir601_lml33 = { 864, 720, 75+34, 804, 625, 576, 18 }; +static struct tvnorm f60ccir601_lml33 = { 858, 720, 57+34, 788, 525, 480, 16 }; + +/* The DC10 (57/16/50) uses VActive as HSync, so HStart must be 0 */ +static struct tvnorm f50sqpixel_dc10 = { 944, 768, 0, 880, 625, 576, 0 }; +static struct tvnorm f60sqpixel_dc10 = { 780, 640, 0, 716, 525, 480, 12 }; + +/* FIXME: I cannot swap U and V in saa7114, so i do one + * pixel left shift in zoran (75 -> 74) + * (Maxim Yevtyushkin ) */ +static struct tvnorm f50ccir601_lm33r10 = { 864, 720, 74+54, 804, 625, 576, 18 }; +static struct tvnorm f60ccir601_lm33r10 = { 858, 720, 56+54, 788, 525, 480, 16 }; + +static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { + { + .type = DC10_old, + .name = "DC10(old)", + .i2c_decoder = I2C_DRIVERID_VPX3220, + /*.i2c_encoder = I2C_DRIVERID_MSE3000,*/ + .video_codec = CODEC_TYPE_ZR36050, + .video_vfe = CODEC_TYPE_ZR36016, + + .inputs = 3, + .input = { + { 1, "Composite" }, + { 2, "S-Video" }, + { 0, "Internal/comp" } + }, + .norms = 3, + .tvn = { + &f50sqpixel_dc10, + &f60sqpixel_dc10, + &f50sqpixel_dc10 + }, + .jpeg_int = 0, + .vsync_int = ZR36057_ISR_GIRQ1, + .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 }, + .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 }, + .gpcs = { -1, 0 }, + .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, + .gws_not_connected = 0, + .init = &dc10_init, + }, { + .type = DC10_new, + .name = "DC10(new)", + .i2c_decoder = I2C_DRIVERID_SAA7110, + .i2c_encoder = I2C_DRIVERID_ADV7175, + .video_codec = CODEC_TYPE_ZR36060, + + .inputs = 3, + .input = { + { 0, "Composite" }, + { 7, "S-Video" }, + { 5, "Internal/comp" } + }, + .norms = 3, + .tvn = { + &f50sqpixel, + &f60sqpixel, + &f50sqpixel}, + .jpeg_int = ZR36057_ISR_GIRQ0, + .vsync_int = ZR36057_ISR_GIRQ1, + .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 }, + .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, + .gpcs = { -1, 1}, + .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 }, + .gws_not_connected = 0, + .init = &dc10plus_init, + }, { + .type = DC10plus, + .name = "DC10plus", + .vendor_id = PCI_VENDOR_ID_MIRO, + .device_id = PCI_DEVICE_ID_MIRO_DC10PLUS, + .i2c_decoder = I2C_DRIVERID_SAA7110, + .i2c_encoder = I2C_DRIVERID_ADV7175, + .video_codec = CODEC_TYPE_ZR36060, + + .inputs = 3, + .input = { + { 0, "Composite" }, + { 7, "S-Video" }, + { 5, "Internal/comp" } + }, + .norms = 3, + .tvn = { + &f50sqpixel, + &f60sqpixel, + &f50sqpixel + }, + .jpeg_int = ZR36057_ISR_GIRQ0, + .vsync_int = ZR36057_ISR_GIRQ1, + .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 }, + .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, + .gpcs = { -1, 1 }, + .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 }, + .gws_not_connected = 0, + .init = &dc10plus_init, + }, { + .type = DC30, + .name = "DC30", + .i2c_decoder = I2C_DRIVERID_VPX3220, + .i2c_encoder = I2C_DRIVERID_ADV7175, + .video_codec = CODEC_TYPE_ZR36050, + .video_vfe = CODEC_TYPE_ZR36016, + + .inputs = 3, + .input = { + { 1, "Composite" }, + { 2, "S-Video" }, + { 0, "Internal/comp" } + }, + .norms = 3, + .tvn = { + &f50sqpixel_dc10, + &f60sqpixel_dc10, + &f50sqpixel_dc10 + }, + .jpeg_int = 0, + .vsync_int = ZR36057_ISR_GIRQ1, + .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 }, + .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 }, + .gpcs = { -1, 0 }, + .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, + .gws_not_connected = 0, + .init = &dc10_init, + }, { + .type = DC30plus, + .name = "DC30plus", + .vendor_id = PCI_VENDOR_ID_MIRO, + .device_id = PCI_DEVICE_ID_MIRO_DC30PLUS, + .i2c_decoder = I2C_DRIVERID_VPX3220, + .i2c_encoder = I2C_DRIVERID_ADV7175, + .video_codec = CODEC_TYPE_ZR36050, + .video_vfe = CODEC_TYPE_ZR36016, + + .inputs = 3, + .input = { + { 1, "Composite" }, + { 2, "S-Video" }, + { 0, "Internal/comp" } + }, + .norms = 3, + .tvn = { + &f50sqpixel_dc10, + &f60sqpixel_dc10, + &f50sqpixel_dc10 + }, + .jpeg_int = 0, + .vsync_int = ZR36057_ISR_GIRQ1, + .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 }, + .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 }, + .gpcs = { -1, 0 }, + .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, + .gws_not_connected = 0, + .init = &dc10_init, + }, { + .type = LML33, + .name = "LML33", + .i2c_decoder = I2C_DRIVERID_BT819, + .i2c_encoder = I2C_DRIVERID_BT856, + .video_codec = CODEC_TYPE_ZR36060, + + .inputs = 2, + .input = { + { 0, "Composite" }, + { 7, "S-Video" } + }, + .norms = 2, + .tvn = { + &f50ccir601_lml33, + &f60ccir601_lml33, + NULL + }, + .jpeg_int = ZR36057_ISR_GIRQ1, + .vsync_int = ZR36057_ISR_GIRQ0, + .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 }, + .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 }, + .gpcs = { 3, 1 }, + .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, + .gws_not_connected = 1, + .init = &lml33_init, + }, { + .type = LML33R10, + .name = "LML33R10", + .vendor_id = PCI_VENDOR_ID_ELECTRONICDESIGNGMBH, + .device_id = PCI_DEVICE_ID_LML_33R10, + .i2c_decoder = I2C_DRIVERID_SAA7114, + .i2c_encoder = I2C_DRIVERID_ADV7170, + .video_codec = CODEC_TYPE_ZR36060, + + .inputs = 2, + .input = { + { 0, "Composite" }, + { 7, "S-Video" } + }, + .norms = 2, + .tvn = { + &f50ccir601_lm33r10, + &f60ccir601_lm33r10, + NULL + }, + .jpeg_int = ZR36057_ISR_GIRQ1, + .vsync_int = ZR36057_ISR_GIRQ0, + .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 }, + .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 }, + .gpcs = { 3, 1 }, + .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, + .gws_not_connected = 1, + .init = &lml33_init, + }, { + .type = BUZ, + .name = "Buz", + .vendor_id = PCI_VENDOR_ID_IOMEGA, + .device_id = PCI_DEVICE_ID_IOMEGA_BUZ, + .i2c_decoder = I2C_DRIVERID_SAA7111A, + .i2c_encoder = I2C_DRIVERID_SAA7185B, + .video_codec = CODEC_TYPE_ZR36060, + + .inputs = 2, + .input = { + { 3, "Composite" }, + { 7, "S-Video" } + }, + .norms = 3, + .tvn = { + &f50ccir601, + &f60ccir601, + &f50ccir601 + }, + .jpeg_int = ZR36057_ISR_GIRQ1, + .vsync_int = ZR36057_ISR_GIRQ0, + .gpio = { 1, -1, 3, -1, -1, -1, -1, -1 }, + .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, + .gpcs = { 3, 1 }, + .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, + .gws_not_connected = 1, + .init = &buz_init, + } +}; + +/* + * I2C functions + */ +/* software I2C functions */ +static int +zoran_i2c_getsda (void *data) +{ + struct zoran *zr = (struct zoran *) data; + + return (btread(ZR36057_I2CBR) >> 1) & 1; +} + +static int +zoran_i2c_getscl (void *data) +{ + struct zoran *zr = (struct zoran *) data; + + return btread(ZR36057_I2CBR) & 1; +} + +static void +zoran_i2c_setsda (void *data, + int state) +{ + struct zoran *zr = (struct zoran *) data; + + if (state) + zr->i2cbr |= 2; + else + zr->i2cbr &= ~2; + btwrite(zr->i2cbr, ZR36057_I2CBR); +} + +static void +zoran_i2c_setscl (void *data, + int state) +{ + struct zoran *zr = (struct zoran *) data; + + if (state) + zr->i2cbr |= 1; + else + zr->i2cbr &= ~1; + btwrite(zr->i2cbr, ZR36057_I2CBR); +} + +static int +zoran_i2c_client_register (struct i2c_client *client) +{ + struct zoran *zr = (struct zoran *) i2c_get_adapdata(client->adapter); + int res = 0; + + dprintk(2, + KERN_DEBUG "%s: i2c_client_register() - driver id = %d\n", + ZR_DEVNAME(zr), client->driver->id); + + mutex_lock(&zr->resource_lock); + + if (zr->user > 0) { + /* we're already busy, so we keep a reference to + * them... Could do a lot of stuff here, but this + * is easiest. (Did I ever mention I'm a lazy ass?) + */ + res = -EBUSY; + goto clientreg_unlock_and_return; + } + + if (client->driver->id == zr->card.i2c_decoder) + zr->decoder = client; + else if (client->driver->id == zr->card.i2c_encoder) + zr->encoder = client; + else { + res = -ENODEV; + goto clientreg_unlock_and_return; + } + +clientreg_unlock_and_return: + mutex_unlock(&zr->resource_lock); + + return res; +} + +static int +zoran_i2c_client_unregister (struct i2c_client *client) +{ + struct zoran *zr = (struct zoran *) i2c_get_adapdata(client->adapter); + int res = 0; + + dprintk(2, KERN_DEBUG "%s: i2c_client_unregister()\n", ZR_DEVNAME(zr)); + + mutex_lock(&zr->resource_lock); + + if (zr->user > 0) { + res = -EBUSY; + goto clientunreg_unlock_and_return; + } + + /* try to locate it */ + if (client == zr->encoder) { + zr->encoder = NULL; + } else if (client == zr->decoder) { + zr->decoder = NULL; + snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%d]", zr->id); + } +clientunreg_unlock_and_return: + mutex_unlock(&zr->resource_lock); + return res; +} + +static struct i2c_algo_bit_data zoran_i2c_bit_data_template = { + .setsda = zoran_i2c_setsda, + .setscl = zoran_i2c_setscl, + .getsda = zoran_i2c_getsda, + .getscl = zoran_i2c_getscl, + .udelay = 10, + .mdelay = 0, + .timeout = 100, +}; + +static struct i2c_adapter zoran_i2c_adapter_template = { + .name = "zr36057", + .id = I2C_HW_B_ZR36067, + .algo = NULL, + .client_register = zoran_i2c_client_register, + .client_unregister = zoran_i2c_client_unregister, +}; + +static int +zoran_register_i2c (struct zoran *zr) +{ + memcpy(&zr->i2c_algo, &zoran_i2c_bit_data_template, + sizeof(struct i2c_algo_bit_data)); + zr->i2c_algo.data = zr; + memcpy(&zr->i2c_adapter, &zoran_i2c_adapter_template, + sizeof(struct i2c_adapter)); + strncpy(I2C_NAME(&zr->i2c_adapter), ZR_DEVNAME(zr), + sizeof(I2C_NAME(&zr->i2c_adapter)) - 1); + i2c_set_adapdata(&zr->i2c_adapter, zr); + zr->i2c_adapter.algo_data = &zr->i2c_algo; + return i2c_bit_add_bus(&zr->i2c_adapter); +} + +static void +zoran_unregister_i2c (struct zoran *zr) +{ + i2c_bit_del_bus((&zr->i2c_adapter)); +} + +/* Check a zoran_params struct for correctness, insert default params */ + +int +zoran_check_jpg_settings (struct zoran *zr, + struct zoran_jpg_settings *settings) +{ + int err = 0, err0 = 0; + + dprintk(4, + KERN_DEBUG + "%s: check_jpg_settings() - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n", + ZR_DEVNAME(zr), settings->decimation, settings->HorDcm, + settings->VerDcm, settings->TmpDcm); + dprintk(4, + KERN_DEBUG + "%s: check_jpg_settings() - x: %d, y: %d, w: %d, y: %d\n", + ZR_DEVNAME(zr), settings->img_x, settings->img_y, + settings->img_width, settings->img_height); + /* Check decimation, set default values for decimation = 1, 2, 4 */ + switch (settings->decimation) { + case 1: + + settings->HorDcm = 1; + settings->VerDcm = 1; + settings->TmpDcm = 1; + settings->field_per_buff = 2; + settings->img_x = 0; + settings->img_y = 0; + settings->img_width = BUZ_MAX_WIDTH; + settings->img_height = BUZ_MAX_HEIGHT / 2; + break; + case 2: + + settings->HorDcm = 2; + settings->VerDcm = 1; + settings->TmpDcm = 2; + settings->field_per_buff = 1; + settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; + settings->img_y = 0; + settings->img_width = + (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; + settings->img_height = BUZ_MAX_HEIGHT / 2; + break; + case 4: + + if (zr->card.type == DC10_new) { + dprintk(1, + KERN_DEBUG + "%s: check_jpg_settings() - HDec by 4 is not supported on the DC10\n", + ZR_DEVNAME(zr)); + err0++; + break; + } + + settings->HorDcm = 4; + settings->VerDcm = 2; + settings->TmpDcm = 2; + settings->field_per_buff = 1; + settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; + settings->img_y = 0; + settings->img_width = + (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; + settings->img_height = BUZ_MAX_HEIGHT / 2; + break; + case 0: + + /* We have to check the data the user has set */ + + if (settings->HorDcm != 1 && settings->HorDcm != 2 && + (zr->card.type == DC10_new || settings->HorDcm != 4)) + err0++; + if (settings->VerDcm != 1 && settings->VerDcm != 2) + err0++; + if (settings->TmpDcm != 1 && settings->TmpDcm != 2) + err0++; + if (settings->field_per_buff != 1 && + settings->field_per_buff != 2) + err0++; + if (settings->img_x < 0) + err0++; + if (settings->img_y < 0) + err0++; + if (settings->img_width < 0) + err0++; + if (settings->img_height < 0) + err0++; + if (settings->img_x + settings->img_width > BUZ_MAX_WIDTH) + err0++; + if (settings->img_y + settings->img_height > + BUZ_MAX_HEIGHT / 2) + err0++; + if (settings->HorDcm && settings->VerDcm) { + if (settings->img_width % + (16 * settings->HorDcm) != 0) + err0++; + if (settings->img_height % + (8 * settings->VerDcm) != 0) + err0++; + } + + if (err0) { + dprintk(1, + KERN_ERR + "%s: check_jpg_settings() - error in params for decimation = 0\n", + ZR_DEVNAME(zr)); + err++; + } + break; + default: + dprintk(1, + KERN_ERR + "%s: check_jpg_settings() - decimation = %d, must be 0, 1, 2 or 4\n", + ZR_DEVNAME(zr), settings->decimation); + err++; + break; + } + + if (settings->jpg_comp.quality > 100) + settings->jpg_comp.quality = 100; + if (settings->jpg_comp.quality < 5) + settings->jpg_comp.quality = 5; + if (settings->jpg_comp.APPn < 0) + settings->jpg_comp.APPn = 0; + if (settings->jpg_comp.APPn > 15) + settings->jpg_comp.APPn = 15; + if (settings->jpg_comp.APP_len < 0) + settings->jpg_comp.APP_len = 0; + if (settings->jpg_comp.APP_len > 60) + settings->jpg_comp.APP_len = 60; + if (settings->jpg_comp.COM_len < 0) + settings->jpg_comp.COM_len = 0; + if (settings->jpg_comp.COM_len > 60) + settings->jpg_comp.COM_len = 60; + if (err) + return -EINVAL; + return 0; +} + +void +zoran_open_init_params (struct zoran *zr) +{ + int i; + + /* User must explicitly set a window */ + zr->overlay_settings.is_set = 0; + zr->overlay_mask = NULL; + zr->overlay_active = ZORAN_FREE; + + zr->v4l_memgrab_active = 0; + zr->v4l_overlay_active = 0; + zr->v4l_grab_frame = NO_GRAB_ACTIVE; + zr->v4l_grab_seq = 0; + zr->v4l_settings.width = 192; + zr->v4l_settings.height = 144; + zr->v4l_settings.format = &zoran_formats[4]; /* YUY2 - YUV-4:2:2 packed */ + zr->v4l_settings.bytesperline = + zr->v4l_settings.width * + ((zr->v4l_settings.format->depth + 7) / 8); + + /* DMA ring stuff for V4L */ + zr->v4l_pend_tail = 0; + zr->v4l_pend_head = 0; + zr->v4l_sync_tail = 0; + zr->v4l_buffers.active = ZORAN_FREE; + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ + } + zr->v4l_buffers.allocated = 0; + + for (i = 0; i < BUZ_MAX_FRAME; i++) { + zr->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ + } + zr->jpg_buffers.active = ZORAN_FREE; + zr->jpg_buffers.allocated = 0; + /* Set necessary params and call zoran_check_jpg_settings to set the defaults */ + zr->jpg_settings.decimation = 1; + zr->jpg_settings.jpg_comp.quality = 50; /* default compression factor 8 */ + if (zr->card.type != BUZ) + zr->jpg_settings.odd_even = 1; + else + zr->jpg_settings.odd_even = 0; + zr->jpg_settings.jpg_comp.APPn = 0; + zr->jpg_settings.jpg_comp.APP_len = 0; /* No APPn marker */ + memset(zr->jpg_settings.jpg_comp.APP_data, 0, + sizeof(zr->jpg_settings.jpg_comp.APP_data)); + zr->jpg_settings.jpg_comp.COM_len = 0; /* No COM marker */ + memset(zr->jpg_settings.jpg_comp.COM_data, 0, + sizeof(zr->jpg_settings.jpg_comp.COM_data)); + zr->jpg_settings.jpg_comp.jpeg_markers = + JPEG_MARKER_DHT | JPEG_MARKER_DQT; + i = zoran_check_jpg_settings(zr, &zr->jpg_settings); + if (i) + dprintk(1, + KERN_ERR + "%s: zoran_open_init_params() internal error\n", + ZR_DEVNAME(zr)); + + clear_interrupt_counters(zr); + zr->testing = 0; +} + +static void __devinit +test_interrupts (struct zoran *zr) +{ + DEFINE_WAIT(wait); + int timeout, icr; + + clear_interrupt_counters(zr); + + zr->testing = 1; + icr = btread(ZR36057_ICR); + btwrite(0x78000000 | ZR36057_ICR_IntPinEn, ZR36057_ICR); + prepare_to_wait(&zr->test_q, &wait, TASK_INTERRUPTIBLE); + timeout = schedule_timeout(HZ); + finish_wait(&zr->test_q, &wait); + btwrite(0, ZR36057_ICR); + btwrite(0x78000000, ZR36057_ISR); + zr->testing = 0; + dprintk(5, KERN_INFO "%s: Testing interrupts...\n", ZR_DEVNAME(zr)); + if (timeout) { + dprintk(1, ": time spent: %d\n", 1 * HZ - timeout); + } + if (*zr_debug > 1) + print_interrupts(zr); + btwrite(icr, ZR36057_ICR); +} + +static int __devinit +zr36057_init (struct zoran *zr) +{ + int j, err; + int two = 2; + int zero = 0; + + dprintk(1, + KERN_INFO + "%s: zr36057_init() - initializing card[%d], zr=%p\n", + ZR_DEVNAME(zr), zr->id, zr); + + /* default setup of all parameters which will persist between opens */ + zr->user = 0; + + init_waitqueue_head(&zr->v4l_capq); + init_waitqueue_head(&zr->jpg_capq); + init_waitqueue_head(&zr->test_q); + zr->jpg_buffers.allocated = 0; + zr->v4l_buffers.allocated = 0; + + zr->buffer.base = (void *) vidmem; + zr->buffer.width = 0; + zr->buffer.height = 0; + zr->buffer.depth = 0; + zr->buffer.bytesperline = 0; + + /* Avoid nonsense settings from user for default input/norm */ + if (default_norm < VIDEO_MODE_PAL && + default_norm > VIDEO_MODE_SECAM) + default_norm = VIDEO_MODE_PAL; + zr->norm = default_norm; + if (!(zr->timing = zr->card.tvn[zr->norm])) { + dprintk(1, + KERN_WARNING + "%s: zr36057_init() - default TV standard not supported by hardware. PAL will be used.\n", + ZR_DEVNAME(zr)); + zr->norm = VIDEO_MODE_PAL; + zr->timing = zr->card.tvn[zr->norm]; + } + + zr->input = default_input = (default_input ? 1 : 0); + + /* Should the following be reset at every open ? */ + zr->hue = 32768; + zr->contrast = 32768; + zr->saturation = 32768; + zr->brightness = 32768; + + /* default setup (will be repeated at every open) */ + zoran_open_init_params(zr); + + /* allocate memory *before* doing anything to the hardware + * in case allocation fails */ + zr->stat_com = kzalloc(BUZ_NUM_STAT_COM * 4, GFP_KERNEL); + zr->video_dev = kmalloc(sizeof(struct video_device), GFP_KERNEL); + if (!zr->stat_com || !zr->video_dev) { + dprintk(1, + KERN_ERR + "%s: zr36057_init() - kmalloc (STAT_COM) failed\n", + ZR_DEVNAME(zr)); + err = -ENOMEM; + goto exit_free; + } + for (j = 0; j < BUZ_NUM_STAT_COM; j++) { + zr->stat_com[j] = 1; /* mark as unavailable to zr36057 */ + } + + /* + * Now add the template and register the device unit. + */ + memcpy(zr->video_dev, &zoran_template, sizeof(zoran_template)); + strcpy(zr->video_dev->name, ZR_DEVNAME(zr)); + err = video_register_device(zr->video_dev, VFL_TYPE_GRABBER, video_nr); + if (err < 0) + goto exit_unregister; + + zoran_init_hardware(zr); + if (*zr_debug > 2) + detect_guest_activity(zr); + test_interrupts(zr); + if (!pass_through) { + decoder_command(zr, DECODER_ENABLE_OUTPUT, &zero); + encoder_command(zr, ENCODER_SET_INPUT, &two); + } + + zr->zoran_proc = NULL; + zr->initialized = 1; + return 0; + +exit_unregister: + zoran_unregister_i2c(zr); +exit_free: + kfree(zr->stat_com); + kfree(zr->video_dev); + return err; +} + +static void +zoran_release (struct zoran *zr) +{ + if (!zr->initialized) + return; + /* unregister videocodec bus */ + if (zr->codec) { + struct videocodec_master *master = zr->codec->master_data; + + videocodec_detach(zr->codec); + kfree(master); + } + if (zr->vfe) { + struct videocodec_master *master = zr->vfe->master_data; + + videocodec_detach(zr->vfe); + kfree(master); + } + + /* unregister i2c bus */ + zoran_unregister_i2c(zr); + /* disable PCI bus-mastering */ + zoran_set_pci_master(zr, 0); + /* put chip into reset */ + btwrite(0, ZR36057_SPGPPCR); + free_irq(zr->pci_dev->irq, zr); + /* unmap and free memory */ + kfree(zr->stat_com); + zoran_proc_cleanup(zr); + iounmap(zr->zr36057_mem); + pci_disable_device(zr->pci_dev); + video_unregister_device(zr->video_dev); +} + +void +zoran_vdev_release (struct video_device *vdev) +{ + kfree(vdev); +} + +static struct videocodec_master * __devinit +zoran_setup_videocodec (struct zoran *zr, + int type) +{ + struct videocodec_master *m = NULL; + + m = kmalloc(sizeof(struct videocodec_master), GFP_KERNEL); + if (!m) { + dprintk(1, + KERN_ERR + "%s: zoran_setup_videocodec() - no memory\n", + ZR_DEVNAME(zr)); + return m; + } + + m->magic = 0L; /* magic not used */ + m->type = VID_HARDWARE_ZR36067; + m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER; + strncpy(m->name, ZR_DEVNAME(zr), sizeof(m->name)); + m->data = zr; + + switch (type) + { + case CODEC_TYPE_ZR36060: + m->readreg = zr36060_read; + m->writereg = zr36060_write; + m->flags |= CODEC_FLAG_JPEG | CODEC_FLAG_VFE; + break; + case CODEC_TYPE_ZR36050: + m->readreg = zr36050_read; + m->writereg = zr36050_write; + m->flags |= CODEC_FLAG_JPEG; + break; + case CODEC_TYPE_ZR36016: + m->readreg = zr36016_read; + m->writereg = zr36016_write; + m->flags |= CODEC_FLAG_VFE; + break; + } + + return m; +} + +/* + * Scan for a Buz card (actually for the PCI contoler ZR36057), + * request the irq and map the io memory + */ +static int __devinit +find_zr36057 (void) +{ + unsigned char latency, need_latency; + struct zoran *zr; + struct pci_dev *dev = NULL; + int result; + struct videocodec_master *master_vfe = NULL; + struct videocodec_master *master_codec = NULL; + int card_num; + char *i2c_enc_name, *i2c_dec_name, *codec_name, *vfe_name; + + zoran_num = 0; + while (zoran_num < BUZ_MAX && + (dev = + pci_find_device(PCI_VENDOR_ID_ZORAN, + PCI_DEVICE_ID_ZORAN_36057, dev)) != NULL) { + card_num = card[zoran_num]; + zr = &zoran[zoran_num]; + memset(zr, 0, sizeof(struct zoran)); // Just in case if previous cycle failed + zr->pci_dev = dev; + //zr->zr36057_mem = NULL; + zr->id = zoran_num; + snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%u]", zr->id); + spin_lock_init(&zr->spinlock); + mutex_init(&zr->resource_lock); + if (pci_enable_device(dev)) + continue; + zr->zr36057_adr = pci_resource_start(zr->pci_dev, 0); + pci_read_config_byte(zr->pci_dev, PCI_CLASS_REVISION, + &zr->revision); + if (zr->revision < 2) { + dprintk(1, + KERN_INFO + "%s: Zoran ZR36057 (rev %d) irq: %d, memory: 0x%08x.\n", + ZR_DEVNAME(zr), zr->revision, zr->pci_dev->irq, + zr->zr36057_adr); + + if (card_num == -1) { + dprintk(1, + KERN_ERR + "%s: find_zr36057() - no card specified, please use the card=X insmod option\n", + ZR_DEVNAME(zr)); + continue; + } + } else { + int i; + unsigned short ss_vendor, ss_device; + + ss_vendor = zr->pci_dev->subsystem_vendor; + ss_device = zr->pci_dev->subsystem_device; + dprintk(1, + KERN_INFO + "%s: Zoran ZR36067 (rev %d) irq: %d, memory: 0x%08x\n", + ZR_DEVNAME(zr), zr->revision, zr->pci_dev->irq, + zr->zr36057_adr); + dprintk(1, + KERN_INFO + "%s: subsystem vendor=0x%04x id=0x%04x\n", + ZR_DEVNAME(zr), ss_vendor, ss_device); + if (card_num == -1) { + dprintk(3, + KERN_DEBUG + "%s: find_zr36057() - trying to autodetect card type\n", + ZR_DEVNAME(zr)); + for (i=0;i= NUM_CARDS) { + dprintk(2, + KERN_ERR + "%s: find_zr36057() - invalid cardnum %d\n", + ZR_DEVNAME(zr), card_num); + continue; + } + + /* even though we make this a non pointer and thus + * theoretically allow for making changes to this struct + * on a per-individual card basis at runtime, this is + * strongly discouraged. This structure is intended to + * keep general card information, no settings or anything */ + zr->card = zoran_cards[card_num]; + snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), + "%s[%u]", zr->card.name, zr->id); + + zr->zr36057_mem = ioremap_nocache(zr->zr36057_adr, 0x1000); + if (!zr->zr36057_mem) { + dprintk(1, + KERN_ERR + "%s: find_zr36057() - ioremap failed\n", + ZR_DEVNAME(zr)); + continue; + } + + result = request_irq(zr->pci_dev->irq, + zoran_irq, + SA_SHIRQ | SA_INTERRUPT, + ZR_DEVNAME(zr), + (void *) zr); + if (result < 0) { + if (result == -EINVAL) { + dprintk(1, + KERN_ERR + "%s: find_zr36057() - bad irq number or handler\n", + ZR_DEVNAME(zr)); + } else if (result == -EBUSY) { + dprintk(1, + KERN_ERR + "%s: find_zr36057() - IRQ %d busy, change your PnP config in BIOS\n", + ZR_DEVNAME(zr), zr->pci_dev->irq); + } else { + dprintk(1, + KERN_ERR + "%s: find_zr36057() - can't assign irq, error code %d\n", + ZR_DEVNAME(zr), result); + } + goto zr_unmap; + } + + /* set PCI latency timer */ + pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, + &latency); + need_latency = zr->revision > 1 ? 32 : 48; + if (latency != need_latency) { + dprintk(2, + KERN_INFO + "%s: Changing PCI latency from %d to %d.\n", + ZR_DEVNAME(zr), latency, need_latency); + pci_write_config_byte(zr->pci_dev, + PCI_LATENCY_TIMER, + need_latency); + } + + zr36057_restart(zr); + /* i2c */ + dprintk(2, KERN_INFO "%s: Initializing i2c bus...\n", + ZR_DEVNAME(zr)); + + /* i2c decoder */ + if (decoder[zr->id] != -1) { + i2c_dec_name = i2cid_to_modulename(decoder[zr->id]); + zr->card.i2c_decoder = decoder[zr->id]; + } else if (zr->card.i2c_decoder != 0) { + i2c_dec_name = + i2cid_to_modulename(zr->card.i2c_decoder); + } else { + i2c_dec_name = NULL; + } + + if (i2c_dec_name) { + if ((result = request_module(i2c_dec_name)) < 0) { + dprintk(1, + KERN_ERR + "%s: failed to load module %s: %d\n", + ZR_DEVNAME(zr), i2c_dec_name, result); + } + } + + /* i2c encoder */ + if (encoder[zr->id] != -1) { + i2c_enc_name = i2cid_to_modulename(encoder[zr->id]); + zr->card.i2c_encoder = encoder[zr->id]; + } else if (zr->card.i2c_encoder != 0) { + i2c_enc_name = + i2cid_to_modulename(zr->card.i2c_encoder); + } else { + i2c_enc_name = NULL; + } + + if (i2c_enc_name) { + if ((result = request_module(i2c_enc_name)) < 0) { + dprintk(1, + KERN_ERR + "%s: failed to load module %s: %d\n", + ZR_DEVNAME(zr), i2c_enc_name, result); + } + } + + if (zoran_register_i2c(zr) < 0) { + dprintk(1, + KERN_ERR + "%s: find_zr36057() - can't initialize i2c bus\n", + ZR_DEVNAME(zr)); + goto zr_free_irq; + } + + dprintk(2, + KERN_INFO "%s: Initializing videocodec bus...\n", + ZR_DEVNAME(zr)); + + if (zr->card.video_codec != 0 && + (codec_name = + codecid_to_modulename(zr->card.video_codec)) != NULL) { + if ((result = request_module(codec_name)) < 0) { + dprintk(1, + KERN_ERR + "%s: failed to load modules %s: %d\n", + ZR_DEVNAME(zr), codec_name, result); + } + } + if (zr->card.video_vfe != 0 && + (vfe_name = + codecid_to_modulename(zr->card.video_vfe)) != NULL) { + if ((result = request_module(vfe_name)) < 0) { + dprintk(1, + KERN_ERR + "%s: failed to load modules %s: %d\n", + ZR_DEVNAME(zr), vfe_name, result); + } + } + + /* reset JPEG codec */ + jpeg_codec_sleep(zr, 1); + jpeg_codec_reset(zr); + /* video bus enabled */ + /* display codec revision */ + if (zr->card.video_codec != 0) { + master_codec = zoran_setup_videocodec(zr, + zr->card.video_codec); + if (!master_codec) + goto zr_unreg_i2c; + zr->codec = videocodec_attach(master_codec); + if (!zr->codec) { + dprintk(1, + KERN_ERR + "%s: find_zr36057() - no codec found\n", + ZR_DEVNAME(zr)); + goto zr_free_codec; + } + if (zr->codec->type != zr->card.video_codec) { + dprintk(1, + KERN_ERR + "%s: find_zr36057() - wrong codec\n", + ZR_DEVNAME(zr)); + goto zr_detach_codec; + } + } + if (zr->card.video_vfe != 0) { + master_vfe = zoran_setup_videocodec(zr, + zr->card.video_vfe); + if (!master_vfe) + goto zr_detach_codec; + zr->vfe = videocodec_attach(master_vfe); + if (!zr->vfe) { + dprintk(1, + KERN_ERR + "%s: find_zr36057() - no VFE found\n", + ZR_DEVNAME(zr)); + goto zr_free_vfe; + } + if (zr->vfe->type != zr->card.video_vfe) { + dprintk(1, + KERN_ERR + "%s: find_zr36057() = wrong VFE\n", + ZR_DEVNAME(zr)); + goto zr_detach_vfe; + } + } + + zoran_num++; + continue; + + // Init errors + zr_detach_vfe: + videocodec_detach(zr->vfe); + zr_free_vfe: + kfree(master_vfe); + zr_detach_codec: + videocodec_detach(zr->codec); + zr_free_codec: + kfree(master_codec); + zr_unreg_i2c: + zoran_unregister_i2c(zr); + zr_free_irq: + btwrite(0, ZR36057_SPGPPCR); + free_irq(zr->pci_dev->irq, zr); + zr_unmap: + iounmap(zr->zr36057_mem); + continue; + } + if (zoran_num == 0) { + dprintk(1, KERN_INFO "No known MJPEG cards found.\n"); + } + return zoran_num; +} + +static int __init +init_dc10_cards (void) +{ + int i; + + memset(zoran, 0, sizeof(zoran)); + printk(KERN_INFO "Zoran MJPEG board driver version %d.%d.%d\n", + MAJOR_VERSION, MINOR_VERSION, RELEASE_VERSION); + + /* Look for cards */ + if (find_zr36057() < 0) { + return -EIO; + } + if (zoran_num == 0) + return -ENODEV; + dprintk(1, KERN_INFO "%s: %d card(s) found\n", ZORAN_NAME, + zoran_num); + /* check the parameters we have been given, adjust if necessary */ + if (v4l_nbufs < 2) + v4l_nbufs = 2; + if (v4l_nbufs > VIDEO_MAX_FRAME) + v4l_nbufs = VIDEO_MAX_FRAME; + /* The user specfies the in KB, we want them in byte + * (and page aligned) */ + v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024); + if (v4l_bufsize < 32768) + v4l_bufsize = 32768; + /* 2 MB is arbitrary but sufficient for the maximum possible images */ + if (v4l_bufsize > 2048 * 1024) + v4l_bufsize = 2048 * 1024; + if (jpg_nbufs < 4) + jpg_nbufs = 4; + if (jpg_nbufs > BUZ_MAX_FRAME) + jpg_nbufs = BUZ_MAX_FRAME; + jpg_bufsize = PAGE_ALIGN(jpg_bufsize * 1024); + if (jpg_bufsize < 8192) + jpg_bufsize = 8192; + if (jpg_bufsize > (512 * 1024)) + jpg_bufsize = 512 * 1024; + /* Use parameter for vidmem or try to find a video card */ + if (vidmem) { + dprintk(1, + KERN_INFO + "%s: Using supplied video memory base address @ 0x%lx\n", + ZORAN_NAME, vidmem); + } + + /* random nonsense */ + dprintk(5, KERN_DEBUG "Jotti is een held!\n"); + + /* some mainboards might not do PCI-PCI data transfer well */ + if (pci_pci_problems & PCIPCI_FAIL) { + dprintk(1, + KERN_WARNING + "%s: chipset may not support reliable PCI-PCI DMA\n", + ZORAN_NAME); + } + + /* take care of Natoma chipset and a revision 1 zr36057 */ + for (i = 0; i < zoran_num; i++) { + struct zoran *zr = &zoran[i]; + + if (pci_pci_problems & PCIPCI_NATOMA && zr->revision <= 1) { + zr->jpg_buffers.need_contiguous = 1; + dprintk(1, + KERN_INFO + "%s: ZR36057/Natoma bug, max. buffer size is 128K\n", + ZR_DEVNAME(zr)); + } + + if (zr36057_init(zr) < 0) { + for (i = 0; i < zoran_num; i++) + zoran_release(&zoran[i]); + return -EIO; + } + zoran_proc_init(zr); + } + + return 0; +} + +static void __exit +unload_dc10_cards (void) +{ + int i; + + for (i = 0; i < zoran_num; i++) + zoran_release(&zoran[i]); +} + +module_init(init_dc10_cards); +module_exit(unload_dc10_cards); diff --git a/linux/drivers/media/video/zoran_card.h b/linux/drivers/media/video/zoran_card.h new file mode 100644 index 000000000..ad997c30b --- /dev/null +++ b/linux/drivers/media/video/zoran_card.h @@ -0,0 +1,45 @@ +/* + * Zoran zr36057/zr36067 PCI controller driver, for the + * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux + * Media Labs LML33/LML33R10. + * + * This part handles card-specific data and detection + * + * Copyright (C) 2000 Serguei Miridonov + * + * Currently maintained by: + * Ronald Bultje + * Laurent Pinchart + * Mailinglist + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ZORAN_CARD_H__ +#define __ZORAN_CARD_H__ + +/* Anybody who uses more than four? */ +#define BUZ_MAX 4 +extern int zoran_num; +extern struct zoran zoran[BUZ_MAX]; + +extern struct video_device zoran_template; + +extern int zoran_check_jpg_settings(struct zoran *zr, + struct zoran_jpg_settings *settings); +extern void zoran_open_init_params(struct zoran *zr); +extern void zoran_vdev_release(struct video_device *vdev); + +#endif /* __ZORAN_CARD_H__ */ diff --git a/linux/drivers/media/video/zoran_device.c b/linux/drivers/media/video/zoran_device.c new file mode 100644 index 000000000..bb5011280 --- /dev/null +++ b/linux/drivers/media/video/zoran_device.c @@ -0,0 +1,1786 @@ +/* + * Zoran zr36057/zr36067 PCI controller driver, for the + * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux + * Media Labs LML33/LML33R10. + * + * This part handles device access (PCI/I2C/codec/...) + * + * Copyright (C) 2000 Serguei Miridonov + * + * Currently maintained by: + * Ronald Bultje + * Laurent Pinchart + * Mailinglist + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "compat.h" +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "videocodec.h" +#include "zoran.h" +#include "zoran_device.h" + +#define IRQ_MASK ( ZR36057_ISR_GIRQ0 | \ + ZR36057_ISR_GIRQ1 | \ + ZR36057_ISR_JPEGRepIRQ ) + +extern const struct zoran_format zoran_formats[]; + +extern int *zr_debug; + +#define dprintk(num, format, args...) \ + do { \ + if (*zr_debug >= num) \ + printk(format, ##args); \ + } while (0) + +static int lml33dpath = 0; /* 1 will use digital path in capture + * mode instead of analog. It can be + * used for picture adjustments using + * tool like xawtv while watching image + * on TV monitor connected to the output. + * However, due to absence of 75 Ohm + * load on Bt819 input, there will be + * some image imperfections */ + +module_param(lml33dpath, bool, 0); +MODULE_PARM_DESC(lml33dpath, + "Use digital path capture mode (on LML33 cards)"); + +static void +zr36057_init_vfe (struct zoran *zr); + +/* + * General Purpose I/O and Guest bus access + */ + +/* + * This is a bit tricky. When a board lacks a GPIO function, the corresponding + * GPIO bit number in the card_info structure is set to 0. + */ + +void +GPIO (struct zoran *zr, + int bit, + unsigned int value) +{ + u32 reg; + u32 mask; + + /* Make sure the bit number is legal + * A bit number of -1 (lacking) gives a mask of 0, + * making it harmless */ + mask = (1 << (24 + bit)) & 0xff000000; + reg = btread(ZR36057_GPPGCR1) & ~mask; + if (value) { + reg |= mask; + } + btwrite(reg, ZR36057_GPPGCR1); + udelay(1); +} + +/* + * Wait til post office is no longer busy + */ + +int +post_office_wait (struct zoran *zr) +{ + u32 por; + +// while (((por = btread(ZR36057_POR)) & (ZR36057_POR_POPen | ZR36057_POR_POTime)) == ZR36057_POR_POPen) { + while ((por = btread(ZR36057_POR)) & ZR36057_POR_POPen) { + /* wait for something to happen */ + } + if ((por & ZR36057_POR_POTime) && !zr->card.gws_not_connected) { + /* In LML33/BUZ \GWS line is not connected, so it has always timeout set */ + dprintk(1, KERN_INFO "%s: pop timeout %08x\n", ZR_DEVNAME(zr), + por); + return -1; + } + + return 0; +} + +int +post_office_write (struct zoran *zr, + unsigned int guest, + unsigned int reg, + unsigned int value) +{ + u32 por; + + por = + ZR36057_POR_PODir | ZR36057_POR_POTime | ((guest & 7) << 20) | + ((reg & 7) << 16) | (value & 0xFF); + btwrite(por, ZR36057_POR); + + return post_office_wait(zr); +} + +int +post_office_read (struct zoran *zr, + unsigned int guest, + unsigned int reg) +{ + u32 por; + + por = ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16); + btwrite(por, ZR36057_POR); + if (post_office_wait(zr) < 0) { + return -1; + } + + return btread(ZR36057_POR) & 0xFF; +} + +/* + * detect guests + */ + +static void +dump_guests (struct zoran *zr) +{ + if (*zr_debug > 2) { + int i, guest[8]; + + for (i = 1; i < 8; i++) { // Don't read jpeg codec here + guest[i] = post_office_read(zr, i, 0); + } + + printk(KERN_INFO "%s: Guests:", ZR_DEVNAME(zr)); + + for (i = 1; i < 8; i++) { + printk(" 0x%02x", guest[i]); + } + printk("\n"); + } +} + +static inline unsigned long +get_time (void) +{ + struct timeval tv; + + do_gettimeofday(&tv); + return (1000000 * tv.tv_sec + tv.tv_usec); +} + +void +detect_guest_activity (struct zoran *zr) +{ + int timeout, i, j, res, guest[8], guest0[8], change[8][3]; + unsigned long t0, t1; + + dump_guests(zr); + printk(KERN_INFO "%s: Detecting guests activity, please wait...\n", + ZR_DEVNAME(zr)); + for (i = 1; i < 8; i++) { // Don't read jpeg codec here + guest0[i] = guest[i] = post_office_read(zr, i, 0); + } + + timeout = 0; + j = 0; + t0 = get_time(); + while (timeout < 10000) { + udelay(10); + timeout++; + for (i = 1; (i < 8) && (j < 8); i++) { + res = post_office_read(zr, i, 0); + if (res != guest[i]) { + t1 = get_time(); + change[j][0] = (t1 - t0); + t0 = t1; + change[j][1] = i; + change[j][2] = res; + j++; + guest[i] = res; + } + } + if (j >= 8) + break; + } + printk(KERN_INFO "%s: Guests:", ZR_DEVNAME(zr)); + + for (i = 1; i < 8; i++) { + printk(" 0x%02x", guest0[i]); + } + printk("\n"); + if (j == 0) { + printk(KERN_INFO "%s: No activity detected.\n", ZR_DEVNAME(zr)); + return; + } + for (i = 0; i < j; i++) { + printk(KERN_INFO "%s: %6d: %d => 0x%02x\n", ZR_DEVNAME(zr), + change[i][0], change[i][1], change[i][2]); + } +} + +/* + * JPEG Codec access + */ + +void +jpeg_codec_sleep (struct zoran *zr, + int sleep) +{ + GPIO(zr, zr->card.gpio[GPIO_JPEG_SLEEP], !sleep); + if (!sleep) { + dprintk(3, + KERN_DEBUG + "%s: jpeg_codec_sleep() - wake GPIO=0x%08x\n", + ZR_DEVNAME(zr), btread(ZR36057_GPPGCR1)); + udelay(500); + } else { + dprintk(3, + KERN_DEBUG + "%s: jpeg_codec_sleep() - sleep GPIO=0x%08x\n", + ZR_DEVNAME(zr), btread(ZR36057_GPPGCR1)); + udelay(2); + } +} + +int +jpeg_codec_reset (struct zoran *zr) +{ + /* Take the codec out of sleep */ + jpeg_codec_sleep(zr, 0); + + if (zr->card.gpcs[GPCS_JPEG_RESET] != 0xff) { + post_office_write(zr, zr->card.gpcs[GPCS_JPEG_RESET], 0, + 0); + udelay(2); + } else { + GPIO(zr, zr->card.gpio[GPIO_JPEG_RESET], 0); + udelay(2); + GPIO(zr, zr->card.gpio[GPIO_JPEG_RESET], 1); + udelay(2); + } + + return 0; +} + +/* + * Set the registers for the size we have specified. Don't bother + * trying to understand this without the ZR36057 manual in front of + * you [AC]. + * + * PS: The manual is free for download in .pdf format from + * www.zoran.com - nicely done those folks. + */ + +static void +zr36057_adjust_vfe (struct zoran *zr, + enum zoran_codec_mode mode) +{ + u32 reg; + + switch (mode) { + case BUZ_MODE_MOTION_DECOMPRESS: + btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR); + reg = btread(ZR36057_VFEHCR); + if ((reg & (1 << 10)) && zr->card.type != LML33R10) { + reg += ((1 << 10) | 1); + } + btwrite(reg, ZR36057_VFEHCR); + break; + case BUZ_MODE_MOTION_COMPRESS: + case BUZ_MODE_IDLE: + default: + if (zr->norm == VIDEO_MODE_NTSC || + (zr->card.type == LML33R10 && + zr->norm == VIDEO_MODE_PAL)) + btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR); + else + btor(ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR); + reg = btread(ZR36057_VFEHCR); + if (!(reg & (1 << 10)) && zr->card.type != LML33R10) { + reg -= ((1 << 10) | 1); + } + btwrite(reg, ZR36057_VFEHCR); + break; + } +} + +/* + * set geometry + */ + +static void +zr36057_set_vfe (struct zoran *zr, + int video_width, + int video_height, + const struct zoran_format *format) +{ + struct tvnorm *tvn; + unsigned HStart, HEnd, VStart, VEnd; + unsigned DispMode; + unsigned VidWinWid, VidWinHt; + unsigned hcrop1, hcrop2, vcrop1, vcrop2; + unsigned Wa, We, Ha, He; + unsigned X, Y, HorDcm, VerDcm; + u32 reg; + unsigned mask_line_size; + + tvn = zr->timing; + + Wa = tvn->Wa; + Ha = tvn->Ha; + + dprintk(2, KERN_INFO "%s: set_vfe() - width = %d, height = %d\n", + ZR_DEVNAME(zr), video_width, video_height); + + if (zr->norm != VIDEO_MODE_PAL && + zr->norm != VIDEO_MODE_NTSC && + zr->norm != VIDEO_MODE_SECAM) { + dprintk(1, + KERN_ERR "%s: set_vfe() - norm = %d not valid\n", + ZR_DEVNAME(zr), zr->norm); + return; + } + if (video_width < BUZ_MIN_WIDTH || + video_height < BUZ_MIN_HEIGHT || + video_width > Wa || video_height > Ha) { + dprintk(1, KERN_ERR "%s: set_vfe: w=%d h=%d not valid\n", + ZR_DEVNAME(zr), video_width, video_height); + return; + } + + /**** zr36057 ****/ + + /* horizontal */ + VidWinWid = video_width; + X = (VidWinWid * 64 + tvn->Wa - 1) / tvn->Wa; + We = (VidWinWid * 64) / X; + HorDcm = 64 - X; + hcrop1 = 2 * ((tvn->Wa - We) / 4); + hcrop2 = tvn->Wa - We - hcrop1; + HStart = tvn->HStart ? tvn->HStart : 1; + /* (Ronald) Original comment: + * "| 1 Doesn't have any effect, tested on both a DC10 and a DC10+" + * this is false. It inverses chroma values on the LML33R10 (so Cr + * suddenly is shown as Cb and reverse, really cool effect if you + * want to see blue faces, not useful otherwise). So don't use |1. + * However, the DC10 has '0' as HStart, but does need |1, so we + * use a dirty check... + */ + HEnd = HStart + tvn->Wa - 1; + HStart += hcrop1; + HEnd -= hcrop2; + reg = ((HStart & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HStart) + | ((HEnd & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HEnd); + if (zr->card.vfe_pol.hsync_pol) + reg |= ZR36057_VFEHCR_HSPol; + btwrite(reg, ZR36057_VFEHCR); + + /* Vertical */ + DispMode = !(video_height > BUZ_MAX_HEIGHT / 2); + VidWinHt = DispMode ? video_height : video_height / 2; + Y = (VidWinHt * 64 * 2 + tvn->Ha - 1) / tvn->Ha; + He = (VidWinHt * 64) / Y; + VerDcm = 64 - Y; + vcrop1 = (tvn->Ha / 2 - He) / 2; + vcrop2 = tvn->Ha / 2 - He - vcrop1; + VStart = tvn->VStart; + VEnd = VStart + tvn->Ha / 2; // - 1; FIXME SnapShot times out with -1 in 768*576 on the DC10 - LP + VStart += vcrop1; + VEnd -= vcrop2; + reg = ((VStart & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VStart) + | ((VEnd & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VEnd); + if (zr->card.vfe_pol.vsync_pol) + reg |= ZR36057_VFEVCR_VSPol; + btwrite(reg, ZR36057_VFEVCR); + + /* scaler and pixel format */ + reg = 0; + reg |= (HorDcm << ZR36057_VFESPFR_HorDcm); + reg |= (VerDcm << ZR36057_VFESPFR_VerDcm); + reg |= (DispMode << ZR36057_VFESPFR_DispMode); + if (format->palette != VIDEO_PALETTE_YUV422) + reg |= ZR36057_VFESPFR_LittleEndian; + /* RJ: I don't know, why the following has to be the opposite + * of the corresponding ZR36060 setting, but only this way + * we get the correct colors when uncompressing to the screen */ + //reg |= ZR36057_VFESPFR_VCLKPol; /**/ + /* RJ: Don't know if that is needed for NTSC also */ + if (zr->norm != VIDEO_MODE_NTSC) + reg |= ZR36057_VFESPFR_ExtFl; // NEEDED!!!!!!! Wolfgang + reg |= ZR36057_VFESPFR_TopField; + switch (format->palette) { + + case VIDEO_PALETTE_YUV422: + reg |= ZR36057_VFESPFR_YUV422; + break; + + case VIDEO_PALETTE_RGB555: + reg |= ZR36057_VFESPFR_RGB555 | ZR36057_VFESPFR_ErrDif; + break; + + case VIDEO_PALETTE_RGB565: + reg |= ZR36057_VFESPFR_RGB565 | ZR36057_VFESPFR_ErrDif; + break; + + case VIDEO_PALETTE_RGB24: + reg |= ZR36057_VFESPFR_RGB888 | ZR36057_VFESPFR_Pack24; + break; + + case VIDEO_PALETTE_RGB32: + reg |= ZR36057_VFESPFR_RGB888; + break; + + default: + dprintk(1, + KERN_INFO "%s: set_vfe() - unknown color_fmt=%x\n", + ZR_DEVNAME(zr), format->palette); + return; + + } + if (HorDcm >= 48) { + reg |= 3 << ZR36057_VFESPFR_HFilter; /* 5 tap filter */ + } else if (HorDcm >= 32) { + reg |= 2 << ZR36057_VFESPFR_HFilter; /* 4 tap filter */ + } else if (HorDcm >= 16) { + reg |= 1 << ZR36057_VFESPFR_HFilter; /* 3 tap filter */ + } + btwrite(reg, ZR36057_VFESPFR); + + /* display configuration */ + reg = (16 << ZR36057_VDCR_MinPix) + | (VidWinHt << ZR36057_VDCR_VidWinHt) + | (VidWinWid << ZR36057_VDCR_VidWinWid); + if (pci_pci_problems & PCIPCI_TRITON) + // || zr->revision < 1) // Revision 1 has also Triton support + reg &= ~ZR36057_VDCR_Triton; + else + reg |= ZR36057_VDCR_Triton; + btwrite(reg, ZR36057_VDCR); + + /* (Ronald) don't write this if overlay_mask = NULL */ + if (zr->overlay_mask) { + /* Write overlay clipping mask data, but don't enable overlay clipping */ + /* RJ: since this makes only sense on the screen, we use + * zr->overlay_settings.width instead of video_width */ + + mask_line_size = (BUZ_MAX_WIDTH + 31) / 32; + reg = virt_to_bus(zr->overlay_mask); + btwrite(reg, ZR36057_MMTR); + reg = virt_to_bus(zr->overlay_mask + mask_line_size); + btwrite(reg, ZR36057_MMBR); + reg = + mask_line_size - (zr->overlay_settings.width + + 31) / 32; + if (DispMode == 0) + reg += mask_line_size; + reg <<= ZR36057_OCR_MaskStride; + btwrite(reg, ZR36057_OCR); + } + + zr36057_adjust_vfe(zr, zr->codec_mode); +} + +/* + * Switch overlay on or off + */ + +void +zr36057_overlay (struct zoran *zr, + int on) +{ + u32 reg; + + if (on) { + /* do the necessary settings ... */ + btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); /* switch it off first */ + + zr36057_set_vfe(zr, + zr->overlay_settings.width, + zr->overlay_settings.height, + zr->overlay_settings.format); + + /* Start and length of each line MUST be 4-byte aligned. + * This should be allready checked before the call to this routine. + * All error messages are internal driver checking only! */ + + /* video display top and bottom registers */ + reg = (u32) zr->buffer.base + + zr->overlay_settings.x * + ((zr->overlay_settings.format->depth + 7) / 8) + + zr->overlay_settings.y * + zr->buffer.bytesperline; + btwrite(reg, ZR36057_VDTR); + if (reg & 3) + dprintk(1, + KERN_ERR + "%s: zr36057_overlay() - video_address not aligned\n", + ZR_DEVNAME(zr)); + if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2) + reg += zr->buffer.bytesperline; + btwrite(reg, ZR36057_VDBR); + + /* video stride, status, and frame grab register */ + reg = zr->buffer.bytesperline - + zr->overlay_settings.width * + ((zr->overlay_settings.format->depth + 7) / 8); + if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2) + reg += zr->buffer.bytesperline; + if (reg & 3) + dprintk(1, + KERN_ERR + "%s: zr36057_overlay() - video_stride not aligned\n", + ZR_DEVNAME(zr)); + reg = (reg << ZR36057_VSSFGR_DispStride); + reg |= ZR36057_VSSFGR_VidOvf; /* clear overflow status */ + btwrite(reg, ZR36057_VSSFGR); + + /* Set overlay clipping */ + if (zr->overlay_settings.clipcount > 0) + btor(ZR36057_OCR_OvlEnable, ZR36057_OCR); + + /* ... and switch it on */ + btor(ZR36057_VDCR_VidEn, ZR36057_VDCR); + } else { + /* Switch it off */ + btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); + } +} + +/* + * The overlay mask has one bit for each pixel on a scan line, + * and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels. + */ + +void +write_overlay_mask (struct file *file, + struct video_clip *vp, + int count) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32; + u32 *mask; + int x, y, width, height; + unsigned i, j, k; + u32 reg; + + /* fill mask with one bits */ + memset(fh->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT); + reg = 0; + + for (i = 0; i < count; ++i) { + /* pick up local copy of clip */ + x = vp[i].x; + y = vp[i].y; + width = vp[i].width; + height = vp[i].height; + + /* trim clips that extend beyond the window */ + if (x < 0) { + width += x; + x = 0; + } + if (y < 0) { + height += y; + y = 0; + } + if (x + width > fh->overlay_settings.width) { + width = fh->overlay_settings.width - x; + } + if (y + height > fh->overlay_settings.height) { + height = fh->overlay_settings.height - y; + } + + /* ignore degenerate clips */ + if (height <= 0) { + continue; + } + if (width <= 0) { + continue; + } + + /* apply clip for each scan line */ + for (j = 0; j < height; ++j) { + /* reset bit for each pixel */ + /* this can be optimized later if need be */ + mask = fh->overlay_mask + (y + j) * mask_line_size; + for (k = 0; k < width; ++k) { + mask[(x + k) / 32] &= + ~((u32) 1 << (x + k) % 32); + } + } + } +} + +/* Enable/Disable uncompressed memory grabbing of the 36057 */ + +void +zr36057_set_memgrab (struct zoran *zr, + int mode) +{ + if (mode) { + if (btread(ZR36057_VSSFGR) & + (ZR36057_VSSFGR_SnapShot | ZR36057_VSSFGR_FrameGrab)) + dprintk(1, + KERN_WARNING + "%s: zr36057_set_memgrab(1) with SnapShot or FrameGrab on!?\n", + ZR_DEVNAME(zr)); + + /* switch on VSync interrupts */ + btwrite(IRQ_MASK, ZR36057_ISR); // Clear Interrupts + btor(zr->card.vsync_int, ZR36057_ICR); // SW + + /* enable SnapShot */ + btor(ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR); + + /* Set zr36057 video front end and enable video */ + zr36057_set_vfe(zr, zr->v4l_settings.width, + zr->v4l_settings.height, + zr->v4l_settings.format); + + zr->v4l_memgrab_active = 1; + } else { + zr->v4l_memgrab_active = 0; + + /* switch off VSync interrupts */ + btand(~zr->card.vsync_int, ZR36057_ICR); // SW + + /* reenable grabbing to screen if it was running */ + if (zr->v4l_overlay_active) { + zr36057_overlay(zr, 1); + } else { + btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); + btand(~ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR); + } + } +} + +int +wait_grab_pending (struct zoran *zr) +{ + unsigned long flags; + + /* wait until all pending grabs are finished */ + + if (!zr->v4l_memgrab_active) + return 0; + + wait_event_interruptible(zr->v4l_capq, + (zr->v4l_pend_tail == zr->v4l_pend_head)); + if (signal_pending(current)) + return -ERESTARTSYS; + + spin_lock_irqsave(&zr->spinlock, flags); + zr36057_set_memgrab(zr, 0); + spin_unlock_irqrestore(&zr->spinlock, flags); + + return 0; +} + +/***************************************************************************** + * * + * Set up the Buz-specific MJPEG part * + * * + *****************************************************************************/ + +static inline void +set_frame (struct zoran *zr, + int val) +{ + GPIO(zr, zr->card.gpio[GPIO_JPEG_FRAME], val); +} + +static void +set_videobus_dir (struct zoran *zr, + int val) +{ + switch (zr->card.type) { + case LML33: + case LML33R10: + if (lml33dpath == 0) + GPIO(zr, 5, val); + else + GPIO(zr, 5, 1); + break; + default: + GPIO(zr, zr->card.gpio[GPIO_VID_DIR], + zr->card.gpio_pol[GPIO_VID_DIR] ? !val : val); + break; + } +} + +static void +init_jpeg_queue (struct zoran *zr) +{ + int i; + + /* re-initialize DMA ring stuff */ + zr->jpg_que_head = 0; + zr->jpg_dma_head = 0; + zr->jpg_dma_tail = 0; + zr->jpg_que_tail = 0; + zr->jpg_seq_num = 0; + zr->JPEG_error = 0; + zr->num_errors = 0; + zr->jpg_err_seq = 0; + zr->jpg_err_shift = 0; + zr->jpg_queued_num = 0; + for (i = 0; i < zr->jpg_buffers.num_buffers; i++) { + zr->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ + } + for (i = 0; i < BUZ_NUM_STAT_COM; i++) { + zr->stat_com[i] = cpu_to_le32(1); /* mark as unavailable to zr36057 */ + } +} + +static void +zr36057_set_jpg (struct zoran *zr, + enum zoran_codec_mode mode) +{ + struct tvnorm *tvn; + u32 reg; + + tvn = zr->timing; + + /* assert P_Reset, disable code transfer, deassert Active */ + btwrite(0, ZR36057_JPC); + + /* MJPEG compression mode */ + switch (mode) { + + case BUZ_MODE_MOTION_COMPRESS: + default: + reg = ZR36057_JMC_MJPGCmpMode; + break; + + case BUZ_MODE_MOTION_DECOMPRESS: + reg = ZR36057_JMC_MJPGExpMode; + reg |= ZR36057_JMC_SyncMstr; + /* RJ: The following is experimental - improves the output to screen */ + //if(zr->jpg_settings.VFIFO_FB) reg |= ZR36057_JMC_VFIFO_FB; // No, it doesn't. SM + break; + + case BUZ_MODE_STILL_COMPRESS: + reg = ZR36057_JMC_JPGCmpMode; + break; + + case BUZ_MODE_STILL_DECOMPRESS: + reg = ZR36057_JMC_JPGExpMode; + break; + + } + reg |= ZR36057_JMC_JPG; + if (zr->jpg_settings.field_per_buff == 1) + reg |= ZR36057_JMC_Fld_per_buff; + btwrite(reg, ZR36057_JMC); + + /* vertical */ + btor(ZR36057_VFEVCR_VSPol, ZR36057_VFEVCR); + reg = (6 << ZR36057_VSP_VsyncSize) | + (tvn->Ht << ZR36057_VSP_FrmTot); + btwrite(reg, ZR36057_VSP); + reg = ((zr->jpg_settings.img_y + tvn->VStart) << ZR36057_FVAP_NAY) | + (zr->jpg_settings.img_height << ZR36057_FVAP_PAY); + btwrite(reg, ZR36057_FVAP); + + /* horizontal */ + if (zr->card.vfe_pol.hsync_pol) + btor(ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR); + else + btand(~ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR); + reg = ((tvn->HSyncStart) << ZR36057_HSP_HsyncStart) | + (tvn->Wt << ZR36057_HSP_LineTot); + btwrite(reg, ZR36057_HSP); + reg = ((zr->jpg_settings.img_x + + tvn->HStart + 4) << ZR36057_FHAP_NAX) | + (zr->jpg_settings.img_width << ZR36057_FHAP_PAX); + btwrite(reg, ZR36057_FHAP); + + /* field process parameters */ + if (zr->jpg_settings.odd_even) + reg = ZR36057_FPP_Odd_Even; + else + reg = 0; + + btwrite(reg, ZR36057_FPP); + + /* Set proper VCLK Polarity, else colors will be wrong during playback */ + //btor(ZR36057_VFESPFR_VCLKPol, ZR36057_VFESPFR); + + /* code base address */ + reg = virt_to_bus(zr->stat_com); + btwrite(reg, ZR36057_JCBA); + + /* FIFO threshold (FIFO is 160. double words) */ + /* NOTE: decimal values here */ + switch (mode) { + + case BUZ_MODE_STILL_COMPRESS: + case BUZ_MODE_MOTION_COMPRESS: + if (zr->card.type != BUZ) + reg = 140; + else + reg = 60; + break; + + case BUZ_MODE_STILL_DECOMPRESS: + case BUZ_MODE_MOTION_DECOMPRESS: + reg = 20; + break; + + default: + reg = 80; + break; + + } + btwrite(reg, ZR36057_JCFT); + zr36057_adjust_vfe(zr, mode); + +} + +void +print_interrupts (struct zoran *zr) +{ + int res, noerr = 0; + + printk(KERN_INFO "%s: interrupts received:", ZR_DEVNAME(zr)); + if ((res = zr->field_counter) < -1 || res > 1) { + printk(" FD:%d", res); + } + if ((res = zr->intr_counter_GIRQ1) != 0) { + printk(" GIRQ1:%d", res); + noerr++; + } + if ((res = zr->intr_counter_GIRQ0) != 0) { + printk(" GIRQ0:%d", res); + noerr++; + } + if ((res = zr->intr_counter_CodRepIRQ) != 0) { + printk(" CodRepIRQ:%d", res); + noerr++; + } + if ((res = zr->intr_counter_JPEGRepIRQ) != 0) { + printk(" JPEGRepIRQ:%d", res); + noerr++; + } + if (zr->JPEG_max_missed) { + printk(" JPEG delays: max=%d min=%d", zr->JPEG_max_missed, + zr->JPEG_min_missed); + } + if (zr->END_event_missed) { + printk(" ENDs missed: %d", zr->END_event_missed); + } + //if (zr->jpg_queued_num) { + printk(" queue_state=%ld/%ld/%ld/%ld", zr->jpg_que_tail, + zr->jpg_dma_tail, zr->jpg_dma_head, zr->jpg_que_head); + //} + if (!noerr) { + printk(": no interrupts detected."); + } + printk("\n"); +} + +void +clear_interrupt_counters (struct zoran *zr) +{ + zr->intr_counter_GIRQ1 = 0; + zr->intr_counter_GIRQ0 = 0; + zr->intr_counter_CodRepIRQ = 0; + zr->intr_counter_JPEGRepIRQ = 0; + zr->field_counter = 0; + zr->IRQ1_in = 0; + zr->IRQ1_out = 0; + zr->JPEG_in = 0; + zr->JPEG_out = 0; + zr->JPEG_0 = 0; + zr->JPEG_1 = 0; + zr->END_event_missed = 0; + zr->JPEG_missed = 0; + zr->JPEG_max_missed = 0; + zr->JPEG_min_missed = 0x7fffffff; +} + +static u32 +count_reset_interrupt (struct zoran *zr) +{ + u32 isr; + + if ((isr = btread(ZR36057_ISR) & 0x78000000)) { + if (isr & ZR36057_ISR_GIRQ1) { + btwrite(ZR36057_ISR_GIRQ1, ZR36057_ISR); + zr->intr_counter_GIRQ1++; + } + if (isr & ZR36057_ISR_GIRQ0) { + btwrite(ZR36057_ISR_GIRQ0, ZR36057_ISR); + zr->intr_counter_GIRQ0++; + } + if (isr & ZR36057_ISR_CodRepIRQ) { + btwrite(ZR36057_ISR_CodRepIRQ, ZR36057_ISR); + zr->intr_counter_CodRepIRQ++; + } + if (isr & ZR36057_ISR_JPEGRepIRQ) { + btwrite(ZR36057_ISR_JPEGRepIRQ, ZR36057_ISR); + zr->intr_counter_JPEGRepIRQ++; + } + } + return isr; +} + +/* hack */ +extern void zr36016_write (struct videocodec *codec, + u16 reg, + u32 val); + +void +jpeg_start (struct zoran *zr) +{ + int reg; + + zr->frame_num = 0; + + /* deassert P_reset, disable code transfer, deassert Active */ + btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC); + /* stop flushing the internal code buffer */ + btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR); + /* enable code transfer */ + btor(ZR36057_JPC_CodTrnsEn, ZR36057_JPC); + + /* clear IRQs */ + btwrite(IRQ_MASK, ZR36057_ISR); + /* enable the JPEG IRQs */ + btwrite(zr->card.jpeg_int | + ZR36057_ICR_JPEGRepIRQ | + ZR36057_ICR_IntPinEn, + ZR36057_ICR); + + set_frame(zr, 0); // \FRAME + + /* set the JPEG codec guest ID */ + reg = (zr->card.gpcs[1] << ZR36057_JCGI_JPEGuestID) | + (0 << ZR36057_JCGI_JPEGuestReg); + btwrite(reg, ZR36057_JCGI); + + if (zr->card.video_vfe == CODEC_TYPE_ZR36016 && + zr->card.video_codec == CODEC_TYPE_ZR36050) { + /* Enable processing on the ZR36016 */ + if (zr->vfe) + zr36016_write(zr->vfe, 0, 1); + + /* load the address of the GO register in the ZR36050 latch */ + post_office_write(zr, 0, 0, 0); + } + + /* assert Active */ + btor(ZR36057_JPC_Active, ZR36057_JPC); + + /* enable the Go generation */ + btor(ZR36057_JMC_Go_en, ZR36057_JMC); + udelay(30); + + set_frame(zr, 1); // /FRAME + + dprintk(3, KERN_DEBUG "%s: jpeg_start\n", ZR_DEVNAME(zr)); +} + +void +zr36057_enable_jpg (struct zoran *zr, + enum zoran_codec_mode mode) +{ + static int zero = 0; + static int one = 1; + struct vfe_settings cap; + int field_size = + zr->jpg_buffers.buffer_size / zr->jpg_settings.field_per_buff; + + zr->codec_mode = mode; + + cap.x = zr->jpg_settings.img_x; + cap.y = zr->jpg_settings.img_y; + cap.width = zr->jpg_settings.img_width; + cap.height = zr->jpg_settings.img_height; + cap.decimation = + zr->jpg_settings.HorDcm | (zr->jpg_settings.VerDcm << 8); + cap.quality = zr->jpg_settings.jpg_comp.quality; + + switch (mode) { + + case BUZ_MODE_MOTION_COMPRESS: { + struct jpeg_app_marker app; + struct jpeg_com_marker com; + + /* In motion compress mode, the decoder output must be enabled, and + * the video bus direction set to input. + */ + set_videobus_dir(zr, 0); + decoder_command(zr, DECODER_ENABLE_OUTPUT, &one); + encoder_command(zr, ENCODER_SET_INPUT, &zero); + + /* Take the JPEG codec and the VFE out of sleep */ + jpeg_codec_sleep(zr, 0); + + /* set JPEG app/com marker */ + app.appn = zr->jpg_settings.jpg_comp.APPn; + app.len = zr->jpg_settings.jpg_comp.APP_len; + memcpy(app.data, zr->jpg_settings.jpg_comp.APP_data, 60); + zr->codec->control(zr->codec, CODEC_S_JPEG_APP_DATA, + sizeof(struct jpeg_app_marker), &app); + + com.len = zr->jpg_settings.jpg_comp.COM_len; + memcpy(com.data, zr->jpg_settings.jpg_comp.COM_data, 60); + zr->codec->control(zr->codec, CODEC_S_JPEG_COM_DATA, + sizeof(struct jpeg_com_marker), &com); + + /* Setup the JPEG codec */ + zr->codec->control(zr->codec, CODEC_S_JPEG_TDS_BYTE, + sizeof(int), &field_size); + zr->codec->set_video(zr->codec, zr->timing, &cap, + &zr->card.vfe_pol); + zr->codec->set_mode(zr->codec, CODEC_DO_COMPRESSION); + + /* Setup the VFE */ + if (zr->vfe) { + zr->vfe->control(zr->vfe, CODEC_S_JPEG_TDS_BYTE, + sizeof(int), &field_size); + zr->vfe->set_video(zr->vfe, zr->timing, &cap, + &zr->card.vfe_pol); + zr->vfe->set_mode(zr->vfe, CODEC_DO_COMPRESSION); + } + + init_jpeg_queue(zr); + zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO + + clear_interrupt_counters(zr); + dprintk(2, KERN_INFO "%s: enable_jpg(MOTION_COMPRESS)\n", + ZR_DEVNAME(zr)); + break; + } + + case BUZ_MODE_MOTION_DECOMPRESS: + /* In motion decompression mode, the decoder output must be disabled, and + * the video bus direction set to output. + */ + decoder_command(zr, DECODER_ENABLE_OUTPUT, &zero); + set_videobus_dir(zr, 1); + encoder_command(zr, ENCODER_SET_INPUT, &one); + + /* Take the JPEG codec and the VFE out of sleep */ + jpeg_codec_sleep(zr, 0); + /* Setup the VFE */ + if (zr->vfe) { + zr->vfe->set_video(zr->vfe, zr->timing, &cap, + &zr->card.vfe_pol); + zr->vfe->set_mode(zr->vfe, CODEC_DO_EXPANSION); + } + /* Setup the JPEG codec */ + zr->codec->set_video(zr->codec, zr->timing, &cap, + &zr->card.vfe_pol); + zr->codec->set_mode(zr->codec, CODEC_DO_EXPANSION); + + init_jpeg_queue(zr); + zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO + + clear_interrupt_counters(zr); + dprintk(2, KERN_INFO "%s: enable_jpg(MOTION_DECOMPRESS)\n", + ZR_DEVNAME(zr)); + break; + + case BUZ_MODE_IDLE: + default: + /* shut down processing */ + btand(~(zr->card.jpeg_int | ZR36057_ICR_JPEGRepIRQ), + ZR36057_ICR); + btwrite(zr->card.jpeg_int | ZR36057_ICR_JPEGRepIRQ, + ZR36057_ISR); + btand(~ZR36057_JMC_Go_en, ZR36057_JMC); // \Go_en + + msleep(50); + + set_videobus_dir(zr, 0); + set_frame(zr, 1); // /FRAME + btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); // /CFlush + btwrite(0, ZR36057_JPC); // \P_Reset,\CodTrnsEn,\Active + btand(~ZR36057_JMC_VFIFO_FB, ZR36057_JMC); + btand(~ZR36057_JMC_SyncMstr, ZR36057_JMC); + jpeg_codec_reset(zr); + jpeg_codec_sleep(zr, 1); + zr36057_adjust_vfe(zr, mode); + + decoder_command(zr, DECODER_ENABLE_OUTPUT, &one); + encoder_command(zr, ENCODER_SET_INPUT, &zero); + + dprintk(2, KERN_INFO "%s: enable_jpg(IDLE)\n", ZR_DEVNAME(zr)); + break; + + } +} + +/* when this is called the spinlock must be held */ +void +zoran_feed_stat_com (struct zoran *zr) +{ + /* move frames from pending queue to DMA */ + + int frame, i, max_stat_com; + + max_stat_com = + (zr->jpg_settings.TmpDcm == + 1) ? BUZ_NUM_STAT_COM : (BUZ_NUM_STAT_COM >> 1); + + while ((zr->jpg_dma_head - zr->jpg_dma_tail) < max_stat_com && + zr->jpg_dma_head < zr->jpg_que_head) { + + frame = zr->jpg_pend[zr->jpg_dma_head & BUZ_MASK_FRAME]; + if (zr->jpg_settings.TmpDcm == 1) { + /* fill 1 stat_com entry */ + i = (zr->jpg_dma_head - + zr->jpg_err_shift) & BUZ_MASK_STAT_COM; + if (!(zr->stat_com[i] & cpu_to_le32(1))) + break; + zr->stat_com[i] = + cpu_to_le32(zr->jpg_buffers.buffer[frame].frag_tab_bus); + } else { + /* fill 2 stat_com entries */ + i = ((zr->jpg_dma_head - + zr->jpg_err_shift) & 1) * 2; + if (!(zr->stat_com[i] & cpu_to_le32(1))) + break; + zr->stat_com[i] = + cpu_to_le32(zr->jpg_buffers.buffer[frame].frag_tab_bus); + zr->stat_com[i + 1] = + cpu_to_le32(zr->jpg_buffers.buffer[frame].frag_tab_bus); + } + zr->jpg_buffers.buffer[frame].state = BUZ_STATE_DMA; + zr->jpg_dma_head++; + + } + if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) + zr->jpg_queued_num++; +} + +/* when this is called the spinlock must be held */ +static void +zoran_reap_stat_com (struct zoran *zr) +{ + /* move frames from DMA queue to done queue */ + + int i; + u32 stat_com; + unsigned int seq; + unsigned int dif; + struct zoran_jpg_buffer *buffer; + int frame; + + /* In motion decompress we don't have a hardware frame counter, + * we just count the interrupts here */ + + if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) { + zr->jpg_seq_num++; + } + while (zr->jpg_dma_tail < zr->jpg_dma_head) { + if (zr->jpg_settings.TmpDcm == 1) + i = (zr->jpg_dma_tail - + zr->jpg_err_shift) & BUZ_MASK_STAT_COM; + else + i = ((zr->jpg_dma_tail - + zr->jpg_err_shift) & 1) * 2 + 1; + + stat_com = le32_to_cpu(zr->stat_com[i]); + + if ((stat_com & 1) == 0) { + return; + } + frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; + buffer = &zr->jpg_buffers.buffer[frame]; + do_gettimeofday(&buffer->bs.timestamp); + + if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { + buffer->bs.length = (stat_com & 0x7fffff) >> 1; + + /* update sequence number with the help of the counter in stat_com */ + + seq = ((stat_com >> 24) + zr->jpg_err_seq) & 0xff; + dif = (seq - zr->jpg_seq_num) & 0xff; + zr->jpg_seq_num += dif; + } else { + buffer->bs.length = 0; + } + buffer->bs.seq = + zr->jpg_settings.TmpDcm == + 2 ? (zr->jpg_seq_num >> 1) : zr->jpg_seq_num; + buffer->state = BUZ_STATE_DONE; + + zr->jpg_dma_tail++; + } +} + +static void +error_handler (struct zoran *zr, + u32 astat, + u32 stat) +{ + /* This is JPEG error handling part */ + if ((zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) && + (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS)) { + //dprintk(1, KERN_ERR "%s: Internal error: error handling request in mode %d\n", ZR_DEVNAME(zr), zr->codec_mode); + return; + } + + if ((stat & 1) == 0 && + zr->codec_mode == BUZ_MODE_MOTION_COMPRESS && + zr->jpg_dma_tail - zr->jpg_que_tail >= + zr->jpg_buffers.num_buffers) { + /* No free buffers... */ + zoran_reap_stat_com(zr); + zoran_feed_stat_com(zr); + wake_up_interruptible(&zr->jpg_capq); + zr->JPEG_missed = 0; + return; + } + + if (zr->JPEG_error != 1) { + /* + * First entry: error just happened during normal operation + * + * In BUZ_MODE_MOTION_COMPRESS: + * + * Possible glitch in TV signal. In this case we should + * stop the codec and wait for good quality signal before + * restarting it to avoid further problems + * + * In BUZ_MODE_MOTION_DECOMPRESS: + * + * Bad JPEG frame: we have to mark it as processed (codec crashed + * and was not able to do it itself), and to remove it from queue. + */ + btand(~ZR36057_JMC_Go_en, ZR36057_JMC); + udelay(1); + stat = stat | (post_office_read(zr, 7, 0) & 3) << 8; + btwrite(0, ZR36057_JPC); + btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); + jpeg_codec_reset(zr); + jpeg_codec_sleep(zr, 1); + zr->JPEG_error = 1; + zr->num_errors++; + + /* Report error */ + if (*zr_debug > 1 && zr->num_errors <= 8) { + long frame; + frame = + zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; + printk(KERN_ERR + "%s: JPEG error stat=0x%08x(0x%08x) queue_state=%ld/%ld/%ld/%ld seq=%ld frame=%ld. Codec stopped. ", + ZR_DEVNAME(zr), stat, zr->last_isr, + zr->jpg_que_tail, zr->jpg_dma_tail, + zr->jpg_dma_head, zr->jpg_que_head, + zr->jpg_seq_num, frame); + printk("stat_com frames:"); + { + int i, j; + for (j = 0; j < BUZ_NUM_STAT_COM; j++) { + for (i = 0; + i < zr->jpg_buffers.num_buffers; + i++) { + if (le32_to_cpu(zr->stat_com[j]) == + zr->jpg_buffers. + buffer[i]. + frag_tab_bus) { + printk("% d->%d", + j, i); + } + } + } + printk("\n"); + } + } + /* Find an entry in stat_com and rotate contents */ + { + int i; + + if (zr->jpg_settings.TmpDcm == 1) + i = (zr->jpg_dma_tail - + zr->jpg_err_shift) & BUZ_MASK_STAT_COM; + else + i = ((zr->jpg_dma_tail - + zr->jpg_err_shift) & 1) * 2; + if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) { + /* Mimic zr36067 operation */ + zr->stat_com[i] |= cpu_to_le32(1); + if (zr->jpg_settings.TmpDcm != 1) + zr->stat_com[i + 1] |= cpu_to_le32(1); + /* Refill */ + zoran_reap_stat_com(zr); + zoran_feed_stat_com(zr); + wake_up_interruptible(&zr->jpg_capq); + /* Find an entry in stat_com again after refill */ + if (zr->jpg_settings.TmpDcm == 1) + i = (zr->jpg_dma_tail - + zr->jpg_err_shift) & + BUZ_MASK_STAT_COM; + else + i = ((zr->jpg_dma_tail - + zr->jpg_err_shift) & 1) * 2; + } + if (i) { + /* Rotate stat_comm entries to make current entry first */ + int j; + u32 bus_addr[BUZ_NUM_STAT_COM]; + + /* Here we are copying the stat_com array, which + * is already in little endian format, so + * no endian conversions here + */ + memcpy(bus_addr, zr->stat_com, + sizeof(bus_addr)); + for (j = 0; j < BUZ_NUM_STAT_COM; j++) { + zr->stat_com[j] = + bus_addr[(i + j) & + BUZ_MASK_STAT_COM]; + + } + zr->jpg_err_shift += i; + zr->jpg_err_shift &= BUZ_MASK_STAT_COM; + } + if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) + zr->jpg_err_seq = zr->jpg_seq_num; /* + 1; */ + } + } + + /* Now the stat_comm buffer is ready for restart */ + do { + int status, mode; + + if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { + decoder_command(zr, DECODER_GET_STATUS, &status); + mode = CODEC_DO_COMPRESSION; + } else { + status = 0; + mode = CODEC_DO_EXPANSION; + } + if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS || + (status & DECODER_STATUS_GOOD)) { + /********** RESTART code *************/ + jpeg_codec_reset(zr); + zr->codec->set_mode(zr->codec, mode); + zr36057_set_jpg(zr, zr->codec_mode); + jpeg_start(zr); + + if (zr->num_errors <= 8) + dprintk(2, KERN_INFO "%s: Restart\n", + ZR_DEVNAME(zr)); + + zr->JPEG_missed = 0; + zr->JPEG_error = 2; + /********** End RESTART code ***********/ + } + } while (0); +} + +irqreturn_t +zoran_irq (int irq, + void *dev_id, + struct pt_regs *regs) +{ + u32 stat, astat; + int count; + struct zoran *zr; + unsigned long flags; + + zr = (struct zoran *) dev_id; + count = 0; + + if (zr->testing) { + /* Testing interrupts */ + spin_lock_irqsave(&zr->spinlock, flags); + while ((stat = count_reset_interrupt(zr))) { + if (count++ > 100) { + btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR); + dprintk(1, + KERN_ERR + "%s: IRQ lockup while testing, isr=0x%08x, cleared int mask\n", + ZR_DEVNAME(zr), stat); + wake_up_interruptible(&zr->test_q); + } + } + zr->last_isr = stat; + spin_unlock_irqrestore(&zr->spinlock, flags); + return IRQ_HANDLED; + } + + spin_lock_irqsave(&zr->spinlock, flags); + while (1) { + /* get/clear interrupt status bits */ + stat = count_reset_interrupt(zr); + astat = stat & IRQ_MASK; + if (!astat) { + break; + } + dprintk(4, + KERN_DEBUG + "zoran_irq: astat: 0x%08x, mask: 0x%08x\n", + astat, btread(ZR36057_ICR)); + if (astat & zr->card.vsync_int) { // SW + + if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS || + zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { + /* count missed interrupts */ + zr->JPEG_missed++; + } + //post_office_read(zr,1,0); + /* Interrupts may still happen when + * zr->v4l_memgrab_active is switched off. + * We simply ignore them */ + + if (zr->v4l_memgrab_active) { + + /* A lot more checks should be here ... */ + if ((btread(ZR36057_VSSFGR) & + ZR36057_VSSFGR_SnapShot) == 0) + dprintk(1, + KERN_WARNING + "%s: BuzIRQ with SnapShot off ???\n", + ZR_DEVNAME(zr)); + + if (zr->v4l_grab_frame != NO_GRAB_ACTIVE) { + /* There is a grab on a frame going on, check if it has finished */ + + if ((btread(ZR36057_VSSFGR) & + ZR36057_VSSFGR_FrameGrab) == + 0) { + /* it is finished, notify the user */ + + zr->v4l_buffers.buffer[zr->v4l_grab_frame].state = BUZ_STATE_DONE; + zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.seq = zr->v4l_grab_seq; + do_gettimeofday(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp); + zr->v4l_grab_frame = NO_GRAB_ACTIVE; + zr->v4l_pend_tail++; + } + } + + if (zr->v4l_grab_frame == NO_GRAB_ACTIVE) + wake_up_interruptible(&zr->v4l_capq); + + /* Check if there is another grab queued */ + + if (zr->v4l_grab_frame == NO_GRAB_ACTIVE && + zr->v4l_pend_tail != zr->v4l_pend_head) { + + int frame = zr->v4l_pend[zr->v4l_pend_tail & + V4L_MASK_FRAME]; + u32 reg; + + zr->v4l_grab_frame = frame; + + /* Set zr36057 video front end and enable video */ + + /* Buffer address */ + + reg = + zr->v4l_buffers.buffer[frame]. + fbuffer_bus; + btwrite(reg, ZR36057_VDTR); + if (zr->v4l_settings.height > + BUZ_MAX_HEIGHT / 2) + reg += + zr->v4l_settings. + bytesperline; + btwrite(reg, ZR36057_VDBR); + + /* video stride, status, and frame grab register */ + reg = 0; + if (zr->v4l_settings.height > + BUZ_MAX_HEIGHT / 2) + reg += + zr->v4l_settings. + bytesperline; + reg = + (reg << + ZR36057_VSSFGR_DispStride); + reg |= ZR36057_VSSFGR_VidOvf; + reg |= ZR36057_VSSFGR_SnapShot; + reg |= ZR36057_VSSFGR_FrameGrab; + btwrite(reg, ZR36057_VSSFGR); + + btor(ZR36057_VDCR_VidEn, + ZR36057_VDCR); + } + } + + /* even if we don't grab, we do want to increment + * the sequence counter to see lost frames */ + zr->v4l_grab_seq++; + } +#if (IRQ_MASK & ZR36057_ISR_CodRepIRQ) + if (astat & ZR36057_ISR_CodRepIRQ) { + zr->intr_counter_CodRepIRQ++; + IDEBUG(printk + (KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n", + ZR_DEVNAME(zr))); + btand(~ZR36057_ICR_CodRepIRQ, ZR36057_ICR); + } +#endif /* (IRQ_MASK & ZR36057_ISR_CodRepIRQ) */ + +#if (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) + if (astat & ZR36057_ISR_JPEGRepIRQ) { + + if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS || + zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { + if (*zr_debug > 1 && + (!zr->frame_num || zr->JPEG_error)) { + printk(KERN_INFO + "%s: first frame ready: state=0x%08x odd_even=%d field_per_buff=%d delay=%d\n", + ZR_DEVNAME(zr), stat, + zr->jpg_settings.odd_even, + zr->jpg_settings. + field_per_buff, + zr->JPEG_missed); + { + char sc[] = "0000"; + char sv[5]; + int i; + strcpy(sv, sc); + for (i = 0; i < 4; i++) { + if (le32_to_cpu(zr->stat_com[i]) & 1) + sv[i] = '1'; + } + sv[4] = 0; + printk(KERN_INFO + "%s: stat_com=%s queue_state=%ld/%ld/%ld/%ld\n", + ZR_DEVNAME(zr), sv, + zr->jpg_que_tail, + zr->jpg_dma_tail, + zr->jpg_dma_head, + zr->jpg_que_head); + } + } else { + if (zr->JPEG_missed > zr->JPEG_max_missed) // Get statistics + zr->JPEG_max_missed = + zr->JPEG_missed; + if (zr->JPEG_missed < + zr->JPEG_min_missed) + zr->JPEG_min_missed = + zr->JPEG_missed; + } + + if (*zr_debug > 2 && zr->frame_num < 6) { + int i; + printk("%s: seq=%ld stat_com:", + ZR_DEVNAME(zr), zr->jpg_seq_num); + for (i = 0; i < 4; i++) { + printk(" %08x", + le32_to_cpu(zr->stat_com[i])); + } + printk("\n"); + } + zr->frame_num++; + zr->JPEG_missed = 0; + zr->JPEG_error = 0; + zoran_reap_stat_com(zr); + zoran_feed_stat_com(zr); + wake_up_interruptible(&zr->jpg_capq); + } /*else { + dprintk(1, + KERN_ERR + "%s: JPEG interrupt while not in motion (de)compress mode!\n", + ZR_DEVNAME(zr)); + }*/ + } +#endif /* (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) */ + + /* DATERR, too many fields missed, error processing */ + if ((astat & zr->card.jpeg_int) || + zr->JPEG_missed > 25 || + zr->JPEG_error == 1 || + ((zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) && + (zr->frame_num & (zr->JPEG_missed > + zr->jpg_settings.field_per_buff)))) { + error_handler(zr, astat, stat); + } + + count++; + if (count > 10) { + dprintk(2, KERN_WARNING "%s: irq loop %d\n", + ZR_DEVNAME(zr), count); + if (count > 20) { + btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR); + dprintk(2, + KERN_ERR + "%s: IRQ lockup, cleared int mask\n", + ZR_DEVNAME(zr)); + break; + } + } + zr->last_isr = stat; + } + spin_unlock_irqrestore(&zr->spinlock, flags); + + return IRQ_HANDLED; +} + +void +zoran_set_pci_master (struct zoran *zr, + int set_master) +{ + if (set_master) { + pci_set_master(zr->pci_dev); + } else { + u16 command; + + pci_read_config_word(zr->pci_dev, PCI_COMMAND, &command); + command &= ~PCI_COMMAND_MASTER; + pci_write_config_word(zr->pci_dev, PCI_COMMAND, command); + } +} + +void +zoran_init_hardware (struct zoran *zr) +{ + int j, zero = 0; + + /* Enable bus-mastering */ + zoran_set_pci_master(zr, 1); + + /* Initialize the board */ + if (zr->card.init) { + zr->card.init(zr); + } + + j = zr->card.input[zr->input].muxsel; + + decoder_command(zr, 0, NULL); + decoder_command(zr, DECODER_SET_NORM, &zr->norm); + decoder_command(zr, DECODER_SET_INPUT, &j); + + encoder_command(zr, 0, NULL); + encoder_command(zr, ENCODER_SET_NORM, &zr->norm); + encoder_command(zr, ENCODER_SET_INPUT, &zero); + + /* toggle JPEG codec sleep to sync PLL */ + jpeg_codec_sleep(zr, 1); + jpeg_codec_sleep(zr, 0); + + /* set individual interrupt enables (without GIRQ1) + * but don't global enable until zoran_open() */ + + //btwrite(IRQ_MASK & ~ZR36057_ISR_GIRQ1, ZR36057_ICR); // SW + // It looks like using only JPEGRepIRQEn is not always reliable, + // may be when JPEG codec crashes it won't generate IRQ? So, + /*CP*/ // btwrite(IRQ_MASK, ZR36057_ICR); // Enable Vsync interrupts too. SM WHY ? LP + zr36057_init_vfe(zr); + + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); + + btwrite(IRQ_MASK, ZR36057_ISR); // Clears interrupts +} + +void +zr36057_restart (struct zoran *zr) +{ + btwrite(0, ZR36057_SPGPPCR); + mdelay(1); + btor(ZR36057_SPGPPCR_SoftReset, ZR36057_SPGPPCR); + mdelay(1); + + /* assert P_Reset */ + btwrite(0, ZR36057_JPC); + /* set up GPIO direction - all output */ + btwrite(ZR36057_SPGPPCR_SoftReset | 0, ZR36057_SPGPPCR); + + /* set up GPIO pins and guest bus timing */ + btwrite((0x81 << 24) | 0x8888, ZR36057_GPPGCR1); +} + +/* + * initialize video front end + */ + +static void +zr36057_init_vfe (struct zoran *zr) +{ + u32 reg; + + reg = btread(ZR36057_VFESPFR); + reg |= ZR36057_VFESPFR_LittleEndian; + reg &= ~ZR36057_VFESPFR_VCLKPol; + reg |= ZR36057_VFESPFR_ExtFl; + reg |= ZR36057_VFESPFR_TopField; + btwrite(reg, ZR36057_VFESPFR); + reg = btread(ZR36057_VDCR); + if (pci_pci_problems & PCIPCI_TRITON) + // || zr->revision < 1) // Revision 1 has also Triton support + reg &= ~ZR36057_VDCR_Triton; + else + reg |= ZR36057_VDCR_Triton; + btwrite(reg, ZR36057_VDCR); +} + +/* + * Interface to decoder and encoder chips using i2c bus + */ + +int +decoder_command (struct zoran *zr, + int cmd, + void *data) +{ + if (zr->decoder == NULL) + return -EIO; + + if (zr->card.type == LML33 && + (cmd == DECODER_SET_NORM || DECODER_SET_INPUT)) { + int res; + + // Bt819 needs to reset its FIFO buffer using #FRST pin and + // LML33 card uses GPIO(7) for that. + GPIO(zr, 7, 0); + res = zr->decoder->driver->command(zr->decoder, cmd, data); + // Pull #FRST high. + GPIO(zr, 7, 1); + return res; + } else + return zr->decoder->driver->command(zr->decoder, cmd, + data); +} + +int +encoder_command (struct zoran *zr, + int cmd, + void *data) +{ + if (zr->encoder == NULL) + return -1; + + return zr->encoder->driver->command(zr->encoder, cmd, data); +} diff --git a/linux/drivers/media/video/zoran_device.h b/linux/drivers/media/video/zoran_device.h new file mode 100644 index 000000000..f19705cbd --- /dev/null +++ b/linux/drivers/media/video/zoran_device.h @@ -0,0 +1,91 @@ +/* + * Zoran zr36057/zr36067 PCI controller driver, for the + * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux + * Media Labs LML33/LML33R10. + * + * This part handles card-specific data and detection + * + * Copyright (C) 2000 Serguei Miridonov + * + * Currently maintained by: + * Ronald Bultje + * Laurent Pinchart + * Mailinglist + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ZORAN_DEVICE_H__ +#define __ZORAN_DEVICE_H__ + +/* general purpose I/O */ +extern void GPIO(struct zoran *zr, + int bit, + unsigned int value); + +/* codec (or actually: guest bus) access */ +extern int post_office_wait(struct zoran *zr); +extern int post_office_write(struct zoran *zr, + unsigned guest, + unsigned reg, + unsigned value); +extern int post_office_read(struct zoran *zr, + unsigned guest, + unsigned reg); + +extern void detect_guest_activity(struct zoran *zr); + +extern void jpeg_codec_sleep(struct zoran *zr, + int sleep); +extern int jpeg_codec_reset(struct zoran *zr); + +/* zr360x7 access to raw capture */ +extern void zr36057_overlay(struct zoran *zr, + int on); +extern void write_overlay_mask(struct file *file, + struct video_clip *vp, + int count); +extern void zr36057_set_memgrab(struct zoran *zr, + int mode); +extern int wait_grab_pending(struct zoran *zr); + +/* interrupts */ +extern void print_interrupts(struct zoran *zr); +extern void clear_interrupt_counters(struct zoran *zr); +extern irqreturn_t zoran_irq(int irq, + void *dev_id, + struct pt_regs *regs); + +/* JPEG codec access */ +extern void jpeg_start(struct zoran *zr); +extern void zr36057_enable_jpg(struct zoran *zr, + enum zoran_codec_mode mode); +extern void zoran_feed_stat_com(struct zoran *zr); + +/* general */ +extern void zoran_set_pci_master(struct zoran *zr, + int set_master); +extern void zoran_init_hardware(struct zoran *zr); +extern void zr36057_restart(struct zoran *zr); + +/* i2c */ +extern int decoder_command(struct zoran *zr, + int cmd, + void *data); +extern int encoder_command(struct zoran *zr, + int cmd, + void *data); + +#endif /* __ZORAN_DEVICE_H__ */ diff --git a/linux/drivers/media/video/zoran_driver.c b/linux/drivers/media/video/zoran_driver.c new file mode 100644 index 000000000..b5a576a37 --- /dev/null +++ b/linux/drivers/media/video/zoran_driver.c @@ -0,0 +1,4700 @@ +/* + * Zoran zr36057/zr36067 PCI controller driver, for the + * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux + * Media Labs LML33/LML33R10. + * + * Copyright (C) 2000 Serguei Miridonov + * + * Changes for BUZ by Wolfgang Scherr + * + * Changes for DC10/DC30 by Laurent Pinchart + * + * Changes for LML33R10 by Maxim Yevtyushkin + * + * Changes for videodev2/v4l2 by Ronald Bultje + * + * Based on + * + * Miro DC10 driver + * Copyright (C) 1999 Wolfgang Scherr + * + * Iomega Buz driver version 1.0 + * Copyright (C) 1999 Rainer Johanni + * + * buz.0.0.3 + * Copyright (C) 1998 Dave Perks + * + * bttv - Bt848 frame grabber driver + * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) + * & Marcus Metzler (mocm@thp.uni-koeln.de) + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#define MAP_NR(x) virt_to_page(x) +#define ZORAN_HARDWARE VID_HARDWARE_ZR36067 +#define ZORAN_VID_TYPE ( \ + VID_TYPE_CAPTURE | \ + VID_TYPE_OVERLAY | \ + VID_TYPE_CLIPPING | \ + VID_TYPE_FRAMERAM | \ + VID_TYPE_SCALES | \ + VID_TYPE_MJPEG_DECODER | \ + VID_TYPE_MJPEG_ENCODER \ + ) + +#include +#include "videocodec.h" + +#include +#include +#include + +#include +#include +#include +#include "zoran.h" +#include "zoran_device.h" +#include "zoran_card.h" + +#ifdef HAVE_V4L2 + /* we declare some card type definitions here, they mean + * the same as the v4l1 ZORAN_VID_TYPE above, except it's v4l2 */ +#define ZORAN_V4L2_VID_FLAGS ( \ + V4L2_CAP_STREAMING |\ + V4L2_CAP_VIDEO_CAPTURE |\ + V4L2_CAP_VIDEO_OUTPUT |\ + V4L2_CAP_VIDEO_OVERLAY \ + ) +#endif + +#include + +const struct zoran_format zoran_formats[] = { + { + .name = "15-bit RGB", + .palette = VIDEO_PALETTE_RGB555, +#ifdef HAVE_V4L2 +#ifdef __LITTLE_ENDIAN + .fourcc = V4L2_PIX_FMT_RGB555, +#else + .fourcc = V4L2_PIX_FMT_RGB555X, +#endif + .colorspace = V4L2_COLORSPACE_SRGB, +#endif + .depth = 15, + .flags = ZORAN_FORMAT_CAPTURE | + ZORAN_FORMAT_OVERLAY, + }, { + .name = "16-bit RGB", + .palette = VIDEO_PALETTE_RGB565, +#ifdef HAVE_V4L2 +#ifdef __LITTLE_ENDIAN + .fourcc = V4L2_PIX_FMT_RGB565, +#else + .fourcc = V4L2_PIX_FMT_RGB565X, +#endif + .colorspace = V4L2_COLORSPACE_SRGB, +#endif + .depth = 16, + .flags = ZORAN_FORMAT_CAPTURE | + ZORAN_FORMAT_OVERLAY, + }, { + .name = "24-bit RGB", + .palette = VIDEO_PALETTE_RGB24, +#ifdef HAVE_V4L2 +#ifdef __LITTLE_ENDIAN + .fourcc = V4L2_PIX_FMT_BGR24, +#else + .fourcc = V4L2_PIX_FMT_RGB24, +#endif + .colorspace = V4L2_COLORSPACE_SRGB, +#endif + .depth = 24, + .flags = ZORAN_FORMAT_CAPTURE | + ZORAN_FORMAT_OVERLAY, + }, { + .name = "32-bit RGB", + .palette = VIDEO_PALETTE_RGB32, +#ifdef HAVE_V4L2 +#ifdef __LITTLE_ENDIAN + .fourcc = V4L2_PIX_FMT_BGR32, +#else + .fourcc = V4L2_PIX_FMT_RGB32, +#endif + .colorspace = V4L2_COLORSPACE_SRGB, +#endif + .depth = 32, + .flags = ZORAN_FORMAT_CAPTURE | + ZORAN_FORMAT_OVERLAY, + }, { + .name = "4:2:2, packed, YUYV", + .palette = VIDEO_PALETTE_YUV422, +#ifdef HAVE_V4L2 + .fourcc = V4L2_PIX_FMT_YUYV, + .colorspace = V4L2_COLORSPACE_SMPTE170M, +#endif + .depth = 16, + .flags = ZORAN_FORMAT_CAPTURE | + ZORAN_FORMAT_OVERLAY, + }, { + .name = "Hardware-encoded Motion-JPEG", + .palette = -1, +#ifdef HAVE_V4L2 + .fourcc = V4L2_PIX_FMT_MJPEG, + .colorspace = V4L2_COLORSPACE_SMPTE170M, +#endif + .depth = 0, + .flags = ZORAN_FORMAT_CAPTURE | + ZORAN_FORMAT_PLAYBACK | + ZORAN_FORMAT_COMPRESSED, + } +}; +static const int zoran_num_formats = + (sizeof(zoran_formats) / sizeof(struct zoran_format)); + +// RJ: Test only - want to test BUZ_USE_HIMEM even when CONFIG_BIGPHYS_AREA is defined +#if !defined(CONFIG_BIGPHYS_AREA) +//#undef CONFIG_BIGPHYS_AREA +#define BUZ_USE_HIMEM +#endif + +#if defined(CONFIG_BIGPHYS_AREA) +# include +#endif + +extern int *zr_debug; + +#define dprintk(num, format, args...) \ + do { \ + if (*zr_debug >= num) \ + printk(format, ##args); \ + } while (0) + +extern int v4l_nbufs; +extern int v4l_bufsize; +extern int jpg_nbufs; +extern int jpg_bufsize; +extern int pass_through; + +static int lock_norm = 0; /* 1=Don't change TV standard (norm) */ +module_param(lock_norm, int, 0); +MODULE_PARM_DESC(lock_norm, "Users can't change norm"); + +#ifdef HAVE_V4L2 + /* small helper function for calculating buffersizes for v4l2 + * we calculate the nearest higher power-of-two, which + * will be the recommended buffersize */ +static __u32 +zoran_v4l2_calc_bufsize (struct zoran_jpg_settings *settings) +{ + __u8 div = settings->VerDcm * settings->HorDcm * settings->TmpDcm; + __u32 num = (1024 * 512) / (div); + __u32 result = 2; + + num--; + while (num) { + num >>= 1; + result <<= 1; + } + + if (result > jpg_bufsize) + return jpg_bufsize; + if (result < 8192) + return 8192; + return result; +} +#endif + +/* forward references */ +static void v4l_fbuffer_free(struct file *file); +static void jpg_fbuffer_free(struct file *file); + +/* + * Allocate the V4L grab buffers + * + * These have to be pysically contiguous. + * If v4l_bufsize <= MAX_KMALLOC_MEM we use kmalloc + * else we try to allocate them with bigphysarea_alloc_pages + * if the bigphysarea patch is present in the kernel, + * else we try to use high memory (if the user has bootet + * Linux with the necessary memory left over). + */ + +#if defined(BUZ_USE_HIMEM) && !defined(CONFIG_BIGPHYS_AREA) +static unsigned long +get_high_mem (unsigned long size) +{ +/* + * Check if there is usable memory at the end of Linux memory + * of at least size. Return the physical address of this memory, + * return 0 on failure. + * + * The idea is from Alexandro Rubini's book "Linux device drivers". + * The driver from him which is downloadable from O'Reilly's + * web site misses the "virt_to_phys(high_memory)" part + * (and therefore doesn't work at all - at least with 2.2.x kernels). + * + * It should be unnecessary to mention that THIS IS DANGEROUS, + * if more than one driver at a time has the idea to use this memory!!!! + */ + + volatile unsigned char __iomem *mem; + unsigned char c; + unsigned long hi_mem_ph; + unsigned long i; + + /* Map the high memory to user space */ + + hi_mem_ph = virt_to_phys(high_memory); + + mem = ioremap(hi_mem_ph, size); + if (!mem) { + dprintk(1, + KERN_ERR "%s: get_high_mem() - ioremap failed\n", + ZORAN_NAME); + return 0; + } + + for (i = 0; i < size; i++) { + /* Check if it is memory */ + c = i & 0xff; + writeb(c, mem + i); + if (readb(mem + i) != c) + break; + c = 255 - c; + writeb(c, mem + i); + if (readb(mem + i) != c) + break; + writeb(0, mem + i); /* zero out memory */ + + /* give the kernel air to breath */ + if ((i & 0x3ffff) == 0x3ffff) + schedule(); + } + + iounmap(mem); + + if (i != size) { + dprintk(1, + KERN_ERR + "%s: get_high_mem() - requested %lu, avail %lu\n", + ZORAN_NAME, size, i); + return 0; + } + + return hi_mem_ph; +} +#endif + +static int +v4l_fbuffer_alloc (struct file *file) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + int i, off; + unsigned char *mem; +#if defined(BUZ_USE_HIMEM) && !defined(CONFIG_BIGPHYS_AREA) + unsigned long pmem = 0; +#endif + + /* we might have old buffers lying around... */ + if (fh->v4l_buffers.ready_to_be_freed) { + v4l_fbuffer_free(file); + } + + for (i = 0; i < fh->v4l_buffers.num_buffers; i++) { + if (fh->v4l_buffers.buffer[i].fbuffer) + dprintk(2, + KERN_WARNING + "%s: v4l_fbuffer_alloc() - buffer %d allready allocated!?\n", + ZR_DEVNAME(zr), i); + + //udelay(20); + if (fh->v4l_buffers.buffer_size <= MAX_KMALLOC_MEM) { + /* Use kmalloc */ + + mem = + (unsigned char *) kmalloc(fh->v4l_buffers. + buffer_size, + GFP_KERNEL); + if (mem == 0) { + dprintk(1, + KERN_ERR + "%s: v4l_fbuffer_alloc() - kmalloc for V4L buf %d failed\n", + ZR_DEVNAME(zr), i); + v4l_fbuffer_free(file); + return -ENOBUFS; + } + fh->v4l_buffers.buffer[i].fbuffer = mem; + fh->v4l_buffers.buffer[i].fbuffer_phys = + virt_to_phys(mem); + fh->v4l_buffers.buffer[i].fbuffer_bus = + virt_to_bus(mem); + for (off = 0; off < fh->v4l_buffers.buffer_size; + off += PAGE_SIZE) + SetPageReserved(MAP_NR(mem + off)); + dprintk(4, + KERN_INFO + "%s: v4l_fbuffer_alloc() - V4L frame %d mem 0x%lx (bus: 0x%lx)\n", + ZR_DEVNAME(zr), i, (unsigned long) mem, + virt_to_bus(mem)); + } else { +#if defined(CONFIG_BIGPHYS_AREA) + /* Use bigphysarea_alloc_pages */ + + int n = + (fh->v4l_buffers.buffer_size + PAGE_SIZE - + 1) / PAGE_SIZE; + + mem = + (unsigned char *) bigphysarea_alloc_pages(n, 0, + GFP_KERNEL); + if (mem == 0) { + dprintk(1, + KERN_ERR + "%s: v4l_fbuffer_alloc() - bigphysarea_alloc_pages for V4L buf %d failed\n", + ZR_DEVNAME(zr), i); + v4l_fbuffer_free(file); + return -ENOBUFS; + } + fh->v4l_buffers.buffer[i].fbuffer = mem; + fh->v4l_buffers.buffer[i].fbuffer_phys = + virt_to_phys(mem); + fh->v4l_buffers.buffer[i].fbuffer_bus = + virt_to_bus(mem); + dprintk(4, + KERN_INFO + "%s: Bigphysarea frame %d mem 0x%x (bus: 0x%x)\n", + ZR_DEVNAME(zr), i, (unsigned) mem, + (unsigned) virt_to_bus(mem)); + + /* Zero out the allocated memory */ + memset(fh->v4l_buffers.buffer[i].fbuffer, 0, + fh->v4l_buffers.buffer_size); +#elif defined(BUZ_USE_HIMEM) + + /* Use high memory which has been left at boot time */ + + /* Ok., Ok. this is an evil hack - we make + * the assumption that physical addresses are + * the same as bus addresses (true at least + * for Intel processors). The whole method of + * obtaining and using this memory is not very + * nice - but I hope it saves some poor users + * from kernel hacking, which might have even + * more evil results */ + + if (i == 0) { + int size = + fh->v4l_buffers.num_buffers * + fh->v4l_buffers.buffer_size; + + pmem = get_high_mem(size); + if (pmem == 0) { + dprintk(1, + KERN_ERR + "%s: v4l_fbuffer_alloc() - get_high_mem (size = %d KB) for V4L bufs failed\n", + ZR_DEVNAME(zr), size >> 10); + return -ENOBUFS; + } + fh->v4l_buffers.buffer[0].fbuffer = NULL; + fh->v4l_buffers.buffer[0].fbuffer_phys = pmem; + fh->v4l_buffers.buffer[0].fbuffer_bus = pmem; + dprintk(4, + KERN_INFO + "%s: v4l_fbuffer_alloc() - using %d KB high memory\n", + ZR_DEVNAME(zr), size >> 10); + } else { + fh->v4l_buffers.buffer[i].fbuffer = NULL; + fh->v4l_buffers.buffer[i].fbuffer_phys = + pmem + i * fh->v4l_buffers.buffer_size; + fh->v4l_buffers.buffer[i].fbuffer_bus = + pmem + i * fh->v4l_buffers.buffer_size; + } +#else + /* No bigphysarea present, usage of high memory disabled, + * but user wants buffers of more than MAX_KMALLOC_MEM */ + dprintk(1, + KERN_ERR + "%s: v4l_fbuffer_alloc() - no bigphysarea_patch present, usage of high memory disabled,\n", + ZR_DEVNAME(zr)); + dprintk(1, + KERN_ERR + "%s: v4l_fbuffer_alloc() - sorry, could not allocate %d V4L buffers of size %d KB.\n", + ZR_DEVNAME(zr), fh->v4l_buffers.num_buffers, + fh->v4l_buffers.buffer_size >> 10); + return -ENOBUFS; +#endif + } + } + + fh->v4l_buffers.allocated = 1; + + return 0; +} + +/* free the V4L grab buffers */ +static void +v4l_fbuffer_free (struct file *file) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + int i, off; + unsigned char *mem; + + dprintk(4, KERN_INFO "%s: v4l_fbuffer_free()\n", ZR_DEVNAME(zr)); + + for (i = 0; i < fh->v4l_buffers.num_buffers; i++) { + if (!fh->v4l_buffers.buffer[i].fbuffer) + continue; + + if (fh->v4l_buffers.buffer_size <= MAX_KMALLOC_MEM) { + mem = fh->v4l_buffers.buffer[i].fbuffer; + for (off = 0; off < fh->v4l_buffers.buffer_size; + off += PAGE_SIZE) + ClearPageReserved(MAP_NR(mem + off)); + kfree((void *) fh->v4l_buffers.buffer[i].fbuffer); + } +#if defined(CONFIG_BIGPHYS_AREA) + else + bigphysarea_free_pages((void *) fh->v4l_buffers. + buffer[i].fbuffer); +#endif + fh->v4l_buffers.buffer[i].fbuffer = NULL; + } + + fh->v4l_buffers.allocated = 0; + fh->v4l_buffers.ready_to_be_freed = 0; +} + +/* + * Allocate the MJPEG grab buffers. + * + * If the requested buffer size is smaller than MAX_KMALLOC_MEM, + * kmalloc is used to request a physically contiguous area, + * else we allocate the memory in framgents with get_zeroed_page. + * + * If a Natoma chipset is present and this is a revision 1 zr36057, + * each MJPEG buffer needs to be physically contiguous. + * (RJ: This statement is from Dave Perks' original driver, + * I could never check it because I have a zr36067) + * The driver cares about this because it reduces the buffer + * size to MAX_KMALLOC_MEM in that case (which forces contiguous allocation). + * + * RJ: The contents grab buffers needs never be accessed in the driver. + * Therefore there is no need to allocate them with vmalloc in order + * to get a contiguous virtual memory space. + * I don't understand why many other drivers first allocate them with + * vmalloc (which uses internally also get_zeroed_page, but delivers you + * virtual addresses) and then again have to make a lot of efforts + * to get the physical address. + * + * Ben Capper: + * On big-endian architectures (such as ppc) some extra steps + * are needed. When reading and writing to the stat_com array + * and fragment buffers, the device expects to see little- + * endian values. The use of cpu_to_le32() and le32_to_cpu() + * in this function (and one or two others in zoran_device.c) + * ensure that these values are always stored in little-endian + * form, regardless of architecture. The zr36057 does Very Bad + * Things on big endian architectures if the stat_com array + * and fragment buffers are not little-endian. + */ + +static int +jpg_fbuffer_alloc (struct file *file) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + int i, j, off; + unsigned long mem; + + /* we might have old buffers lying around */ + if (fh->jpg_buffers.ready_to_be_freed) { + jpg_fbuffer_free(file); + } + + for (i = 0; i < fh->jpg_buffers.num_buffers; i++) { + if (fh->jpg_buffers.buffer[i].frag_tab) + dprintk(2, + KERN_WARNING + "%s: jpg_fbuffer_alloc() - buffer %d allready allocated!?\n", + ZR_DEVNAME(zr), i); + + /* Allocate fragment table for this buffer */ + + mem = get_zeroed_page(GFP_KERNEL); + if (mem == 0) { + dprintk(1, + KERN_ERR + "%s: jpg_fbuffer_alloc() - get_zeroed_page (frag_tab) failed for buffer %d\n", + ZR_DEVNAME(zr), i); + jpg_fbuffer_free(file); + return -ENOBUFS; + } + memset((void *) mem, 0, PAGE_SIZE); + fh->jpg_buffers.buffer[i].frag_tab = (u32 *) mem; + fh->jpg_buffers.buffer[i].frag_tab_bus = + virt_to_bus((void *) mem); + + //if (alloc_contig) { + if (fh->jpg_buffers.need_contiguous) { + mem = + (unsigned long) kmalloc(fh->jpg_buffers. + buffer_size, + GFP_KERNEL); + if (mem == 0) { + dprintk(1, + KERN_ERR + "%s: jpg_fbuffer_alloc() - kmalloc failed for buffer %d\n", + ZR_DEVNAME(zr), i); + jpg_fbuffer_free(file); + return -ENOBUFS; + } + fh->jpg_buffers.buffer[i].frag_tab[0] = + cpu_to_le32(virt_to_bus((void *) mem)); + fh->jpg_buffers.buffer[i].frag_tab[1] = + cpu_to_le32(((fh->jpg_buffers.buffer_size / 4) << 1) | 1); + for (off = 0; off < fh->jpg_buffers.buffer_size; + off += PAGE_SIZE) + SetPageReserved(MAP_NR(mem + off)); + } else { + /* jpg_bufsize is allreay page aligned */ + for (j = 0; + j < fh->jpg_buffers.buffer_size / PAGE_SIZE; + j++) { + mem = get_zeroed_page(GFP_KERNEL); + if (mem == 0) { + dprintk(1, + KERN_ERR + "%s: jpg_fbuffer_alloc() - get_zeroed_page failed for buffer %d\n", + ZR_DEVNAME(zr), i); + jpg_fbuffer_free(file); + return -ENOBUFS; + } + + fh->jpg_buffers.buffer[i].frag_tab[2 * j] = + cpu_to_le32(virt_to_bus((void *) mem)); + fh->jpg_buffers.buffer[i].frag_tab[2 * j + + 1] = + cpu_to_le32((PAGE_SIZE / 4) << 1); + SetPageReserved(MAP_NR(mem)); + } + + fh->jpg_buffers.buffer[i].frag_tab[2 * j - 1] |= cpu_to_le32(1); + } + } + + dprintk(4, + KERN_DEBUG "%s: jpg_fbuffer_alloc() - %d KB allocated\n", + ZR_DEVNAME(zr), + (fh->jpg_buffers.num_buffers * + fh->jpg_buffers.buffer_size) >> 10); + + fh->jpg_buffers.allocated = 1; + + return 0; +} + +/* free the MJPEG grab buffers */ +static void +jpg_fbuffer_free (struct file *file) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + int i, j, off; + unsigned char *mem; + + dprintk(4, KERN_DEBUG "%s: jpg_fbuffer_free()\n", ZR_DEVNAME(zr)); + + for (i = 0; i < fh->jpg_buffers.num_buffers; i++) { + if (!fh->jpg_buffers.buffer[i].frag_tab) + continue; + + //if (alloc_contig) { + if (fh->jpg_buffers.need_contiguous) { + if (fh->jpg_buffers.buffer[i].frag_tab[0]) { + mem = (unsigned char *) bus_to_virt(le32_to_cpu( + fh->jpg_buffers.buffer[i].frag_tab[0])); + for (off = 0; + off < fh->jpg_buffers.buffer_size; + off += PAGE_SIZE) + ClearPageReserved(MAP_NR + (mem + off)); + kfree(mem); + fh->jpg_buffers.buffer[i].frag_tab[0] = 0; + fh->jpg_buffers.buffer[i].frag_tab[1] = 0; + } + } else { + for (j = 0; + j < fh->jpg_buffers.buffer_size / PAGE_SIZE; + j++) { + if (!fh->jpg_buffers.buffer[i]. + frag_tab[2 * j]) + break; + ClearPageReserved(MAP_NR + (bus_to_virt + (le32_to_cpu + (fh->jpg_buffers. + buffer[i].frag_tab[2 * + j])))); + free_page((unsigned long) + bus_to_virt + (le32_to_cpu + (fh->jpg_buffers. + buffer[i]. + frag_tab[2 * j]))); + fh->jpg_buffers.buffer[i].frag_tab[2 * j] = + 0; + fh->jpg_buffers.buffer[i].frag_tab[2 * j + + 1] = 0; + } + } + + free_page((unsigned long) fh->jpg_buffers.buffer[i]. + frag_tab); + fh->jpg_buffers.buffer[i].frag_tab = NULL; + } + + fh->jpg_buffers.allocated = 0; + fh->jpg_buffers.ready_to_be_freed = 0; +} + +/* + * V4L Buffer grabbing + */ + +static int +zoran_v4l_set_format (struct file *file, + int width, + int height, + const struct zoran_format *format) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + int bpp; + + /* Check size and format of the grab wanted */ + + if (height < BUZ_MIN_HEIGHT || width < BUZ_MIN_WIDTH || + height > BUZ_MAX_HEIGHT || width > BUZ_MAX_WIDTH) { + dprintk(1, + KERN_ERR + "%s: v4l_set_format() - wrong frame size (%dx%d)\n", + ZR_DEVNAME(zr), width, height); + return -EINVAL; + } + + bpp = (format->depth + 7) / 8; + + /* Check against available buffer size */ + if (height * width * bpp > fh->v4l_buffers.buffer_size) { + dprintk(1, + KERN_ERR + "%s: v4l_set_format() - video buffer size (%d kB) is too small\n", + ZR_DEVNAME(zr), fh->v4l_buffers.buffer_size >> 10); + return -EINVAL; + } + + /* The video front end needs 4-byte alinged line sizes */ + + if ((bpp == 2 && (width & 1)) || (bpp == 3 && (width & 3))) { + dprintk(1, + KERN_ERR + "%s: v4l_set_format() - wrong frame alingment\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + + fh->v4l_settings.width = width; + fh->v4l_settings.height = height; + fh->v4l_settings.format = format; + fh->v4l_settings.bytesperline = bpp * fh->v4l_settings.width; + + return 0; +} + +static int +zoran_v4l_queue_frame (struct file *file, + int num) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + unsigned long flags; + int res = 0; + + if (!fh->v4l_buffers.allocated) { + dprintk(1, + KERN_ERR + "%s: v4l_queue_frame() - buffers not yet allocated\n", + ZR_DEVNAME(zr)); + res = -ENOMEM; + } + + /* No grabbing outside the buffer range! */ + if (num >= fh->v4l_buffers.num_buffers || num < 0) { + dprintk(1, + KERN_ERR + "%s: v4l_queue_frame() - buffer %d is out of range\n", + ZR_DEVNAME(zr), num); + res = -EINVAL; + } + + spin_lock_irqsave(&zr->spinlock, flags); + + if (fh->v4l_buffers.active == ZORAN_FREE) { + if (zr->v4l_buffers.active == ZORAN_FREE) { + zr->v4l_buffers = fh->v4l_buffers; + fh->v4l_buffers.active = ZORAN_ACTIVE; + } else { + dprintk(1, + KERN_ERR + "%s: v4l_queue_frame() - another session is already capturing\n", + ZR_DEVNAME(zr)); + res = -EBUSY; + } + } + + /* make sure a grab isn't going on currently with this buffer */ + if (!res) { + switch (zr->v4l_buffers.buffer[num].state) { + default: + case BUZ_STATE_PEND: + if (zr->v4l_buffers.active == ZORAN_FREE) { + fh->v4l_buffers.active = ZORAN_FREE; + zr->v4l_buffers.allocated = 0; + } + res = -EBUSY; /* what are you doing? */ + break; + case BUZ_STATE_DONE: + dprintk(2, + KERN_WARNING + "%s: v4l_queue_frame() - queueing buffer %d in state DONE!?\n", + ZR_DEVNAME(zr), num); + case BUZ_STATE_USER: + /* since there is at least one unused buffer there's room for at least + * one more pend[] entry */ + zr->v4l_pend[zr->v4l_pend_head++ & + V4L_MASK_FRAME] = num; + zr->v4l_buffers.buffer[num].state = BUZ_STATE_PEND; + zr->v4l_buffers.buffer[num].bs.length = + fh->v4l_settings.bytesperline * + zr->v4l_settings.height; + fh->v4l_buffers.buffer[num] = + zr->v4l_buffers.buffer[num]; + break; + } + } + + spin_unlock_irqrestore(&zr->spinlock, flags); + + if (!res && zr->v4l_buffers.active == ZORAN_FREE) + zr->v4l_buffers.active = fh->v4l_buffers.active; + + return res; +} + +static int +v4l_grab (struct file *file, + struct video_mmap *mp) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + int res = 0, i; + + for (i = 0; i < zoran_num_formats; i++) { + if (zoran_formats[i].palette == mp->format && + zoran_formats[i].flags & ZORAN_FORMAT_CAPTURE && + !(zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED)) + break; + } + if (i == zoran_num_formats || zoran_formats[i].depth == 0) { + dprintk(1, + KERN_ERR + "%s: v4l_grab() - wrong bytes-per-pixel format\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + + /* + * To minimize the time spent in the IRQ routine, we avoid setting up + * the video front end there. + * If this grab has different parameters from a running streaming capture + * we stop the streaming capture and start it over again. + */ + if (zr->v4l_memgrab_active && + (zr->v4l_settings.width != mp->width || + zr->v4l_settings.height != mp->height || + zr->v4l_settings.format->palette != mp->format)) { + res = wait_grab_pending(zr); + if (res) + return res; + } + if ((res = zoran_v4l_set_format(file, + mp->width, + mp->height, + &zoran_formats[i]))) + return res; + zr->v4l_settings = fh->v4l_settings; + + /* queue the frame in the pending queue */ + if ((res = zoran_v4l_queue_frame(file, mp->frame))) { + fh->v4l_buffers.active = ZORAN_FREE; + return res; + } + + /* put the 36057 into frame grabbing mode */ + if (!res && !zr->v4l_memgrab_active) + zr36057_set_memgrab(zr, 1); + + //dprintk(4, KERN_INFO "%s: Frame grab 3...\n", ZR_DEVNAME(zr)); + + return res; +} + +/* + * Sync on a V4L buffer + */ + +static int +v4l_sync (struct file *file, + int frame) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + unsigned long flags; + + if (fh->v4l_buffers.active == ZORAN_FREE) { + dprintk(1, + KERN_ERR + "%s: v4l_sync() - no grab active for this session\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + + /* check passed-in frame number */ + if (frame >= fh->v4l_buffers.num_buffers || frame < 0) { + dprintk(1, + KERN_ERR "%s: v4l_sync() - frame %d is invalid\n", + ZR_DEVNAME(zr), frame); + return -EINVAL; + } + + /* Check if is buffer was queued at all */ + if (zr->v4l_buffers.buffer[frame].state == BUZ_STATE_USER) { + dprintk(1, + KERN_ERR + "%s: v4l_sync() - attempt to sync on a buffer which was not queued?\n", + ZR_DEVNAME(zr)); + return -EPROTO; + } + + /* wait on this buffer to get ready */ + if (!wait_event_interruptible_timeout(zr->v4l_capq, + (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND), + 10*HZ)) + return -ETIME; + if (signal_pending(current)) + return -ERESTARTSYS; + + /* buffer should now be in BUZ_STATE_DONE */ + if (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_DONE) + dprintk(2, + KERN_ERR "%s: v4l_sync() - internal state error\n", + ZR_DEVNAME(zr)); + + zr->v4l_buffers.buffer[frame].state = BUZ_STATE_USER; + fh->v4l_buffers.buffer[frame] = zr->v4l_buffers.buffer[frame]; + + spin_lock_irqsave(&zr->spinlock, flags); + + /* Check if streaming capture has finished */ + if (zr->v4l_pend_tail == zr->v4l_pend_head) { + zr36057_set_memgrab(zr, 0); + if (zr->v4l_buffers.active == ZORAN_ACTIVE) { + fh->v4l_buffers.active = zr->v4l_buffers.active = + ZORAN_FREE; + zr->v4l_buffers.allocated = 0; + } + } + + spin_unlock_irqrestore(&zr->spinlock, flags); + + return 0; +} + +/* + * Queue a MJPEG buffer for capture/playback + */ + +static int +zoran_jpg_queue_frame (struct file *file, + int num, + enum zoran_codec_mode mode) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + unsigned long flags; + int res = 0; + + /* Check if buffers are allocated */ + if (!fh->jpg_buffers.allocated) { + dprintk(1, + KERN_ERR + "%s: jpg_queue_frame() - buffers not yet allocated\n", + ZR_DEVNAME(zr)); + return -ENOMEM; + } + + /* No grabbing outside the buffer range! */ + if (num >= fh->jpg_buffers.num_buffers || num < 0) { + dprintk(1, + KERN_ERR + "%s: jpg_queue_frame() - buffer %d out of range\n", + ZR_DEVNAME(zr), num); + return -EINVAL; + } + + /* what is the codec mode right now? */ + if (zr->codec_mode == BUZ_MODE_IDLE) { + zr->jpg_settings = fh->jpg_settings; + } else if (zr->codec_mode != mode) { + /* wrong codec mode active - invalid */ + dprintk(1, + KERN_ERR + "%s: jpg_queue_frame() - codec in wrong mode\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + + if (fh->jpg_buffers.active == ZORAN_FREE) { + if (zr->jpg_buffers.active == ZORAN_FREE) { + zr->jpg_buffers = fh->jpg_buffers; + fh->jpg_buffers.active = ZORAN_ACTIVE; + } else { + dprintk(1, + KERN_ERR + "%s: jpg_queue_frame() - another session is already capturing\n", + ZR_DEVNAME(zr)); + res = -EBUSY; + } + } + + if (!res && zr->codec_mode == BUZ_MODE_IDLE) { + /* Ok load up the jpeg codec */ + zr36057_enable_jpg(zr, mode); + } + + spin_lock_irqsave(&zr->spinlock, flags); + + if (!res) { + switch (zr->jpg_buffers.buffer[num].state) { + case BUZ_STATE_DONE: + dprintk(2, + KERN_WARNING + "%s: jpg_queue_frame() - queing frame in BUZ_STATE_DONE state!?\n", + ZR_DEVNAME(zr)); + case BUZ_STATE_USER: + /* since there is at least one unused buffer there's room for at + *least one more pend[] entry */ + zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = + num; + zr->jpg_buffers.buffer[num].state = BUZ_STATE_PEND; + fh->jpg_buffers.buffer[num] = + zr->jpg_buffers.buffer[num]; + zoran_feed_stat_com(zr); + break; + default: + case BUZ_STATE_DMA: + case BUZ_STATE_PEND: + if (zr->jpg_buffers.active == ZORAN_FREE) { + fh->jpg_buffers.active = ZORAN_FREE; + zr->jpg_buffers.allocated = 0; + } + res = -EBUSY; /* what are you doing? */ + break; + } + } + + spin_unlock_irqrestore(&zr->spinlock, flags); + + if (!res && zr->jpg_buffers.active == ZORAN_FREE) { + zr->jpg_buffers.active = fh->jpg_buffers.active; + } + + return res; +} + +static int +jpg_qbuf (struct file *file, + int frame, + enum zoran_codec_mode mode) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + int res = 0; + + /* Does the user want to stop streaming? */ + if (frame < 0) { + if (zr->codec_mode == mode) { + if (fh->jpg_buffers.active == ZORAN_FREE) { + dprintk(1, + KERN_ERR + "%s: jpg_qbuf(-1) - session not active\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + fh->jpg_buffers.active = zr->jpg_buffers.active = + ZORAN_FREE; + zr->jpg_buffers.allocated = 0; + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); + return 0; + } else { + dprintk(1, + KERN_ERR + "%s: jpg_qbuf() - stop streaming but not in streaming mode\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + } + + if ((res = zoran_jpg_queue_frame(file, frame, mode))) + return res; + + /* Start the jpeg codec when the first frame is queued */ + if (!res && zr->jpg_que_head == 1) + jpeg_start(zr); + + return res; +} + +/* + * Sync on a MJPEG buffer + */ + +static int +jpg_sync (struct file *file, + struct zoran_sync *bs) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + unsigned long flags; + int frame; + + if (fh->jpg_buffers.active == ZORAN_FREE) { + dprintk(1, + KERN_ERR + "%s: jpg_sync() - capture is not currently active\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS && + zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) { + dprintk(1, + KERN_ERR + "%s: jpg_sync() - codec not in streaming mode\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + if (!wait_event_interruptible_timeout(zr->jpg_capq, + (zr->jpg_que_tail != zr->jpg_dma_tail || + zr->jpg_dma_tail == zr->jpg_dma_head), + 10*HZ)) { + int isr; + + btand(~ZR36057_JMC_Go_en, ZR36057_JMC); + udelay(1); + zr->codec->control(zr->codec, CODEC_G_STATUS, + sizeof(isr), &isr); + dprintk(1, + KERN_ERR + "%s: jpg_sync() - timeout: codec isr=0x%02x\n", + ZR_DEVNAME(zr), isr); + + return -ETIME; + + } + if (signal_pending(current)) + return -ERESTARTSYS; + + spin_lock_irqsave(&zr->spinlock, flags); + + if (zr->jpg_dma_tail != zr->jpg_dma_head) + frame = zr->jpg_pend[zr->jpg_que_tail++ & BUZ_MASK_FRAME]; + else + frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME]; + + /* buffer should now be in BUZ_STATE_DONE */ + if (*zr_debug > 0) + if (zr->jpg_buffers.buffer[frame].state != BUZ_STATE_DONE) + dprintk(2, + KERN_ERR + "%s: jpg_sync() - internal state error\n", + ZR_DEVNAME(zr)); + + *bs = zr->jpg_buffers.buffer[frame].bs; + bs->frame = frame; + zr->jpg_buffers.buffer[frame].state = BUZ_STATE_USER; + fh->jpg_buffers.buffer[frame] = zr->jpg_buffers.buffer[frame]; + + spin_unlock_irqrestore(&zr->spinlock, flags); + + return 0; +} + +static void +zoran_open_init_session (struct file *file) +{ + int i; + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + + /* Per default, map the V4L Buffers */ + fh->map_mode = ZORAN_MAP_MODE_RAW; + + /* take over the card's current settings */ + fh->overlay_settings = zr->overlay_settings; + fh->overlay_settings.is_set = 0; + fh->overlay_settings.format = zr->overlay_settings.format; + fh->overlay_active = ZORAN_FREE; + + /* v4l settings */ + fh->v4l_settings = zr->v4l_settings; + + /* v4l_buffers */ + memset(&fh->v4l_buffers, 0, sizeof(struct zoran_v4l_struct)); + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + fh->v4l_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ + fh->v4l_buffers.buffer[i].bs.frame = i; + } + fh->v4l_buffers.allocated = 0; + fh->v4l_buffers.ready_to_be_freed = 0; + fh->v4l_buffers.active = ZORAN_FREE; + fh->v4l_buffers.buffer_size = v4l_bufsize; + fh->v4l_buffers.num_buffers = v4l_nbufs; + + /* jpg settings */ + fh->jpg_settings = zr->jpg_settings; + + /* jpg_buffers */ + memset(&fh->jpg_buffers, 0, sizeof(struct zoran_jpg_struct)); + for (i = 0; i < BUZ_MAX_FRAME; i++) { + fh->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ + fh->jpg_buffers.buffer[i].bs.frame = i; + } + fh->jpg_buffers.need_contiguous = zr->jpg_buffers.need_contiguous; + fh->jpg_buffers.allocated = 0; + fh->jpg_buffers.ready_to_be_freed = 0; + fh->jpg_buffers.active = ZORAN_FREE; + fh->jpg_buffers.buffer_size = jpg_bufsize; + fh->jpg_buffers.num_buffers = jpg_nbufs; +} + +static void +zoran_close_end_session (struct file *file) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + + /* overlay */ + if (fh->overlay_active != ZORAN_FREE) { + fh->overlay_active = zr->overlay_active = ZORAN_FREE; + zr->v4l_overlay_active = 0; + if (!zr->v4l_memgrab_active) + zr36057_overlay(zr, 0); + zr->overlay_mask = NULL; + } + + /* v4l capture */ + if (fh->v4l_buffers.active != ZORAN_FREE) { + zr36057_set_memgrab(zr, 0); + zr->v4l_buffers.allocated = 0; + zr->v4l_buffers.active = fh->v4l_buffers.active = + ZORAN_FREE; + } + + /* v4l buffers */ + if (fh->v4l_buffers.allocated || + fh->v4l_buffers.ready_to_be_freed) { + v4l_fbuffer_free(file); + } + + /* jpg capture */ + if (fh->jpg_buffers.active != ZORAN_FREE) { + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); + zr->jpg_buffers.allocated = 0; + zr->jpg_buffers.active = fh->jpg_buffers.active = + ZORAN_FREE; + } + + /* jpg buffers */ + if (fh->jpg_buffers.allocated || + fh->jpg_buffers.ready_to_be_freed) { + jpg_fbuffer_free(file); + } +} + +/* + * Open a zoran card. Right now the flags stuff is just playing + */ + +static int +zoran_open (struct inode *inode, + struct file *file) +{ + unsigned int minor = iminor(inode); + struct zoran *zr = NULL; + struct zoran_fh *fh; + int i, res, first_open = 0, have_module_locks = 0; + + /* find the device */ + for (i = 0; i < zoran_num; i++) { + if (zoran[i].video_dev->minor == minor) { + zr = &zoran[i]; + break; + } + } + + if (!zr) { + dprintk(1, KERN_ERR "%s: device not found!\n", ZORAN_NAME); + res = -ENODEV; + goto open_unlock_and_return; + } + + /* see fs/device.c - the kernel already locks during open(), + * so locking ourselves only causes deadlocks */ + /*mutex_lock(&zr->resource_lock);*/ + + if (!zr->decoder) { + dprintk(1, + KERN_ERR "%s: no TV decoder loaded for device!\n", + ZR_DEVNAME(zr)); + res = -EIO; + goto open_unlock_and_return; + } + + /* try to grab a module lock */ + if (!try_module_get(THIS_MODULE)) { + dprintk(1, + KERN_ERR + "%s: failed to acquire my own lock! PANIC!\n", + ZR_DEVNAME(zr)); + res = -ENODEV; + goto open_unlock_and_return; + } + if (!try_module_get(zr->decoder->driver->driver.owner)) { + dprintk(1, + KERN_ERR + "%s: failed to grab ownership of i2c decoder\n", + ZR_DEVNAME(zr)); + res = -EIO; + module_put(THIS_MODULE); + goto open_unlock_and_return; + } + if (zr->encoder && + !try_module_get(zr->encoder->driver->driver.owner)) { + dprintk(1, + KERN_ERR + "%s: failed to grab ownership of i2c encoder\n", + ZR_DEVNAME(zr)); + res = -EIO; + module_put(zr->decoder->driver->driver.owner); + module_put(THIS_MODULE); + goto open_unlock_and_return; + } + + have_module_locks = 1; + + if (zr->user >= 2048) { + dprintk(1, KERN_ERR "%s: too many users (%d) on device\n", + ZR_DEVNAME(zr), zr->user); + res = -EBUSY; + goto open_unlock_and_return; + } + + dprintk(1, KERN_INFO "%s: zoran_open(%s, pid=[%d]), users(-)=%d\n", + ZR_DEVNAME(zr), current->comm, current->pid, zr->user); + + /* now, create the open()-specific file_ops struct */ + fh = kzalloc(sizeof(struct zoran_fh), GFP_KERNEL); + if (!fh) { + dprintk(1, + KERN_ERR + "%s: zoran_open() - allocation of zoran_fh failed\n", + ZR_DEVNAME(zr)); + res = -ENOMEM; + goto open_unlock_and_return; + } + /* used to be BUZ_MAX_WIDTH/HEIGHT, but that gives overflows + * on norm-change! */ + fh->overlay_mask = + kmalloc(((768 + 31) / 32) * 576 * 4, GFP_KERNEL); + if (!fh->overlay_mask) { + dprintk(1, + KERN_ERR + "%s: zoran_open() - allocation of overlay_mask failed\n", + ZR_DEVNAME(zr)); + kfree(fh); + res = -ENOMEM; + goto open_unlock_and_return; + } + + if (zr->user++ == 0) + first_open = 1; + + /*mutex_unlock(&zr->resource_lock);*/ + + /* default setup - TODO: look at flags */ + if (first_open) { /* First device open */ + zr36057_restart(zr); + zoran_open_init_params(zr); + zoran_init_hardware(zr); + + btor(ZR36057_ICR_IntPinEn, ZR36057_ICR); + } + + /* set file_ops stuff */ + file->private_data = fh; + fh->zr = zr; + zoran_open_init_session(file); + + return 0; + +open_unlock_and_return: + /* if we grabbed locks, release them accordingly */ + if (have_module_locks) { + module_put(zr->decoder->driver->driver.owner); + if (zr->encoder) { + module_put(zr->encoder->driver->driver.owner); + } + module_put(THIS_MODULE); + } + + /* if there's no device found, we didn't obtain the lock either */ + if (zr) { + /*mutex_unlock(&zr->resource_lock);*/ + } + + return res; +} + +static int +zoran_close (struct inode *inode, + struct file *file) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + + dprintk(1, KERN_INFO "%s: zoran_close(%s, pid=[%d]), users(+)=%d\n", + ZR_DEVNAME(zr), current->comm, current->pid, zr->user); + + /* kernel locks (fs/device.c), so don't do that ourselves + * (prevents deadlocks) */ + /*mutex_lock(&zr->resource_lock);*/ + + zoran_close_end_session(file); + + if (zr->user-- == 1) { /* Last process */ + /* Clean up JPEG process */ + wake_up_interruptible(&zr->jpg_capq); + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); + zr->jpg_buffers.allocated = 0; + zr->jpg_buffers.active = ZORAN_FREE; + + /* disable interrupts */ + btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR); + + if (*zr_debug > 1) + print_interrupts(zr); + + /* Overlay off */ + zr->v4l_overlay_active = 0; + zr36057_overlay(zr, 0); + zr->overlay_mask = NULL; + + /* capture off */ + wake_up_interruptible(&zr->v4l_capq); + zr36057_set_memgrab(zr, 0); + zr->v4l_buffers.allocated = 0; + zr->v4l_buffers.active = ZORAN_FREE; + zoran_set_pci_master(zr, 0); + + if (!pass_through) { /* Switch to color bar */ + int zero = 0, two = 2; + decoder_command(zr, DECODER_ENABLE_OUTPUT, &zero); + encoder_command(zr, ENCODER_SET_INPUT, &two); + } + } + + file->private_data = NULL; + kfree(fh->overlay_mask); + kfree(fh); + + /* release locks on the i2c modules */ + module_put(zr->decoder->driver->driver.owner); + if (zr->encoder) { + module_put(zr->encoder->driver->driver.owner); + } + module_put(THIS_MODULE); + + /*mutex_unlock(&zr->resource_lock);*/ + + dprintk(4, KERN_INFO "%s: zoran_close() done\n", ZR_DEVNAME(zr)); + + return 0; +} + + +static ssize_t +zoran_read (struct file *file, + char __user *data, + size_t count, + loff_t *ppos) +{ + /* we simply don't support read() (yet)... */ + + return -EINVAL; +} + +static ssize_t +zoran_write (struct file *file, + const char __user *data, + size_t count, + loff_t *ppos) +{ + /* ...and the same goes for write() */ + + return -EINVAL; +} + +static int +setup_fbuffer (struct file *file, + void *base, + const struct zoran_format *fmt, + int width, + int height, + int bytesperline) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + + /* (Ronald) v4l/v4l2 guidelines */ + if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* we need a bytesperline value, even if not given */ + if (!bytesperline) + bytesperline = width * ((fmt->depth + 7) & ~7) / 8; + +#if 0 + if (zr->overlay_active) { + /* dzjee... stupid users... don't even bother to turn off + * overlay before changing the memory location... + * normally, we would return errors here. However, one of + * the tools that does this is... xawtv! and since xawtv + * is used by +/- 99% of the users, we'd rather be user- + * friendly and silently do as if nothing went wrong */ + dprintk(3, + KERN_ERR + "%s: setup_fbuffer() - forced overlay turnoff because framebuffer changed\n", + ZR_DEVNAME(zr)); + zr36057_overlay(zr, 0); + } +#endif + + if (!(fmt->flags & ZORAN_FORMAT_OVERLAY)) { + dprintk(1, + KERN_ERR + "%s: setup_fbuffer() - no valid overlay format given\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + if (height <= 0 || width <= 0 || bytesperline <= 0) { + dprintk(1, + KERN_ERR + "%s: setup_fbuffer() - invalid height/width/bpl value (%d|%d|%d)\n", + ZR_DEVNAME(zr), width, height, bytesperline); + return -EINVAL; + } + if (bytesperline & 3) { + dprintk(1, + KERN_ERR + "%s: setup_fbuffer() - bytesperline (%d) must be 4-byte aligned\n", + ZR_DEVNAME(zr), bytesperline); + return -EINVAL; + } + + zr->buffer.base = (void *) ((unsigned long) base & ~3); + zr->buffer.height = height; + zr->buffer.width = width; + zr->buffer.depth = fmt->depth; + zr->overlay_settings.format = fmt; + zr->buffer.bytesperline = bytesperline; + + /* The user should set new window parameters */ + zr->overlay_settings.is_set = 0; + + return 0; +} + + +static int +setup_window (struct file *file, + int x, + int y, + int width, + int height, + struct video_clip __user *clips, + int clipcount, + void __user *bitmap) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + struct video_clip *vcp = NULL; + int on, end; + + + if (!zr->buffer.base) { + dprintk(1, + KERN_ERR + "%s: setup_window() - frame buffer has to be set first\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + + if (!fh->overlay_settings.format) { + dprintk(1, + KERN_ERR + "%s: setup_window() - no overlay format set\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + + /* + * The video front end needs 4-byte alinged line sizes, we correct that + * silently here if necessary + */ + if (zr->buffer.depth == 15 || zr->buffer.depth == 16) { + end = (x + width) & ~1; /* round down */ + x = (x + 1) & ~1; /* round up */ + width = end - x; + } + + if (zr->buffer.depth == 24) { + end = (x + width) & ~3; /* round down */ + x = (x + 3) & ~3; /* round up */ + width = end - x; + } + + if (width > BUZ_MAX_WIDTH) + width = BUZ_MAX_WIDTH; + if (height > BUZ_MAX_HEIGHT) + height = BUZ_MAX_HEIGHT; + + /* Check for vaild parameters */ + if (width < BUZ_MIN_WIDTH || height < BUZ_MIN_HEIGHT || + width > BUZ_MAX_WIDTH || height > BUZ_MAX_HEIGHT) { + dprintk(1, + KERN_ERR + "%s: setup_window() - width = %d or height = %d invalid\n", + ZR_DEVNAME(zr), width, height); + return -EINVAL; + } + + fh->overlay_settings.x = x; + fh->overlay_settings.y = y; + fh->overlay_settings.width = width; + fh->overlay_settings.height = height; + fh->overlay_settings.clipcount = clipcount; + + /* + * If an overlay is running, we have to switch it off + * and switch it on again in order to get the new settings in effect. + * + * We also want to avoid that the overlay mask is written + * when an overlay is running. + */ + + on = zr->v4l_overlay_active && !zr->v4l_memgrab_active && + zr->overlay_active != ZORAN_FREE && + fh->overlay_active != ZORAN_FREE; + if (on) + zr36057_overlay(zr, 0); + + /* + * Write the overlay mask if clips are wanted. + * We prefer a bitmap. + */ + if (bitmap) { + /* fake value - it just means we want clips */ + fh->overlay_settings.clipcount = 1; + + if (copy_from_user(fh->overlay_mask, bitmap, + (width * height + 7) / 8)) { + return -EFAULT; + } + } else if (clipcount > 0) { + /* write our own bitmap from the clips */ + vcp = vmalloc(sizeof(struct video_clip) * (clipcount + 4)); + if (vcp == NULL) { + dprintk(1, + KERN_ERR + "%s: setup_window() - Alloc of clip mask failed\n", + ZR_DEVNAME(zr)); + return -ENOMEM; + } + if (copy_from_user + (vcp, clips, sizeof(struct video_clip) * clipcount)) { + vfree(vcp); + return -EFAULT; + } + write_overlay_mask(file, vcp, clipcount); + vfree(vcp); + } + + fh->overlay_settings.is_set = 1; + if (fh->overlay_active != ZORAN_FREE && + zr->overlay_active != ZORAN_FREE) + zr->overlay_settings = fh->overlay_settings; + + if (on) + zr36057_overlay(zr, 1); + + /* Make sure the changes come into effect */ + return wait_grab_pending(zr); +} + +static int +setup_overlay (struct file *file, + int on) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + + /* If there is nothing to do, return immediatly */ + if ((on && fh->overlay_active != ZORAN_FREE) || + (!on && fh->overlay_active == ZORAN_FREE)) + return 0; + + /* check whether we're touching someone else's overlay */ + if (on && zr->overlay_active != ZORAN_FREE && + fh->overlay_active == ZORAN_FREE) { + dprintk(1, + KERN_ERR + "%s: setup_overlay() - overlay is already active for another session\n", + ZR_DEVNAME(zr)); + return -EBUSY; + } + if (!on && zr->overlay_active != ZORAN_FREE && + fh->overlay_active == ZORAN_FREE) { + dprintk(1, + KERN_ERR + "%s: setup_overlay() - you cannot cancel someone else's session\n", + ZR_DEVNAME(zr)); + return -EPERM; + } + + if (on == 0) { + zr->overlay_active = fh->overlay_active = ZORAN_FREE; + zr->v4l_overlay_active = 0; + /* When a grab is running, the video simply + * won't be switched on any more */ + if (!zr->v4l_memgrab_active) + zr36057_overlay(zr, 0); + zr->overlay_mask = NULL; + } else { + if (!zr->buffer.base || !fh->overlay_settings.is_set) { + dprintk(1, + KERN_ERR + "%s: setup_overlay() - buffer or window not set\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + if (!fh->overlay_settings.format) { + dprintk(1, + KERN_ERR + "%s: setup_overlay() - no overlay format set\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + zr->overlay_active = fh->overlay_active = ZORAN_LOCKED; + zr->v4l_overlay_active = 1; + zr->overlay_mask = fh->overlay_mask; + zr->overlay_settings = fh->overlay_settings; + if (!zr->v4l_memgrab_active) + zr36057_overlay(zr, 1); + /* When a grab is running, the video will be + * switched on when grab is finished */ + } + + /* Make sure the changes come into effect */ + return wait_grab_pending(zr); +} + +#ifdef HAVE_V4L2 + /* get the status of a buffer in the clients buffer queue */ +static int +zoran_v4l2_buffer_status (struct file *file, + struct v4l2_buffer *buf, + int num) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + + buf->flags = V4L2_BUF_FLAG_MAPPED; + + switch (fh->map_mode) { + case ZORAN_MAP_MODE_RAW: + + /* check range */ + if (num < 0 || num >= fh->v4l_buffers.num_buffers || + !fh->v4l_buffers.allocated) { + dprintk(1, + KERN_ERR + "%s: v4l2_buffer_status() - wrong number or buffers not allocated\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + + buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->length = fh->v4l_buffers.buffer_size; + + /* get buffer */ + buf->bytesused = fh->v4l_buffers.buffer[num].bs.length; + if (fh->v4l_buffers.buffer[num].state == BUZ_STATE_DONE || + fh->v4l_buffers.buffer[num].state == BUZ_STATE_USER) { + buf->sequence = fh->v4l_buffers.buffer[num].bs.seq; + buf->flags |= V4L2_BUF_FLAG_DONE; + buf->timestamp = + fh->v4l_buffers.buffer[num].bs.timestamp; + } else { + buf->flags |= V4L2_BUF_FLAG_QUEUED; + } + + if (fh->v4l_settings.height <= BUZ_MAX_HEIGHT / 2) + buf->field = V4L2_FIELD_TOP; + else + buf->field = V4L2_FIELD_INTERLACED; + + break; + + case ZORAN_MAP_MODE_JPG_REC: + case ZORAN_MAP_MODE_JPG_PLAY: + + /* check range */ + if (num < 0 || num >= fh->jpg_buffers.num_buffers || + !fh->jpg_buffers.allocated) { + dprintk(1, + KERN_ERR + "%s: v4l2_buffer_status() - wrong number or buffers not allocated\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + + buf->type = (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ? + V4L2_BUF_TYPE_VIDEO_CAPTURE : + V4L2_BUF_TYPE_VIDEO_OUTPUT; + buf->length = fh->jpg_buffers.buffer_size; + + /* these variables are only written after frame has been captured */ + if (fh->jpg_buffers.buffer[num].state == BUZ_STATE_DONE || + fh->jpg_buffers.buffer[num].state == BUZ_STATE_USER) { + buf->sequence = fh->jpg_buffers.buffer[num].bs.seq; + buf->timestamp = + fh->jpg_buffers.buffer[num].bs.timestamp; + buf->bytesused = + fh->jpg_buffers.buffer[num].bs.length; + buf->flags |= V4L2_BUF_FLAG_DONE; + } else { + buf->flags |= V4L2_BUF_FLAG_QUEUED; + } + + /* which fields are these? */ + if (fh->jpg_settings.TmpDcm != 1) + buf->field = + fh->jpg_settings. + odd_even ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM; + else + buf->field = + fh->jpg_settings. + odd_even ? V4L2_FIELD_SEQ_TB : + V4L2_FIELD_SEQ_BT; + + break; + + default: + + dprintk(5, + KERN_ERR + "%s: v4l2_buffer_status() - invalid buffer type|map_mode (%d|%d)\n", + ZR_DEVNAME(zr), buf->type, fh->map_mode); + return -EINVAL; + } + + buf->memory = V4L2_MEMORY_MMAP; + buf->index = num; + buf->m.offset = buf->length * num; + + return 0; +} +#endif + +static int +zoran_set_norm (struct zoran *zr, + int norm) /* VIDEO_MODE_* */ +{ + int norm_encoder, on; + + if (zr->v4l_buffers.active != ZORAN_FREE || + zr->jpg_buffers.active != ZORAN_FREE) { + dprintk(1, + KERN_WARNING + "%s: set_norm() called while in playback/capture mode\n", + ZR_DEVNAME(zr)); + return -EBUSY; + } + + if (lock_norm && norm != zr->norm) { + if (lock_norm > 1) { + dprintk(1, + KERN_WARNING + "%s: set_norm() - TV standard is locked, can not switch norm\n", + ZR_DEVNAME(zr)); + return -EPERM; + } else { + dprintk(1, + KERN_WARNING + "%s: set_norm() - TV standard is locked, norm was not changed\n", + ZR_DEVNAME(zr)); + norm = zr->norm; + } + } + + if (norm != VIDEO_MODE_AUTO && + (norm < 0 || norm >= zr->card.norms || + !zr->card.tvn[norm])) { + dprintk(1, + KERN_ERR "%s: set_norm() - unsupported norm %d\n", + ZR_DEVNAME(zr), norm); + return -EINVAL; + } + + if (norm == VIDEO_MODE_AUTO) { + int status; + + /* if we have autodetect, ... */ + struct video_decoder_capability caps; + decoder_command(zr, DECODER_GET_CAPABILITIES, &caps); + if (!(caps.flags & VIDEO_DECODER_AUTO)) { + dprintk(1, KERN_ERR "%s: norm=auto unsupported\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + + decoder_command(zr, DECODER_SET_NORM, &norm); + + /* let changes come into effect */ + ssleep(2); + + decoder_command(zr, DECODER_GET_STATUS, &status); + if (!(status & DECODER_STATUS_GOOD)) { + dprintk(1, + KERN_ERR + "%s: set_norm() - no norm detected\n", + ZR_DEVNAME(zr)); + /* reset norm */ + decoder_command(zr, DECODER_SET_NORM, &zr->norm); + return -EIO; + } + + if (status & DECODER_STATUS_NTSC) + norm = VIDEO_MODE_NTSC; + else if (status & DECODER_STATUS_SECAM) + norm = VIDEO_MODE_SECAM; + else + norm = VIDEO_MODE_PAL; + } + zr->timing = zr->card.tvn[norm]; + norm_encoder = norm; + + /* We switch overlay off and on since a change in the + * norm needs different VFE settings */ + on = zr->overlay_active && !zr->v4l_memgrab_active; + if (on) + zr36057_overlay(zr, 0); + + decoder_command(zr, DECODER_SET_NORM, &norm); + encoder_command(zr, ENCODER_SET_NORM, &norm_encoder); + + if (on) + zr36057_overlay(zr, 1); + + /* Make sure the changes come into effect */ + zr->norm = norm; + + return 0; +} + +static int +zoran_set_input (struct zoran *zr, + int input) +{ + int realinput; + + if (input == zr->input) { + return 0; + } + + if (zr->v4l_buffers.active != ZORAN_FREE || + zr->jpg_buffers.active != ZORAN_FREE) { + dprintk(1, + KERN_WARNING + "%s: set_input() called while in playback/capture mode\n", + ZR_DEVNAME(zr)); + return -EBUSY; + } + + if (input < 0 || input >= zr->card.inputs) { + dprintk(1, + KERN_ERR + "%s: set_input() - unnsupported input %d\n", + ZR_DEVNAME(zr), input); + return -EINVAL; + } + + realinput = zr->card.input[input].muxsel; + zr->input = input; + + decoder_command(zr, DECODER_SET_INPUT, &realinput); + + return 0; +} + +/* + * ioctl routine + */ + +static int +zoran_do_ioctl (struct inode *inode, + struct file *file, + unsigned int cmd, + void *arg) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + /* CAREFUL: used in multiple places here */ + struct zoran_jpg_settings settings; + + /* we might have older buffers lying around... We don't want + * to wait, but we do want to try cleaning them up ASAP. So + * we try to obtain the lock and free them. If that fails, we + * don't do anything and wait for the next turn. In the end, + * zoran_close() or a new allocation will still free them... + * This is just a 'the sooner the better' extra 'feature' + * + * We don't free the buffers right on munmap() because that + * causes oopses (kfree() inside munmap() oopses for no + * apparent reason - it's also not reproduceable in any way, + * but moving the free code outside the munmap() handler fixes + * all this... If someone knows why, please explain me (Ronald) + */ + if (!!mutex_trylock(&zr->resource_lock)) { + /* we obtained it! Let's try to free some things */ + if (fh->jpg_buffers.ready_to_be_freed) + jpg_fbuffer_free(file); + if (fh->v4l_buffers.ready_to_be_freed) + v4l_fbuffer_free(file); + + mutex_unlock(&zr->resource_lock); + } + + switch (cmd) { + + case VIDIOCGCAP: + { + struct video_capability *vcap = arg; + + dprintk(3, KERN_DEBUG "%s: VIDIOCGCAP\n", ZR_DEVNAME(zr)); + + memset(vcap, 0, sizeof(struct video_capability)); + strncpy(vcap->name, ZR_DEVNAME(zr), sizeof(vcap->name)); + vcap->type = ZORAN_VID_TYPE; + + vcap->channels = zr->card.inputs; + vcap->audios = 0; + mutex_lock(&zr->resource_lock); + vcap->maxwidth = BUZ_MAX_WIDTH; + vcap->maxheight = BUZ_MAX_HEIGHT; + vcap->minwidth = BUZ_MIN_WIDTH; + vcap->minheight = BUZ_MIN_HEIGHT; + mutex_unlock(&zr->resource_lock); + + return 0; + } + break; + + case VIDIOCGCHAN: + { + struct video_channel *vchan = arg; + int channel = vchan->channel; + + dprintk(3, KERN_DEBUG "%s: VIDIOCGCHAN - channel=%d\n", + ZR_DEVNAME(zr), vchan->channel); + + memset(vchan, 0, sizeof(struct video_channel)); + if (channel > zr->card.inputs || channel < 0) { + dprintk(1, + KERN_ERR + "%s: VIDIOCGCHAN on not existing channel %d\n", + ZR_DEVNAME(zr), channel); + return -EINVAL; + } + + strcpy(vchan->name, zr->card.input[channel].name); + + vchan->tuners = 0; + vchan->flags = 0; + vchan->type = VIDEO_TYPE_CAMERA; + mutex_lock(&zr->resource_lock); + vchan->norm = zr->norm; + mutex_unlock(&zr->resource_lock); + vchan->channel = channel; + + return 0; + } + break; + + /* RJ: the documentation at http://roadrunner.swansea.linux.org.uk/v4lapi.shtml says: + * + * * "The VIDIOCSCHAN ioctl takes an integer argument and switches the capture to this input." + * * ^^^^^^^ + * * The famos BTTV driver has it implemented with a struct video_channel argument + * * and we follow it for compatibility reasons + * * + * * BTW: this is the only way the user can set the norm! + */ + + case VIDIOCSCHAN: + { + struct video_channel *vchan = arg; + int res; + + dprintk(3, + KERN_DEBUG + "%s: VIDIOCSCHAN - channel=%d, norm=%d\n", + ZR_DEVNAME(zr), vchan->channel, vchan->norm); + + mutex_lock(&zr->resource_lock); + if ((res = zoran_set_input(zr, vchan->channel))) + goto schan_unlock_and_return; + if ((res = zoran_set_norm(zr, vchan->norm))) + goto schan_unlock_and_return; + + /* Make sure the changes come into effect */ + res = wait_grab_pending(zr); + schan_unlock_and_return: + mutex_unlock(&zr->resource_lock); + return res; + } + break; + + case VIDIOCGPICT: + { + struct video_picture *vpict = arg; + + dprintk(3, KERN_DEBUG "%s: VIDIOCGPICT\n", ZR_DEVNAME(zr)); + + memset(vpict, 0, sizeof(struct video_picture)); + mutex_lock(&zr->resource_lock); + vpict->hue = zr->hue; + vpict->brightness = zr->brightness; + vpict->contrast = zr->contrast; + vpict->colour = zr->saturation; + if (fh->overlay_settings.format) { + vpict->depth = fh->overlay_settings.format->depth; + vpict->palette = fh->overlay_settings.format->palette; + } else { + vpict->depth = 0; + } + mutex_unlock(&zr->resource_lock); + + return 0; + } + break; + + case VIDIOCSPICT: + { + struct video_picture *vpict = arg; + int i; + + dprintk(3, + KERN_DEBUG + "%s: VIDIOCSPICT - bri=%d, hue=%d, col=%d, con=%d, dep=%d, pal=%d\n", + ZR_DEVNAME(zr), vpict->brightness, vpict->hue, + vpict->colour, vpict->contrast, vpict->depth, + vpict->palette); + + for (i = 0; i < zoran_num_formats; i++) { + const struct zoran_format *fmt = &zoran_formats[i]; + + if (fmt->palette != -1 && + fmt->flags & ZORAN_FORMAT_OVERLAY && + fmt->palette == vpict->palette && + fmt->depth == vpict->depth) + break; + } + if (i == zoran_num_formats) { + dprintk(1, + KERN_ERR + "%s: VIDIOCSPICT - Invalid palette %d\n", + ZR_DEVNAME(zr), vpict->palette); + return -EINVAL; + } + + mutex_lock(&zr->resource_lock); + + decoder_command(zr, DECODER_SET_PICTURE, vpict); + + zr->hue = vpict->hue; + zr->contrast = vpict->contrast; + zr->saturation = vpict->colour; + zr->brightness = vpict->brightness; + + fh->overlay_settings.format = &zoran_formats[i]; + + mutex_unlock(&zr->resource_lock); + + return 0; + } + break; + + case VIDIOCCAPTURE: + { + int *on = arg, res; + + dprintk(3, KERN_DEBUG "%s: VIDIOCCAPTURE - on=%d\n", + ZR_DEVNAME(zr), *on); + + mutex_lock(&zr->resource_lock); + res = setup_overlay(file, *on); + mutex_unlock(&zr->resource_lock); + + return res; + } + break; + + case VIDIOCGWIN: + { + struct video_window *vwin = arg; + + dprintk(3, KERN_DEBUG "%s: VIDIOCGWIN\n", ZR_DEVNAME(zr)); + + memset(vwin, 0, sizeof(struct video_window)); + mutex_lock(&zr->resource_lock); + vwin->x = fh->overlay_settings.x; + vwin->y = fh->overlay_settings.y; + vwin->width = fh->overlay_settings.width; + vwin->height = fh->overlay_settings.height; + mutex_unlock(&zr->resource_lock); + vwin->clipcount = 0; + return 0; + } + break; + + case VIDIOCSWIN: + { + struct video_window *vwin = arg; + int res; + + dprintk(3, + KERN_DEBUG + "%s: VIDIOCSWIN - x=%d, y=%d, w=%d, h=%d, clipcount=%d\n", + ZR_DEVNAME(zr), vwin->x, vwin->y, vwin->width, + vwin->height, vwin->clipcount); + + mutex_lock(&zr->resource_lock); + res = + setup_window(file, vwin->x, vwin->y, vwin->width, + vwin->height, vwin->clips, + vwin->clipcount, NULL); + mutex_unlock(&zr->resource_lock); + + return res; + } + break; + + case VIDIOCGFBUF: + { + struct video_buffer *vbuf = arg; + + dprintk(3, KERN_DEBUG "%s: VIDIOCGFBUF\n", ZR_DEVNAME(zr)); + + mutex_lock(&zr->resource_lock); + *vbuf = zr->buffer; + mutex_unlock(&zr->resource_lock); + return 0; + } + break; + + case VIDIOCSFBUF: + { + struct video_buffer *vbuf = arg; + int i, res = 0; + + dprintk(3, + KERN_DEBUG + "%s: VIDIOCSFBUF - base=%p, w=%d, h=%d, depth=%d, bpl=%d\n", + ZR_DEVNAME(zr), vbuf->base, vbuf->width, + vbuf->height, vbuf->depth, vbuf->bytesperline); + + for (i = 0; i < zoran_num_formats; i++) + if (zoran_formats[i].depth == vbuf->depth) + break; + if (i == zoran_num_formats) { + dprintk(1, + KERN_ERR + "%s: VIDIOCSFBUF - invalid fbuf depth %d\n", + ZR_DEVNAME(zr), vbuf->depth); + return -EINVAL; + } + + mutex_lock(&zr->resource_lock); + res = + setup_fbuffer(file, vbuf->base, &zoran_formats[i], + vbuf->width, vbuf->height, + vbuf->bytesperline); + mutex_unlock(&zr->resource_lock); + + return res; + } + break; + + case VIDIOCSYNC: + { + int *frame = arg, res; + + dprintk(3, KERN_DEBUG "%s: VIDIOCSYNC - frame=%d\n", + ZR_DEVNAME(zr), *frame); + + mutex_lock(&zr->resource_lock); + res = v4l_sync(file, *frame); + mutex_unlock(&zr->resource_lock); + if (!res) + zr->v4l_sync_tail++; + return res; + } + break; + + case VIDIOCMCAPTURE: + { + struct video_mmap *vmap = arg; + int res; + + dprintk(3, + KERN_DEBUG + "%s: VIDIOCMCAPTURE - frame=%d, geom=%dx%d, fmt=%d\n", + ZR_DEVNAME(zr), vmap->frame, vmap->width, vmap->height, + vmap->format); + + mutex_lock(&zr->resource_lock); + res = v4l_grab(file, vmap); + mutex_unlock(&zr->resource_lock); + return res; + } + break; + + case VIDIOCGMBUF: + { + struct video_mbuf *vmbuf = arg; + int i, res = 0; + + dprintk(3, KERN_DEBUG "%s: VIDIOCGMBUF\n", ZR_DEVNAME(zr)); + + vmbuf->size = + fh->v4l_buffers.num_buffers * + fh->v4l_buffers.buffer_size; + vmbuf->frames = fh->v4l_buffers.num_buffers; + for (i = 0; i < vmbuf->frames; i++) { + vmbuf->offsets[i] = + i * fh->v4l_buffers.buffer_size; + } + + mutex_lock(&zr->resource_lock); + + if (fh->jpg_buffers.allocated || fh->v4l_buffers.allocated) { + dprintk(1, + KERN_ERR + "%s: VIDIOCGMBUF - buffers already allocated\n", + ZR_DEVNAME(zr)); + res = -EINVAL; + goto v4l1reqbuf_unlock_and_return; + } + + if (v4l_fbuffer_alloc(file)) { + res = -ENOMEM; + goto v4l1reqbuf_unlock_and_return; + } + + /* The next mmap will map the V4L buffers */ + fh->map_mode = ZORAN_MAP_MODE_RAW; + v4l1reqbuf_unlock_and_return: + mutex_unlock(&zr->resource_lock); + + return res; + } + break; + + case VIDIOCGUNIT: + { + struct video_unit *vunit = arg; + + dprintk(3, KERN_DEBUG "%s: VIDIOCGUNIT\n", ZR_DEVNAME(zr)); + + vunit->video = zr->video_dev->minor; + vunit->vbi = VIDEO_NO_UNIT; + vunit->radio = VIDEO_NO_UNIT; + vunit->audio = VIDEO_NO_UNIT; + vunit->teletext = VIDEO_NO_UNIT; + + return 0; + } + break; + + /* + * RJ: In principal we could support subcaptures for V4L grabbing. + * Not even the famous BTTV driver has them, however. + * If there should be a strong demand, one could consider + * to implement them. + */ + case VIDIOCGCAPTURE: + { + dprintk(3, KERN_ERR "%s: VIDIOCGCAPTURE not supported\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + break; + + case VIDIOCSCAPTURE: + { + dprintk(3, KERN_ERR "%s: VIDIOCSCAPTURE not supported\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + break; + + case BUZIOC_G_PARAMS: + { + struct zoran_params *bparams = arg; + + dprintk(3, KERN_DEBUG "%s: BUZIOC_G_PARAMS\n", ZR_DEVNAME(zr)); + + memset(bparams, 0, sizeof(struct zoran_params)); + bparams->major_version = MAJOR_VERSION; + bparams->minor_version = MINOR_VERSION; + + mutex_lock(&zr->resource_lock); + + bparams->norm = zr->norm; + bparams->input = zr->input; + + bparams->decimation = fh->jpg_settings.decimation; + bparams->HorDcm = fh->jpg_settings.HorDcm; + bparams->VerDcm = fh->jpg_settings.VerDcm; + bparams->TmpDcm = fh->jpg_settings.TmpDcm; + bparams->field_per_buff = fh->jpg_settings.field_per_buff; + bparams->img_x = fh->jpg_settings.img_x; + bparams->img_y = fh->jpg_settings.img_y; + bparams->img_width = fh->jpg_settings.img_width; + bparams->img_height = fh->jpg_settings.img_height; + bparams->odd_even = fh->jpg_settings.odd_even; + + bparams->quality = fh->jpg_settings.jpg_comp.quality; + bparams->APPn = fh->jpg_settings.jpg_comp.APPn; + bparams->APP_len = fh->jpg_settings.jpg_comp.APP_len; + memcpy(bparams->APP_data, + fh->jpg_settings.jpg_comp.APP_data, + sizeof(bparams->APP_data)); + bparams->COM_len = zr->jpg_settings.jpg_comp.COM_len; + memcpy(bparams->COM_data, + fh->jpg_settings.jpg_comp.COM_data, + sizeof(bparams->COM_data)); + bparams->jpeg_markers = + fh->jpg_settings.jpg_comp.jpeg_markers; + + mutex_unlock(&zr->resource_lock); + + bparams->VFIFO_FB = 0; + + return 0; + } + break; + + case BUZIOC_S_PARAMS: + { + struct zoran_params *bparams = arg; + int res = 0; + + dprintk(3, KERN_DEBUG "%s: BUZIOC_S_PARAMS\n", ZR_DEVNAME(zr)); + + settings.decimation = bparams->decimation; + settings.HorDcm = bparams->HorDcm; + settings.VerDcm = bparams->VerDcm; + settings.TmpDcm = bparams->TmpDcm; + settings.field_per_buff = bparams->field_per_buff; + settings.img_x = bparams->img_x; + settings.img_y = bparams->img_y; + settings.img_width = bparams->img_width; + settings.img_height = bparams->img_height; + settings.odd_even = bparams->odd_even; + + settings.jpg_comp.quality = bparams->quality; + settings.jpg_comp.APPn = bparams->APPn; + settings.jpg_comp.APP_len = bparams->APP_len; + memcpy(settings.jpg_comp.APP_data, bparams->APP_data, + sizeof(bparams->APP_data)); + settings.jpg_comp.COM_len = bparams->COM_len; + memcpy(settings.jpg_comp.COM_data, bparams->COM_data, + sizeof(bparams->COM_data)); + settings.jpg_comp.jpeg_markers = bparams->jpeg_markers; + + mutex_lock(&zr->resource_lock); + + if (zr->codec_mode != BUZ_MODE_IDLE) { + dprintk(1, + KERN_ERR + "%s: BUZIOC_S_PARAMS called, but Buz in capture/playback mode\n", + ZR_DEVNAME(zr)); + res = -EINVAL; + goto sparams_unlock_and_return; + } + + /* Check the params first before overwriting our + * nternal values */ + if (zoran_check_jpg_settings(zr, &settings)) { + res = -EINVAL; + goto sparams_unlock_and_return; + } + + fh->jpg_settings = settings; + sparams_unlock_and_return: + mutex_unlock(&zr->resource_lock); + + return res; + } + break; + + case BUZIOC_REQBUFS: + { + struct zoran_requestbuffers *breq = arg; + int res = 0; + + dprintk(3, + KERN_DEBUG + "%s: BUZIOC_REQBUFS - count=%lu, size=%lu\n", + ZR_DEVNAME(zr), breq->count, breq->size); + + /* Enforce reasonable lower and upper limits */ + if (breq->count < 4) + breq->count = 4; /* Could be choosen smaller */ + if (breq->count > jpg_nbufs) + breq->count = jpg_nbufs; + breq->size = PAGE_ALIGN(breq->size); + if (breq->size < 8192) + breq->size = 8192; /* Arbitrary */ + /* breq->size is limited by 1 page for the stat_com + * tables to a Maximum of 2 MB */ + if (breq->size > jpg_bufsize) + breq->size = jpg_bufsize; + if (fh->jpg_buffers.need_contiguous && + breq->size > MAX_KMALLOC_MEM) + breq->size = MAX_KMALLOC_MEM; + + mutex_lock(&zr->resource_lock); + + if (fh->jpg_buffers.allocated || fh->v4l_buffers.allocated) { + dprintk(1, + KERN_ERR + "%s: BUZIOC_REQBUFS - buffers allready allocated\n", + ZR_DEVNAME(zr)); + res = -EBUSY; + goto jpgreqbuf_unlock_and_return; + } + + fh->jpg_buffers.num_buffers = breq->count; + fh->jpg_buffers.buffer_size = breq->size; + + if (jpg_fbuffer_alloc(file)) { + res = -ENOMEM; + goto jpgreqbuf_unlock_and_return; + } + + /* The next mmap will map the MJPEG buffers - could + * also be *_PLAY, but it doesn't matter here */ + fh->map_mode = ZORAN_MAP_MODE_JPG_REC; + jpgreqbuf_unlock_and_return: + mutex_unlock(&zr->resource_lock); + + return res; + } + break; + + case BUZIOC_QBUF_CAPT: + { + int *frame = arg, res; + + dprintk(3, KERN_DEBUG "%s: BUZIOC_QBUF_CAPT - frame=%d\n", + ZR_DEVNAME(zr), *frame); + + mutex_lock(&zr->resource_lock); + res = jpg_qbuf(file, *frame, BUZ_MODE_MOTION_COMPRESS); + mutex_unlock(&zr->resource_lock); + + return res; + } + break; + + case BUZIOC_QBUF_PLAY: + { + int *frame = arg, res; + + dprintk(3, KERN_DEBUG "%s: BUZIOC_QBUF_PLAY - frame=%d\n", + ZR_DEVNAME(zr), *frame); + + mutex_lock(&zr->resource_lock); + res = jpg_qbuf(file, *frame, BUZ_MODE_MOTION_DECOMPRESS); + mutex_unlock(&zr->resource_lock); + + return res; + } + break; + + case BUZIOC_SYNC: + { + struct zoran_sync *bsync = arg; + int res; + + dprintk(3, KERN_DEBUG "%s: BUZIOC_SYNC\n", ZR_DEVNAME(zr)); + + mutex_lock(&zr->resource_lock); + res = jpg_sync(file, bsync); + mutex_unlock(&zr->resource_lock); + + return res; + } + break; + + case BUZIOC_G_STATUS: + { + struct zoran_status *bstat = arg; + int norm, input, status, res = 0; + + dprintk(3, KERN_DEBUG "%s: BUZIOC_G_STATUS\n", ZR_DEVNAME(zr)); + + if (zr->codec_mode != BUZ_MODE_IDLE) { + dprintk(1, + KERN_ERR + "%s: BUZIOC_G_STATUS called but Buz in capture/playback mode\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + + input = zr->card.input[bstat->input].muxsel; + norm = VIDEO_MODE_AUTO; + + mutex_lock(&zr->resource_lock); + + if (zr->codec_mode != BUZ_MODE_IDLE) { + dprintk(1, + KERN_ERR + "%s: BUZIOC_G_STATUS called, but Buz in capture/playback mode\n", + ZR_DEVNAME(zr)); + res = -EINVAL; + goto gstat_unlock_and_return; + } + + decoder_command(zr, DECODER_SET_INPUT, &input); + decoder_command(zr, DECODER_SET_NORM, &norm); + + /* sleep 1 second */ + ssleep(1); + + /* Get status of video decoder */ + decoder_command(zr, DECODER_GET_STATUS, &status); + + /* restore previous input and norm */ + input = zr->card.input[zr->input].muxsel; + decoder_command(zr, DECODER_SET_INPUT, &input); + decoder_command(zr, DECODER_SET_NORM, &zr->norm); + gstat_unlock_and_return: + mutex_unlock(&zr->resource_lock); + + if (!res) { + bstat->signal = + (status & DECODER_STATUS_GOOD) ? 1 : 0; + if (status & DECODER_STATUS_NTSC) + bstat->norm = VIDEO_MODE_NTSC; + else if (status & DECODER_STATUS_SECAM) + bstat->norm = VIDEO_MODE_SECAM; + else + bstat->norm = VIDEO_MODE_PAL; + + bstat->color = + (status & DECODER_STATUS_COLOR) ? 1 : 0; + } + + return res; + } + break; + +#ifdef HAVE_V4L2 + + /* The new video4linux2 capture interface - much nicer than video4linux1, since + * it allows for integrating the JPEG capturing calls inside standard v4l2 + */ + + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = arg; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_QUERYCAP\n", ZR_DEVNAME(zr)); + + memset(cap, 0, sizeof(*cap)); + strncpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)); + strncpy(cap->driver, "zoran", sizeof(cap->driver)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", + pci_name(zr->pci_dev)); + cap->version = + KERNEL_VERSION(MAJOR_VERSION, MINOR_VERSION, + RELEASE_VERSION); + cap->capabilities = ZORAN_V4L2_VID_FLAGS; + + return 0; + } + break; + + case VIDIOC_ENUM_FMT: + { + struct v4l2_fmtdesc *fmt = arg; + int index = fmt->index, num = -1, i, flag = 0, type = + fmt->type; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_ENUM_FMT - index=%d\n", + ZR_DEVNAME(zr), fmt->index); + + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + flag = ZORAN_FORMAT_CAPTURE; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + flag = ZORAN_FORMAT_PLAYBACK; + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + flag = ZORAN_FORMAT_OVERLAY; + break; + default: + dprintk(1, + KERN_ERR + "%s: VIDIOC_ENUM_FMT - unknown type %d\n", + ZR_DEVNAME(zr), fmt->type); + return -EINVAL; + } + + for (i = 0; i < zoran_num_formats; i++) { + if (zoran_formats[i].flags & flag) + num++; + if (num == fmt->index) + break; + } + if (fmt->index < 0 /* late, but not too late */ || + i == zoran_num_formats) + return -EINVAL; + + memset(fmt, 0, sizeof(*fmt)); + fmt->index = index; + fmt->type = type; + strncpy(fmt->description, zoran_formats[i].name, 31); + fmt->pixelformat = zoran_formats[i].fourcc; + if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED) + fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; + + return 0; + } + break; + + case VIDIOC_G_FMT: + { + struct v4l2_format *fmt = arg; + int type = fmt->type; + + dprintk(5, KERN_DEBUG "%s: VIDIOC_G_FMT\n", ZR_DEVNAME(zr)); + + memset(fmt, 0, sizeof(*fmt)); + fmt->type = type; + + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + + mutex_lock(&zr->resource_lock); + + fmt->fmt.win.w.left = fh->overlay_settings.x; + fmt->fmt.win.w.top = fh->overlay_settings.y; + fmt->fmt.win.w.width = fh->overlay_settings.width; + fmt->fmt.win.w.height = + fh->overlay_settings.height; + if (fh->overlay_settings.width * 2 > + BUZ_MAX_HEIGHT) + fmt->fmt.win.field = V4L2_FIELD_INTERLACED; + else + fmt->fmt.win.field = V4L2_FIELD_TOP; + + mutex_unlock(&zr->resource_lock); + + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + + mutex_lock(&zr->resource_lock); + + if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + fh->map_mode == ZORAN_MAP_MODE_RAW) { + + fmt->fmt.pix.width = + fh->v4l_settings.width; + fmt->fmt.pix.height = + fh->v4l_settings.height; + fmt->fmt.pix.sizeimage = + fh->v4l_buffers.buffer_size; + fmt->fmt.pix.pixelformat = + fh->v4l_settings.format->fourcc; + fmt->fmt.pix.colorspace = + fh->v4l_settings.format->colorspace; + fmt->fmt.pix.bytesperline = 0; + if (BUZ_MAX_HEIGHT < + (fh->v4l_settings.height * 2)) + fmt->fmt.pix.field = + V4L2_FIELD_INTERLACED; + else + fmt->fmt.pix.field = + V4L2_FIELD_TOP; + + } else { + + fmt->fmt.pix.width = + fh->jpg_settings.img_width / + fh->jpg_settings.HorDcm; + fmt->fmt.pix.height = + fh->jpg_settings.img_height / + (fh->jpg_settings.VerDcm * + fh->jpg_settings.TmpDcm); + fmt->fmt.pix.sizeimage = + zoran_v4l2_calc_bufsize(&fh-> + jpg_settings); + fmt->fmt.pix.pixelformat = + V4L2_PIX_FMT_MJPEG; + if (fh->jpg_settings.TmpDcm == 1) + fmt->fmt.pix.field = + (fh->jpg_settings. + odd_even ? V4L2_FIELD_SEQ_BT : + V4L2_FIELD_SEQ_BT); + else + fmt->fmt.pix.field = + (fh->jpg_settings. + odd_even ? V4L2_FIELD_TOP : + V4L2_FIELD_BOTTOM); + + fmt->fmt.pix.bytesperline = 0; + fmt->fmt.pix.colorspace = + V4L2_COLORSPACE_SMPTE170M; + } + + mutex_unlock(&zr->resource_lock); + + break; + + default: + dprintk(1, + KERN_ERR + "%s: VIDIOC_G_FMT - unsupported type %d\n", + ZR_DEVNAME(zr), fmt->type); + return -EINVAL; + } + return 0; + } + break; + + case VIDIOC_S_FMT: + { + struct v4l2_format *fmt = arg; + int i, res = 0; + __u32 printformat; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_S_FMT - type=%d, ", + ZR_DEVNAME(zr), fmt->type); + + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + + dprintk(3, "x=%d, y=%d, w=%d, h=%d, cnt=%d, map=0x%p\n", + fmt->fmt.win.w.left, fmt->fmt.win.w.top, + fmt->fmt.win.w.width, + fmt->fmt.win.w.height, + fmt->fmt.win.clipcount, + fmt->fmt.win.bitmap); + mutex_lock(&zr->resource_lock); + res = + setup_window(file, fmt->fmt.win.w.left, + fmt->fmt.win.w.top, + fmt->fmt.win.w.width, + fmt->fmt.win.w.height, + (struct video_clip __user *) + fmt->fmt.win.clips, + fmt->fmt.win.clipcount, + fmt->fmt.win.bitmap); + mutex_unlock(&zr->resource_lock); + return res; + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + + printformat = + __cpu_to_le32(fmt->fmt.pix.pixelformat); + dprintk(3, "size=%dx%d, fmt=0x%x (%4.4s)\n", + fmt->fmt.pix.width, fmt->fmt.pix.height, + fmt->fmt.pix.pixelformat, + (char *) &printformat); + + if (fmt->fmt.pix.bytesperline > 0) { + dprintk(5, + KERN_ERR "%s: bpl not supported\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + + /* we can be requested to do JPEG/raw playback/capture */ + if (! + (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE || + (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && + fmt->fmt.pix.pixelformat == + V4L2_PIX_FMT_MJPEG))) { + dprintk(1, + KERN_ERR + "%s: VIDIOC_S_FMT - unknown type %d/0x%x(%4.4s) combination\n", + ZR_DEVNAME(zr), fmt->type, + fmt->fmt.pix.pixelformat, + (char *) &printformat); + return -EINVAL; + } + + if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) { + mutex_lock(&zr->resource_lock); + + settings = fh->jpg_settings; + + if (fh->v4l_buffers.allocated || + fh->jpg_buffers.allocated) { + dprintk(1, + KERN_ERR + "%s: VIDIOC_S_FMT - cannot change capture mode\n", + ZR_DEVNAME(zr)); + res = -EBUSY; + goto sfmtjpg_unlock_and_return; + } + + /* we actually need to set 'real' parameters now */ + if ((fmt->fmt.pix.height * 2) > + BUZ_MAX_HEIGHT) + settings.TmpDcm = 1; + else + settings.TmpDcm = 2; + settings.decimation = 0; + if (fmt->fmt.pix.height <= + fh->jpg_settings.img_height / 2) + settings.VerDcm = 2; + else + settings.VerDcm = 1; + if (fmt->fmt.pix.width <= + fh->jpg_settings.img_width / 4) + settings.HorDcm = 4; + else if (fmt->fmt.pix.width <= + fh->jpg_settings.img_width / 2) + settings.HorDcm = 2; + else + settings.HorDcm = 1; + if (settings.TmpDcm == 1) + settings.field_per_buff = 2; + else + settings.field_per_buff = 1; + + /* check */ + if ((res = + zoran_check_jpg_settings(zr, + &settings))) + goto sfmtjpg_unlock_and_return; + + /* it's ok, so set them */ + fh->jpg_settings = settings; + + /* tell the user what we actually did */ + fmt->fmt.pix.width = + settings.img_width / settings.HorDcm; + fmt->fmt.pix.height = + settings.img_height * 2 / + (settings.TmpDcm * settings.VerDcm); + if (settings.TmpDcm == 1) + fmt->fmt.pix.field = + (fh->jpg_settings. + odd_even ? V4L2_FIELD_SEQ_TB : + V4L2_FIELD_SEQ_BT); + else + fmt->fmt.pix.field = + (fh->jpg_settings. + odd_even ? V4L2_FIELD_TOP : + V4L2_FIELD_BOTTOM); + fh->jpg_buffers.buffer_size = + zoran_v4l2_calc_bufsize(&fh-> + jpg_settings); + fmt->fmt.pix.sizeimage = + fh->jpg_buffers.buffer_size; + + /* we hereby abuse this variable to show that + * we're gonna do mjpeg capture */ + fh->map_mode = + (fmt->type == + V4L2_BUF_TYPE_VIDEO_CAPTURE) ? + ZORAN_MAP_MODE_JPG_REC : + ZORAN_MAP_MODE_JPG_PLAY; + sfmtjpg_unlock_and_return: + mutex_unlock(&zr->resource_lock); + } else { + for (i = 0; i < zoran_num_formats; i++) + if (fmt->fmt.pix.pixelformat == + zoran_formats[i].fourcc) + break; + if (i == zoran_num_formats) { + dprintk(1, + KERN_ERR + "%s: VIDIOC_S_FMT - unknown/unsupported format 0x%x (%4.4s)\n", + ZR_DEVNAME(zr), + fmt->fmt.pix.pixelformat, + (char *) &printformat); + return -EINVAL; + } + mutex_lock(&zr->resource_lock); + if (fh->jpg_buffers.allocated || + (fh->v4l_buffers.allocated && + fh->v4l_buffers.active != + ZORAN_FREE)) { + dprintk(1, + KERN_ERR + "%s: VIDIOC_S_FMT - cannot change capture mode\n", + ZR_DEVNAME(zr)); + res = -EBUSY; + goto sfmtv4l_unlock_and_return; + } + if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT) + fmt->fmt.pix.height = + BUZ_MAX_HEIGHT; + if (fmt->fmt.pix.width > BUZ_MAX_WIDTH) + fmt->fmt.pix.width = BUZ_MAX_WIDTH; + + if ((res = + zoran_v4l_set_format(file, + fmt->fmt.pix. + width, + fmt->fmt.pix. + height, + &zoran_formats + [i]))) + goto sfmtv4l_unlock_and_return; + + /* tell the user the + * results/missing stuff */ + fmt->fmt.pix.sizeimage = fh->v4l_buffers.buffer_size /*zr->gbpl * zr->gheight */ + ; + if (BUZ_MAX_HEIGHT < + (fh->v4l_settings.height * 2)) + fmt->fmt.pix.field = + V4L2_FIELD_INTERLACED; + else + fmt->fmt.pix.field = + V4L2_FIELD_TOP; + + fh->map_mode = ZORAN_MAP_MODE_RAW; + sfmtv4l_unlock_and_return: + mutex_unlock(&zr->resource_lock); + } + + break; + + default: + dprintk(3, "unsupported\n"); + dprintk(1, + KERN_ERR + "%s: VIDIOC_S_FMT - unsupported type %d\n", + ZR_DEVNAME(zr), fmt->type); + return -EINVAL; + } + + return res; + } + break; + + case VIDIOC_G_FBUF: + { + struct v4l2_framebuffer *fb = arg; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_G_FBUF\n", ZR_DEVNAME(zr)); + + memset(fb, 0, sizeof(*fb)); + mutex_lock(&zr->resource_lock); + fb->base = zr->buffer.base; + fb->fmt.width = zr->buffer.width; + fb->fmt.height = zr->buffer.height; + if (zr->overlay_settings.format) { + fb->fmt.pixelformat = + fh->overlay_settings.format->fourcc; + } + fb->fmt.bytesperline = zr->buffer.bytesperline; + mutex_unlock(&zr->resource_lock); + fb->fmt.colorspace = V4L2_COLORSPACE_SRGB; + fb->fmt.field = V4L2_FIELD_INTERLACED; + fb->flags = V4L2_FBUF_FLAG_OVERLAY; + fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; + + return 0; + } + break; + + case VIDIOC_S_FBUF: + { + int i, res = 0; + struct v4l2_framebuffer *fb = arg; + __u32 printformat = __cpu_to_le32(fb->fmt.pixelformat); + + dprintk(3, + KERN_DEBUG + "%s: VIDIOC_S_FBUF - base=0x%p, size=%dx%d, bpl=%d, fmt=0x%x (%4.4s)\n", + ZR_DEVNAME(zr), fb->base, fb->fmt.width, fb->fmt.height, + fb->fmt.bytesperline, fb->fmt.pixelformat, + (char *) &printformat); + + for (i = 0; i < zoran_num_formats; i++) + if (zoran_formats[i].fourcc == fb->fmt.pixelformat) + break; + if (i == zoran_num_formats) { + dprintk(1, + KERN_ERR + "%s: VIDIOC_S_FBUF - format=0x%x (%4.4s) not allowed\n", + ZR_DEVNAME(zr), fb->fmt.pixelformat, + (char *) &printformat); + return -EINVAL; + } + + mutex_lock(&zr->resource_lock); + res = + setup_fbuffer(file, fb->base, &zoran_formats[i], + fb->fmt.width, fb->fmt.height, + fb->fmt.bytesperline); + mutex_unlock(&zr->resource_lock); + + return res; + } + break; + + case VIDIOC_OVERLAY: + { + int *on = arg, res; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_PREVIEW - on=%d\n", + ZR_DEVNAME(zr), *on); + + mutex_lock(&zr->resource_lock); + res = setup_overlay(file, *on); + mutex_unlock(&zr->resource_lock); + + return res; + } + break; + + case VIDIOC_REQBUFS: + { + struct v4l2_requestbuffers *req = arg; + int res = 0; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_REQBUFS - type=%d\n", + ZR_DEVNAME(zr), req->type); + + if (req->memory != V4L2_MEMORY_MMAP) { + dprintk(1, + KERN_ERR + "%s: only MEMORY_MMAP capture is supported, not %d\n", + ZR_DEVNAME(zr), req->memory); + return -EINVAL; + } + + mutex_lock(&zr->resource_lock); + + if (fh->v4l_buffers.allocated || fh->jpg_buffers.allocated) { + dprintk(1, + KERN_ERR + "%s: VIDIOC_REQBUFS - buffers allready allocated\n", + ZR_DEVNAME(zr)); + res = -EBUSY; + goto v4l2reqbuf_unlock_and_return; + } + + if (fh->map_mode == ZORAN_MAP_MODE_RAW && + req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + + /* control user input */ + if (req->count < 2) + req->count = 2; + if (req->count > v4l_nbufs) + req->count = v4l_nbufs; + fh->v4l_buffers.num_buffers = req->count; + + if (v4l_fbuffer_alloc(file)) { + res = -ENOMEM; + goto v4l2reqbuf_unlock_and_return; + } + + /* The next mmap will map the V4L buffers */ + fh->map_mode = ZORAN_MAP_MODE_RAW; + + } else if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC || + fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) { + + /* we need to calculate size ourselves now */ + if (req->count < 4) + req->count = 4; + if (req->count > jpg_nbufs) + req->count = jpg_nbufs; + fh->jpg_buffers.num_buffers = req->count; + fh->jpg_buffers.buffer_size = + zoran_v4l2_calc_bufsize(&fh->jpg_settings); + + if (jpg_fbuffer_alloc(file)) { + res = -ENOMEM; + goto v4l2reqbuf_unlock_and_return; + } + + /* The next mmap will map the MJPEG buffers */ + if (req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + fh->map_mode = ZORAN_MAP_MODE_JPG_REC; + else + fh->map_mode = ZORAN_MAP_MODE_JPG_PLAY; + + } else { + dprintk(1, + KERN_ERR + "%s: VIDIOC_REQBUFS - unknown type %d\n", + ZR_DEVNAME(zr), req->type); + res = -EINVAL; + goto v4l2reqbuf_unlock_and_return; + } + v4l2reqbuf_unlock_and_return: + mutex_unlock(&zr->resource_lock); + + return 0; + } + break; + + case VIDIOC_QUERYBUF: + { + struct v4l2_buffer *buf = arg; + __u32 type = buf->type; + int index = buf->index, res; + + dprintk(3, + KERN_DEBUG + "%s: VIDIOC_QUERYBUF - index=%d, type=%d\n", + ZR_DEVNAME(zr), buf->index, buf->type); + + memset(buf, 0, sizeof(buf)); + buf->type = type; + buf->index = index; + + mutex_lock(&zr->resource_lock); + res = zoran_v4l2_buffer_status(file, buf, buf->index); + mutex_unlock(&zr->resource_lock); + + return res; + } + break; + + case VIDIOC_QBUF: + { + struct v4l2_buffer *buf = arg; + int res = 0, codec_mode, buf_type; + + dprintk(3, + KERN_DEBUG "%s: VIDIOC_QBUF - type=%d, index=%d\n", + ZR_DEVNAME(zr), buf->type, buf->index); + + mutex_lock(&zr->resource_lock); + + switch (fh->map_mode) { + case ZORAN_MAP_MODE_RAW: + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dprintk(1, + KERN_ERR + "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", + ZR_DEVNAME(zr), buf->type, fh->map_mode); + res = -EINVAL; + goto qbuf_unlock_and_return; + } + + res = zoran_v4l_queue_frame(file, buf->index); + if (res) + goto qbuf_unlock_and_return; + if (!zr->v4l_memgrab_active && + fh->v4l_buffers.active == ZORAN_LOCKED) + zr36057_set_memgrab(zr, 1); + break; + + case ZORAN_MAP_MODE_JPG_REC: + case ZORAN_MAP_MODE_JPG_PLAY: + if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) { + buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + codec_mode = BUZ_MODE_MOTION_DECOMPRESS; + } else { + buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + codec_mode = BUZ_MODE_MOTION_COMPRESS; + } + + if (buf->type != buf_type) { + dprintk(1, + KERN_ERR + "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", + ZR_DEVNAME(zr), buf->type, fh->map_mode); + res = -EINVAL; + goto qbuf_unlock_and_return; + } + + res = + zoran_jpg_queue_frame(file, buf->index, + codec_mode); + if (res != 0) + goto qbuf_unlock_and_return; + if (zr->codec_mode == BUZ_MODE_IDLE && + fh->jpg_buffers.active == ZORAN_LOCKED) { + zr36057_enable_jpg(zr, codec_mode); + } + break; + + default: + dprintk(1, + KERN_ERR + "%s: VIDIOC_QBUF - unsupported type %d\n", + ZR_DEVNAME(zr), buf->type); + res = -EINVAL; + goto qbuf_unlock_and_return; + } + qbuf_unlock_and_return: + mutex_unlock(&zr->resource_lock); + + return res; + } + break; + + case VIDIOC_DQBUF: + { + struct v4l2_buffer *buf = arg; + int res = 0, buf_type, num = -1; /* compiler borks here (?) */ + + dprintk(3, KERN_DEBUG "%s: VIDIOC_DQBUF - type=%d\n", + ZR_DEVNAME(zr), buf->type); + + mutex_lock(&zr->resource_lock); + + switch (fh->map_mode) { + case ZORAN_MAP_MODE_RAW: + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dprintk(1, + KERN_ERR + "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", + ZR_DEVNAME(zr), buf->type, fh->map_mode); + res = -EINVAL; + goto dqbuf_unlock_and_return; + } + + num = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME]; + if (file->f_flags & O_NONBLOCK && + zr->v4l_buffers.buffer[num].state != + BUZ_STATE_DONE) { + res = -EAGAIN; + goto dqbuf_unlock_and_return; + } + res = v4l_sync(file, num); + if (res) + goto dqbuf_unlock_and_return; + else + zr->v4l_sync_tail++; + res = zoran_v4l2_buffer_status(file, buf, num); + break; + + case ZORAN_MAP_MODE_JPG_REC: + case ZORAN_MAP_MODE_JPG_PLAY: + { + struct zoran_sync bs; + + if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) + buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + else + buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (buf->type != buf_type) { + dprintk(1, + KERN_ERR + "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", + ZR_DEVNAME(zr), buf->type, fh->map_mode); + res = -EINVAL; + goto dqbuf_unlock_and_return; + } + + num = + zr->jpg_pend[zr-> + jpg_que_tail & BUZ_MASK_FRAME]; + + if (file->f_flags & O_NONBLOCK && + zr->jpg_buffers.buffer[num].state != + BUZ_STATE_DONE) { + res = -EAGAIN; + goto dqbuf_unlock_and_return; + } + res = jpg_sync(file, &bs); + if (res) + goto dqbuf_unlock_and_return; + res = + zoran_v4l2_buffer_status(file, buf, bs.frame); + break; + } + + default: + dprintk(1, + KERN_ERR + "%s: VIDIOC_DQBUF - unsupported type %d\n", + ZR_DEVNAME(zr), buf->type); + res = -EINVAL; + goto dqbuf_unlock_and_return; + } + dqbuf_unlock_and_return: + mutex_unlock(&zr->resource_lock); + + return res; + } + break; + + case VIDIOC_STREAMON: + { + int res = 0; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_STREAMON\n", ZR_DEVNAME(zr)); + + mutex_lock(&zr->resource_lock); + + switch (fh->map_mode) { + case ZORAN_MAP_MODE_RAW: /* raw capture */ + if (zr->v4l_buffers.active != ZORAN_ACTIVE || + fh->v4l_buffers.active != ZORAN_ACTIVE) { + res = -EBUSY; + goto strmon_unlock_and_return; + } + + zr->v4l_buffers.active = fh->v4l_buffers.active = + ZORAN_LOCKED; + zr->v4l_settings = fh->v4l_settings; + + zr->v4l_sync_tail = zr->v4l_pend_tail; + if (!zr->v4l_memgrab_active && + zr->v4l_pend_head != zr->v4l_pend_tail) { + zr36057_set_memgrab(zr, 1); + } + break; + + case ZORAN_MAP_MODE_JPG_REC: + case ZORAN_MAP_MODE_JPG_PLAY: + /* what is the codec mode right now? */ + if (zr->jpg_buffers.active != ZORAN_ACTIVE || + fh->jpg_buffers.active != ZORAN_ACTIVE) { + res = -EBUSY; + goto strmon_unlock_and_return; + } + + zr->jpg_buffers.active = fh->jpg_buffers.active = + ZORAN_LOCKED; + + if (zr->jpg_que_head != zr->jpg_que_tail) { + /* Start the jpeg codec when the first frame is queued */ + jpeg_start(zr); + } + + break; + default: + dprintk(1, + KERN_ERR + "%s: VIDIOC_STREAMON - invalid map mode %d\n", + ZR_DEVNAME(zr), fh->map_mode); + res = -EINVAL; + goto strmon_unlock_and_return; + } + strmon_unlock_and_return: + mutex_unlock(&zr->resource_lock); + + return res; + } + break; + + case VIDIOC_STREAMOFF: + { + int i, res = 0; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_STREAMOFF\n", ZR_DEVNAME(zr)); + + mutex_lock(&zr->resource_lock); + + switch (fh->map_mode) { + case ZORAN_MAP_MODE_RAW: /* raw capture */ + if (fh->v4l_buffers.active == ZORAN_FREE && + zr->v4l_buffers.active != ZORAN_FREE) { + res = -EPERM; /* stay off other's settings! */ + goto strmoff_unlock_and_return; + } + if (zr->v4l_buffers.active == ZORAN_FREE) + goto strmoff_unlock_and_return; + + /* unload capture */ + if (zr->v4l_memgrab_active) + zr36057_set_memgrab(zr, 0); + + for (i = 0; i < fh->v4l_buffers.num_buffers; i++) + zr->v4l_buffers.buffer[i].state = + BUZ_STATE_USER; + fh->v4l_buffers = zr->v4l_buffers; + + zr->v4l_buffers.active = fh->v4l_buffers.active = + ZORAN_FREE; + + zr->v4l_grab_seq = 0; + zr->v4l_pend_head = zr->v4l_pend_tail = 0; + zr->v4l_sync_tail = 0; + + break; + + case ZORAN_MAP_MODE_JPG_REC: + case ZORAN_MAP_MODE_JPG_PLAY: + if (fh->jpg_buffers.active == ZORAN_FREE && + zr->jpg_buffers.active != ZORAN_FREE) { + res = -EPERM; /* stay off other's settings! */ + goto strmoff_unlock_and_return; + } + if (zr->jpg_buffers.active == ZORAN_FREE) + goto strmoff_unlock_and_return; + + res = + jpg_qbuf(file, -1, + (fh->map_mode == + ZORAN_MAP_MODE_JPG_REC) ? + BUZ_MODE_MOTION_COMPRESS : + BUZ_MODE_MOTION_DECOMPRESS); + if (res) + goto strmoff_unlock_and_return; + break; + default: + dprintk(1, + KERN_ERR + "%s: VIDIOC_STREAMOFF - invalid map mode %d\n", + ZR_DEVNAME(zr), fh->map_mode); + res = -EINVAL; + goto strmoff_unlock_and_return; + } + strmoff_unlock_and_return: + mutex_unlock(&zr->resource_lock); + + return res; + } + break; + + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *ctrl = arg; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_QUERYCTRL - id=%d\n", + ZR_DEVNAME(zr), ctrl->id); + + /* we only support hue/saturation/contrast/brightness */ + if (ctrl->id < V4L2_CID_BRIGHTNESS || + ctrl->id > V4L2_CID_HUE) + return -EINVAL; + else { + int id = ctrl->id; + memset(ctrl, 0, sizeof(*ctrl)); + ctrl->id = id; + } + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + strncpy(ctrl->name, "Brightness", 31); + break; + case V4L2_CID_CONTRAST: + strncpy(ctrl->name, "Contrast", 31); + break; + case V4L2_CID_SATURATION: + strncpy(ctrl->name, "Saturation", 31); + break; + case V4L2_CID_HUE: + strncpy(ctrl->name, "Hue", 31); + break; + } + + ctrl->minimum = 0; + ctrl->maximum = 65535; + ctrl->step = 1; + ctrl->default_value = 32768; + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + + return 0; + } + break; + + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl = arg; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_G_CTRL - id=%d\n", + ZR_DEVNAME(zr), ctrl->id); + + /* we only support hue/saturation/contrast/brightness */ + if (ctrl->id < V4L2_CID_BRIGHTNESS || + ctrl->id > V4L2_CID_HUE) + return -EINVAL; + + mutex_lock(&zr->resource_lock); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = zr->brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = zr->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = zr->saturation; + break; + case V4L2_CID_HUE: + ctrl->value = zr->hue; + break; + } + mutex_unlock(&zr->resource_lock); + + return 0; + } + break; + + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl = arg; + struct video_picture pict; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_S_CTRL - id=%d\n", + ZR_DEVNAME(zr), ctrl->id); + + /* we only support hue/saturation/contrast/brightness */ + if (ctrl->id < V4L2_CID_BRIGHTNESS || + ctrl->id > V4L2_CID_HUE) + return -EINVAL; + + if (ctrl->value < 0 || ctrl->value > 65535) { + dprintk(1, + KERN_ERR + "%s: VIDIOC_S_CTRL - invalid value %d for id=%d\n", + ZR_DEVNAME(zr), ctrl->value, ctrl->id); + return -EINVAL; + } + + mutex_lock(&zr->resource_lock); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + zr->brightness = ctrl->value; + break; + case V4L2_CID_CONTRAST: + zr->contrast = ctrl->value; + break; + case V4L2_CID_SATURATION: + zr->saturation = ctrl->value; + break; + case V4L2_CID_HUE: + zr->hue = ctrl->value; + break; + } + pict.brightness = zr->brightness; + pict.contrast = zr->contrast; + pict.colour = zr->saturation; + pict.hue = zr->hue; + + decoder_command(zr, DECODER_SET_PICTURE, &pict); + + mutex_unlock(&zr->resource_lock); + + return 0; + } + break; + + case VIDIOC_ENUMSTD: + { + struct v4l2_standard *std = arg; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_ENUMSTD - index=%d\n", + ZR_DEVNAME(zr), std->index); + + if (std->index < 0 || std->index >= (zr->card.norms + 1)) + return -EINVAL; + else { + int id = std->index; + memset(std, 0, sizeof(*std)); + std->index = id; + } + + if (std->index == zr->card.norms) { + /* if we have autodetect, ... */ + struct video_decoder_capability caps; + decoder_command(zr, DECODER_GET_CAPABILITIES, + &caps); + if (caps.flags & VIDEO_DECODER_AUTO) { + std->id = V4L2_STD_ALL; + strncpy(std->name, "Autodetect", 31); + return 0; + } else + return -EINVAL; + } + switch (std->index) { + case 0: + std->id = V4L2_STD_PAL; + strncpy(std->name, "PAL", 31); + std->frameperiod.numerator = 1; + std->frameperiod.denominator = 25; + std->framelines = zr->card.tvn[0]->Ht; + break; + case 1: + std->id = V4L2_STD_NTSC; + strncpy(std->name, "NTSC", 31); + std->frameperiod.numerator = 1001; + std->frameperiod.denominator = 30000; + std->framelines = zr->card.tvn[1]->Ht; + break; + case 2: + std->id = V4L2_STD_SECAM; + strncpy(std->name, "SECAM", 31); + std->frameperiod.numerator = 1; + std->frameperiod.denominator = 25; + std->framelines = zr->card.tvn[2]->Ht; + break; + } + + return 0; + } + break; + + case VIDIOC_G_STD: + { + v4l2_std_id *std = arg; + int norm; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_G_STD\n", ZR_DEVNAME(zr)); + + mutex_lock(&zr->resource_lock); + norm = zr->norm; + mutex_unlock(&zr->resource_lock); + + switch (norm) { + case VIDEO_MODE_PAL: + *std = V4L2_STD_PAL; + break; + case VIDEO_MODE_NTSC: + *std = V4L2_STD_NTSC; + break; + case VIDEO_MODE_SECAM: + *std = V4L2_STD_SECAM; + break; + } + + return 0; + } + break; + + case VIDIOC_S_STD: + { + int norm = -1, res = 0; + v4l2_std_id *std = arg; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_S_STD - norm=0x%llx\n", + ZR_DEVNAME(zr), (unsigned long long)*std); + + if (*std == V4L2_STD_PAL) + norm = VIDEO_MODE_PAL; + else if (*std == V4L2_STD_NTSC) + norm = VIDEO_MODE_NTSC; + else if (*std == V4L2_STD_SECAM) + norm = VIDEO_MODE_SECAM; + else if (*std == V4L2_STD_ALL) + norm = VIDEO_MODE_AUTO; + else { + dprintk(1, + KERN_ERR + "%s: VIDIOC_S_STD - invalid norm 0x%llx\n", + ZR_DEVNAME(zr), (unsigned long long)*std); + return -EINVAL; + } + + mutex_lock(&zr->resource_lock); + if ((res = zoran_set_norm(zr, norm))) + goto sstd_unlock_and_return; + + res = wait_grab_pending(zr); + sstd_unlock_and_return: + mutex_unlock(&zr->resource_lock); + return res; + } + break; + + case VIDIOC_ENUMINPUT: + { + struct v4l2_input *inp = arg; + int status; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_ENUMINPUT - index=%d\n", + ZR_DEVNAME(zr), inp->index); + + if (inp->index < 0 || inp->index >= zr->card.inputs) + return -EINVAL; + else { + int id = inp->index; + memset(inp, 0, sizeof(*inp)); + inp->index = id; + } + + strncpy(inp->name, zr->card.input[inp->index].name, + sizeof(inp->name) - 1); + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = V4L2_STD_ALL; + + /* Get status of video decoder */ + mutex_lock(&zr->resource_lock); + decoder_command(zr, DECODER_GET_STATUS, &status); + mutex_unlock(&zr->resource_lock); + + if (!(status & DECODER_STATUS_GOOD)) { + inp->status |= V4L2_IN_ST_NO_POWER; + inp->status |= V4L2_IN_ST_NO_SIGNAL; + } + if (!(status & DECODER_STATUS_COLOR)) + inp->status |= V4L2_IN_ST_NO_COLOR; + + return 0; + } + break; + + case VIDIOC_G_INPUT: + { + int *input = arg; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_G_INPUT\n", ZR_DEVNAME(zr)); + + mutex_lock(&zr->resource_lock); + *input = zr->input; + mutex_unlock(&zr->resource_lock); + + return 0; + } + break; + + case VIDIOC_S_INPUT: + { + int *input = arg, res = 0; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_S_INPUT - input=%d\n", + ZR_DEVNAME(zr), *input); + + mutex_lock(&zr->resource_lock); + if ((res = zoran_set_input(zr, *input))) + goto sinput_unlock_and_return; + + /* Make sure the changes come into effect */ + res = wait_grab_pending(zr); + sinput_unlock_and_return: + mutex_unlock(&zr->resource_lock); + return res; + } + break; + + case VIDIOC_ENUMOUTPUT: + { + struct v4l2_output *outp = arg; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_ENUMOUTPUT - index=%d\n", + ZR_DEVNAME(zr), outp->index); + + if (outp->index != 0) + return -EINVAL; + + memset(outp, 0, sizeof(*outp)); + outp->index = 0; + outp->type = V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY; + strncpy(outp->name, "Autodetect", 31); + + return 0; + } + break; + + case VIDIOC_G_OUTPUT: + { + int *output = arg; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_G_OUTPUT\n", ZR_DEVNAME(zr)); + + *output = 0; + + return 0; + } + break; + + case VIDIOC_S_OUTPUT: + { + int *output = arg; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_S_OUTPUT - output=%d\n", + ZR_DEVNAME(zr), *output); + + if (*output != 0) + return -EINVAL; + + return 0; + } + break; + + /* cropping (sub-frame capture) */ + case VIDIOC_CROPCAP: + { + struct v4l2_cropcap *cropcap = arg; + int type = cropcap->type, res = 0; + + dprintk(3, KERN_ERR "%s: VIDIOC_CROPCAP - type=%d\n", + ZR_DEVNAME(zr), cropcap->type); + + memset(cropcap, 0, sizeof(*cropcap)); + cropcap->type = type; + + mutex_lock(&zr->resource_lock); + + if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && + (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + fh->map_mode == ZORAN_MAP_MODE_RAW)) { + dprintk(1, + KERN_ERR + "%s: VIDIOC_CROPCAP - subcapture only supported for compressed capture\n", + ZR_DEVNAME(zr)); + res = -EINVAL; + goto cropcap_unlock_and_return; + } + + cropcap->bounds.top = cropcap->bounds.left = 0; + cropcap->bounds.width = BUZ_MAX_WIDTH; + cropcap->bounds.height = BUZ_MAX_HEIGHT; + cropcap->defrect.top = cropcap->defrect.left = 0; + cropcap->defrect.width = BUZ_MIN_WIDTH; + cropcap->defrect.height = BUZ_MIN_HEIGHT; + cropcap_unlock_and_return: + mutex_unlock(&zr->resource_lock); + return res; + } + break; + + case VIDIOC_G_CROP: + { + struct v4l2_crop *crop = arg; + int type = crop->type, res = 0; + + dprintk(3, KERN_ERR "%s: VIDIOC_G_CROP - type=%d\n", + ZR_DEVNAME(zr), crop->type); + + memset(crop, 0, sizeof(*crop)); + crop->type = type; + + mutex_lock(&zr->resource_lock); + + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && + (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + fh->map_mode == ZORAN_MAP_MODE_RAW)) { + dprintk(1, + KERN_ERR + "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n", + ZR_DEVNAME(zr)); + res = -EINVAL; + goto gcrop_unlock_and_return; + } + + crop->c.top = fh->jpg_settings.img_y; + crop->c.left = fh->jpg_settings.img_x; + crop->c.width = fh->jpg_settings.img_width; + crop->c.height = fh->jpg_settings.img_height; + + gcrop_unlock_and_return: + mutex_unlock(&zr->resource_lock); + + return res; + } + break; + + case VIDIOC_S_CROP: + { + struct v4l2_crop *crop = arg; + int res = 0; + + settings = fh->jpg_settings; + + dprintk(3, + KERN_ERR + "%s: VIDIOC_S_CROP - type=%d, x=%d,y=%d,w=%d,h=%d\n", + ZR_DEVNAME(zr), crop->type, crop->c.left, crop->c.top, + crop->c.width, crop->c.height); + + mutex_lock(&zr->resource_lock); + + if (fh->jpg_buffers.allocated || fh->v4l_buffers.allocated) { + dprintk(1, + KERN_ERR + "%s: VIDIOC_S_CROP - cannot change settings while active\n", + ZR_DEVNAME(zr)); + res = -EBUSY; + goto scrop_unlock_and_return; + } + + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && + (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + fh->map_mode == ZORAN_MAP_MODE_RAW)) { + dprintk(1, + KERN_ERR + "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n", + ZR_DEVNAME(zr)); + res = -EINVAL; + goto scrop_unlock_and_return; + } + + /* move into a form that we understand */ + settings.img_x = crop->c.left; + settings.img_y = crop->c.top; + settings.img_width = crop->c.width; + settings.img_height = crop->c.height; + + /* check validity */ + if ((res = zoran_check_jpg_settings(zr, &settings))) + goto scrop_unlock_and_return; + + /* accept */ + fh->jpg_settings = settings; + + scrop_unlock_and_return: + mutex_unlock(&zr->resource_lock); + return res; + } + break; + + case VIDIOC_G_JPEGCOMP: + { + struct v4l2_jpegcompression *params = arg; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_G_JPEGCOMP\n", + ZR_DEVNAME(zr)); + + memset(params, 0, sizeof(*params)); + + mutex_lock(&zr->resource_lock); + + params->quality = fh->jpg_settings.jpg_comp.quality; + params->APPn = fh->jpg_settings.jpg_comp.APPn; + memcpy(params->APP_data, + fh->jpg_settings.jpg_comp.APP_data, + fh->jpg_settings.jpg_comp.APP_len); + params->APP_len = fh->jpg_settings.jpg_comp.APP_len; + memcpy(params->COM_data, + fh->jpg_settings.jpg_comp.COM_data, + fh->jpg_settings.jpg_comp.COM_len); + params->COM_len = fh->jpg_settings.jpg_comp.COM_len; + params->jpeg_markers = + fh->jpg_settings.jpg_comp.jpeg_markers; + + mutex_unlock(&zr->resource_lock); + + return 0; + } + break; + + case VIDIOC_S_JPEGCOMP: + { + struct v4l2_jpegcompression *params = arg; + int res = 0; + + settings = fh->jpg_settings; + + dprintk(3, + KERN_DEBUG + "%s: VIDIOC_S_JPEGCOMP - quality=%d, APPN=%d, APP_len=%d, COM_len=%d\n", + ZR_DEVNAME(zr), params->quality, params->APPn, + params->APP_len, params->COM_len); + + settings.jpg_comp = *params; + + mutex_lock(&zr->resource_lock); + + if (fh->v4l_buffers.active != ZORAN_FREE || + fh->jpg_buffers.active != ZORAN_FREE) { + dprintk(1, + KERN_WARNING + "%s: VIDIOC_S_JPEGCOMP called while in playback/capture mode\n", + ZR_DEVNAME(zr)); + res = -EBUSY; + goto sjpegc_unlock_and_return; + } + + if ((res = zoran_check_jpg_settings(zr, &settings))) + goto sjpegc_unlock_and_return; + if (!fh->jpg_buffers.allocated) + fh->jpg_buffers.buffer_size = + zoran_v4l2_calc_bufsize(&fh->jpg_settings); + fh->jpg_settings.jpg_comp = *params = settings.jpg_comp; + sjpegc_unlock_and_return: + mutex_unlock(&zr->resource_lock); + + return 0; + } + break; + + case VIDIOC_QUERYSTD: /* why is this useful? */ + { + v4l2_std_id *std = arg; + + dprintk(3, + KERN_DEBUG "%s: VIDIOC_QUERY_STD - std=0x%llx\n", + ZR_DEVNAME(zr), (unsigned long long)*std); + + if (*std == V4L2_STD_ALL || *std == V4L2_STD_NTSC || + *std == V4L2_STD_PAL || (*std == V4L2_STD_SECAM && + zr->card.norms == 3)) { + return 0; + } + + return -EINVAL; + } + break; + + case VIDIOC_TRY_FMT: + { + struct v4l2_format *fmt = arg; + int res = 0; + + dprintk(3, KERN_DEBUG "%s: VIDIOC_TRY_FMT - type=%d\n", + ZR_DEVNAME(zr), fmt->type); + + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + mutex_lock(&zr->resource_lock); + + if (fmt->fmt.win.w.width > BUZ_MAX_WIDTH) + fmt->fmt.win.w.width = BUZ_MAX_WIDTH; + if (fmt->fmt.win.w.width < BUZ_MIN_WIDTH) + fmt->fmt.win.w.width = BUZ_MIN_WIDTH; + if (fmt->fmt.win.w.height > BUZ_MAX_HEIGHT) + fmt->fmt.win.w.height = BUZ_MAX_HEIGHT; + if (fmt->fmt.win.w.height < BUZ_MIN_HEIGHT) + fmt->fmt.win.w.height = BUZ_MIN_HEIGHT; + + mutex_unlock(&zr->resource_lock); + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (fmt->fmt.pix.bytesperline > 0) + return -EINVAL; + + mutex_lock(&zr->resource_lock); + + if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) { + settings = fh->jpg_settings; + + /* we actually need to set 'real' parameters now */ + if ((fmt->fmt.pix.height * 2) > + BUZ_MAX_HEIGHT) + settings.TmpDcm = 1; + else + settings.TmpDcm = 2; + settings.decimation = 0; + if (fmt->fmt.pix.height <= + fh->jpg_settings.img_height / 2) + settings.VerDcm = 2; + else + settings.VerDcm = 1; + if (fmt->fmt.pix.width <= + fh->jpg_settings.img_width / 4) + settings.HorDcm = 4; + else if (fmt->fmt.pix.width <= + fh->jpg_settings.img_width / 2) + settings.HorDcm = 2; + else + settings.HorDcm = 1; + if (settings.TmpDcm == 1) + settings.field_per_buff = 2; + else + settings.field_per_buff = 1; + + /* check */ + if ((res = + zoran_check_jpg_settings(zr, + &settings))) + goto tryfmt_unlock_and_return; + + /* tell the user what we actually did */ + fmt->fmt.pix.width = + settings.img_width / settings.HorDcm; + fmt->fmt.pix.height = + settings.img_height * 2 / + (settings.TmpDcm * settings.VerDcm); + if (settings.TmpDcm == 1) + fmt->fmt.pix.field = + (fh->jpg_settings. + odd_even ? V4L2_FIELD_SEQ_TB : + V4L2_FIELD_SEQ_BT); + else + fmt->fmt.pix.field = + (fh->jpg_settings. + odd_even ? V4L2_FIELD_TOP : + V4L2_FIELD_BOTTOM); + + fmt->fmt.pix.sizeimage = + zoran_v4l2_calc_bufsize(&settings); + } else if (fmt->type == + V4L2_BUF_TYPE_VIDEO_CAPTURE) { + int i; + + for (i = 0; i < zoran_num_formats; i++) + if (zoran_formats[i].fourcc == + fmt->fmt.pix.pixelformat) + break; + if (i == zoran_num_formats) { + res = -EINVAL; + goto tryfmt_unlock_and_return; + } + + if (fmt->fmt.pix.width > BUZ_MAX_WIDTH) + fmt->fmt.pix.width = BUZ_MAX_WIDTH; + if (fmt->fmt.pix.width < BUZ_MIN_WIDTH) + fmt->fmt.pix.width = BUZ_MIN_WIDTH; + if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT) + fmt->fmt.pix.height = + BUZ_MAX_HEIGHT; + if (fmt->fmt.pix.height < BUZ_MIN_HEIGHT) + fmt->fmt.pix.height = + BUZ_MIN_HEIGHT; + } else { + res = -EINVAL; + goto tryfmt_unlock_and_return; + } + tryfmt_unlock_and_return: + mutex_unlock(&zr->resource_lock); + + return res; + break; + + default: + return -EINVAL; + } + + return 0; + } + break; +#endif + + default: + dprintk(1, KERN_DEBUG "%s: UNKNOWN ioctl cmd: 0x%x\n", + ZR_DEVNAME(zr), cmd); + return -ENOIOCTLCMD; + break; + + } + return 0; +} + + +static int +zoran_ioctl (struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, zoran_do_ioctl); +} + +static unsigned int +zoran_poll (struct file *file, + poll_table *wait) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + wait_queue_head_t *queue = NULL; + int res = 0, frame; + + /* we should check whether buffers are ready to be synced on + * (w/o waits - O_NONBLOCK) here + * if ready for read (sync), return POLLIN|POLLRDNORM, + * if ready for write (sync), return POLLOUT|POLLWRNORM, + * if error, return POLLERR, + * if no buffers queued or so, return POLLNVAL + */ + + mutex_lock(&zr->resource_lock); + + switch (fh->map_mode) { + case ZORAN_MAP_MODE_RAW: + if (fh->v4l_buffers.active == ZORAN_FREE || + zr->v4l_pend_head == zr->v4l_pend_tail) { + dprintk(1, + "%s: zoran_poll() - no buffers queued\n", + ZR_DEVNAME(zr)); + res = POLLNVAL; + goto poll_unlock_and_return; + } + queue = &zr->v4l_capq; + frame = zr->v4l_pend[zr->v4l_pend_tail & V4L_MASK_FRAME]; + poll_wait(file, queue, wait); + if (fh->v4l_buffers.buffer[frame].state == BUZ_STATE_DONE) + res = POLLIN | POLLRDNORM; + break; + + case ZORAN_MAP_MODE_JPG_REC: + case ZORAN_MAP_MODE_JPG_PLAY: + if (fh->jpg_buffers.active == ZORAN_FREE || + zr->jpg_que_head == zr->jpg_que_tail) { + dprintk(1, + "%s: zoran_poll() - no buffers queued\n", + ZR_DEVNAME(zr)); + res = POLLNVAL; + goto poll_unlock_and_return; + } + queue = &zr->jpg_capq; + frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME]; + poll_wait(file, queue, wait); + if (fh->jpg_buffers.buffer[frame].state == BUZ_STATE_DONE) { + if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) + res = POLLIN | POLLRDNORM; + else + res = POLLOUT | POLLWRNORM; + } + break; + + default: + dprintk(1, + "%s: zoran_poll() - internal error, unknown map_mode=%d\n", + ZR_DEVNAME(zr), fh->map_mode); + res = POLLNVAL; + goto poll_unlock_and_return; + } + +poll_unlock_and_return: + mutex_unlock(&zr->resource_lock); + + return res; +} + + +/* + * This maps the buffers to user space. + * + * Depending on the state of fh->map_mode + * the V4L or the MJPEG buffers are mapped + * per buffer or all together + * + * Note that we need to connect to some + * unmap signal event to unmap the de-allocate + * the buffer accordingly (zoran_vm_close()) + */ + +static void +zoran_vm_open (struct vm_area_struct *vma) +{ + struct zoran_mapping *map = vma->vm_private_data; + + map->count++; +} + +static void +zoran_vm_close (struct vm_area_struct *vma) +{ + struct zoran_mapping *map = vma->vm_private_data; + struct file *file = map->file; + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + int i; + + map->count--; + if (map->count == 0) { + switch (fh->map_mode) { + case ZORAN_MAP_MODE_JPG_REC: + case ZORAN_MAP_MODE_JPG_PLAY: + + dprintk(3, KERN_INFO "%s: munmap(MJPEG)\n", + ZR_DEVNAME(zr)); + + for (i = 0; i < fh->jpg_buffers.num_buffers; i++) { + if (fh->jpg_buffers.buffer[i].map == map) { + fh->jpg_buffers.buffer[i].map = + NULL; + } + } + kfree(map); + + for (i = 0; i < fh->jpg_buffers.num_buffers; i++) + if (fh->jpg_buffers.buffer[i].map) + break; + if (i == fh->jpg_buffers.num_buffers) { + mutex_lock(&zr->resource_lock); + + if (fh->jpg_buffers.active != ZORAN_FREE) { + jpg_qbuf(file, -1, zr->codec_mode); + zr->jpg_buffers.allocated = 0; + zr->jpg_buffers.active = + fh->jpg_buffers.active = + ZORAN_FREE; + } + //jpg_fbuffer_free(file); + fh->jpg_buffers.allocated = 0; + fh->jpg_buffers.ready_to_be_freed = 1; + + mutex_unlock(&zr->resource_lock); + } + + break; + + case ZORAN_MAP_MODE_RAW: + + dprintk(3, KERN_INFO "%s: munmap(V4L)\n", + ZR_DEVNAME(zr)); + + for (i = 0; i < fh->v4l_buffers.num_buffers; i++) { + if (fh->v4l_buffers.buffer[i].map == map) { + /* unqueue/unmap */ + fh->v4l_buffers.buffer[i].map = + NULL; + } + } + kfree(map); + + for (i = 0; i < fh->v4l_buffers.num_buffers; i++) + if (fh->v4l_buffers.buffer[i].map) + break; + if (i == fh->v4l_buffers.num_buffers) { + mutex_lock(&zr->resource_lock); + + if (fh->v4l_buffers.active != ZORAN_FREE) { + zr36057_set_memgrab(zr, 0); + zr->v4l_buffers.allocated = 0; + zr->v4l_buffers.active = + fh->v4l_buffers.active = + ZORAN_FREE; + } + //v4l_fbuffer_free(file); + fh->v4l_buffers.allocated = 0; + fh->v4l_buffers.ready_to_be_freed = 1; + + mutex_unlock(&zr->resource_lock); + } + + break; + + default: + printk(KERN_ERR + "%s: munmap() - internal error - unknown map mode %d\n", + ZR_DEVNAME(zr), fh->map_mode); + break; + + } + } +} + +static struct vm_operations_struct zoran_vm_ops = { + .open = zoran_vm_open, + .close = zoran_vm_close, +}; + +static int +zoran_mmap (struct file *file, + struct vm_area_struct *vma) +{ + struct zoran_fh *fh = file->private_data; + struct zoran *zr = fh->zr; + unsigned long size = (vma->vm_end - vma->vm_start); + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + int i, j; + unsigned long page, start = vma->vm_start, todo, pos, fraglen; + int first, last; + struct zoran_mapping *map; + int res = 0; + + dprintk(3, + KERN_INFO "%s: mmap(%s) of 0x%08lx-0x%08lx (size=%lu)\n", + ZR_DEVNAME(zr), + fh->map_mode == ZORAN_MAP_MODE_RAW ? "V4L" : "MJPEG", + vma->vm_start, vma->vm_end, size); + + if (!(vma->vm_flags & VM_SHARED) || !(vma->vm_flags & VM_READ) || + !(vma->vm_flags & VM_WRITE)) { + dprintk(1, + KERN_ERR + "%s: mmap() - no MAP_SHARED/PROT_{READ,WRITE} given\n", + ZR_DEVNAME(zr)); + return -EINVAL; + } + + switch (fh->map_mode) { + + case ZORAN_MAP_MODE_JPG_REC: + case ZORAN_MAP_MODE_JPG_PLAY: + + /* lock */ + mutex_lock(&zr->resource_lock); + + /* Map the MJPEG buffers */ + if (!fh->jpg_buffers.allocated) { + dprintk(1, + KERN_ERR + "%s: zoran_mmap(MJPEG) - buffers not yet allocated\n", + ZR_DEVNAME(zr)); + res = -ENOMEM; + goto jpg_mmap_unlock_and_return; + } + + first = offset / fh->jpg_buffers.buffer_size; + last = first - 1 + size / fh->jpg_buffers.buffer_size; + if (offset % fh->jpg_buffers.buffer_size != 0 || + size % fh->jpg_buffers.buffer_size != 0 || first < 0 || + last < 0 || first >= fh->jpg_buffers.num_buffers || + last >= fh->jpg_buffers.num_buffers) { + dprintk(1, + KERN_ERR + "%s: mmap(MJPEG) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n", + ZR_DEVNAME(zr), offset, size, + fh->jpg_buffers.buffer_size, + fh->jpg_buffers.num_buffers); + res = -EINVAL; + goto jpg_mmap_unlock_and_return; + } + for (i = first; i <= last; i++) { + if (fh->jpg_buffers.buffer[i].map) { + dprintk(1, + KERN_ERR + "%s: mmap(MJPEG) - buffer %d already mapped\n", + ZR_DEVNAME(zr), i); + res = -EBUSY; + goto jpg_mmap_unlock_and_return; + } + } + + /* map these buffers (v4l_buffers[i]) */ + map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL); + if (!map) { + res = -ENOMEM; + goto jpg_mmap_unlock_and_return; + } + map->file = file; + map->count = 1; + + vma->vm_ops = &zoran_vm_ops; + vma->vm_flags |= VM_DONTEXPAND; + vma->vm_private_data = map; + + for (i = first; i <= last; i++) { + for (j = 0; + j < fh->jpg_buffers.buffer_size / PAGE_SIZE; + j++) { + fraglen = + (le32_to_cpu(fh->jpg_buffers.buffer[i]. + frag_tab[2 * j + 1]) & ~1) << 1; + todo = size; + if (todo > fraglen) + todo = fraglen; + pos = + le32_to_cpu((unsigned long) fh->jpg_buffers. + buffer[i].frag_tab[2 * j]); + /* should just be pos on i386 */ + page = virt_to_phys(bus_to_virt(pos)) + >> PAGE_SHIFT; + if (remap_pfn_range(vma, start, page, + todo, PAGE_SHARED)) { + dprintk(1, + KERN_ERR + "%s: zoran_mmap(V4L) - remap_pfn_range failed\n", + ZR_DEVNAME(zr)); + res = -EAGAIN; + goto jpg_mmap_unlock_and_return; + } + size -= todo; + start += todo; + if (size == 0) + break; + if (le32_to_cpu(fh->jpg_buffers.buffer[i]. + frag_tab[2 * j + 1]) & 1) + break; /* was last fragment */ + } + fh->jpg_buffers.buffer[i].map = map; + if (size == 0) + break; + + } + jpg_mmap_unlock_and_return: + mutex_unlock(&zr->resource_lock); + + break; + + case ZORAN_MAP_MODE_RAW: + + mutex_lock(&zr->resource_lock); + + /* Map the V4L buffers */ + if (!fh->v4l_buffers.allocated) { + dprintk(1, + KERN_ERR + "%s: zoran_mmap(V4L) - buffers not yet allocated\n", + ZR_DEVNAME(zr)); + res = -ENOMEM; + goto v4l_mmap_unlock_and_return; + } + + first = offset / fh->v4l_buffers.buffer_size; + last = first - 1 + size / fh->v4l_buffers.buffer_size; + if (offset % fh->v4l_buffers.buffer_size != 0 || + size % fh->v4l_buffers.buffer_size != 0 || first < 0 || + last < 0 || first >= fh->v4l_buffers.num_buffers || + last >= fh->v4l_buffers.buffer_size) { + dprintk(1, + KERN_ERR + "%s: mmap(V4L) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n", + ZR_DEVNAME(zr), offset, size, + fh->v4l_buffers.buffer_size, + fh->v4l_buffers.num_buffers); + res = -EINVAL; + goto v4l_mmap_unlock_and_return; + } + for (i = first; i <= last; i++) { + if (fh->v4l_buffers.buffer[i].map) { + dprintk(1, + KERN_ERR + "%s: mmap(V4L) - buffer %d already mapped\n", + ZR_DEVNAME(zr), i); + res = -EBUSY; + goto v4l_mmap_unlock_and_return; + } + } + + /* map these buffers (v4l_buffers[i]) */ + map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL); + if (!map) { + res = -ENOMEM; + goto v4l_mmap_unlock_and_return; + } + map->file = file; + map->count = 1; + + vma->vm_ops = &zoran_vm_ops; + vma->vm_flags |= VM_DONTEXPAND; + vma->vm_private_data = map; + + for (i = first; i <= last; i++) { + todo = size; + if (todo > fh->v4l_buffers.buffer_size) + todo = fh->v4l_buffers.buffer_size; + page = fh->v4l_buffers.buffer[i].fbuffer_phys; + if (remap_pfn_range(vma, start, page >> PAGE_SHIFT, + todo, PAGE_SHARED)) { + dprintk(1, + KERN_ERR + "%s: zoran_mmap(V4L)i - remap_pfn_range failed\n", + ZR_DEVNAME(zr)); + res = -EAGAIN; + goto v4l_mmap_unlock_and_return; + } + size -= todo; + start += todo; + fh->v4l_buffers.buffer[i].map = map; + if (size == 0) + break; + } + v4l_mmap_unlock_and_return: + mutex_unlock(&zr->resource_lock); + + break; + + default: + dprintk(1, + KERN_ERR + "%s: zoran_mmap() - internal error - unknown map mode %d\n", + ZR_DEVNAME(zr), fh->map_mode); + break; + } + + return 0; +} + +static struct file_operations zoran_fops = { + .owner = THIS_MODULE, + .open = zoran_open, + .release = zoran_close, + .ioctl = zoran_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, + .read = zoran_read, + .write = zoran_write, + .mmap = zoran_mmap, + .poll = zoran_poll, +}; + +struct video_device zoran_template __devinitdata = { + .name = ZORAN_NAME, + .type = ZORAN_VID_TYPE, +#ifdef HAVE_V4L2 + .type2 = ZORAN_V4L2_VID_FLAGS, +#endif + .hardware = ZORAN_HARDWARE, + .fops = &zoran_fops, + .release = &zoran_vdev_release, + .minor = -1 +}; + diff --git a/linux/drivers/media/video/zoran_procfs.c b/linux/drivers/media/video/zoran_procfs.c new file mode 100644 index 000000000..3472c00d4 --- /dev/null +++ b/linux/drivers/media/video/zoran_procfs.c @@ -0,0 +1,234 @@ +/* + * Zoran zr36057/zr36067 PCI controller driver, for the + * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux + * Media Labs LML33/LML33R10. + * + * This part handles the procFS entries (/proc/ZORAN[%d]) + * + * Copyright (C) 2000 Serguei Miridonov + * + * Currently maintained by: + * Ronald Bultje + * Laurent Pinchart + * Mailinglist + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "compat.h" +#include +#include +#include +#include + +#include +#include + +#include "videocodec.h" +#include "zoran.h" +#include "zoran_procfs.h" + +extern int *zr_debug; + +#define dprintk(num, format, args...) \ + do { \ + if (*zr_debug >= num) \ + printk(format, ##args); \ + } while (0) + +#ifdef CONFIG_PROC_FS +struct procfs_params_zr36067 { + char *name; + short reg; + u32 mask; + short bit; +}; + +static const struct procfs_params_zr36067 zr67[] = { + {"HSPol", 0x000, 1, 30}, + {"HStart", 0x000, 0x3ff, 10}, + {"HEnd", 0x000, 0x3ff, 0}, + + {"VSPol", 0x004, 1, 30}, + {"VStart", 0x004, 0x3ff, 10}, + {"VEnd", 0x004, 0x3ff, 0}, + + {"ExtFl", 0x008, 1, 26}, + {"TopField", 0x008, 1, 25}, + {"VCLKPol", 0x008, 1, 24}, + {"DupFld", 0x008, 1, 20}, + {"LittleEndian", 0x008, 1, 0}, + + {"HsyncStart", 0x10c, 0xffff, 16}, + {"LineTot", 0x10c, 0xffff, 0}, + + {"NAX", 0x110, 0xffff, 16}, + {"PAX", 0x110, 0xffff, 0}, + + {"NAY", 0x114, 0xffff, 16}, + {"PAY", 0x114, 0xffff, 0}, + + /* {"",,,}, */ + + {NULL, 0, 0, 0}, +}; + +static void +setparam (struct zoran *zr, + char *name, + char *sval) +{ + int i = 0, reg0, reg, val; + + while (zr67[i].name != NULL) { + if (!strncmp(name, zr67[i].name, strlen(zr67[i].name))) { + reg = reg0 = btread(zr67[i].reg); + reg &= ~(zr67[i].mask << zr67[i].bit); + if (!isdigit(sval[0])) + break; + val = simple_strtoul(sval, NULL, 0); + if ((val & ~zr67[i].mask)) + break; + reg |= (val & zr67[i].mask) << zr67[i].bit; + dprintk(4, + KERN_INFO + "%s: setparam: setting ZR36067 register 0x%03x: 0x%08x=>0x%08x %s=%d\n", + ZR_DEVNAME(zr), zr67[i].reg, reg0, reg, + zr67[i].name, val); + btwrite(reg, zr67[i].reg); + break; + } + i++; + } +} + +static int zoran_show(struct seq_file *p, void *v) +{ + struct zoran *zr = p->private; + int i; + + seq_printf(p, "ZR36067 registers:\n"); + for (i = 0; i < 0x130; i += 16) + seq_printf(p, "%03X %08X %08X %08X %08X \n", i, + btread(i), btread(i+4), btread(i+8), btread(i+12)); + return 0; +} + +static int zoran_open(struct inode *inode, struct file *file) +{ + struct zoran *data = PDE(inode)->data; + return single_open(file, zoran_show, data); +} + +static ssize_t zoran_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct zoran *zr = PDE(file->f_dentry->d_inode)->data; + char *string, *sp; + char *line, *ldelim, *varname, *svar, *tdelim; + + if (count > 32768) /* Stupidity filter */ + return -EINVAL; + + string = sp = vmalloc(count + 1); + if (!string) { + dprintk(1, + KERN_ERR + "%s: write_proc: can not allocate memory\n", + ZR_DEVNAME(zr)); + return -ENOMEM; + } + if (copy_from_user(string, buffer, count)) { + vfree (string); + return -EFAULT; + } + string[count] = 0; + dprintk(4, KERN_INFO "%s: write_proc: name=%s count=%zu zr=%p\n", + ZR_DEVNAME(zr), file->f_dentry->d_name.name, count, zr); + ldelim = " \t\n"; + tdelim = "="; + line = strpbrk(sp, ldelim); + while (line) { + *line = 0; + svar = strpbrk(sp, tdelim); + if (svar) { + *svar = 0; + varname = sp; + svar++; + setparam(zr, varname, svar); + } + sp = line + 1; + line = strpbrk(sp, ldelim); + } + vfree(string); + + return count; +} + +static struct file_operations zoran_operations = { + .open = zoran_open, + .read = seq_read, + .write = zoran_write, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +int +zoran_proc_init (struct zoran *zr) +{ +#ifdef CONFIG_PROC_FS + char name[8]; + + snprintf(name, 7, "zoran%d", zr->id); + if ((zr->zoran_proc = create_proc_entry(name, 0, NULL))) { + zr->zoran_proc->data = zr; + zr->zoran_proc->owner = THIS_MODULE; + zr->zoran_proc->proc_fops = &zoran_operations; + dprintk(2, + KERN_INFO + "%s: procfs entry /proc/%s allocated. data=%p\n", + ZR_DEVNAME(zr), name, zr->zoran_proc->data); + } else { + dprintk(1, KERN_ERR "%s: Unable to initialise /proc/%s\n", + ZR_DEVNAME(zr), name); + return 1; + } +#endif + return 0; +} + +void +zoran_proc_cleanup (struct zoran *zr) +{ +#ifdef CONFIG_PROC_FS + char name[8]; + + snprintf(name, 7, "zoran%d", zr->id); + if (zr->zoran_proc) + remove_proc_entry(name, NULL); + zr->zoran_proc = NULL; +#endif +} diff --git a/linux/drivers/media/video/zoran_procfs.h b/linux/drivers/media/video/zoran_procfs.h new file mode 100644 index 000000000..f2d5b1ba4 --- /dev/null +++ b/linux/drivers/media/video/zoran_procfs.h @@ -0,0 +1,36 @@ +/* + * Zoran zr36057/zr36067 PCI controller driver, for the + * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux + * Media Labs LML33/LML33R10. + * + * This part handles card-specific data and detection + * + * Copyright (C) 2000 Serguei Miridonov + * + * Currently maintained by: + * Ronald Bultje + * Laurent Pinchart + * Mailinglist + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ZORAN_PROCFS_H__ +#define __ZORAN_PROCFS_H__ + +extern int zoran_proc_init(struct zoran *zr); +extern void zoran_proc_cleanup(struct zoran *zr); + +#endif /* __ZORAN_PROCFS_H__ */ diff --git a/linux/drivers/media/video/zr36016.c b/linux/drivers/media/video/zr36016.c new file mode 100644 index 000000000..7f958d803 --- /dev/null +++ b/linux/drivers/media/video/zr36016.c @@ -0,0 +1,531 @@ +/* + * Zoran ZR36016 basic configuration functions + * + * Copyright (C) 2001 Wolfgang Scherr + * + * $Id: zr36016.c,v 1.1.2.14 2003/08/20 19:46:55 rbultje Exp $ + * + * ------------------------------------------------------------------------ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * ------------------------------------------------------------------------ + */ + +#define ZR016_VERSION "v0.7" + +#include +#include +#include +#include + +#include +#include + +/* includes for structures and defines regarding video + #include */ + +/* I/O commands, error codes */ +#include +//#include + +/* v4l API */ +#include "compat.h" +#include + +/* headerfile of this module */ +#include"zr36016.h" + +/* codec io API */ +#include"videocodec.h" + +/* it doesn't make sense to have more than 20 or so, + just to prevent some unwanted loops */ +#define MAX_CODECS 20 + +/* amount of chips attached via this driver */ +static int zr36016_codecs = 0; + +/* debugging is available via module parameter */ + +static int debug = 0; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-4)"); + +#define dprintk(num, format, args...) \ + do { \ + if (debug >= num) \ + printk(format, ##args); \ + } while (0) + +/* ========================================================================= + Local hardware I/O functions: + + read/write via codec layer (registers are located in the master device) + ========================================================================= */ + +/* read and write functions */ +static u8 +zr36016_read (struct zr36016 *ptr, + u16 reg) +{ + u8 value = 0; + + // just in case something is wrong... + if (ptr->codec->master_data->readreg) + value = + (ptr->codec->master_data-> + readreg(ptr->codec, reg)) & 0xFF; + else + dprintk(1, + KERN_ERR "%s: invalid I/O setup, nothing read!\n", + ptr->name); + + dprintk(4, "%s: reading from 0x%04x: %02x\n", ptr->name, reg, + value); + + return value; +} + +static void +zr36016_write (struct zr36016 *ptr, + u16 reg, + u8 value) +{ + dprintk(4, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value, + reg); + + // just in case something is wrong... + if (ptr->codec->master_data->writereg) { + ptr->codec->master_data->writereg(ptr->codec, reg, value); + } else + dprintk(1, + KERN_ERR + "%s: invalid I/O setup, nothing written!\n", + ptr->name); +} + +/* indirect read and write functions */ +/* the 016 supports auto-addr-increment, but + * writing it all time cost not much and is safer... */ +static u8 +zr36016_readi (struct zr36016 *ptr, + u16 reg) +{ + u8 value = 0; + + // just in case something is wrong... + if ((ptr->codec->master_data->writereg) && + (ptr->codec->master_data->readreg)) { + ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); // ADDR + value = (ptr->codec->master_data->readreg(ptr->codec, ZR016_IDATA)) & 0xFF; // DATA + } else + dprintk(1, + KERN_ERR + "%s: invalid I/O setup, nothing read (i)!\n", + ptr->name); + + dprintk(4, "%s: reading indirect from 0x%04x: %02x\n", ptr->name, + reg, value); + return value; +} + +static void +zr36016_writei (struct zr36016 *ptr, + u16 reg, + u8 value) +{ + dprintk(4, "%s: writing indirect 0x%02x to 0x%04x\n", ptr->name, + value, reg); + + // just in case something is wrong... + if (ptr->codec->master_data->writereg) { + ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); // ADDR + ptr->codec->master_data->writereg(ptr->codec, ZR016_IDATA, value & 0x0FF); // DATA + } else + dprintk(1, + KERN_ERR + "%s: invalid I/O setup, nothing written (i)!\n", + ptr->name); +} + +/* ========================================================================= + Local helper function: + + version read + ========================================================================= */ + +/* version kept in datastructure */ +static u8 +zr36016_read_version (struct zr36016 *ptr) +{ + ptr->version = zr36016_read(ptr, 0) >> 4; + return ptr->version; +} + +/* ========================================================================= + Local helper function: + + basic test of "connectivity", writes/reads to/from PAX-Lo register + ========================================================================= */ + +static int +zr36016_basic_test (struct zr36016 *ptr) +{ + if (debug) { + int i; + zr36016_writei(ptr, ZR016I_PAX_LO, 0x55); + dprintk(1, KERN_INFO "%s: registers: ", ptr->name); + for (i = 0; i <= 0x0b; i++) + dprintk(1, "%02x ", zr36016_readi(ptr, i)); + dprintk(1, "\n"); + } + // for testing just write 0, then the default value to a register and read + // it back in both cases + zr36016_writei(ptr, ZR016I_PAX_LO, 0x00); + if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0) { + dprintk(1, + KERN_ERR + "%s: attach failed, can't connect to vfe processor!\n", + ptr->name); + return -ENXIO; + } + zr36016_writei(ptr, ZR016I_PAX_LO, 0x0d0); + if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0d0) { + dprintk(1, + KERN_ERR + "%s: attach failed, can't connect to vfe processor!\n", + ptr->name); + return -ENXIO; + } + // we allow version numbers from 0-3, should be enough, though + zr36016_read_version(ptr); + if (ptr->version & 0x0c) { + dprintk(1, + KERN_ERR + "%s: attach failed, suspicious version %d found...\n", + ptr->name, ptr->version); + return -ENXIO; + } + + return 0; /* looks good! */ +} + +/* ========================================================================= + Local helper function: + + simple loop for pushing the init datasets - NO USE -- + ========================================================================= */ + +#if 0 +static int zr36016_pushit (struct zr36016 *ptr, + u16 startreg, + u16 len, + const char *data) +{ + int i=0; + + dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", + ptr->name, startreg,len); + while (imode == CODEC_DO_COMPRESSION ? + ZR016_COMPRESSION : ZR016_EXPANSION)); + + // misc setup + zr36016_writei(ptr, ZR016I_SETUP1, + (ptr->xdec ? (ZR016_HRFL | ZR016_HORZ) : 0) | + (ptr->ydec ? ZR016_VERT : 0) | ZR016_CNTI); + zr36016_writei(ptr, ZR016I_SETUP2, ZR016_CCIR); + + // Window setup + // (no extra offset for now, norm defines offset, default width height) + zr36016_writei(ptr, ZR016I_PAX_HI, ptr->width >> 8); + zr36016_writei(ptr, ZR016I_PAX_LO, ptr->width & 0xFF); + zr36016_writei(ptr, ZR016I_PAY_HI, ptr->height >> 8); + zr36016_writei(ptr, ZR016I_PAY_LO, ptr->height & 0xFF); + zr36016_writei(ptr, ZR016I_NAX_HI, ptr->xoff >> 8); + zr36016_writei(ptr, ZR016I_NAX_LO, ptr->xoff & 0xFF); + zr36016_writei(ptr, ZR016I_NAY_HI, ptr->yoff >> 8); + zr36016_writei(ptr, ZR016I_NAY_LO, ptr->yoff & 0xFF); + + /* shall we continue now, please? */ + zr36016_write(ptr, ZR016_GOSTOP, 1); +} + +/* ========================================================================= + CODEC API FUNCTIONS + + this functions are accessed by the master via the API structure + ========================================================================= */ + +/* set compression/expansion mode and launches codec - + this should be the last call from the master before starting processing */ +static int +zr36016_set_mode (struct videocodec *codec, + int mode) +{ + struct zr36016 *ptr = (struct zr36016 *) codec->data; + + dprintk(2, "%s: set_mode %d call\n", ptr->name, mode); + + if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION)) + return -EINVAL; + + ptr->mode = mode; + zr36016_init(ptr); + + return 0; +} + +/* set picture size */ +static int +zr36016_set_video (struct videocodec *codec, + struct tvnorm *norm, + struct vfe_settings *cap, + struct vfe_polarity *pol) +{ + struct zr36016 *ptr = (struct zr36016 *) codec->data; + + dprintk(2, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) call\n", + ptr->name, norm->HStart, norm->VStart, + cap->x, cap->y, cap->width, cap->height, + cap->decimation); + + /* if () return -EINVAL; + * trust the master driver that it knows what it does - so + * we allow invalid startx/y for now ... */ + ptr->width = cap->width; + ptr->height = cap->height; + /* (Ronald) This is ugly. zoran_device.c, line 387 + * already mentions what happens if HStart is even + * (blue faces, etc., cr/cb inversed). There's probably + * some good reason why HStart is 0 instead of 1, so I'm + * leaving it to this for now, but really... This can be + * done a lot simpler */ + ptr->xoff = (norm->HStart ? norm->HStart : 1) + cap->x; + /* Something to note here (I don't understand it), setting + * VStart too high will cause the codec to 'not work'. I + * really don't get it. values of 16 (VStart) already break + * it here. Just '0' seems to work. More testing needed! */ + ptr->yoff = norm->VStart + cap->y; + /* (Ronald) dzjeeh, can't this thing do hor_decimation = 4? */ + ptr->xdec = ((cap->decimation & 0xff) == 1) ? 0 : 1; + ptr->ydec = (((cap->decimation >> 8) & 0xff) == 1) ? 0 : 1; + + return 0; +} + +/* additional control functions */ +static int +zr36016_control (struct videocodec *codec, + int type, + int size, + void *data) +{ + struct zr36016 *ptr = (struct zr36016 *) codec->data; + int *ival = (int *) data; + + dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type, + size); + + switch (type) { + case CODEC_G_STATUS: /* get last status - we don't know it ... */ + if (size != sizeof(int)) + return -EFAULT; + *ival = 0; + break; + + case CODEC_G_CODEC_MODE: + if (size != sizeof(int)) + return -EFAULT; + *ival = 0; + break; + + case CODEC_S_CODEC_MODE: + if (size != sizeof(int)) + return -EFAULT; + if (*ival != 0) + return -EINVAL; + /* not needed, do nothing */ + return 0; + + case CODEC_G_VFE: + case CODEC_S_VFE: + return 0; + + case CODEC_S_MMAP: + /* not available, give an error */ + return -ENXIO; + + default: + return -EINVAL; + } + + return size; +} + +/* ========================================================================= + Exit and unregister function: + + Deinitializes Zoran's JPEG processor + ========================================================================= */ + +static int +zr36016_unset (struct videocodec *codec) +{ + struct zr36016 *ptr = codec->data; + + if (ptr) { + /* do wee need some codec deinit here, too ???? */ + + dprintk(1, "%s: finished codec #%d\n", ptr->name, + ptr->num); + kfree(ptr); + codec->data = NULL; + + zr36016_codecs--; + return 0; + } + + return -EFAULT; +} + +/* ========================================================================= + Setup and registry function: + + Initializes Zoran's JPEG processor + + Also sets pixel size, average code size, mode (compr./decompr.) + (the given size is determined by the processor with the video interface) + ========================================================================= */ + +static int +zr36016_setup (struct videocodec *codec) +{ + struct zr36016 *ptr; + int res; + + dprintk(2, "zr36016: initializing VFE subsystem #%d.\n", + zr36016_codecs); + + if (zr36016_codecs == MAX_CODECS) { + dprintk(1, + KERN_ERR "zr36016: Can't attach more codecs!\n"); + return -ENOSPC; + } + //mem structure init + codec->data = ptr = kzalloc(sizeof(struct zr36016), GFP_KERNEL); + if (NULL == ptr) { + dprintk(1, KERN_ERR "zr36016: Can't get enough memory!\n"); + return -ENOMEM; + } + + snprintf(ptr->name, sizeof(ptr->name), "zr36016[%d]", + zr36016_codecs); + ptr->num = zr36016_codecs++; + ptr->codec = codec; + + //testing + res = zr36016_basic_test(ptr); + if (res < 0) { + zr36016_unset(codec); + return res; + } + //final setup + ptr->mode = CODEC_DO_COMPRESSION; + ptr->width = 768; + ptr->height = 288; + ptr->xdec = 1; + ptr->ydec = 0; + zr36016_init(ptr); + + dprintk(1, KERN_INFO "%s: codec v%d attached and running\n", + ptr->name, ptr->version); + + return 0; +} + +static const struct videocodec zr36016_codec = { + .owner = THIS_MODULE, + .name = "zr36016", + .magic = 0L, // magic not used + .flags = + CODEC_FLAG_HARDWARE | CODEC_FLAG_VFE | CODEC_FLAG_ENCODER | + CODEC_FLAG_DECODER, + .type = CODEC_TYPE_ZR36016, + .setup = zr36016_setup, // functionality + .unset = zr36016_unset, + .set_mode = zr36016_set_mode, + .set_video = zr36016_set_video, + .control = zr36016_control, + // others are not used +}; + +/* ========================================================================= + HOOK IN DRIVER AS KERNEL MODULE + ========================================================================= */ + +static int __init +zr36016_init_module (void) +{ + //dprintk(1, "ZR36016 driver %s\n",ZR016_VERSION); + zr36016_codecs = 0; + return videocodec_register(&zr36016_codec); +} + +static void __exit +zr36016_cleanup_module (void) +{ + if (zr36016_codecs) { + dprintk(1, + "zr36016: something's wrong - %d codecs left somehow.\n", + zr36016_codecs); + } + videocodec_unregister(&zr36016_codec); +} + +module_init(zr36016_init_module); +module_exit(zr36016_cleanup_module); + +MODULE_AUTHOR("Wolfgang Scherr "); +MODULE_DESCRIPTION("Driver module for ZR36016 video frontends " + ZR016_VERSION); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/zr36016.h b/linux/drivers/media/video/zr36016.h new file mode 100644 index 000000000..8c79229f6 --- /dev/null +++ b/linux/drivers/media/video/zr36016.h @@ -0,0 +1,111 @@ +/* + * Zoran ZR36016 basic configuration functions - header file + * + * Copyright (C) 2001 Wolfgang Scherr + * + * $Id: zr36016.h,v 1.1.2.3 2003/01/14 21:18:07 rbultje Exp $ + * + * ------------------------------------------------------------------------ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * ------------------------------------------------------------------------ + */ + +#ifndef ZR36016_H +#define ZR36016_H + +/* data stored for each zoran jpeg codec chip */ +struct zr36016 { + char name[32]; + int num; + /* io datastructure */ + struct videocodec *codec; + // coder status + __u8 version; + // actual coder setup + int mode; + + __u16 xoff; + __u16 yoff; + __u16 width; + __u16 height; + __u16 xdec; + __u16 ydec; +}; + +/* direct register addresses */ +#define ZR016_GOSTOP 0x00 +#define ZR016_MODE 0x01 +#define ZR016_IADDR 0x02 +#define ZR016_IDATA 0x03 + +/* indirect register addresses */ +#define ZR016I_SETUP1 0x00 +#define ZR016I_SETUP2 0x01 +#define ZR016I_NAX_LO 0x02 +#define ZR016I_NAX_HI 0x03 +#define ZR016I_PAX_LO 0x04 +#define ZR016I_PAX_HI 0x05 +#define ZR016I_NAY_LO 0x06 +#define ZR016I_NAY_HI 0x07 +#define ZR016I_PAY_LO 0x08 +#define ZR016I_PAY_HI 0x09 +#define ZR016I_NOL_LO 0x0a +#define ZR016I_NOL_HI 0x0b + +/* possible values for mode register */ +#define ZR016_RGB444_YUV444 0x00 +#define ZR016_RGB444_YUV422 0x01 +#define ZR016_RGB444_YUV411 0x02 +#define ZR016_RGB444_Y400 0x03 +#define ZR016_RGB444_RGB444 0x04 +#define ZR016_YUV444_YUV444 0x08 +#define ZR016_YUV444_YUV422 0x09 +#define ZR016_YUV444_YUV411 0x0a +#define ZR016_YUV444_Y400 0x0b +#define ZR016_YUV444_RGB444 0x0c +#define ZR016_YUV422_YUV422 0x11 +#define ZR016_YUV422_YUV411 0x12 +#define ZR016_YUV422_Y400 0x13 +#define ZR016_YUV411_YUV411 0x16 +#define ZR016_YUV411_Y400 0x17 +#define ZR016_4444_4444 0x19 +#define ZR016_100_100 0x1b + +#define ZR016_RGB444 0x00 +#define ZR016_YUV444 0x20 +#define ZR016_YUV422 0x40 + +#define ZR016_COMPRESSION 0x80 +#define ZR016_EXPANSION 0x80 + +/* possible values for setup 1 register */ +#define ZR016_CKRT 0x80 +#define ZR016_VERT 0x40 +#define ZR016_HORZ 0x20 +#define ZR016_HRFL 0x10 +#define ZR016_DSFL 0x08 +#define ZR016_SBFL 0x04 +#define ZR016_RSTR 0x02 +#define ZR016_CNTI 0x01 + +/* possible values for setup 2 register */ +#define ZR016_SYEN 0x40 +#define ZR016_CCIR 0x04 +#define ZR016_SIGN 0x02 +#define ZR016_YMCS 0x01 + +#endif /*fndef ZR36016_H */ diff --git a/linux/drivers/media/video/zr36050.c b/linux/drivers/media/video/zr36050.c new file mode 100644 index 000000000..a6bbd1256 --- /dev/null +++ b/linux/drivers/media/video/zr36050.c @@ -0,0 +1,905 @@ +/* + * Zoran ZR36050 basic configuration functions + * + * Copyright (C) 2001 Wolfgang Scherr + * + * $Id: zr36050.c,v 1.1.2.11 2003/08/03 14:54:53 rbultje Exp $ + * + * ------------------------------------------------------------------------ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * ------------------------------------------------------------------------ + */ + +#define ZR050_VERSION "v0.7.1" + +#include +#include +#include +#include + +#include +#include + +/* includes for structures and defines regarding video + #include */ + +/* I/O commands, error codes */ +#include +//#include + +/* headerfile of this module */ +#include"zr36050.h" + +/* codec io API */ +#include"videocodec.h" + +/* it doesn't make sense to have more than 20 or so, + just to prevent some unwanted loops */ +#define MAX_CODECS 20 + +/* amount of chips attached via this driver */ +static int zr36050_codecs = 0; + +/* debugging is available via module parameter */ + +static int debug = 0; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-4)"); + +#define dprintk(num, format, args...) \ + do { \ + if (debug >= num) \ + printk(format, ##args); \ + } while (0) + +/* ========================================================================= + Local hardware I/O functions: + + read/write via codec layer (registers are located in the master device) + ========================================================================= */ + +/* read and write functions */ +static u8 +zr36050_read (struct zr36050 *ptr, + u16 reg) +{ + u8 value = 0; + + // just in case something is wrong... + if (ptr->codec->master_data->readreg) + value = (ptr->codec->master_data->readreg(ptr->codec, + reg)) & 0xFF; + else + dprintk(1, + KERN_ERR "%s: invalid I/O setup, nothing read!\n", + ptr->name); + + dprintk(4, "%s: reading from 0x%04x: %02x\n", ptr->name, reg, + value); + + return value; +} + +static void +zr36050_write (struct zr36050 *ptr, + u16 reg, + u8 value) +{ + dprintk(4, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value, + reg); + + // just in case something is wrong... + if (ptr->codec->master_data->writereg) + ptr->codec->master_data->writereg(ptr->codec, reg, value); + else + dprintk(1, + KERN_ERR + "%s: invalid I/O setup, nothing written!\n", + ptr->name); +} + +/* ========================================================================= + Local helper function: + + status read + ========================================================================= */ + +/* status is kept in datastructure */ +static u8 +zr36050_read_status1 (struct zr36050 *ptr) +{ + ptr->status1 = zr36050_read(ptr, ZR050_STATUS_1); + + zr36050_read(ptr, 0); + return ptr->status1; +} + +/* ========================================================================= + Local helper function: + + scale factor read + ========================================================================= */ + +/* scale factor is kept in datastructure */ +static u16 +zr36050_read_scalefactor (struct zr36050 *ptr) +{ + ptr->scalefact = (zr36050_read(ptr, ZR050_SF_HI) << 8) | + (zr36050_read(ptr, ZR050_SF_LO) & 0xFF); + + /* leave 0 selected for an eventually GO from master */ + zr36050_read(ptr, 0); + return ptr->scalefact; +} + +/* ========================================================================= + Local helper function: + + wait if codec is ready to proceed (end of processing) or time is over + ========================================================================= */ + +static void +zr36050_wait_end (struct zr36050 *ptr) +{ + int i = 0; + + while (!(zr36050_read_status1(ptr) & 0x4)) { + udelay(1); + if (i++ > 200000) { // 200ms, there is for sure something wrong!!! + dprintk(1, + "%s: timout at wait_end (last status: 0x%02x)\n", + ptr->name, ptr->status1); + break; + } + } +} + +/* ========================================================================= + Local helper function: + + basic test of "connectivity", writes/reads to/from memory the SOF marker + ========================================================================= */ + +static int +zr36050_basic_test (struct zr36050 *ptr) +{ + zr36050_write(ptr, ZR050_SOF_IDX, 0x00); + zr36050_write(ptr, ZR050_SOF_IDX + 1, 0x00); + if ((zr36050_read(ptr, ZR050_SOF_IDX) | + zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0x0000) { + dprintk(1, + KERN_ERR + "%s: attach failed, can't connect to jpeg processor!\n", + ptr->name); + return -ENXIO; + } + zr36050_write(ptr, ZR050_SOF_IDX, 0xff); + zr36050_write(ptr, ZR050_SOF_IDX + 1, 0xc0); + if (((zr36050_read(ptr, ZR050_SOF_IDX) << 8) | + zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0xffc0) { + dprintk(1, + KERN_ERR + "%s: attach failed, can't connect to jpeg processor!\n", + ptr->name); + return -ENXIO; + } + + zr36050_wait_end(ptr); + if ((ptr->status1 & 0x4) == 0) { + dprintk(1, + KERN_ERR + "%s: attach failed, jpeg processor failed (end flag)!\n", + ptr->name); + return -EBUSY; + } + + return 0; /* looks good! */ +} + +/* ========================================================================= + Local helper function: + + simple loop for pushing the init datasets + ========================================================================= */ + +static int +zr36050_pushit (struct zr36050 *ptr, + u16 startreg, + u16 len, + const char *data) +{ + int i = 0; + + dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", ptr->name, + startreg, len); + while (i < len) { + zr36050_write(ptr, startreg++, data[i++]); + } + + return i; +} + +/* ========================================================================= + Basic datasets: + + jpeg baseline setup data (you find it on lots places in internet, or just + extract it from any regular .jpg image...) + + Could be variable, but until it's not needed it they are just fixed to save + memory. Otherwise expand zr36050 structure with arrays, push the values to + it and initalize from there, as e.g. the linux zr36057/60 driver does it. + ========================================================================= */ + +static const char zr36050_dqt[0x86] = { + 0xff, 0xdb, //Marker: DQT + 0x00, 0x84, //Length: 2*65+2 + 0x00, //Pq,Tq first table + 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, + 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, + 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, + 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, + 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, + 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, + 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, + 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, + 0x01, //Pq,Tq second table + 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, + 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 +}; + +static const char zr36050_dht[0x1a4] = { + 0xff, 0xc4, //Marker: DHT + 0x01, 0xa2, //Length: 2*AC, 2*DC + 0x00, //DC first table + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x01, //DC second table + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x10, //AC first table + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, + 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, + 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, + 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, + 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, + 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, + 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, + 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, + 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, + 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, + 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, + 0x11, //AC second table + 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, + 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, + 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, + 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, + 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, + 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, + 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, + 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, + 0xF9, 0xFA +}; + +/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */ +#define NO_OF_COMPONENTS 0x3 //Y,U,V +#define BASELINE_PRECISION 0x8 //MCU size (?) +static const char zr36050_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT +static const char zr36050_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC +static const char zr36050_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC + +/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */ +static const char zr36050_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 }; +static const char zr36050_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 }; + +/* ========================================================================= + Local helper functions: + + calculation and setup of parameter-dependent JPEG baseline segments + (needed for compression only) + ========================================================================= */ + +/* ------------------------------------------------------------------------- */ + +/* SOF (start of frame) segment depends on width, height and sampling ratio + of each color component */ + +static int +zr36050_set_sof (struct zr36050 *ptr) +{ + char sof_data[34]; // max. size of register set + int i; + + dprintk(3, "%s: write SOF (%dx%d, %d components)\n", ptr->name, + ptr->width, ptr->height, NO_OF_COMPONENTS); + sof_data[0] = 0xff; + sof_data[1] = 0xc0; + sof_data[2] = 0x00; + sof_data[3] = (3 * NO_OF_COMPONENTS) + 8; + sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36050 + sof_data[5] = (ptr->height) >> 8; + sof_data[6] = (ptr->height) & 0xff; + sof_data[7] = (ptr->width) >> 8; + sof_data[8] = (ptr->width) & 0xff; + sof_data[9] = NO_OF_COMPONENTS; + for (i = 0; i < NO_OF_COMPONENTS; i++) { + sof_data[10 + (i * 3)] = i; // index identifier + sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | (ptr->v_samp_ratio[i]); // sampling ratios + sof_data[12 + (i * 3)] = zr36050_tq[i]; // Q table selection + } + return zr36050_pushit(ptr, ZR050_SOF_IDX, + (3 * NO_OF_COMPONENTS) + 10, sof_data); +} + +/* ------------------------------------------------------------------------- */ + +/* SOS (start of scan) segment depends on the used scan components + of each color component */ + +static int +zr36050_set_sos (struct zr36050 *ptr) +{ + char sos_data[16]; // max. size of register set + int i; + + dprintk(3, "%s: write SOS\n", ptr->name); + sos_data[0] = 0xff; + sos_data[1] = 0xda; + sos_data[2] = 0x00; + sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3; + sos_data[4] = NO_OF_COMPONENTS; + for (i = 0; i < NO_OF_COMPONENTS; i++) { + sos_data[5 + (i * 2)] = i; // index + sos_data[6 + (i * 2)] = (zr36050_td[i] << 4) | zr36050_ta[i]; // AC/DC tbl.sel. + } + sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start + sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3F; + sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00; + return zr36050_pushit(ptr, ZR050_SOS1_IDX, + 4 + 1 + (2 * NO_OF_COMPONENTS) + 3, + sos_data); +} + +/* ------------------------------------------------------------------------- */ + +/* DRI (define restart interval) */ + +static int +zr36050_set_dri (struct zr36050 *ptr) +{ + char dri_data[6]; // max. size of register set + + dprintk(3, "%s: write DRI\n", ptr->name); + dri_data[0] = 0xff; + dri_data[1] = 0xdd; + dri_data[2] = 0x00; + dri_data[3] = 0x04; + dri_data[4] = ptr->dri >> 8; + dri_data[5] = ptr->dri & 0xff; + return zr36050_pushit(ptr, ZR050_DRI_IDX, 6, dri_data); +} + +/* ========================================================================= + Setup function: + + Setup compression/decompression of Zoran's JPEG processor + ( see also zoran 36050 manual ) + + ... sorry for the spaghetti code ... + ========================================================================= */ +static void +zr36050_init (struct zr36050 *ptr) +{ + int sum = 0; + long bitcnt, tmp; + + if (ptr->mode == CODEC_DO_COMPRESSION) { + dprintk(2, "%s: COMPRESSION SETUP\n", ptr->name); + + /* 050 communicates with 057 in master mode */ + zr36050_write(ptr, ZR050_HARDWARE, ZR050_HW_MSTR); + + /* encoding table preload for compression */ + zr36050_write(ptr, ZR050_MODE, + ZR050_MO_COMP | ZR050_MO_TLM); + zr36050_write(ptr, ZR050_OPTIONS, 0); + + /* disable all IRQs */ + zr36050_write(ptr, ZR050_INT_REQ_0, 0); + zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1 + + /* volume control settings */ + /*zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol);*/ + zr36050_write(ptr, ZR050_SF_HI, ptr->scalefact >> 8); + zr36050_write(ptr, ZR050_SF_LO, ptr->scalefact & 0xff); + + zr36050_write(ptr, ZR050_AF_HI, 0xff); + zr36050_write(ptr, ZR050_AF_M, 0xff); + zr36050_write(ptr, ZR050_AF_LO, 0xff); + + /* setup the variable jpeg tables */ + sum += zr36050_set_sof(ptr); + sum += zr36050_set_sos(ptr); + sum += zr36050_set_dri(ptr); + + /* setup the fixed jpeg tables - maybe variable, though - + * (see table init section above) */ + dprintk(3, "%s: write DQT, DHT, APP\n", ptr->name); + sum += zr36050_pushit(ptr, ZR050_DQT_IDX, + sizeof(zr36050_dqt), zr36050_dqt); + sum += zr36050_pushit(ptr, ZR050_DHT_IDX, + sizeof(zr36050_dht), zr36050_dht); + zr36050_write(ptr, ZR050_APP_IDX, 0xff); + zr36050_write(ptr, ZR050_APP_IDX + 1, 0xe0 + ptr->app.appn); + zr36050_write(ptr, ZR050_APP_IDX + 2, 0x00); + zr36050_write(ptr, ZR050_APP_IDX + 3, ptr->app.len + 2); + sum += zr36050_pushit(ptr, ZR050_APP_IDX + 4, 60, + ptr->app.data) + 4; + zr36050_write(ptr, ZR050_COM_IDX, 0xff); + zr36050_write(ptr, ZR050_COM_IDX + 1, 0xfe); + zr36050_write(ptr, ZR050_COM_IDX + 2, 0x00); + zr36050_write(ptr, ZR050_COM_IDX + 3, ptr->com.len + 2); + sum += zr36050_pushit(ptr, ZR050_COM_IDX + 4, 60, + ptr->com.data) + 4; + + /* do the internal huffman table preload */ + zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI); + + zr36050_write(ptr, ZR050_GO, 1); // launch codec + zr36050_wait_end(ptr); + dprintk(2, "%s: Status after table preload: 0x%02x\n", + ptr->name, ptr->status1); + + if ((ptr->status1 & 0x4) == 0) { + dprintk(1, KERN_ERR "%s: init aborted!\n", + ptr->name); + return; // something is wrong, its timed out!!!! + } + + /* setup misc. data for compression (target code sizes) */ + + /* size of compressed code to reach without header data */ + sum = ptr->real_code_vol - sum; + bitcnt = sum << 3; /* need the size in bits */ + + tmp = bitcnt >> 16; + dprintk(3, + "%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n", + ptr->name, sum, ptr->real_code_vol, bitcnt, tmp); + zr36050_write(ptr, ZR050_TCV_NET_HI, tmp >> 8); + zr36050_write(ptr, ZR050_TCV_NET_MH, tmp & 0xff); + tmp = bitcnt & 0xffff; + zr36050_write(ptr, ZR050_TCV_NET_ML, tmp >> 8); + zr36050_write(ptr, ZR050_TCV_NET_LO, tmp & 0xff); + + bitcnt -= bitcnt >> 7; // bits without stuffing + bitcnt -= ((bitcnt * 5) >> 6); // bits without eob + + tmp = bitcnt >> 16; + dprintk(3, "%s: code: nettobit=%ld, highnettobits=%ld\n", + ptr->name, bitcnt, tmp); + zr36050_write(ptr, ZR050_TCV_DATA_HI, tmp >> 8); + zr36050_write(ptr, ZR050_TCV_DATA_MH, tmp & 0xff); + tmp = bitcnt & 0xffff; + zr36050_write(ptr, ZR050_TCV_DATA_ML, tmp >> 8); + zr36050_write(ptr, ZR050_TCV_DATA_LO, tmp & 0xff); + + /* compression setup with or without bitrate control */ + zr36050_write(ptr, ZR050_MODE, + ZR050_MO_COMP | ZR050_MO_PASS2 | + (ptr->bitrate_ctrl ? ZR050_MO_BRC : 0)); + + /* this headers seem to deliver "valid AVI" jpeg frames */ + zr36050_write(ptr, ZR050_MARKERS_EN, + ZR050_ME_DQT | ZR050_ME_DHT | + ((ptr->app.len > 0) ? ZR050_ME_APP : 0) | + ((ptr->com.len > 0) ? ZR050_ME_COM : 0)); + } else { + dprintk(2, "%s: EXPANSION SETUP\n", ptr->name); + + /* 050 communicates with 055 in master mode */ + zr36050_write(ptr, ZR050_HARDWARE, + ZR050_HW_MSTR | ZR050_HW_CFIS_2_CLK); + + /* encoding table preload */ + zr36050_write(ptr, ZR050_MODE, ZR050_MO_TLM); + + /* disable all IRQs */ + zr36050_write(ptr, ZR050_INT_REQ_0, 0); + zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1 + + dprintk(3, "%s: write DHT\n", ptr->name); + zr36050_pushit(ptr, ZR050_DHT_IDX, sizeof(zr36050_dht), + zr36050_dht); + + /* do the internal huffman table preload */ + zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI); + + zr36050_write(ptr, ZR050_GO, 1); // launch codec + zr36050_wait_end(ptr); + dprintk(2, "%s: Status after table preload: 0x%02x\n", + ptr->name, ptr->status1); + + if ((ptr->status1 & 0x4) == 0) { + dprintk(1, KERN_ERR "%s: init aborted!\n", + ptr->name); + return; // something is wrong, its timed out!!!! + } + + /* setup misc. data for expansion */ + zr36050_write(ptr, ZR050_MODE, 0); + zr36050_write(ptr, ZR050_MARKERS_EN, 0); + } + + /* adr on selected, to allow GO from master */ + zr36050_read(ptr, 0); +} + +/* ========================================================================= + CODEC API FUNCTIONS + + this functions are accessed by the master via the API structure + ========================================================================= */ + +/* set compression/expansion mode and launches codec - + this should be the last call from the master before starting processing */ +static int +zr36050_set_mode (struct videocodec *codec, + int mode) +{ + struct zr36050 *ptr = (struct zr36050 *) codec->data; + + dprintk(2, "%s: set_mode %d call\n", ptr->name, mode); + + if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION)) + return -EINVAL; + + ptr->mode = mode; + zr36050_init(ptr); + + return 0; +} + +/* set picture size (norm is ignored as the codec doesn't know about it) */ +static int +zr36050_set_video (struct videocodec *codec, + struct tvnorm *norm, + struct vfe_settings *cap, + struct vfe_polarity *pol) +{ + struct zr36050 *ptr = (struct zr36050 *) codec->data; + int size; + + dprintk(2, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) q%d call\n", + ptr->name, norm->HStart, norm->VStart, + cap->x, cap->y, cap->width, cap->height, + cap->decimation, cap->quality); + /* if () return -EINVAL; + * trust the master driver that it knows what it does - so + * we allow invalid startx/y and norm for now ... */ + ptr->width = cap->width / (cap->decimation & 0xff); + ptr->height = cap->height / ((cap->decimation >> 8) & 0xff); + + /* (KM) JPEG quality */ + size = ptr->width * ptr->height; + size *= 16; /* size in bits */ + /* apply quality setting */ + size = size * cap->quality / 200; + + /* Minimum: 1kb */ + if (size < 8192) + size = 8192; + /* Maximum: 7/8 of code buffer */ + if (size > ptr->total_code_vol * 7) + size = ptr->total_code_vol * 7; + + ptr->real_code_vol = size >> 3; /* in bytes */ + + /* Set max_block_vol here (previously in zr36050_init, moved + * here for consistency with zr36060 code */ + zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol); + + return 0; +} + +/* additional control functions */ +static int +zr36050_control (struct videocodec *codec, + int type, + int size, + void *data) +{ + struct zr36050 *ptr = (struct zr36050 *) codec->data; + int *ival = (int *) data; + + dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type, + size); + + switch (type) { + case CODEC_G_STATUS: /* get last status */ + if (size != sizeof(int)) + return -EFAULT; + zr36050_read_status1(ptr); + *ival = ptr->status1; + break; + + case CODEC_G_CODEC_MODE: + if (size != sizeof(int)) + return -EFAULT; + *ival = CODEC_MODE_BJPG; + break; + + case CODEC_S_CODEC_MODE: + if (size != sizeof(int)) + return -EFAULT; + if (*ival != CODEC_MODE_BJPG) + return -EINVAL; + /* not needed, do nothing */ + return 0; + + case CODEC_G_VFE: + case CODEC_S_VFE: + /* not needed, do nothing */ + return 0; + + case CODEC_S_MMAP: + /* not available, give an error */ + return -ENXIO; + + case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */ + if (size != sizeof(int)) + return -EFAULT; + *ival = ptr->total_code_vol; + break; + + case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */ + if (size != sizeof(int)) + return -EFAULT; + ptr->total_code_vol = *ival; + /* (Kieran Morrissey) + * code copied from zr36060.c to ensure proper bitrate */ + ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; + break; + + case CODEC_G_JPEG_SCALE: /* get scaling factor */ + if (size != sizeof(int)) + return -EFAULT; + *ival = zr36050_read_scalefactor(ptr); + break; + + case CODEC_S_JPEG_SCALE: /* set scaling factor */ + if (size != sizeof(int)) + return -EFAULT; + ptr->scalefact = *ival; + break; + + case CODEC_G_JPEG_APP_DATA: { /* get appn marker data */ + struct jpeg_app_marker *app = data; + + if (size != sizeof(struct jpeg_app_marker)) + return -EFAULT; + + *app = ptr->app; + break; + } + + case CODEC_S_JPEG_APP_DATA: { /* set appn marker data */ + struct jpeg_app_marker *app = data; + + if (size != sizeof(struct jpeg_app_marker)) + return -EFAULT; + + ptr->app = *app; + break; + } + + case CODEC_G_JPEG_COM_DATA: { /* get comment marker data */ + struct jpeg_com_marker *com = data; + + if (size != sizeof(struct jpeg_com_marker)) + return -EFAULT; + + *com = ptr->com; + break; + } + + case CODEC_S_JPEG_COM_DATA: { /* set comment marker data */ + struct jpeg_com_marker *com = data; + + if (size != sizeof(struct jpeg_com_marker)) + return -EFAULT; + + ptr->com = *com; + break; + } + + default: + return -EINVAL; + } + + return size; +} + +/* ========================================================================= + Exit and unregister function: + + Deinitializes Zoran's JPEG processor + ========================================================================= */ + +static int +zr36050_unset (struct videocodec *codec) +{ + struct zr36050 *ptr = codec->data; + + if (ptr) { + /* do wee need some codec deinit here, too ???? */ + + dprintk(1, "%s: finished codec #%d\n", ptr->name, + ptr->num); + kfree(ptr); + codec->data = NULL; + + zr36050_codecs--; + return 0; + } + + return -EFAULT; +} + +/* ========================================================================= + Setup and registry function: + + Initializes Zoran's JPEG processor + + Also sets pixel size, average code size, mode (compr./decompr.) + (the given size is determined by the processor with the video interface) + ========================================================================= */ + +static int +zr36050_setup (struct videocodec *codec) +{ + struct zr36050 *ptr; + int res; + + dprintk(2, "zr36050: initializing MJPEG subsystem #%d.\n", + zr36050_codecs); + + if (zr36050_codecs == MAX_CODECS) { + dprintk(1, + KERN_ERR "zr36050: Can't attach more codecs!\n"); + return -ENOSPC; + } + //mem structure init + codec->data = ptr = kzalloc(sizeof(struct zr36050), GFP_KERNEL); + if (NULL == ptr) { + dprintk(1, KERN_ERR "zr36050: Can't get enough memory!\n"); + return -ENOMEM; + } + + snprintf(ptr->name, sizeof(ptr->name), "zr36050[%d]", + zr36050_codecs); + ptr->num = zr36050_codecs++; + ptr->codec = codec; + + //testing + res = zr36050_basic_test(ptr); + if (res < 0) { + zr36050_unset(codec); + return res; + } + //final setup + memcpy(ptr->h_samp_ratio, zr36050_decimation_h, 8); + memcpy(ptr->v_samp_ratio, zr36050_decimation_v, 8); + + ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag + * (what is the difference?) */ + ptr->mode = CODEC_DO_COMPRESSION; + ptr->width = 384; + ptr->height = 288; + ptr->total_code_vol = 16000; + ptr->max_block_vol = 240; + ptr->scalefact = 0x100; + ptr->dri = 1; + + /* no app/com marker by default */ + ptr->app.appn = 0; + ptr->app.len = 0; + ptr->com.len = 0; + + zr36050_init(ptr); + + dprintk(1, KERN_INFO "%s: codec attached and running\n", + ptr->name); + + return 0; +} + +static const struct videocodec zr36050_codec = { + .owner = THIS_MODULE, + .name = "zr36050", + .magic = 0L, // magic not used + .flags = + CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER | + CODEC_FLAG_DECODER, + .type = CODEC_TYPE_ZR36050, + .setup = zr36050_setup, // functionality + .unset = zr36050_unset, + .set_mode = zr36050_set_mode, + .set_video = zr36050_set_video, + .control = zr36050_control, + // others are not used +}; + +/* ========================================================================= + HOOK IN DRIVER AS KERNEL MODULE + ========================================================================= */ + +static int __init +zr36050_init_module (void) +{ + //dprintk(1, "ZR36050 driver %s\n",ZR050_VERSION); + zr36050_codecs = 0; + return videocodec_register(&zr36050_codec); +} + +static void __exit +zr36050_cleanup_module (void) +{ + if (zr36050_codecs) { + dprintk(1, + "zr36050: something's wrong - %d codecs left somehow.\n", + zr36050_codecs); + } + videocodec_unregister(&zr36050_codec); +} + +module_init(zr36050_init_module); +module_exit(zr36050_cleanup_module); + +MODULE_AUTHOR("Wolfgang Scherr "); +MODULE_DESCRIPTION("Driver module for ZR36050 jpeg processors " + ZR050_VERSION); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/zr36050.h b/linux/drivers/media/video/zr36050.h new file mode 100644 index 000000000..9f52f0cdd --- /dev/null +++ b/linux/drivers/media/video/zr36050.h @@ -0,0 +1,184 @@ +/* + * Zoran ZR36050 basic configuration functions - header file + * + * Copyright (C) 2001 Wolfgang Scherr + * + * $Id: zr36050.h,v 1.1.2.2 2003/01/14 21:18:22 rbultje Exp $ + * + * ------------------------------------------------------------------------ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * ------------------------------------------------------------------------ + */ + +#ifndef ZR36050_H +#define ZR36050_H + +#include "videocodec.h" + +/* data stored for each zoran jpeg codec chip */ +struct zr36050 { + char name[32]; + int num; + /* io datastructure */ + struct videocodec *codec; + // last coder status + __u8 status1; + // actual coder setup + int mode; + + __u16 width; + __u16 height; + + __u16 bitrate_ctrl; + + __u32 total_code_vol; + __u32 real_code_vol; + __u16 max_block_vol; + + __u8 h_samp_ratio[8]; + __u8 v_samp_ratio[8]; + __u16 scalefact; + __u16 dri; + + /* com/app marker */ + struct jpeg_com_marker com; + struct jpeg_app_marker app; +}; + +/* zr36050 register addresses */ +#define ZR050_GO 0x000 +#define ZR050_HARDWARE 0x002 +#define ZR050_MODE 0x003 +#define ZR050_OPTIONS 0x004 +#define ZR050_MBCV 0x005 +#define ZR050_MARKERS_EN 0x006 +#define ZR050_INT_REQ_0 0x007 +#define ZR050_INT_REQ_1 0x008 +#define ZR050_TCV_NET_HI 0x009 +#define ZR050_TCV_NET_MH 0x00a +#define ZR050_TCV_NET_ML 0x00b +#define ZR050_TCV_NET_LO 0x00c +#define ZR050_TCV_DATA_HI 0x00d +#define ZR050_TCV_DATA_MH 0x00e +#define ZR050_TCV_DATA_ML 0x00f +#define ZR050_TCV_DATA_LO 0x010 +#define ZR050_SF_HI 0x011 +#define ZR050_SF_LO 0x012 +#define ZR050_AF_HI 0x013 +#define ZR050_AF_M 0x014 +#define ZR050_AF_LO 0x015 +#define ZR050_ACV_HI 0x016 +#define ZR050_ACV_MH 0x017 +#define ZR050_ACV_ML 0x018 +#define ZR050_ACV_LO 0x019 +#define ZR050_ACT_HI 0x01a +#define ZR050_ACT_MH 0x01b +#define ZR050_ACT_ML 0x01c +#define ZR050_ACT_LO 0x01d +#define ZR050_ACV_TRUN_HI 0x01e +#define ZR050_ACV_TRUN_MH 0x01f +#define ZR050_ACV_TRUN_ML 0x020 +#define ZR050_ACV_TRUN_LO 0x021 +#define ZR050_STATUS_0 0x02e +#define ZR050_STATUS_1 0x02f + +#define ZR050_SOF_IDX 0x040 +#define ZR050_SOS1_IDX 0x07a +#define ZR050_SOS2_IDX 0x08a +#define ZR050_SOS3_IDX 0x09a +#define ZR050_SOS4_IDX 0x0aa +#define ZR050_DRI_IDX 0x0c0 +#define ZR050_DNL_IDX 0x0c6 +#define ZR050_DQT_IDX 0x0cc +#define ZR050_DHT_IDX 0x1d4 +#define ZR050_APP_IDX 0x380 +#define ZR050_COM_IDX 0x3c0 + +/* zr36050 hardware register bits */ + +#define ZR050_HW_BSWD 0x80 +#define ZR050_HW_MSTR 0x40 +#define ZR050_HW_DMA 0x20 +#define ZR050_HW_CFIS_1_CLK 0x00 +#define ZR050_HW_CFIS_2_CLK 0x04 +#define ZR050_HW_CFIS_3_CLK 0x08 +#define ZR050_HW_CFIS_4_CLK 0x0C +#define ZR050_HW_CFIS_5_CLK 0x10 +#define ZR050_HW_CFIS_6_CLK 0x14 +#define ZR050_HW_CFIS_7_CLK 0x18 +#define ZR050_HW_CFIS_8_CLK 0x1C +#define ZR050_HW_BELE 0x01 + +/* zr36050 mode register bits */ + +#define ZR050_MO_COMP 0x80 +#define ZR050_MO_COMP 0x80 +#define ZR050_MO_ATP 0x40 +#define ZR050_MO_PASS2 0x20 +#define ZR050_MO_TLM 0x10 +#define ZR050_MO_DCONLY 0x08 +#define ZR050_MO_BRC 0x04 + +#define ZR050_MO_ATP 0x40 +#define ZR050_MO_PASS2 0x20 +#define ZR050_MO_TLM 0x10 +#define ZR050_MO_DCONLY 0x08 + +/* zr36050 option register bits */ + +#define ZR050_OP_NSCN_1 0x00 +#define ZR050_OP_NSCN_2 0x20 +#define ZR050_OP_NSCN_3 0x40 +#define ZR050_OP_NSCN_4 0x60 +#define ZR050_OP_NSCN_5 0x80 +#define ZR050_OP_NSCN_6 0xA0 +#define ZR050_OP_NSCN_7 0xC0 +#define ZR050_OP_NSCN_8 0xE0 +#define ZR050_OP_OVF 0x10 + + +/* zr36050 markers-enable register bits */ + +#define ZR050_ME_APP 0x80 +#define ZR050_ME_COM 0x40 +#define ZR050_ME_DRI 0x20 +#define ZR050_ME_DQT 0x10 +#define ZR050_ME_DHT 0x08 +#define ZR050_ME_DNL 0x04 +#define ZR050_ME_DQTI 0x02 +#define ZR050_ME_DHTI 0x01 + +/* zr36050 status0/1 register bit masks */ + +#define ZR050_ST_RST_MASK 0x20 +#define ZR050_ST_SOF_MASK 0x02 +#define ZR050_ST_SOS_MASK 0x02 +#define ZR050_ST_DATRDY_MASK 0x80 +#define ZR050_ST_MRKDET_MASK 0x40 +#define ZR050_ST_RFM_MASK 0x10 +#define ZR050_ST_RFD_MASK 0x08 +#define ZR050_ST_END_MASK 0x04 +#define ZR050_ST_TCVOVF_MASK 0x02 +#define ZR050_ST_DATOVF_MASK 0x01 + +/* pixel component idx */ + +#define ZR050_Y_COMPONENT 0 +#define ZR050_U_COMPONENT 1 +#define ZR050_V_COMPONENT 2 + +#endif /*fndef ZR36050_H */ diff --git a/linux/drivers/media/video/zr36057.h b/linux/drivers/media/video/zr36057.h new file mode 100644 index 000000000..54c9362aa --- /dev/null +++ b/linux/drivers/media/video/zr36057.h @@ -0,0 +1,168 @@ +/* + * zr36057.h - zr36057 register offsets + * + * Copyright (C) 1998 Dave Perks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _ZR36057_H_ +#define _ZR36057_H_ + + +/* Zoran ZR36057 registers */ + +#define ZR36057_VFEHCR 0x000 /* Video Front End, Horizontal Configuration Register */ +#define ZR36057_VFEHCR_HSPol (1<<30) +#define ZR36057_VFEHCR_HStart 10 +#define ZR36057_VFEHCR_HEnd 0 +#define ZR36057_VFEHCR_Hmask 0x3ff + +#define ZR36057_VFEVCR 0x004 /* Video Front End, Vertical Configuration Register */ +#define ZR36057_VFEVCR_VSPol (1<<30) +#define ZR36057_VFEVCR_VStart 10 +#define ZR36057_VFEVCR_VEnd 0 +#define ZR36057_VFEVCR_Vmask 0x3ff + +#define ZR36057_VFESPFR 0x008 /* Video Front End, Scaler and Pixel Format Register */ +#define ZR36057_VFESPFR_ExtFl (1<<26) +#define ZR36057_VFESPFR_TopField (1<<25) +#define ZR36057_VFESPFR_VCLKPol (1<<24) +#define ZR36057_VFESPFR_HFilter 21 +#define ZR36057_VFESPFR_HorDcm 14 +#define ZR36057_VFESPFR_VerDcm 8 +#define ZR36057_VFESPFR_DispMode 6 +#define ZR36057_VFESPFR_YUV422 (0<<3) +#define ZR36057_VFESPFR_RGB888 (1<<3) +#define ZR36057_VFESPFR_RGB565 (2<<3) +#define ZR36057_VFESPFR_RGB555 (3<<3) +#define ZR36057_VFESPFR_ErrDif (1<<2) +#define ZR36057_VFESPFR_Pack24 (1<<1) +#define ZR36057_VFESPFR_LittleEndian (1<<0) + +#define ZR36057_VDTR 0x00c /* Video Display "Top" Register */ + +#define ZR36057_VDBR 0x010 /* Video Display "Bottom" Register */ + +#define ZR36057_VSSFGR 0x014 /* Video Stride, Status, and Frame Grab Register */ +#define ZR36057_VSSFGR_DispStride 16 +#define ZR36057_VSSFGR_VidOvf (1<<8) +#define ZR36057_VSSFGR_SnapShot (1<<1) +#define ZR36057_VSSFGR_FrameGrab (1<<0) + +#define ZR36057_VDCR 0x018 /* Video Display Configuration Register */ +#define ZR36057_VDCR_VidEn (1<<31) +#define ZR36057_VDCR_MinPix 24 +#define ZR36057_VDCR_Triton (1<<24) +#define ZR36057_VDCR_VidWinHt 12 +#define ZR36057_VDCR_VidWinWid 0 + +#define ZR36057_MMTR 0x01c /* Masking Map "Top" Register */ + +#define ZR36057_MMBR 0x020 /* Masking Map "Bottom" Register */ + +#define ZR36057_OCR 0x024 /* Overlay Control Register */ +#define ZR36057_OCR_OvlEnable (1 << 15) +#define ZR36057_OCR_MaskStride 0 + +#define ZR36057_SPGPPCR 0x028 /* System, PCI, and General Purpose Pins Control Register */ +#define ZR36057_SPGPPCR_SoftReset (1<<24) + +#define ZR36057_GPPGCR1 0x02c /* General Purpose Pins and GuestBus Control Register (1) */ + +#define ZR36057_MCSAR 0x030 /* MPEG Code Source Address Register */ + +#define ZR36057_MCTCR 0x034 /* MPEG Code Transfer Control Register */ +#define ZR36057_MCTCR_CodTime (1 << 30) +#define ZR36057_MCTCR_CEmpty (1 << 29) +#define ZR36057_MCTCR_CFlush (1 << 28) +#define ZR36057_MCTCR_CodGuestID 20 +#define ZR36057_MCTCR_CodGuestReg 16 + +#define ZR36057_MCMPR 0x038 /* MPEG Code Memory Pointer Register */ + +#define ZR36057_ISR 0x03c /* Interrupt Status Register */ +#define ZR36057_ISR_GIRQ1 (1<<30) +#define ZR36057_ISR_GIRQ0 (1<<29) +#define ZR36057_ISR_CodRepIRQ (1<<28) +#define ZR36057_ISR_JPEGRepIRQ (1<<27) + +#define ZR36057_ICR 0x040 /* Interrupt Control Register */ +#define ZR36057_ICR_GIRQ1 (1<<30) +#define ZR36057_ICR_GIRQ0 (1<<29) +#define ZR36057_ICR_CodRepIRQ (1<<28) +#define ZR36057_ICR_JPEGRepIRQ (1<<27) +#define ZR36057_ICR_IntPinEn (1<<24) + +#define ZR36057_I2CBR 0x044 /* I2C Bus Register */ +#define ZR36057_I2CBR_SDA (1<<1) +#define ZR36057_I2CBR_SCL (1<<0) + +#define ZR36057_JMC 0x100 /* JPEG Mode and Control */ +#define ZR36057_JMC_JPG (1 << 31) +#define ZR36057_JMC_JPGExpMode (0 << 29) +#define ZR36057_JMC_JPGCmpMode (1 << 29) +#define ZR36057_JMC_MJPGExpMode (2 << 29) +#define ZR36057_JMC_MJPGCmpMode (3 << 29) +#define ZR36057_JMC_RTBUSY_FB (1 << 6) +#define ZR36057_JMC_Go_en (1 << 5) +#define ZR36057_JMC_SyncMstr (1 << 4) +#define ZR36057_JMC_Fld_per_buff (1 << 3) +#define ZR36057_JMC_VFIFO_FB (1 << 2) +#define ZR36057_JMC_CFIFO_FB (1 << 1) +#define ZR36057_JMC_Stll_LitEndian (1 << 0) + +#define ZR36057_JPC 0x104 /* JPEG Process Control */ +#define ZR36057_JPC_P_Reset (1 << 7) +#define ZR36057_JPC_CodTrnsEn (1 << 5) +#define ZR36057_JPC_Active (1 << 0) + +#define ZR36057_VSP 0x108 /* Vertical Sync Parameters */ +#define ZR36057_VSP_VsyncSize 16 +#define ZR36057_VSP_FrmTot 0 + +#define ZR36057_HSP 0x10c /* Horizontal Sync Parameters */ +#define ZR36057_HSP_HsyncStart 16 +#define ZR36057_HSP_LineTot 0 + +#define ZR36057_FHAP 0x110 /* Field Horizontal Active Portion */ +#define ZR36057_FHAP_NAX 16 +#define ZR36057_FHAP_PAX 0 + +#define ZR36057_FVAP 0x114 /* Field Vertical Active Portion */ +#define ZR36057_FVAP_NAY 16 +#define ZR36057_FVAP_PAY 0 + +#define ZR36057_FPP 0x118 /* Field Process Parameters */ +#define ZR36057_FPP_Odd_Even (1 << 0) + +#define ZR36057_JCBA 0x11c /* JPEG Code Base Address */ + +#define ZR36057_JCFT 0x120 /* JPEG Code FIFO Threshold */ + +#define ZR36057_JCGI 0x124 /* JPEG Codec Guest ID */ +#define ZR36057_JCGI_JPEGuestID 4 +#define ZR36057_JCGI_JPEGuestReg 0 + +#define ZR36057_GCR2 0x12c /* GuestBus Control Register (2) */ + +#define ZR36057_POR 0x200 /* Post Office Register */ +#define ZR36057_POR_POPen (1<<25) +#define ZR36057_POR_POTime (1<<24) +#define ZR36057_POR_PODir (1<<23) + +#define ZR36057_STR 0x300 /* "Still" Transfer Register */ + +#endif diff --git a/linux/drivers/media/video/zr36060.c b/linux/drivers/media/video/zr36060.c new file mode 100644 index 000000000..97c8f9b9d --- /dev/null +++ b/linux/drivers/media/video/zr36060.c @@ -0,0 +1,1014 @@ +/* + * Zoran ZR36060 basic configuration functions + * + * Copyright (C) 2002 Laurent Pinchart + * + * $Id: zr36060.c,v 1.1.2.22 2003/05/06 09:35:36 rbultje Exp $ + * + * ------------------------------------------------------------------------ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * ------------------------------------------------------------------------ + */ + +#define ZR060_VERSION "v0.7" + +#include +#include +#include +#include + +#include +#include + +/* includes for structures and defines regarding video + #include */ + +/* I/O commands, error codes */ +#include +//#include + +/* headerfile of this module */ +#include"zr36060.h" + +/* codec io API */ +#include"videocodec.h" + +/* it doesn't make sense to have more than 20 or so, + just to prevent some unwanted loops */ +#define MAX_CODECS 20 + +/* amount of chips attached via this driver */ +static int zr36060_codecs = 0; + +static int low_bitrate = 0; +module_param(low_bitrate, bool, 0); +MODULE_PARM_DESC(low_bitrate, "Buz compatibility option, halves bitrate"); + +/* debugging is available via module parameter */ +static int debug = 0; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0-4)"); + +#define dprintk(num, format, args...) \ + do { \ + if (debug >= num) \ + printk(format, ##args); \ + } while (0) + +/* ========================================================================= + Local hardware I/O functions: + + read/write via codec layer (registers are located in the master device) + ========================================================================= */ + +/* read and write functions */ +static u8 +zr36060_read (struct zr36060 *ptr, + u16 reg) +{ + u8 value = 0; + + // just in case something is wrong... + if (ptr->codec->master_data->readreg) + value = (ptr->codec->master_data->readreg(ptr->codec, + reg)) & 0xff; + else + dprintk(1, + KERN_ERR "%s: invalid I/O setup, nothing read!\n", + ptr->name); + + //dprintk(4, "%s: reading from 0x%04x: %02x\n",ptr->name,reg,value); + + return value; +} + +static void +zr36060_write(struct zr36060 *ptr, + u16 reg, + u8 value) +{ + //dprintk(4, "%s: writing 0x%02x to 0x%04x\n",ptr->name,value,reg); + dprintk(4, "0x%02x @0x%04x\n", value, reg); + + // just in case something is wrong... + if (ptr->codec->master_data->writereg) + ptr->codec->master_data->writereg(ptr->codec, reg, value); + else + dprintk(1, + KERN_ERR + "%s: invalid I/O setup, nothing written!\n", + ptr->name); +} + +/* ========================================================================= + Local helper function: + + status read + ========================================================================= */ + +/* status is kept in datastructure */ +static u8 +zr36060_read_status (struct zr36060 *ptr) +{ + ptr->status = zr36060_read(ptr, ZR060_CFSR); + + zr36060_read(ptr, 0); + return ptr->status; +} + +/* ========================================================================= + Local helper function: + + scale factor read + ========================================================================= */ + +/* scale factor is kept in datastructure */ +static u16 +zr36060_read_scalefactor (struct zr36060 *ptr) +{ + ptr->scalefact = (zr36060_read(ptr, ZR060_SF_HI) << 8) | + (zr36060_read(ptr, ZR060_SF_LO) & 0xFF); + + /* leave 0 selected for an eventually GO from master */ + zr36060_read(ptr, 0); + return ptr->scalefact; +} + +/* ========================================================================= + Local helper function: + + wait if codec is ready to proceed (end of processing) or time is over + ========================================================================= */ + +static void +zr36060_wait_end (struct zr36060 *ptr) +{ + int i = 0; + + while (zr36060_read_status(ptr) & ZR060_CFSR_Busy) { + udelay(1); + if (i++ > 200000) { // 200ms, there is for sure something wrong!!! + dprintk(1, + "%s: timout at wait_end (last status: 0x%02x)\n", + ptr->name, ptr->status); + break; + } + } +} + +/* ========================================================================= + Local helper function: + + basic test of "connectivity", writes/reads to/from memory the SOF marker + ========================================================================= */ + +static int +zr36060_basic_test (struct zr36060 *ptr) +{ + if ((zr36060_read(ptr, ZR060_IDR_DEV) != 0x33) && + (zr36060_read(ptr, ZR060_IDR_REV) != 0x01)) { + dprintk(1, + KERN_ERR + "%s: attach failed, can't connect to jpeg processor!\n", + ptr->name); + return -ENXIO; + } + + zr36060_wait_end(ptr); + if (ptr->status & ZR060_CFSR_Busy) { + dprintk(1, + KERN_ERR + "%s: attach failed, jpeg processor failed (end flag)!\n", + ptr->name); + return -EBUSY; + } + + return 0; /* looks good! */ +} + +/* ========================================================================= + Local helper function: + + simple loop for pushing the init datasets + ========================================================================= */ + +static int +zr36060_pushit (struct zr36060 *ptr, + u16 startreg, + u16 len, + const char *data) +{ + int i = 0; + + dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", ptr->name, + startreg, len); + while (i < len) { + zr36060_write(ptr, startreg++, data[i++]); + } + + return i; +} + +/* ========================================================================= + Basic datasets: + + jpeg baseline setup data (you find it on lots places in internet, or just + extract it from any regular .jpg image...) + + Could be variable, but until it's not needed it they are just fixed to save + memory. Otherwise expand zr36060 structure with arrays, push the values to + it and initalize from there, as e.g. the linux zr36057/60 driver does it. + ========================================================================= */ + +static const char zr36060_dqt[0x86] = { + 0xff, 0xdb, //Marker: DQT + 0x00, 0x84, //Length: 2*65+2 + 0x00, //Pq,Tq first table + 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, + 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, + 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, + 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, + 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, + 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, + 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, + 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, + 0x01, //Pq,Tq second table + 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, + 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 +}; + +static const char zr36060_dht[0x1a4] = { + 0xff, 0xc4, //Marker: DHT + 0x01, 0xa2, //Length: 2*AC, 2*DC + 0x00, //DC first table + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x01, //DC second table + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x10, //AC first table + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, + 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, + 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, + 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, + 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, + 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, + 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, + 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, + 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, + 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, + 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, + 0x11, //AC second table + 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, + 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, + 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, + 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, + 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, + 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, + 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, + 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, + 0xF9, 0xFA +}; + +/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */ +#define NO_OF_COMPONENTS 0x3 //Y,U,V +#define BASELINE_PRECISION 0x8 //MCU size (?) +static const char zr36060_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT +static const char zr36060_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC +static const char zr36060_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC + +/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */ +static const char zr36060_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 }; +static const char zr36060_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 }; + +/* ========================================================================= + Local helper functions: + + calculation and setup of parameter-dependent JPEG baseline segments + (needed for compression only) + ========================================================================= */ + +/* ------------------------------------------------------------------------- */ + +/* SOF (start of frame) segment depends on width, height and sampling ratio + of each color component */ + +static int +zr36060_set_sof (struct zr36060 *ptr) +{ + char sof_data[34]; // max. size of register set + int i; + + dprintk(3, "%s: write SOF (%dx%d, %d components)\n", ptr->name, + ptr->width, ptr->height, NO_OF_COMPONENTS); + sof_data[0] = 0xff; + sof_data[1] = 0xc0; + sof_data[2] = 0x00; + sof_data[3] = (3 * NO_OF_COMPONENTS) + 8; + sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36060 + sof_data[5] = (ptr->height) >> 8; + sof_data[6] = (ptr->height) & 0xff; + sof_data[7] = (ptr->width) >> 8; + sof_data[8] = (ptr->width) & 0xff; + sof_data[9] = NO_OF_COMPONENTS; + for (i = 0; i < NO_OF_COMPONENTS; i++) { + sof_data[10 + (i * 3)] = i; // index identifier + sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | + (ptr->v_samp_ratio[i]); // sampling ratios + sof_data[12 + (i * 3)] = zr36060_tq[i]; // Q table selection + } + return zr36060_pushit(ptr, ZR060_SOF_IDX, + (3 * NO_OF_COMPONENTS) + 10, sof_data); +} + +/* ------------------------------------------------------------------------- */ + +/* SOS (start of scan) segment depends on the used scan components + of each color component */ + +static int +zr36060_set_sos (struct zr36060 *ptr) +{ + char sos_data[16]; // max. size of register set + int i; + + dprintk(3, "%s: write SOS\n", ptr->name); + sos_data[0] = 0xff; + sos_data[1] = 0xda; + sos_data[2] = 0x00; + sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3; + sos_data[4] = NO_OF_COMPONENTS; + for (i = 0; i < NO_OF_COMPONENTS; i++) { + sos_data[5 + (i * 2)] = i; // index + sos_data[6 + (i * 2)] = (zr36060_td[i] << 4) | + zr36060_ta[i]; // AC/DC tbl.sel. + } + sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start + sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3f; + sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00; + return zr36060_pushit(ptr, ZR060_SOS_IDX, + 4 + 1 + (2 * NO_OF_COMPONENTS) + 3, + sos_data); +} + +/* ------------------------------------------------------------------------- */ + +/* DRI (define restart interval) */ + +static int +zr36060_set_dri (struct zr36060 *ptr) +{ + char dri_data[6]; // max. size of register set + + dprintk(3, "%s: write DRI\n", ptr->name); + dri_data[0] = 0xff; + dri_data[1] = 0xdd; + dri_data[2] = 0x00; + dri_data[3] = 0x04; + dri_data[4] = (ptr->dri) >> 8; + dri_data[5] = (ptr->dri) & 0xff; + return zr36060_pushit(ptr, ZR060_DRI_IDX, 6, dri_data); +} + +/* ========================================================================= + Setup function: + + Setup compression/decompression of Zoran's JPEG processor + ( see also zoran 36060 manual ) + + ... sorry for the spaghetti code ... + ========================================================================= */ +static void +zr36060_init (struct zr36060 *ptr) +{ + int sum = 0; + long bitcnt, tmp; + + if (ptr->mode == CODEC_DO_COMPRESSION) { + dprintk(2, "%s: COMPRESSION SETUP\n", ptr->name); + + zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst); + + /* 060 communicates with 067 in master mode */ + zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CodeMstr); + + /* Compression with or without variable scale factor */ + /*FIXME: What about ptr->bitrate_ctrl? */ + zr36060_write(ptr, ZR060_CMR, + ZR060_CMR_Comp | ZR060_CMR_Pass2 | + ZR060_CMR_BRB); + + /* Must be zero */ + zr36060_write(ptr, ZR060_MBZ, 0x00); + zr36060_write(ptr, ZR060_TCR_HI, 0x00); + zr36060_write(ptr, ZR060_TCR_LO, 0x00); + + /* Disable all IRQs - no DataErr means autoreset */ + zr36060_write(ptr, ZR060_IMR, 0); + + /* volume control settings */ + zr36060_write(ptr, ZR060_SF_HI, ptr->scalefact >> 8); + zr36060_write(ptr, ZR060_SF_LO, ptr->scalefact & 0xff); + + zr36060_write(ptr, ZR060_AF_HI, 0xff); + zr36060_write(ptr, ZR060_AF_M, 0xff); + zr36060_write(ptr, ZR060_AF_LO, 0xff); + + /* setup the variable jpeg tables */ + sum += zr36060_set_sof(ptr); + sum += zr36060_set_sos(ptr); + sum += zr36060_set_dri(ptr); + + /* setup the fixed jpeg tables - maybe variable, though - + * (see table init section above) */ + sum += + zr36060_pushit(ptr, ZR060_DQT_IDX, sizeof(zr36060_dqt), + zr36060_dqt); + sum += + zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht), + zr36060_dht); + zr36060_write(ptr, ZR060_APP_IDX, 0xff); + zr36060_write(ptr, ZR060_APP_IDX + 1, 0xe0 + ptr->app.appn); + zr36060_write(ptr, ZR060_APP_IDX + 2, 0x00); + zr36060_write(ptr, ZR060_APP_IDX + 3, ptr->app.len + 2); + sum += zr36060_pushit(ptr, ZR060_APP_IDX + 4, 60, + ptr->app.data) + 4; + zr36060_write(ptr, ZR060_COM_IDX, 0xff); + zr36060_write(ptr, ZR060_COM_IDX + 1, 0xfe); + zr36060_write(ptr, ZR060_COM_IDX + 2, 0x00); + zr36060_write(ptr, ZR060_COM_IDX + 3, ptr->com.len + 2); + sum += zr36060_pushit(ptr, ZR060_COM_IDX + 4, 60, + ptr->com.data) + 4; + + /* setup misc. data for compression (target code sizes) */ + + /* size of compressed code to reach without header data */ + sum = ptr->real_code_vol - sum; + bitcnt = sum << 3; /* need the size in bits */ + + tmp = bitcnt >> 16; + dprintk(3, + "%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n", + ptr->name, sum, ptr->real_code_vol, bitcnt, tmp); + zr36060_write(ptr, ZR060_TCV_NET_HI, tmp >> 8); + zr36060_write(ptr, ZR060_TCV_NET_MH, tmp & 0xff); + tmp = bitcnt & 0xffff; + zr36060_write(ptr, ZR060_TCV_NET_ML, tmp >> 8); + zr36060_write(ptr, ZR060_TCV_NET_LO, tmp & 0xff); + + bitcnt -= bitcnt >> 7; // bits without stuffing + bitcnt -= ((bitcnt * 5) >> 6); // bits without eob + + tmp = bitcnt >> 16; + dprintk(3, "%s: code: nettobit=%ld, highnettobits=%ld\n", + ptr->name, bitcnt, tmp); + zr36060_write(ptr, ZR060_TCV_DATA_HI, tmp >> 8); + zr36060_write(ptr, ZR060_TCV_DATA_MH, tmp & 0xff); + tmp = bitcnt & 0xffff; + zr36060_write(ptr, ZR060_TCV_DATA_ML, tmp >> 8); + zr36060_write(ptr, ZR060_TCV_DATA_LO, tmp & 0xff); + + /* JPEG markers to be included in the compressed stream */ + zr36060_write(ptr, ZR060_MER, + ZR060_MER_DQT | ZR060_MER_DHT | + ((ptr->com.len > 0) ? ZR060_MER_Com : 0) | + ((ptr->app.len > 0) ? ZR060_MER_App : 0)); + + /* Setup the Video Frontend */ + /* Limit pixel range to 16..235 as per CCIR-601 */ + zr36060_write(ptr, ZR060_VCR, ZR060_VCR_Range); + + } else { + dprintk(2, "%s: EXPANSION SETUP\n", ptr->name); + + zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst); + + /* 060 communicates with 067 in master mode */ + zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CodeMstr); + + /* Decompression */ + zr36060_write(ptr, ZR060_CMR, 0); + + /* Must be zero */ + zr36060_write(ptr, ZR060_MBZ, 0x00); + zr36060_write(ptr, ZR060_TCR_HI, 0x00); + zr36060_write(ptr, ZR060_TCR_LO, 0x00); + + /* Disable all IRQs - no DataErr means autoreset */ + zr36060_write(ptr, ZR060_IMR, 0); + + /* setup misc. data for expansion */ + zr36060_write(ptr, ZR060_MER, 0); + + /* setup the fixed jpeg tables - maybe variable, though - + * (see table init section above) */ + zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht), + zr36060_dht); + + /* Setup the Video Frontend */ + //zr36060_write(ptr, ZR060_VCR, ZR060_VCR_FIExt); + //this doesn't seem right and doesn't work... + zr36060_write(ptr, ZR060_VCR, ZR060_VCR_Range); + } + + /* Load the tables */ + zr36060_write(ptr, ZR060_LOAD, + ZR060_LOAD_SyncRst | ZR060_LOAD_Load); + zr36060_wait_end(ptr); + dprintk(2, "%s: Status after table preload: 0x%02x\n", ptr->name, + ptr->status); + + if (ptr->status & ZR060_CFSR_Busy) { + dprintk(1, KERN_ERR "%s: init aborted!\n", ptr->name); + return; // something is wrong, its timed out!!!! + } +} + +/* ========================================================================= + CODEC API FUNCTIONS + + this functions are accessed by the master via the API structure + ========================================================================= */ + +/* set compression/expansion mode and launches codec - + this should be the last call from the master before starting processing */ +static int +zr36060_set_mode (struct videocodec *codec, + int mode) +{ + struct zr36060 *ptr = (struct zr36060 *) codec->data; + + dprintk(2, "%s: set_mode %d call\n", ptr->name, mode); + + if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION)) + return -EINVAL; + + ptr->mode = mode; + zr36060_init(ptr); + + return 0; +} + +/* set picture size (norm is ignored as the codec doesn't know about it) */ +static int +zr36060_set_video (struct videocodec *codec, + struct tvnorm *norm, + struct vfe_settings *cap, + struct vfe_polarity *pol) +{ + struct zr36060 *ptr = (struct zr36060 *) codec->data; + u32 reg; + int size; + + dprintk(2, "%s: set_video %d/%d-%dx%d (%%%d) call\n", ptr->name, + cap->x, cap->y, cap->width, cap->height, cap->decimation); + + /* if () return -EINVAL; + * trust the master driver that it knows what it does - so + * we allow invalid startx/y and norm for now ... */ + ptr->width = cap->width / (cap->decimation & 0xff); + ptr->height = cap->height / (cap->decimation >> 8); + + zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst); + + /* Note that VSPol/HSPol bits in zr36060 have the opposite + * meaning of their zr360x7 counterparts with the same names + * N.b. for VSPol this is only true if FIVEdge = 0 (default, + * left unchanged here - in accordance with datasheet). + */ + reg = (!pol->vsync_pol ? ZR060_VPR_VSPol : 0) + | (!pol->hsync_pol ? ZR060_VPR_HSPol : 0) + | (pol->field_pol ? ZR060_VPR_FIPol : 0) + | (pol->blank_pol ? ZR060_VPR_BLPol : 0) + | (pol->subimg_pol ? ZR060_VPR_SImgPol : 0) + | (pol->poe_pol ? ZR060_VPR_PoePol : 0) + | (pol->pvalid_pol ? ZR060_VPR_PValPol : 0) + | (pol->vclk_pol ? ZR060_VPR_VCLKPol : 0); + zr36060_write(ptr, ZR060_VPR, reg); + + reg = 0; + switch (cap->decimation & 0xff) { + default: + case 1: + break; + + case 2: + reg |= ZR060_SR_HScale2; + break; + + case 4: + reg |= ZR060_SR_HScale4; + break; + } + + switch (cap->decimation >> 8) { + default: + case 1: + break; + + case 2: + reg |= ZR060_SR_VScale; + break; + } + zr36060_write(ptr, ZR060_SR, reg); + + zr36060_write(ptr, ZR060_BCR_Y, 0x00); + zr36060_write(ptr, ZR060_BCR_U, 0x80); + zr36060_write(ptr, ZR060_BCR_V, 0x80); + + /* sync generator */ + + reg = norm->Ht - 1; /* Vtotal */ + zr36060_write(ptr, ZR060_SGR_VTOTAL_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_SGR_VTOTAL_LO, (reg >> 0) & 0xff); + + reg = norm->Wt - 1; /* Htotal */ + zr36060_write(ptr, ZR060_SGR_HTOTAL_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_SGR_HTOTAL_LO, (reg >> 0) & 0xff); + + reg = 6 - 1; /* VsyncSize */ + zr36060_write(ptr, ZR060_SGR_VSYNC, reg); + + //reg = 30 - 1; /* HsyncSize */ +///*CP*/ reg = (zr->params.norm == 1 ? 57 : 68); + reg = 68; + zr36060_write(ptr, ZR060_SGR_HSYNC, reg); + + reg = norm->VStart - 1; /* BVstart */ + zr36060_write(ptr, ZR060_SGR_BVSTART, reg); + + reg += norm->Ha / 2; /* BVend */ + zr36060_write(ptr, ZR060_SGR_BVEND_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_SGR_BVEND_LO, (reg >> 0) & 0xff); + + reg = norm->HStart - 1; /* BHstart */ + zr36060_write(ptr, ZR060_SGR_BHSTART, reg); + + reg += norm->Wa; /* BHend */ + zr36060_write(ptr, ZR060_SGR_BHEND_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_SGR_BHEND_LO, (reg >> 0) & 0xff); + + /* active area */ + reg = cap->y + norm->VStart; /* Vstart */ + zr36060_write(ptr, ZR060_AAR_VSTART_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_AAR_VSTART_LO, (reg >> 0) & 0xff); + + reg += cap->height; /* Vend */ + zr36060_write(ptr, ZR060_AAR_VEND_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_AAR_VEND_LO, (reg >> 0) & 0xff); + + reg = cap->x + norm->HStart; /* Hstart */ + zr36060_write(ptr, ZR060_AAR_HSTART_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_AAR_HSTART_LO, (reg >> 0) & 0xff); + + reg += cap->width; /* Hend */ + zr36060_write(ptr, ZR060_AAR_HEND_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_AAR_HEND_LO, (reg >> 0) & 0xff); + + /* subimage area */ + reg = norm->VStart - 4; /* SVstart */ + zr36060_write(ptr, ZR060_SWR_VSTART_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_SWR_VSTART_LO, (reg >> 0) & 0xff); + + reg += norm->Ha / 2 + 8; /* SVend */ + zr36060_write(ptr, ZR060_SWR_VEND_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_SWR_VEND_LO, (reg >> 0) & 0xff); + + reg = norm->HStart /*+ 64 */ - 4; /* SHstart */ + zr36060_write(ptr, ZR060_SWR_HSTART_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_SWR_HSTART_LO, (reg >> 0) & 0xff); + + reg += norm->Wa + 8; /* SHend */ + zr36060_write(ptr, ZR060_SWR_HEND_HI, (reg >> 8) & 0xff); + zr36060_write(ptr, ZR060_SWR_HEND_LO, (reg >> 0) & 0xff); + + size = ptr->width * ptr->height; + /* Target compressed field size in bits: */ + size = size * 16; /* uncompressed size in bits */ + /* (Ronald) by default, quality = 100 is a compression + * ratio 1:2. Setting low_bitrate (insmod option) sets + * it to 1:4 (instead of 1:2, zr36060 max) as limit because the + * buz can't handle more at decimation=1... Use low_bitrate if + * you have a Buz, unless you know what you're doing */ + size = size * cap->quality / (low_bitrate ? 400 : 200); + /* Lower limit (arbitrary, 1 KB) */ + if (size < 8192) + size = 8192; + /* Upper limit: 7/8 of the code buffers */ + if (size > ptr->total_code_vol * 7) + size = ptr->total_code_vol * 7; + + ptr->real_code_vol = size >> 3; /* in bytes */ + + /* the MBCVR is the *maximum* block volume, according to the + * JPEG ISO specs, this shouldn't be used, since that allows + * for the best encoding quality. So set it to it's max value */ + reg = ptr->max_block_vol; + zr36060_write(ptr, ZR060_MBCVR, reg); + + return 0; +} + +/* additional control functions */ +static int +zr36060_control (struct videocodec *codec, + int type, + int size, + void *data) +{ + struct zr36060 *ptr = (struct zr36060 *) codec->data; + int *ival = (int *) data; + + dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type, + size); + + switch (type) { + case CODEC_G_STATUS: /* get last status */ + if (size != sizeof(int)) + return -EFAULT; + zr36060_read_status(ptr); + *ival = ptr->status; + break; + + case CODEC_G_CODEC_MODE: + if (size != sizeof(int)) + return -EFAULT; + *ival = CODEC_MODE_BJPG; + break; + + case CODEC_S_CODEC_MODE: + if (size != sizeof(int)) + return -EFAULT; + if (*ival != CODEC_MODE_BJPG) + return -EINVAL; + /* not needed, do nothing */ + return 0; + + case CODEC_G_VFE: + case CODEC_S_VFE: + /* not needed, do nothing */ + return 0; + + case CODEC_S_MMAP: + /* not available, give an error */ + return -ENXIO; + + case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */ + if (size != sizeof(int)) + return -EFAULT; + *ival = ptr->total_code_vol; + break; + + case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */ + if (size != sizeof(int)) + return -EFAULT; + ptr->total_code_vol = *ival; + ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; + break; + + case CODEC_G_JPEG_SCALE: /* get scaling factor */ + if (size != sizeof(int)) + return -EFAULT; + *ival = zr36060_read_scalefactor(ptr); + break; + + case CODEC_S_JPEG_SCALE: /* set scaling factor */ + if (size != sizeof(int)) + return -EFAULT; + ptr->scalefact = *ival; + break; + + case CODEC_G_JPEG_APP_DATA: { /* get appn marker data */ + struct jpeg_app_marker *app = data; + + if (size != sizeof(struct jpeg_app_marker)) + return -EFAULT; + + *app = ptr->app; + break; + } + + case CODEC_S_JPEG_APP_DATA: { /* set appn marker data */ + struct jpeg_app_marker *app = data; + + if (size != sizeof(struct jpeg_app_marker)) + return -EFAULT; + + ptr->app = *app; + break; + } + + case CODEC_G_JPEG_COM_DATA: { /* get comment marker data */ + struct jpeg_com_marker *com = data; + + if (size != sizeof(struct jpeg_com_marker)) + return -EFAULT; + + *com = ptr->com; + break; + } + + case CODEC_S_JPEG_COM_DATA: { /* set comment marker data */ + struct jpeg_com_marker *com = data; + + if (size != sizeof(struct jpeg_com_marker)) + return -EFAULT; + + ptr->com = *com; + break; + } + + default: + return -EINVAL; + } + + return size; +} + +/* ========================================================================= + Exit and unregister function: + + Deinitializes Zoran's JPEG processor + ========================================================================= */ + +static int +zr36060_unset (struct videocodec *codec) +{ + struct zr36060 *ptr = codec->data; + + if (ptr) { + /* do wee need some codec deinit here, too ???? */ + + dprintk(1, "%s: finished codec #%d\n", ptr->name, + ptr->num); + kfree(ptr); + codec->data = NULL; + + zr36060_codecs--; + return 0; + } + + return -EFAULT; +} + +/* ========================================================================= + Setup and registry function: + + Initializes Zoran's JPEG processor + + Also sets pixel size, average code size, mode (compr./decompr.) + (the given size is determined by the processor with the video interface) + ========================================================================= */ + +static int +zr36060_setup (struct videocodec *codec) +{ + struct zr36060 *ptr; + int res; + + dprintk(2, "zr36060: initializing MJPEG subsystem #%d.\n", + zr36060_codecs); + + if (zr36060_codecs == MAX_CODECS) { + dprintk(1, + KERN_ERR "zr36060: Can't attach more codecs!\n"); + return -ENOSPC; + } + //mem structure init + codec->data = ptr = kzalloc(sizeof(struct zr36060), GFP_KERNEL); + if (NULL == ptr) { + dprintk(1, KERN_ERR "zr36060: Can't get enough memory!\n"); + return -ENOMEM; + } + + snprintf(ptr->name, sizeof(ptr->name), "zr36060[%d]", + zr36060_codecs); + ptr->num = zr36060_codecs++; + ptr->codec = codec; + + //testing + res = zr36060_basic_test(ptr); + if (res < 0) { + zr36060_unset(codec); + return res; + } + //final setup + memcpy(ptr->h_samp_ratio, zr36060_decimation_h, 8); + memcpy(ptr->v_samp_ratio, zr36060_decimation_v, 8); + + ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag + * (what is the difference?) */ + ptr->mode = CODEC_DO_COMPRESSION; + ptr->width = 384; + ptr->height = 288; + ptr->total_code_vol = 16000; /* CHECKME */ + ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; + ptr->max_block_vol = 240; /* CHECKME, was 120 is 240 */ + ptr->scalefact = 0x100; + ptr->dri = 1; /* CHECKME, was 8 is 1 */ + + /* by default, no COM or APP markers - app should set those */ + ptr->com.len = 0; + ptr->app.appn = 0; + ptr->app.len = 0; + + zr36060_init(ptr); + + dprintk(1, KERN_INFO "%s: codec attached and running\n", + ptr->name); + + return 0; +} + +static const struct videocodec zr36060_codec = { + .owner = THIS_MODULE, + .name = "zr36060", + .magic = 0L, // magic not used + .flags = + CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER | + CODEC_FLAG_DECODER | CODEC_FLAG_VFE, + .type = CODEC_TYPE_ZR36060, + .setup = zr36060_setup, // functionality + .unset = zr36060_unset, + .set_mode = zr36060_set_mode, + .set_video = zr36060_set_video, + .control = zr36060_control, + // others are not used +}; + +/* ========================================================================= + HOOK IN DRIVER AS KERNEL MODULE + ========================================================================= */ + +static int __init +zr36060_init_module (void) +{ + //dprintk(1, "zr36060 driver %s\n",ZR060_VERSION); + zr36060_codecs = 0; + return videocodec_register(&zr36060_codec); +} + +static void __exit +zr36060_cleanup_module (void) +{ + if (zr36060_codecs) { + dprintk(1, + "zr36060: something's wrong - %d codecs left somehow.\n", + zr36060_codecs); + } + + /* however, we can't just stay alive */ + videocodec_unregister(&zr36060_codec); +} + +module_init(zr36060_init_module); +module_exit(zr36060_cleanup_module); + +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_DESCRIPTION("Driver module for ZR36060 jpeg processors " + ZR060_VERSION); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/zr36060.h b/linux/drivers/media/video/zr36060.h new file mode 100644 index 000000000..914ffa4ad --- /dev/null +++ b/linux/drivers/media/video/zr36060.h @@ -0,0 +1,220 @@ +/* + * Zoran ZR36060 basic configuration functions - header file + * + * Copyright (C) 2002 Laurent Pinchart + * + * $Id: zr36060.h,v 1.1.1.1.2.3 2003/01/14 21:18:47 rbultje Exp $ + * + * ------------------------------------------------------------------------ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * ------------------------------------------------------------------------ + */ + +#ifndef ZR36060_H +#define ZR36060_H + +#include "videocodec.h" + +/* data stored for each zoran jpeg codec chip */ +struct zr36060 { + char name[32]; + int num; + /* io datastructure */ + struct videocodec *codec; + // last coder status + __u8 status; + // actual coder setup + int mode; + + __u16 width; + __u16 height; + + __u16 bitrate_ctrl; + + __u32 total_code_vol; + __u32 real_code_vol; + __u16 max_block_vol; + + __u8 h_samp_ratio[8]; + __u8 v_samp_ratio[8]; + __u16 scalefact; + __u16 dri; + + /* app/com marker data */ + struct jpeg_app_marker app; + struct jpeg_com_marker com; +}; + +/* ZR36060 register addresses */ +#define ZR060_LOAD 0x000 +#define ZR060_CFSR 0x001 +#define ZR060_CIR 0x002 +#define ZR060_CMR 0x003 +#define ZR060_MBZ 0x004 +#define ZR060_MBCVR 0x005 +#define ZR060_MER 0x006 +#define ZR060_IMR 0x007 +#define ZR060_ISR 0x008 +#define ZR060_TCV_NET_HI 0x009 +#define ZR060_TCV_NET_MH 0x00a +#define ZR060_TCV_NET_ML 0x00b +#define ZR060_TCV_NET_LO 0x00c +#define ZR060_TCV_DATA_HI 0x00d +#define ZR060_TCV_DATA_MH 0x00e +#define ZR060_TCV_DATA_ML 0x00f +#define ZR060_TCV_DATA_LO 0x010 +#define ZR060_SF_HI 0x011 +#define ZR060_SF_LO 0x012 +#define ZR060_AF_HI 0x013 +#define ZR060_AF_M 0x014 +#define ZR060_AF_LO 0x015 +#define ZR060_ACV_HI 0x016 +#define ZR060_ACV_MH 0x017 +#define ZR060_ACV_ML 0x018 +#define ZR060_ACV_LO 0x019 +#define ZR060_ACT_HI 0x01a +#define ZR060_ACT_MH 0x01b +#define ZR060_ACT_ML 0x01c +#define ZR060_ACT_LO 0x01d +#define ZR060_ACV_TRUN_HI 0x01e +#define ZR060_ACV_TRUN_MH 0x01f +#define ZR060_ACV_TRUN_ML 0x020 +#define ZR060_ACV_TRUN_LO 0x021 +#define ZR060_IDR_DEV 0x022 +#define ZR060_IDR_REV 0x023 +#define ZR060_TCR_HI 0x024 +#define ZR060_TCR_LO 0x025 +#define ZR060_VCR 0x030 +#define ZR060_VPR 0x031 +#define ZR060_SR 0x032 +#define ZR060_BCR_Y 0x033 +#define ZR060_BCR_U 0x034 +#define ZR060_BCR_V 0x035 +#define ZR060_SGR_VTOTAL_HI 0x036 +#define ZR060_SGR_VTOTAL_LO 0x037 +#define ZR060_SGR_HTOTAL_HI 0x038 +#define ZR060_SGR_HTOTAL_LO 0x039 +#define ZR060_SGR_VSYNC 0x03a +#define ZR060_SGR_HSYNC 0x03b +#define ZR060_SGR_BVSTART 0x03c +#define ZR060_SGR_BHSTART 0x03d +#define ZR060_SGR_BVEND_HI 0x03e +#define ZR060_SGR_BVEND_LO 0x03f +#define ZR060_SGR_BHEND_HI 0x040 +#define ZR060_SGR_BHEND_LO 0x041 +#define ZR060_AAR_VSTART_HI 0x042 +#define ZR060_AAR_VSTART_LO 0x043 +#define ZR060_AAR_VEND_HI 0x044 +#define ZR060_AAR_VEND_LO 0x045 +#define ZR060_AAR_HSTART_HI 0x046 +#define ZR060_AAR_HSTART_LO 0x047 +#define ZR060_AAR_HEND_HI 0x048 +#define ZR060_AAR_HEND_LO 0x049 +#define ZR060_SWR_VSTART_HI 0x04a +#define ZR060_SWR_VSTART_LO 0x04b +#define ZR060_SWR_VEND_HI 0x04c +#define ZR060_SWR_VEND_LO 0x04d +#define ZR060_SWR_HSTART_HI 0x04e +#define ZR060_SWR_HSTART_LO 0x04f +#define ZR060_SWR_HEND_HI 0x050 +#define ZR060_SWR_HEND_LO 0x051 + +#define ZR060_SOF_IDX 0x060 +#define ZR060_SOS_IDX 0x07a +#define ZR060_DRI_IDX 0x0c0 +#define ZR060_DQT_IDX 0x0cc +#define ZR060_DHT_IDX 0x1d4 +#define ZR060_APP_IDX 0x380 +#define ZR060_COM_IDX 0x3c0 + +/* ZR36060 LOAD register bits */ + +#define ZR060_LOAD_Load (1 << 7) +#define ZR060_LOAD_SyncRst (1 << 0) + +/* ZR36060 Code FIFO Status register bits */ + +#define ZR060_CFSR_Busy (1 << 7) +#define ZR060_CFSR_CBusy (1 << 2) +#define ZR060_CFSR_CFIFO (3 << 0) + +/* ZR36060 Code Interface register */ + +#define ZR060_CIR_Code16 (1 << 7) +#define ZR060_CIR_Endian (1 << 6) +#define ZR060_CIR_CFIS (1 << 2) +#define ZR060_CIR_CodeMstr (1 << 0) + +/* ZR36060 Codec Mode register */ + +#define ZR060_CMR_Comp (1 << 7) +#define ZR060_CMR_ATP (1 << 6) +#define ZR060_CMR_Pass2 (1 << 5) +#define ZR060_CMR_TLM (1 << 4) +#define ZR060_CMR_BRB (1 << 2) +#define ZR060_CMR_FSF (1 << 1) + +/* ZR36060 Markers Enable register */ + +#define ZR060_MER_App (1 << 7) +#define ZR060_MER_Com (1 << 6) +#define ZR060_MER_DRI (1 << 5) +#define ZR060_MER_DQT (1 << 4) +#define ZR060_MER_DHT (1 << 3) + +/* ZR36060 Interrupt Mask register */ + +#define ZR060_IMR_EOAV (1 << 3) +#define ZR060_IMR_EOI (1 << 2) +#define ZR060_IMR_End (1 << 1) +#define ZR060_IMR_DataErr (1 << 0) + +/* ZR36060 Interrupt Status register */ + +#define ZR060_ISR_ProCnt (3 << 6) +#define ZR060_ISR_EOAV (1 << 3) +#define ZR060_ISR_EOI (1 << 2) +#define ZR060_ISR_End (1 << 1) +#define ZR060_ISR_DataErr (1 << 0) + +/* ZR36060 Video Control register */ + +#define ZR060_VCR_Video8 (1 << 7) +#define ZR060_VCR_Range (1 << 6) +#define ZR060_VCR_FIDet (1 << 3) +#define ZR060_VCR_FIVedge (1 << 2) +#define ZR060_VCR_FIExt (1 << 1) +#define ZR060_VCR_SyncMstr (1 << 0) + +/* ZR36060 Video Polarity register */ + +#define ZR060_VPR_VCLKPol (1 << 7) +#define ZR060_VPR_PValPol (1 << 6) +#define ZR060_VPR_PoePol (1 << 5) +#define ZR060_VPR_SImgPol (1 << 4) +#define ZR060_VPR_BLPol (1 << 3) +#define ZR060_VPR_FIPol (1 << 2) +#define ZR060_VPR_HSPol (1 << 1) +#define ZR060_VPR_VSPol (1 << 0) + +/* ZR36060 Scaling register */ + +#define ZR060_SR_VScale (1 << 2) +#define ZR060_SR_HScale2 (1 << 0) +#define ZR060_SR_HScale4 (2 << 0) + +#endif /*fndef ZR36060_H */ -- cgit v1.2.3 From 655e16287856b6732f59e3397bd273d4ec361268 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 8 Apr 2006 16:06:16 -0300 Subject: Whitespace cleanups at media/radio From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/radio/Kconfig | 2 +- linux/drivers/media/radio/miropcm20-radio.c | 14 +- linux/drivers/media/radio/miropcm20-rds-core.c | 32 +-- linux/drivers/media/radio/miropcm20-rds.c | 2 +- linux/drivers/media/radio/radio-aimslab.c | 50 ++-- linux/drivers/media/radio/radio-aztech.c | 44 ++-- linux/drivers/media/radio/radio-cadet.c | 316 ++++++++++++------------- linux/drivers/media/radio/radio-gemtek-pci.c | 46 ++-- linux/drivers/media/radio/radio-gemtek.c | 32 +-- linux/drivers/media/radio/radio-maestro.c | 8 +- linux/drivers/media/radio/radio-maxiradio.c | 80 +++---- linux/drivers/media/radio/radio-rtrack2.c | 34 +-- linux/drivers/media/radio/radio-sf16fmi.c | 40 ++-- linux/drivers/media/radio/radio-sf16fmr2.c | 4 +- linux/drivers/media/radio/radio-terratec.c | 62 ++--- linux/drivers/media/radio/radio-trust.c | 26 +- linux/drivers/media/radio/radio-typhoon.c | 2 +- linux/drivers/media/radio/radio-zoltrix.c | 30 +-- linux/drivers/media/video/cpia.c | 8 +- 19 files changed, 416 insertions(+), 416 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/radio/Kconfig b/linux/drivers/media/radio/Kconfig index d318be383..c65bbc5fd 100644 --- a/linux/drivers/media/radio/Kconfig +++ b/linux/drivers/media/radio/Kconfig @@ -136,7 +136,7 @@ config RADIO_GEMTEK_PCI Choose Y here if you have this PCI FM radio card. In order to control your radio card, you will need to use programs - that are compatible with the Video for Linux API. Information on + that are compatible with the Video for Linux API. Information on this API and pointers to "v4l" programs may be found at . diff --git a/linux/drivers/media/radio/miropcm20-radio.c b/linux/drivers/media/radio/miropcm20-radio.c index c305ecbc0..ec8b16e8f 100644 --- a/linux/drivers/media/radio/miropcm20-radio.c +++ b/linux/drivers/media/radio/miropcm20-radio.c @@ -16,7 +16,7 @@ /* What ever you think about the ACI, version 0x07 is not very well! * I can't get frequency, 'tuner status', 'tuner flags' or mute/mono - * conditions... Robert + * conditions... Robert */ #include @@ -124,7 +124,7 @@ static int pcm20_do_ioctl(struct inode *inode, struct file *file, struct video_device *dev = video_devdata(file); struct pcm20_device *pcm20 = dev->priv; int i; - + switch(cmd) { case VIDIOCGCAP: @@ -140,7 +140,7 @@ static int pcm20_do_ioctl(struct inode *inode, struct file *file, case VIDIOCGTUNER: { struct video_tuner *v = arg; - if(v->tuner) /* Only 1 tuner */ + if(v->tuner) /* Only 1 tuner */ return -EINVAL; v->rangelow=87*16000; v->rangehigh=108*16000; @@ -173,7 +173,7 @@ static int pcm20_do_ioctl(struct inode *inode, struct file *file, return i; } case VIDIOCGAUDIO: - { + { struct video_audio *v = arg; memset(v,0, sizeof(*v)); v->flags=VIDEO_AUDIO_MUTABLE; @@ -184,12 +184,12 @@ static int pcm20_do_ioctl(struct inode *inode, struct file *file, v->mode|=VIDEO_SOUND_MONO; /* v->step=2048; */ strcpy(v->name, "Radio"); - return 0; + return 0; } case VIDIOCSAUDIO: { struct video_audio *v = arg; - if(v->audio) + if(v->audio) return -EINVAL; pcm20_mute(pcm20, !!(v->flags&VIDEO_AUDIO_MUTE)); @@ -238,7 +238,7 @@ static int __init pcm20_init(void) { if(video_register_device(&pcm20_radio, VFL_TYPE_RADIO, radio_nr)==-1) goto video_register_device; - + if(attach_aci_rds()<0) goto attach_aci_rds; diff --git a/linux/drivers/media/radio/miropcm20-rds-core.c b/linux/drivers/media/radio/miropcm20-rds-core.c index b602c73e2..dfb8252b2 100644 --- a/linux/drivers/media/radio/miropcm20-rds-core.c +++ b/linux/drivers/media/radio/miropcm20-rds-core.c @@ -33,24 +33,24 @@ static struct mutex aci_rds_mutex; #define RDS_BUSYMASK 0x10 /* Bit 4 */ #define RDS_CLOCKMASK 0x08 /* Bit 3 */ -#define RDS_DATA(x) (((x) >> RDS_DATASHIFT) & 1) +#define RDS_DATA(x) (((x) >> RDS_DATASHIFT) & 1) #if DEBUG static void print_matrix(char array[], unsigned int length) { - int i, j; - - for (i=0; i=0; j--) { - printk("%d", (array[i] >> j) & 0x1); - } - if (i%8 == 0) - printk(" byte-border\n"); - else - printk("\n"); - } + int i, j; + + for (i=0; i=0; j--) { + printk("%d", (array[i] >> j) & 0x1); + } + if (i%8 == 0) + printk(" byte-border\n"); + else + printk("\n"); + } } #endif /* DEBUG */ @@ -114,7 +114,7 @@ static int rds_write(unsigned char cmd) { unsigned char sendbuffer[8]; int i; - + if (byte2trans(cmd, sendbuffer, 8) != 0){ return -1; } else { @@ -151,7 +151,7 @@ static int rds_read(unsigned char databuffer[], int datasize) I have to waitread() here */ if (rds_waitread() < 0) return -1; - + memset(databuffer, 0, datasize); for (i=0; i< READSIZE; i++) @@ -194,7 +194,7 @@ int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize) ret = 0; mutex_unlock(&aci_rds_mutex); - + return ret; } EXPORT_SYMBOL(aci_rds_cmd); diff --git a/linux/drivers/media/radio/miropcm20-rds.c b/linux/drivers/media/radio/miropcm20-rds.c index e09214082..87b37b769 100644 --- a/linux/drivers/media/radio/miropcm20-rds.c +++ b/linux/drivers/media/radio/miropcm20-rds.c @@ -48,7 +48,7 @@ static int rds_f_release(struct inode *in, struct file *fi) static void print_matrix(char *ch, char out[]) { - int j; + int j; for (j=7; j>=0; j--) { out[7-j] = ((*ch >> j) & 0x1) + '0'; diff --git a/linux/drivers/media/radio/radio-aimslab.c b/linux/drivers/media/radio/radio-aimslab.c index 0fa385ef8..8e966733b 100644 --- a/linux/drivers/media/radio/radio-aimslab.c +++ b/linux/drivers/media/radio/radio-aimslab.c @@ -24,7 +24,7 @@ * out(port, start_increasing_volume); * wait(a_wee_while); * out(port, stop_changing_the_volume); - * + * */ #include /* Modules */ @@ -42,7 +42,7 @@ #define CONFIG_RADIO_RTRACK_PORT -1 #endif -static int io = CONFIG_RADIO_RTRACK_PORT; +static int io = CONFIG_RADIO_RTRACK_PORT; static int radio_nr = -1; static struct mutex lock; @@ -94,12 +94,12 @@ static int rt_setvol(struct rt_device *dev, int vol) int i; mutex_lock(&lock); - + if(vol == dev->curvol) { /* requested volume = current */ if (dev->muted) { /* user is unmuting the card */ dev->muted = 0; outb (0xd8, io); /* enable card */ - } + } mutex_unlock(&lock); return 0; } @@ -115,10 +115,10 @@ static int rt_setvol(struct rt_device *dev, int vol) dev->muted = 0; if(vol > dev->curvol) - for(i = dev->curvol; i < vol; i++) + for(i = dev->curvol; i < vol; i++) rt_incvol(); else - for(i = dev->curvol; i > vol; i--) + for(i = dev->curvol; i > vol; i--) rt_decvol(); dev->curvol = vol; @@ -126,7 +126,7 @@ static int rt_setvol(struct rt_device *dev, int vol) return 0; } -/* the 128+64 on these outb's is to keep the volume stable while tuning +/* the 128+64 on these outb's is to keep the volume stable while tuning * without them, the volume _will_ creep up with each frequency change * and bit 4 (+16) is to keep the signal strength meter enabled */ @@ -141,7 +141,7 @@ static void send_0_byte(int port, struct rt_device *dev) outb_p(128+64+16+8+ 1, port); /* on + wr-enable + data low */ outb_p(128+64+16+8+2+1, port); /* clock */ } - sleep_delay(1000); + sleep_delay(1000); } static void send_1_byte(int port, struct rt_device *dev) @@ -149,13 +149,13 @@ static void send_1_byte(int port, struct rt_device *dev) if ((dev->curvol == 0) || (dev->muted)) { outb_p(128+64+16+4 +1, port); /* wr-enable+data high */ outb_p(128+64+16+4+2+1, port); /* clock */ - } + } else { outb_p(128+64+16+8+4 +1, port); /* on+wr-enable+data high */ outb_p(128+64+16+8+4+2+1, port); /* clock */ } - sleep_delay(1000); + sleep_delay(1000); } static int rt_setfreq(struct rt_device *dev, unsigned long freq) @@ -168,9 +168,9 @@ static int rt_setfreq(struct rt_device *dev, unsigned long freq) freq += 171200; /* Add 10.7 MHz IF */ freq /= 800; /* Convert to 50 kHz units */ - + mutex_lock(&lock); /* Stop other ops interfering */ - + send_0_byte (io, dev); /* 0: LSB of frequency */ for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ @@ -196,7 +196,7 @@ static int rt_setfreq(struct rt_device *dev, unsigned long freq) outb (0xd0, io); /* volume steady + sigstr */ else outb (0xd8, io); /* volume steady + sigstr + on */ - + mutex_unlock(&lock); return 0; @@ -214,7 +214,7 @@ static int rt_do_ioctl(struct inode *inode, struct file *file, { struct video_device *dev = video_devdata(file); struct rt_device *rt=dev->priv; - + switch(cmd) { case VIDIOCGCAP: @@ -230,7 +230,7 @@ static int rt_do_ioctl(struct inode *inode, struct file *file, case VIDIOCGTUNER: { struct video_tuner *v = arg; - if(v->tuner) /* Only 1 tuner */ + if(v->tuner) /* Only 1 tuner */ return -EINVAL; v->rangelow=(87*16000); v->rangehigh=(108*16000); @@ -262,21 +262,21 @@ static int rt_do_ioctl(struct inode *inode, struct file *file, return 0; } case VIDIOCGAUDIO: - { + { struct video_audio *v = arg; memset(v,0, sizeof(*v)); v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME; v->volume=rt->curvol * 6554; v->step=6554; strcpy(v->name, "Radio"); - return 0; + return 0; } case VIDIOCSAUDIO: { struct video_audio *v = arg; - if(v->audio) + if(v->audio) return -EINVAL; - if(v->flags&VIDEO_AUDIO_MUTE) + if(v->flags&VIDEO_AUDIO_MUTE) rt_mute(rt); else rt_setvol(rt,v->volume/6554); @@ -299,7 +299,7 @@ static struct file_operations rtrack_fops = { .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = rt_ioctl, + .ioctl = rt_ioctl, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -321,14 +321,14 @@ static int __init rtrack_init(void) return -EINVAL; } - if (!request_region(io, 2, "rtrack")) + if (!request_region(io, 2, "rtrack")) { printk(KERN_ERR "rtrack: port 0x%x already in use\n", io); return -EBUSY; } rtrack_radio.priv=&rtrack_unit; - + if(video_register_device(&rtrack_radio, VFL_TYPE_RADIO, radio_nr)==-1) { release_region(io, 2); @@ -337,10 +337,10 @@ static int __init rtrack_init(void) printk(KERN_INFO "AIMSlab RadioTrack/RadioReveal card driver.\n"); /* Set up the I/O locking */ - + mutex_init(&lock); - - /* mute card - prevents noisy bootups */ + + /* mute card - prevents noisy bootups */ /* this ensures that the volume is all the way down */ outb(0x48, io); /* volume down but still "on" */ diff --git a/linux/drivers/media/radio/radio-aztech.c b/linux/drivers/media/radio/radio-aztech.c index 97f88c907..3aa679a47 100644 --- a/linux/drivers/media/radio/radio-aztech.c +++ b/linux/drivers/media/radio/radio-aztech.c @@ -1,11 +1,11 @@ -/* radio-aztech.c - Aztech radio card driver for Linux 2.2 +/* radio-aztech.c - Aztech radio card driver for Linux 2.2 * - * Adapted to support the Video for Linux API by + * Adapted to support the Video for Linux API by * Russell Kroll . Based on original tuner code by: * * Quay Ly * Donald Song - * Jason Lewis (jlewis@twilight.vtc.vsc.edu) + * Jason Lewis (jlewis@twilight.vtc.vsc.edu) * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) * William McGrath (wmcgrath@twilight.vtc.vsc.edu) * @@ -40,7 +40,7 @@ #define CONFIG_RADIO_AZTECH_PORT -1 #endif -static int io = CONFIG_RADIO_AZTECH_PORT; +static int io = CONFIG_RADIO_AZTECH_PORT; static int radio_nr = -1; static int radio_wait_time = 1000; static struct mutex lock; @@ -54,15 +54,15 @@ struct az_device static int volconvert(int level) { - level>>=14; /* Map 16bits down to 2 bit */ - level&=3; - + level>>=14; /* Map 16bits down to 2 bit */ + level&=3; + /* convert to card-friendly values */ - switch (level) + switch (level) { - case 0: + case 0: return 0; - case 1: + case 1: return 1; case 2: return 4; @@ -122,9 +122,9 @@ static int az_setfreq(struct az_device *dev, unsigned long frequency) frequency += 171200; /* Add 10.7 MHz IF */ frequency /= 800; /* Convert to 50 kHz units */ - + mutex_lock(&lock); - + send_0_byte (dev); /* 0: LSB of frequency */ for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ @@ -152,7 +152,7 @@ static int az_setfreq(struct az_device *dev, unsigned long frequency) udelay (radio_wait_time); outb_p(128+64+volconvert(dev->curvol), io); - + mutex_unlock(&lock); return 0; @@ -163,7 +163,7 @@ static int az_do_ioctl(struct inode *inode, struct file *file, { struct video_device *dev = video_devdata(file); struct az_device *az = dev->priv; - + switch(cmd) { case VIDIOCGCAP: @@ -179,7 +179,7 @@ static int az_do_ioctl(struct inode *inode, struct file *file, case VIDIOCGTUNER: { struct video_tuner *v = arg; - if(v->tuner) /* Only 1 tuner */ + if(v->tuner) /* Only 1 tuner */ return -EINVAL; v->rangelow=(87*16000); v->rangehigh=(108*16000); @@ -212,7 +212,7 @@ static int az_do_ioctl(struct inode *inode, struct file *file, return 0; } case VIDIOCGAUDIO: - { + { struct video_audio *v = arg; memset(v,0, sizeof(*v)); v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME; @@ -223,17 +223,17 @@ static int az_do_ioctl(struct inode *inode, struct file *file, v->volume=az->curvol; v->step=16384; strcpy(v->name, "Radio"); - return 0; + return 0; } case VIDIOCSAUDIO: { struct video_audio *v = arg; - if(v->audio) + if(v->audio) return -EINVAL; az->curvol=v->volume; az->stereo=(v->mode&VIDEO_SOUND_STEREO)?1:0; - if(v->flags&VIDEO_AUDIO_MUTE) + if(v->flags&VIDEO_AUDIO_MUTE) az_setvol(az,0); else az_setvol(az,az->curvol); @@ -278,7 +278,7 @@ static int __init aztech_init(void) return -EINVAL; } - if (!request_region(io, 2, "aztech")) + if (!request_region(io, 2, "aztech")) { printk(KERN_ERR "aztech: port 0x%x already in use\n", io); return -EBUSY; @@ -286,13 +286,13 @@ static int __init aztech_init(void) mutex_init(&lock); aztech_radio.priv=&aztech_unit; - + if(video_register_device(&aztech_radio, VFL_TYPE_RADIO, radio_nr)==-1) { release_region(io,2); return -EINVAL; } - + printk(KERN_INFO "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n"); /* mute card - prevents noisy bootups */ outb (0, io); diff --git a/linux/drivers/media/radio/radio-cadet.c b/linux/drivers/media/radio/radio-cadet.c index 559be5d00..9866c3860 100644 --- a/linux/drivers/media/radio/radio-cadet.c +++ b/linux/drivers/media/radio/radio-cadet.c @@ -8,7 +8,7 @@ * Russell Kroll (rkroll@exploits.org) * Quay Ly * Donald Song - * Jason Lewis (jlewis@twilight.vtc.vsc.edu) + * Jason Lewis (jlewis@twilight.vtc.vsc.edu) * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) * William McGrath (wmcgrath@twilight.vtc.vsc.edu) * @@ -56,29 +56,29 @@ static int cadet_probe(void); /* * Signal Strength Threshold Values - * The V4L API spec does not define any particular unit for the signal + * The V4L API spec does not define any particular unit for the signal * strength value. These values are in microvolts of RF at the tuner's input. */ static __u16 sigtable[2][4]={{5,10,30,150},{28,40,63,1000}}; static int cadet_getrds(void) { - int rdsstat=0; + int rdsstat=0; spin_lock(&cadet_io_lock); - outb(3,io); /* Select Decoder Control/Status */ + outb(3,io); /* Select Decoder Control/Status */ outb(inb(io+1)&0x7f,io+1); /* Reset RDS detection */ spin_unlock(&cadet_io_lock); - + msleep(100); - spin_lock(&cadet_io_lock); - outb(3,io); /* Select Decoder Control/Status */ + spin_lock(&cadet_io_lock); + outb(3,io); /* Select Decoder Control/Status */ if((inb(io+1)&0x80)!=0) { - rdsstat|=VIDEO_TUNER_RDS_ON; + rdsstat|=VIDEO_TUNER_RDS_ON; } if((inb(io+1)&0x10)!=0) { - rdsstat|=VIDEO_TUNER_MBS_ON; + rdsstat|=VIDEO_TUNER_MBS_ON; } spin_unlock(&cadet_io_lock); return rdsstat; @@ -87,49 +87,49 @@ static int cadet_getrds(void) static int cadet_getstereo(void) { int ret = 0; - if(curtuner != 0) /* Only FM has stereo capability! */ - return 0; + if(curtuner != 0) /* Only FM has stereo capability! */ + return 0; spin_lock(&cadet_io_lock); - outb(7,io); /* Select tuner control */ + outb(7,io); /* Select tuner control */ if( (inb(io+1) & 0x40) == 0) - ret = 1; - spin_unlock(&cadet_io_lock); - return ret; + ret = 1; + spin_unlock(&cadet_io_lock); + return ret; } static unsigned cadet_gettune(void) { - int curvol,i; + int curvol,i; unsigned fifo=0; - /* - * Prepare for read - */ + /* + * Prepare for read + */ spin_lock(&cadet_io_lock); - - outb(7,io); /* Select tuner control */ - curvol=inb(io+1); /* Save current volume/mute setting */ - outb(0x00,io+1); /* Ensure WRITE-ENABLE is LOW */ + + outb(7,io); /* Select tuner control */ + curvol=inb(io+1); /* Save current volume/mute setting */ + outb(0x00,io+1); /* Ensure WRITE-ENABLE is LOW */ tunestat=0xffff; - /* - * Read the shift register - */ - for(i=0;i<25;i++) { - fifo=(fifo<<1)|((inb(io+1)>>7)&0x01); - if(i<24) { - outb(0x01,io+1); + /* + * Read the shift register + */ + for(i=0;i<25;i++) { + fifo=(fifo<<1)|((inb(io+1)>>7)&0x01); + if(i<24) { + outb(0x01,io+1); tunestat&=inb(io+1); - outb(0x00,io+1); - } - } - - /* - * Restore volume/mute setting - */ - outb(curvol,io+1); + outb(0x00,io+1); + } + } + + /* + * Restore volume/mute setting + */ + outb(curvol,io+1); spin_unlock(&cadet_io_lock); return fifo; @@ -137,43 +137,43 @@ static unsigned cadet_gettune(void) static unsigned cadet_getfreq(void) { - int i; - unsigned freq=0,test,fifo=0; + int i; + unsigned freq=0,test,fifo=0; /* * Read current tuning */ fifo=cadet_gettune(); - /* - * Convert to actual frequency - */ + /* + * Convert to actual frequency + */ if(curtuner==0) { /* FM */ - test=12500; - for(i=0;i<14;i++) { - if((fifo&0x01)!=0) { - freq+=test; - } - test=test<<1; - fifo=fifo>>1; - } - freq-=10700000; /* IF frequency is 10.7 MHz */ - freq=(freq*16)/1000000; /* Make it 1/16 MHz */ + test=12500; + for(i=0;i<14;i++) { + if((fifo&0x01)!=0) { + freq+=test; + } + test=test<<1; + fifo=fifo>>1; + } + freq-=10700000; /* IF frequency is 10.7 MHz */ + freq=(freq*16)/1000000; /* Make it 1/16 MHz */ } if(curtuner==1) { /* AM */ - freq=((fifo&0x7fff)-2010)*16; + freq=((fifo&0x7fff)-2010)*16; } - return freq; + return freq; } static void cadet_settune(unsigned fifo) { - int i; - unsigned test; + int i; + unsigned test; spin_lock(&cadet_io_lock); - + outb(7,io); /* Select tuner control */ /* * Write the shift register @@ -184,7 +184,7 @@ static void cadet_settune(unsigned fifo) outb(7,io); /* Select tuner control */ outb(test,io+1); /* Initialize for write */ for(i=0;i<25;i++) { - test|=0x01; /* Toggle SCK High */ + test|=0x01; /* Toggle SCK High */ outb(test,io+1); test&=0xfe; /* Toggle SCK Low */ outb(test,io+1); @@ -197,57 +197,57 @@ static void cadet_settune(unsigned fifo) static void cadet_setfreq(unsigned freq) { - unsigned fifo; - int i,j,test; - int curvol; + unsigned fifo; + int i,j,test; + int curvol; - /* - * Formulate a fifo command - */ + /* + * Formulate a fifo command + */ fifo=0; if(curtuner==0) { /* FM */ - test=102400; - freq=(freq*1000)/16; /* Make it kHz */ - freq+=10700; /* IF is 10700 kHz */ - for(i=0;i<14;i++) { - fifo=fifo<<1; - if(freq>=test) { - fifo|=0x01; - freq-=test; - } - test=test>>1; - } + test=102400; + freq=(freq*1000)/16; /* Make it kHz */ + freq+=10700; /* IF is 10700 kHz */ + for(i=0;i<14;i++) { + fifo=fifo<<1; + if(freq>=test) { + fifo|=0x01; + freq-=test; + } + test=test>>1; + } } if(curtuner==1) { /* AM */ - fifo=(freq/16)+2010; /* Make it kHz */ + fifo=(freq/16)+2010; /* Make it kHz */ fifo|=0x100000; /* Select AM Band */ } - /* - * Save current volume/mute setting - */ + /* + * Save current volume/mute setting + */ spin_lock(&cadet_io_lock); outb(7,io); /* Select tuner control */ - curvol=inb(io+1); - spin_unlock(&cadet_io_lock); + curvol=inb(io+1); + spin_unlock(&cadet_io_lock); /* * Tune the card */ for(j=3;j>-1;j--) { - cadet_settune(fifo|(j<<16)); - - spin_lock(&cadet_io_lock); + cadet_settune(fifo|(j<<16)); + + spin_lock(&cadet_io_lock); outb(7,io); /* Select tuner control */ outb(curvol,io+1); spin_unlock(&cadet_io_lock); - + msleep(100); cadet_gettune(); if((tunestat & 0x40) == 0) { /* Tuned */ - sigstrength=sigtable[curtuner][j]; + sigstrength=sigtable[curtuner][j]; return; } } @@ -258,28 +258,28 @@ static void cadet_setfreq(unsigned freq) static int cadet_getvol(void) { int ret = 0; - + spin_lock(&cadet_io_lock); - - outb(7,io); /* Select tuner control */ - if((inb(io + 1) & 0x20) != 0) - ret = 0xffff; - - spin_unlock(&cadet_io_lock); - return ret; + + outb(7,io); /* Select tuner control */ + if((inb(io + 1) & 0x20) != 0) + ret = 0xffff; + + spin_unlock(&cadet_io_lock); + return ret; } static void cadet_setvol(int vol) { spin_lock(&cadet_io_lock); - outb(7,io); /* Select tuner control */ - if(vol>0) - outb(0x20,io+1); - else - outb(0x00,io+1); + outb(7,io); /* Select tuner control */ + if(vol>0) + outb(0x20,io+1); + else + outb(0x00,io+1); spin_unlock(&cadet_io_lock); -} +} static void cadet_handler(unsigned long data) { @@ -289,15 +289,15 @@ static void cadet_handler(unsigned long data) if(spin_trylock(&cadet_io_lock)) { - outb(0x3,io); /* Select RDS Decoder Control */ + outb(0x3,io); /* Select RDS Decoder Control */ if((inb(io+1)&0x20)!=0) { - printk(KERN_CRIT "cadet: RDS fifo overflow\n"); + printk(KERN_CRIT "cadet: RDS fifo overflow\n"); } outb(0x80,io); /* Select RDS fifo */ while((inb(io)&0x80)!=0) { - rdsbuf[rdsin]=inb(io+1); + rdsbuf[rdsin]=inb(io+1); if(rdsin==rdsout) - printk(KERN_WARNING "cadet: RDS buffer overflow\n"); + printk(KERN_WARNING "cadet: RDS buffer overflow\n"); else rdsin++; } @@ -308,9 +308,9 @@ static void cadet_handler(unsigned long data) * Service pending read */ if( rdsin!=rdsout) - wake_up_interruptible(&read_queue); + wake_up_interruptible(&read_queue); - /* + /* * Clean up and exit */ init_timer(&readtimer); @@ -325,12 +325,12 @@ static void cadet_handler(unsigned long data) static ssize_t cadet_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { - int i=0; + int i=0; unsigned char readbuf[RDS_BUFFER]; - if(rdsstat==0) { + if(rdsstat==0) { spin_lock(&cadet_io_lock); - rdsstat=1; + rdsstat=1; outb(0x80,io); /* Select RDS fifo */ spin_unlock(&cadet_io_lock); init_timer(&readtimer); @@ -340,15 +340,15 @@ static ssize_t cadet_read(struct file *file, char __user *data, add_timer(&readtimer); } if(rdsin==rdsout) { - if (file->f_flags & O_NONBLOCK) - return -EWOULDBLOCK; - interruptible_sleep_on(&read_queue); - } + if (file->f_flags & O_NONBLOCK) + return -EWOULDBLOCK; + interruptible_sleep_on(&read_queue); + } while( ituner) { - case 0: - strcpy(v->name,"FM"); - v->rangelow=1400; /* 87.5 MHz */ - v->rangehigh=1728; /* 108.0 MHz */ - v->flags=0; - v->mode=0; - v->mode|=VIDEO_MODE_AUTO; - v->signal=sigstrength; - if(cadet_getstereo()==1) { - v->flags|=VIDEO_TUNER_STEREO_ON; - } + case 0: + strcpy(v->name,"FM"); + v->rangelow=1400; /* 87.5 MHz */ + v->rangehigh=1728; /* 108.0 MHz */ + v->flags=0; + v->mode=0; + v->mode|=VIDEO_MODE_AUTO; + v->signal=sigstrength; + if(cadet_getstereo()==1) { + v->flags|=VIDEO_TUNER_STEREO_ON; + } v->flags|=cadet_getrds(); - break; - case 1: - strcpy(v->name,"AM"); - v->rangelow=8320; /* 520 kHz */ - v->rangehigh=26400; /* 1650 kHz */ - v->flags=0; - v->flags|=VIDEO_TUNER_LOW; - v->mode=0; - v->mode|=VIDEO_MODE_AUTO; - v->signal=sigstrength; - break; + break; + case 1: + strcpy(v->name,"AM"); + v->rangelow=8320; /* 520 kHz */ + v->rangehigh=26400; /* 1650 kHz */ + v->flags=0; + v->flags|=VIDEO_TUNER_LOW; + v->mode=0; + v->mode|=VIDEO_MODE_AUTO; + v->signal=sigstrength; + break; } return 0; } @@ -408,49 +408,49 @@ static int cadet_do_ioctl(struct inode *inode, struct file *file, if((v->tuner<0)||(v->tuner>1)) { return -EINVAL; } - curtuner=v->tuner; + curtuner=v->tuner; return 0; } case VIDIOCGFREQ: { - unsigned long *freq = arg; + unsigned long *freq = arg; *freq = cadet_getfreq(); return 0; } case VIDIOCSFREQ: { - unsigned long *freq = arg; + unsigned long *freq = arg; if((curtuner==0)&&((*freq<1400)||(*freq>1728))) { - return -EINVAL; + return -EINVAL; } if((curtuner==1)&&((*freq<8320)||(*freq>26400))) { - return -EINVAL; + return -EINVAL; } cadet_setfreq(*freq); return 0; } case VIDIOCGAUDIO: - { + { struct video_audio *v = arg; memset(v,0, sizeof(*v)); v->flags=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME; if(cadet_getstereo()==0) { - v->mode=VIDEO_SOUND_MONO; + v->mode=VIDEO_SOUND_MONO; } else { v->mode=VIDEO_SOUND_STEREO; } v->volume=cadet_getvol(); v->step=0xffff; strcpy(v->name, "Radio"); - return 0; + return 0; } case VIDIOCSAUDIO: { struct video_audio *v = arg; - if(v->audio) + if(v->audio) return -EINVAL; cadet_setvol(v->volume); - if(v->flags&VIDEO_AUDIO_MUTE) + if(v->flags&VIDEO_AUDIO_MUTE) cadet_setvol(0); else cadet_setvol(0xffff); @@ -540,16 +540,16 @@ static struct pnp_driver cadet_pnp_driver = { static int cadet_probe(void) { - static int iovals[8]={0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e}; + static int iovals[8]={0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e}; int i; for(i=0;i<8;i++) { - io=iovals[i]; + io=iovals[i]; if (request_region(io, 2, "cadet-probe")) { - cadet_setfreq(1410); + cadet_setfreq(1410); if(cadet_getfreq()==1410) { release_region(io, 2); - return io; + return io; } release_region(io, 2); } @@ -557,7 +557,7 @@ static int cadet_probe(void) return -1; } -/* +/* * io should only be set if the user has used something like * isapnp (the userspace program) to initialize this card for us */ @@ -565,7 +565,7 @@ static int cadet_probe(void) static int __init cadet_init(void) { spin_lock_init(&cadet_io_lock); - + /* * If a probe was requested then probe ISAPnP first (safest) */ @@ -580,12 +580,12 @@ static int __init cadet_init(void) /* * Else we bail out */ - - if(io < 0) { -#ifdef MODULE + + if(io < 0) { +#ifdef MODULE printk(KERN_ERR "You must set an I/O address with io=0x???\n"); #endif - goto fail; + goto fail; } if (!request_region(io,2,"cadet")) goto fail; diff --git a/linux/drivers/media/radio/radio-gemtek-pci.c b/linux/drivers/media/radio/radio-gemtek-pci.c index c8777e953..40a50a628 100644 --- a/linux/drivers/media/radio/radio-gemtek-pci.c +++ b/linux/drivers/media/radio/radio-gemtek-pci.c @@ -1,6 +1,6 @@ /* *************************************************************************** - * + * * radio-gemtek-pci.c - Gemtek PCI Radio driver * (C) 2001 Vladimir Shebordaev * @@ -31,7 +31,7 @@ * radio device driver. * * Please, let me know if this piece of code was useful :) - * + * * TODO: multiple device support and portability were not tested * *************************************************************************** @@ -70,18 +70,18 @@ #define TRUE (1) #endif -#ifndef FALSE +#ifndef FALSE #define FALSE (0) #endif struct gemtek_pci_card { struct video_device *videodev; - + u32 iobase; u32 length; u8 chiprev; u16 model; - + u32 current_frequency; u8 mute; }; @@ -97,7 +97,7 @@ static inline u8 gemtek_pci_out( u16 value, u32 port ) return (u8)value; } -#define _b0( v ) *((u8 *)&v) +#define _b0( v ) *((u8 *)&v) static void __gemtek_pci_cmd( u16 value, u32 port, u8 *last_byte, int keep ) { register u8 byte = *last_byte; @@ -105,7 +105,7 @@ static void __gemtek_pci_cmd( u16 value, u32 port, u8 *last_byte, int keep ) if ( !value ) { if ( !keep ) value = (u16)port; - byte &= 0xfd; + byte &= 0xfd; } else byte |= 2; @@ -117,7 +117,7 @@ static void __gemtek_pci_cmd( u16 value, u32 port, u8 *last_byte, int keep ) byte &= 0xfe; _b0( value ) = byte; outw( value, port ); - + *last_byte = byte; } @@ -194,13 +194,13 @@ static int gemtek_pci_do_ioctl(struct inode *inode, struct file *file, c->audios = 1; strcpy( c->name, "Gemtek PCI Radio" ); return 0; - } + } case VIDIOCGTUNER: { struct video_tuner *t = arg; - if ( t->tuner ) + if ( t->tuner ) return -EINVAL; t->rangelow = GEMTEK_PCI_RANGE_LOW; @@ -229,7 +229,7 @@ static int gemtek_pci_do_ioctl(struct inode *inode, struct file *file, case VIDIOCSFREQ: { unsigned long *freq = arg; - + if ( (*freq < GEMTEK_PCI_RANGE_LOW) || (*freq > GEMTEK_PCI_RANGE_HIGH) ) return -EINVAL; @@ -240,9 +240,9 @@ static int gemtek_pci_do_ioctl(struct inode *inode, struct file *file, return 0; } - + case VIDIOCGAUDIO: - { + { struct video_audio *a = arg; memset( a, 0, sizeof( *a ) ); @@ -250,17 +250,17 @@ static int gemtek_pci_do_ioctl(struct inode *inode, struct file *file, a->volume = 1; a->step = 65535; strcpy( a->name, "Radio" ); - return 0; + return 0; } case VIDIOCSAUDIO: { struct video_audio *a = arg; - if ( a->audio ) + if ( a->audio ) return -EINVAL; - if ( a->flags & VIDEO_AUDIO_MUTE ) + if ( a->flags & VIDEO_AUDIO_MUTE ) gemtek_pci_mute( card ); else gemtek_pci_unmute( card ); @@ -324,9 +324,9 @@ static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci return -ENOMEM; } - if ( pci_enable_device( pci_dev ) ) + if ( pci_enable_device( pci_dev ) ) goto err_pci; - + card->iobase = pci_resource_start( pci_dev, 0 ); card->length = pci_resource_len( pci_dev, 0 ); @@ -339,7 +339,7 @@ static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci pci_read_config_word( pci_dev, PCI_SUBSYSTEM_ID, &card->model ); pci_set_drvdata( pci_dev, card ); - + if ( (devradio = kmalloc( sizeof( struct video_device ), GFP_KERNEL )) == NULL ) { printk( KERN_ERR "gemtek_pci: out of memory\n" ); goto err_video; @@ -355,7 +355,7 @@ static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci devradio->priv = card; gemtek_pci_mute( card ); - printk( KERN_INFO "Gemtek PCI Radio (rev. %d) found at 0x%04x-0x%04x.\n", + printk( KERN_INFO "Gemtek PCI Radio (rev. %d) found at 0x%04x-0x%04x.\n", card->chiprev, card->iobase, card->iobase + card->length - 1 ); return 0; @@ -365,7 +365,7 @@ err_video: err_pci: kfree( card ); - return -ENODEV; + return -ENODEV; } static void __devexit gemtek_pci_remove( struct pci_dev *pci_dev ) @@ -376,12 +376,12 @@ static void __devexit gemtek_pci_remove( struct pci_dev *pci_dev ) kfree( card->videodev ); release_region( card->iobase, card->length ); - + if ( mx ) gemtek_pci_mute( card ); kfree( card ); - + pci_set_drvdata( pci_dev, NULL ); } diff --git a/linux/drivers/media/radio/radio-gemtek.c b/linux/drivers/media/radio/radio-gemtek.c index 199f2df7a..5b3ef00a9 100644 --- a/linux/drivers/media/radio/radio-gemtek.c +++ b/linux/drivers/media/radio/radio-gemtek.c @@ -6,7 +6,7 @@ * Besides the protocol changes, this is mostly a copy of: * * RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff - * + * * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood * Converted to new API by Alan Cox * Various bugfixes and enhancements by Russell Kroll @@ -30,7 +30,7 @@ #define CONFIG_RADIO_GEMTEK_PORT -1 #endif -static int io = CONFIG_RADIO_GEMTEK_PORT; +static int io = CONFIG_RADIO_GEMTEK_PORT; static int radio_nr = -1; static spinlock_t lock; @@ -49,7 +49,7 @@ struct gemtek_device */ static void gemtek_mute(struct gemtek_device *dev) { - if(dev->muted) + if(dev->muted) return; spin_lock(&lock); outb(0x10, io); @@ -95,20 +95,20 @@ static int gemtek_setfreq(struct gemtek_device *dev, unsigned long freq) freq /= 100000; spin_lock(&lock); - + /* 2 start bits */ outb_p(0x03, io); udelay(5); outb_p(0x07, io); udelay(5); - /* 28 frequency bits (lsb first) */ + /* 28 frequency bits (lsb first) */ for (i = 0; i < 14; i++) if (freq & (1 << i)) one(); else zero(); - /* 36 unknown bits */ + /* 36 unknown bits */ for (i = 0; i < 11; i++) zero(); one(); @@ -124,7 +124,7 @@ static int gemtek_setfreq(struct gemtek_device *dev, unsigned long freq) udelay(5); spin_unlock(&lock); - + return 0; } @@ -160,7 +160,7 @@ static int gemtek_do_ioctl(struct inode *inode, struct file *file, case VIDIOCGTUNER: { struct video_tuner *v = arg; - if(v->tuner) /* Only 1 tuner */ + if(v->tuner) /* Only 1 tuner */ return -EINVAL; v->rangelow=87*16000; v->rangehigh=108*16000; @@ -194,25 +194,25 @@ static int gemtek_do_ioctl(struct inode *inode, struct file *file, return 0; } case VIDIOCGAUDIO: - { + { struct video_audio *v = arg; memset(v,0, sizeof(*v)); v->flags|=VIDEO_AUDIO_MUTABLE; v->volume=1; v->step=65535; strcpy(v->name, "Radio"); - return 0; + return 0; } case VIDIOCSAUDIO: { struct video_audio *v = arg; - if(v->audio) + if(v->audio) return -EINVAL; - if(v->flags&VIDEO_AUDIO_MUTE) + if(v->flags&VIDEO_AUDIO_MUTE) gemtek_mute(rt); else - gemtek_unmute(rt); + gemtek_unmute(rt); return 0; } @@ -255,14 +255,14 @@ static int __init gemtek_init(void) return -EINVAL; } - if (!request_region(io, 4, "gemtek")) + if (!request_region(io, 4, "gemtek")) { printk(KERN_ERR "gemtek: port 0x%x already in use\n", io); return -EBUSY; } gemtek_radio.priv=&gemtek_unit; - + if(video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr)==-1) { release_region(io, 4); @@ -275,7 +275,7 @@ static int __init gemtek_init(void) /* this is _maybe_ unnecessary */ outb(0x01, io); - /* mute card - prevents noisy bootups */ + /* mute card - prevents noisy bootups */ gemtek_unit.muted = 0; gemtek_mute(&gemtek_unit); diff --git a/linux/drivers/media/radio/radio-maestro.c b/linux/drivers/media/radio/radio-maestro.c index 613a84142..d93357579 100644 --- a/linux/drivers/media/radio/radio-maestro.c +++ b/linux/drivers/media/radio/radio-maestro.c @@ -2,7 +2,7 @@ * (c) 2000 A. Tlalka, atlka@pg.gda.pl * Notes on the hardware * - * + Frequency control is done digitally + * + Frequency control is done digitally * + No volume control - only mute/unmute - you have to use Aux line volume * control on Maestro card to set the volume * + Radio status (tuned/not_tuned and stereo/mono) is valid some time after @@ -104,7 +104,7 @@ static struct video_device maestro_radio = { struct radio_device { u16 io, /* base of Maestro card radio io (GPIO_DATA)*/ muted, /* VIDEO_AUDIO_MUTE */ - stereo, /* VIDEO_TUNER_STEREO_ON */ + stereo, /* VIDEO_TUNER_STEREO_ON */ tuned; /* signal strength (0 or 0xffff) */ struct mutex lock; }; @@ -123,14 +123,14 @@ static u32 radio_bits_get(struct radio_device *dev) for (l=24;l--;) { outw(STR_CLK, io); /* HI state */ udelay(2); - if(!l) + if(!l) dev->tuned = inw(io) & STR_MOST ? 0 : 0xffff; outw(0, io); /* LO state */ udelay(2); data <<= 1; /* shift data */ rdata = inw(io); if(!l) - dev->stereo = rdata & STR_MOST ? + dev->stereo = rdata & STR_MOST ? 0 : VIDEO_TUNER_STEREO_ON; else if(rdata & STR_DATA) diff --git a/linux/drivers/media/radio/radio-maxiradio.c b/linux/drivers/media/radio/radio-maxiradio.c index da6b6148e..2b83fa519 100644 --- a/linux/drivers/media/radio/radio-maxiradio.c +++ b/linux/drivers/media/radio/radio-maxiradio.c @@ -1,15 +1,15 @@ -/* - * Guillemot Maxi Radio FM 2000 PCI radio card driver for Linux +/* + * Guillemot Maxi Radio FM 2000 PCI radio card driver for Linux * (C) 2001 Dimitromanolakis Apostolos * * Based in the radio Maestro PCI driver. Actually it uses the same chip * for radio but different pci controller. * * I didn't have any specs I reversed engineered the protocol from - * the windows driver (radio.dll). + * the windows driver (radio.dll). * * The card uses the TEA5757 chip that includes a search function but it - * is useless as I haven't found any way to read back the frequency. If + * is useless as I haven't found any way to read back the frequency. If * anybody does please mail me. * * For the pdf file see: @@ -24,7 +24,7 @@ * - tiding up * - removed support for multiple devices as it didn't work anyway * - * BUGS: + * BUGS: * - card unmutes if you change frequency * */ @@ -81,7 +81,7 @@ static struct file_operations maxiradio_fops = { .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = radio_ioctl, + .ioctl = radio_ioctl, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -98,11 +98,11 @@ static struct radio_device { __u16 io, /* base of radio io */ muted, /* VIDEO_AUDIO_MUTE */ - stereo, /* VIDEO_TUNER_STEREO_ON */ + stereo, /* VIDEO_TUNER_STEREO_ON */ tuned; /* signal strength (0 or 0xffff) */ - + unsigned long freq; - + struct mutex lock; } radio_unit = {0, 0, 0, 0, }; @@ -115,7 +115,7 @@ static void outbit(unsigned long bit, __u16 io) outb( power|wren|data|clk ,io); udelay(4); outb( power|wren|data ,io); udelay(4); } - else + else { outb( power|wren ,io); udelay(4); outb( power|wren|clk ,io); udelay(4); @@ -133,12 +133,12 @@ static void set_freq(__u16 io, __u32 data) { unsigned long int si; int bl; - + /* TEA5757 shift register bits (see pdf) */ - outbit(0,io); // 24 search + outbit(0,io); // 24 search outbit(1,io); // 23 search up/down - + outbit(0,io); // 22 stereo/mono outbit(0,io); // 21 band @@ -146,24 +146,24 @@ static void set_freq(__u16 io, __u32 data) outbit(0,io); // 19 port ? outbit(0,io); // 18 port ? - + outbit(0,io); // 17 search level outbit(0,io); // 16 search level - + si = 0x8000; for(bl = 1; bl <= 16 ; bl++) { outbit(data & si,io); si >>=1; } - + outb(power,io); } static int get_stereo(__u16 io) -{ +{ outb(power,io); udelay(4); return !(inb(io) & mo_st); } static int get_tune(__u16 io) -{ +{ outb(power+clk,io); udelay(4); return !(inb(io) & mo_st); } @@ -178,7 +178,7 @@ static inline int radio_function(struct inode *inode, struct file *file, switch(cmd) { case VIDIOCGCAP: { struct video_capability *v = arg; - + memset(v,0,sizeof(*v)); strcpy(v->name, "Maxi Radio FM2000 radio"); v->type=VID_TYPE_TUNER; @@ -187,22 +187,22 @@ static inline int radio_function(struct inode *inode, struct file *file, } case VIDIOCGTUNER: { struct video_tuner *v = arg; - + if(v->tuner) return -EINVAL; - + card->stereo = 0xffff * get_stereo(card->io); card->tuned = 0xffff * get_tune(card->io); - + v->flags = VIDEO_TUNER_LOW | card->stereo; v->signal = card->tuned; - + strcpy(v->name, "FM"); - + v->rangelow = FREQ_LO; v->rangehigh = FREQ_HI; v->mode = VIDEO_MODE_AUTO; - + return 0; } case VIDIOCSTUNER: { @@ -213,13 +213,13 @@ static inline int radio_function(struct inode *inode, struct file *file, } case VIDIOCGFREQ: { unsigned long *freq = arg; - + *freq = card->freq; return 0; } case VIDIOCSFREQ: { unsigned long *freq = arg; - + if (*freq < FREQ_LO || *freq > FREQ_HI) return -EINVAL; card->freq = *freq; @@ -227,18 +227,18 @@ static inline int radio_function(struct inode *inode, struct file *file, msleep(125); return 0; } - case VIDIOCGAUDIO: { + case VIDIOCGAUDIO: { struct video_audio *v = arg; memset(v,0,sizeof(*v)); strcpy(v->name, "Radio"); v->flags=VIDEO_AUDIO_MUTABLE | card->muted; v->mode=VIDEO_SOUND_STEREO; - return 0; + return 0; } - + case VIDIOCSAUDIO: { struct video_audio *v = arg; - + if(v->audio) return -EINVAL; card->muted = v->flags & VIDEO_AUDIO_MUTE; @@ -250,13 +250,13 @@ static inline int radio_function(struct inode *inode, struct file *file, } case VIDIOCGUNIT: { struct video_unit *v = arg; - + v->video=VIDEO_NO_UNIT; v->vbi=VIDEO_NO_UNIT; v->radio=dev->minor; v->audio=0; v->teletext=VIDEO_NO_UNIT; - return 0; + return 0; } default: return -ENOIOCTLCMD; } @@ -268,7 +268,7 @@ static int radio_ioctl(struct inode *inode, struct file *file, struct video_device *dev = video_devdata(file); struct radio_device *card=dev->priv; int ret; - + mutex_lock(&card->lock); ret = video_usercopy(inode, file, cmd, arg, radio_function); mutex_unlock(&card->lock); @@ -283,21 +283,21 @@ MODULE_LICENSE("GPL"); static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { if(!request_region(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0), "Maxi Radio FM 2000")) { - printk(KERN_ERR "radio-maxiradio: can't reserve I/O ports\n"); - goto err_out; + pci_resource_len(pdev, 0), "Maxi Radio FM 2000")) { + printk(KERN_ERR "radio-maxiradio: can't reserve I/O ports\n"); + goto err_out; } if (pci_enable_device(pdev)) - goto err_out_free_region; + goto err_out_free_region; radio_unit.io = pci_resource_start(pdev, 0); mutex_init(&radio_unit.lock); maxiradio_radio.priv = &radio_unit; if(video_register_device(&maxiradio_radio, VFL_TYPE_RADIO, radio_nr)==-1) { - printk("radio-maxiradio: can't register device!"); - goto err_out_free_region; + printk("radio-maxiradio: can't register device!"); + goto err_out_free_region; } printk(KERN_INFO "radio-maxiradio: version " diff --git a/linux/drivers/media/radio/radio-rtrack2.c b/linux/drivers/media/radio/radio-rtrack2.c index defdc83e4..cba2fd028 100644 --- a/linux/drivers/media/radio/radio-rtrack2.c +++ b/linux/drivers/media/radio/radio-rtrack2.c @@ -1,5 +1,5 @@ /* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff - * + * * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood * Converted to new API by Alan Cox * Various bugfixes and enhancements by Russell Kroll @@ -23,7 +23,7 @@ #define CONFIG_RADIO_RTRACK2_PORT -1 #endif -static int io = CONFIG_RADIO_RTRACK2_PORT; +static int io = CONFIG_RADIO_RTRACK2_PORT; static int radio_nr = -1; static spinlock_t lock; @@ -39,7 +39,7 @@ struct rt_device static void rt_mute(struct rt_device *dev) { - if(dev->muted) + if(dev->muted) return; spin_lock(&lock); outb(1, io); @@ -59,14 +59,14 @@ static void rt_unmute(struct rt_device *dev) static void zero(void) { - outb_p(1, io); + outb_p(1, io); outb_p(3, io); outb_p(1, io); } static void one(void) { - outb_p(5, io); + outb_p(5, io); outb_p(7, io); outb_p(5, io); } @@ -76,7 +76,7 @@ static int rt_setfreq(struct rt_device *dev, unsigned long freq) int i; freq = freq / 200 + 856; - + spin_lock(&lock); outb_p(0xc8, io); @@ -95,7 +95,7 @@ static int rt_setfreq(struct rt_device *dev, unsigned long freq) outb_p(0xc8, io); if (!dev->muted) outb_p(0, io); - + spin_unlock(&lock); return 0; } @@ -128,7 +128,7 @@ static int rt_do_ioctl(struct inode *inode, struct file *file, case VIDIOCGTUNER: { struct video_tuner *v = arg; - if(v->tuner) /* Only 1 tuner */ + if(v->tuner) /* Only 1 tuner */ return -EINVAL; v->rangelow=88*16000; v->rangehigh=108*16000; @@ -160,25 +160,25 @@ static int rt_do_ioctl(struct inode *inode, struct file *file, return 0; } case VIDIOCGAUDIO: - { + { struct video_audio *v = arg; memset(v,0, sizeof(*v)); v->flags|=VIDEO_AUDIO_MUTABLE; v->volume=1; v->step=65535; strcpy(v->name, "Radio"); - return 0; + return 0; } case VIDIOCSAUDIO: { struct video_audio *v = arg; - if(v->audio) + if(v->audio) return -EINVAL; - if(v->flags&VIDEO_AUDIO_MUTE) + if(v->flags&VIDEO_AUDIO_MUTE) rt_mute(rt); else - rt_unmute(rt); + rt_unmute(rt); return 0; } @@ -220,7 +220,7 @@ static int __init rtrack2_init(void) printk(KERN_ERR "You must set an I/O address with io=0x20c or io=0x30c\n"); return -EINVAL; } - if (!request_region(io, 4, "rtrack2")) + if (!request_region(io, 4, "rtrack2")) { printk(KERN_ERR "rtrack2: port 0x%x already in use\n", io); return -EBUSY; @@ -228,16 +228,16 @@ static int __init rtrack2_init(void) rtrack2_radio.priv=&rtrack2_unit; - spin_lock_init(&lock); + spin_lock_init(&lock); if(video_register_device(&rtrack2_radio, VFL_TYPE_RADIO, radio_nr)==-1) { release_region(io, 4); return -EINVAL; } - + printk(KERN_INFO "AIMSlab Radiotrack II card driver.\n"); - /* mute card - prevents noisy bootups */ + /* mute card - prevents noisy bootups */ outb(1, io); rtrack2_unit.muted = 1; diff --git a/linux/drivers/media/radio/radio-sf16fmi.c b/linux/drivers/media/radio/radio-sf16fmi.c index c1d3b5c33..90ecea017 100644 --- a/linux/drivers/media/radio/radio-sf16fmi.c +++ b/linux/drivers/media/radio/radio-sf16fmi.c @@ -12,7 +12,7 @@ * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); * No volume control - only mute/unmute - you have to use line volume * control on SB-part of SF16FMI - * + * */ #include /* __setup */ @@ -30,19 +30,19 @@ struct fmi_device { int port; - int curvol; /* 1 or 0 */ - unsigned long curfreq; /* freq in kHz */ - __u32 flags; + int curvol; /* 1 or 0 */ + unsigned long curfreq; /* freq in kHz */ + __u32 flags; }; -static int io = -1; +static int io = -1; static int radio_nr = -1; static struct pnp_dev *dev = NULL; static struct mutex lock; /* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */ /* It is only useful to give freq in intervall of 800 (=0.05Mhz), - * other bits will be truncated, e.g 92.7400016 -> 92.7, but + * other bits will be truncated, e.g 92.7400016 -> 92.7, but * 92.7400017 -> 92.75 */ #define RSF16_ENCODE(x) ((x)/800+214) @@ -52,7 +52,7 @@ static struct mutex lock; static void outbits(int bits, unsigned int data, int port) { while(bits--) { - if(data & 1) { + if(data & 1) { outb(5, port); udelay(6); outb(7, port); @@ -102,7 +102,7 @@ static inline int fmi_getsigstr(struct fmi_device *dev) int res; int myport = dev->port; - + mutex_lock(&lock); val = dev->curvol ? 0x08 : 0x00; /* unmute/mute */ outb(val, myport); @@ -110,7 +110,7 @@ static inline int fmi_getsigstr(struct fmi_device *dev) msleep(143); /* was schedule_timeout(HZ/7) */ res = (int)inb(myport+1); outb(val, myport); - + mutex_unlock(&lock); return (res & 2) ? 0 : 0xFFFF; } @@ -120,7 +120,7 @@ static int fmi_do_ioctl(struct inode *inode, struct file *file, { struct video_device *dev = video_devdata(file); struct fmi_device *fmi=dev->priv; - + switch(cmd) { case VIDIOCGCAP: @@ -175,18 +175,18 @@ static int fmi_do_ioctl(struct inode *inode, struct file *file, return -EINVAL; /*rounding in steps of 800 to match th freq that will be used */ - fmi->curfreq = (*freq/800)*800; + fmi->curfreq = (*freq/800)*800; fmi_setfreq(fmi); return 0; } case VIDIOCGAUDIO: - { + { struct video_audio *v = arg; memset(v,0,sizeof(*v)); v->flags=( (!fmi->curvol)*VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE); strcpy(v->name, "Radio"); v->mode=VIDEO_SOUND_STEREO; - return 0; + return 0; } case VIDIOCSAUDIO: { @@ -194,19 +194,19 @@ static int fmi_do_ioctl(struct inode *inode, struct file *file, if(v->audio) return -EINVAL; fmi->curvol= v->flags&VIDEO_AUDIO_MUTE ? 0 : 1; - fmi->curvol ? + fmi->curvol ? fmi_unmute(fmi->port) : fmi_mute(fmi->port); return 0; } - case VIDIOCGUNIT: + case VIDIOCGUNIT: { - struct video_unit *v = arg; + struct video_unit *v = arg; v->video=VIDEO_NO_UNIT; v->vbi=VIDEO_NO_UNIT; v->radio=dev->minor; v->audio=0; /* How do we find out this??? */ v->teletext=VIDEO_NO_UNIT; - return 0; + return 0; } default: return -ENOIOCTLCMD; @@ -296,14 +296,14 @@ static int __init fmi_init(void) fmi_unit.curfreq = 0; fmi_unit.flags = VIDEO_TUNER_LOW; fmi_radio.priv = &fmi_unit; - + mutex_init(&lock); - + if (video_register_device(&fmi_radio, VFL_TYPE_RADIO, radio_nr) == -1) { release_region(io, 2); return -EINVAL; } - + printk(KERN_INFO "SF16FMx radio card driver at 0x%x\n", io); /* mute card - prevents noisy bootups */ fmi_mute(io); diff --git a/linux/drivers/media/radio/radio-sf16fmr2.c b/linux/drivers/media/radio/radio-sf16fmr2.c index d90d87840..f4d80c0e4 100644 --- a/linux/drivers/media/radio/radio-sf16fmr2.c +++ b/linux/drivers/media/radio/radio-sf16fmr2.c @@ -203,7 +203,7 @@ static int fmr2_setvolume(struct fmr2_device *dev) } static int fmr2_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) + unsigned int cmd, void *arg) { struct video_device *dev = video_devdata(file); struct fmr2_device *fmr2 = dev->priv; @@ -345,7 +345,7 @@ static int fmr2_do_ioctl(struct inode *inode, struct file *file, } static int fmr2_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { return video_usercopy(inode, file, cmd, arg, fmr2_do_ioctl); } diff --git a/linux/drivers/media/radio/radio-terratec.c b/linux/drivers/media/radio/radio-terratec.c index ee8cc71a0..c2406e6e6 100644 --- a/linux/drivers/media/radio/radio-terratec.c +++ b/linux/drivers/media/radio/radio-terratec.c @@ -2,11 +2,11 @@ * (c) 1999 R. Offermanns (rolf@offermanns.de) * based on the aimslab radio driver from M. Kirkwood * many thanks to Michael Becker and Friedhelm Birth (from TerraTec) - * + * * * History: * 1999-05-21 First preview release - * + * * Notes on the hardware: * There are two "main" chips on the card: * - Philips OM5610 (http://www-us.semiconductors.philips.com/acrobat/datasheets/OM5610_2.pdf) @@ -20,7 +20,7 @@ * (as soon i have understand how to get started :) * If you can help me out with that, please contact me!! * - * + * */ #include /* Modules */ @@ -50,7 +50,7 @@ #define WRT_EN 0x10 /*******************************************************************/ -static int io = CONFIG_RADIO_TERRATEC_PORT; +static int io = CONFIG_RADIO_TERRATEC_PORT; static int radio_nr = -1; static spinlock_t lock; @@ -89,15 +89,15 @@ static void tt_mute(struct tt_device *dev) static int tt_setvol(struct tt_device *dev, int vol) { - + // printk(KERN_ERR "setvol called, vol = %d\n", vol); if(vol == dev->curvol) { /* requested volume = current */ if (dev->muted) { /* user is unmuting the card */ dev->muted = 0; cardWriteVol(vol); /* enable card */ - } - + } + return 0; } @@ -108,9 +108,9 @@ static int tt_setvol(struct tt_device *dev, int vol) } dev->muted = 0; - + cardWriteVol(vol); - + dev->curvol = vol; return 0; @@ -122,13 +122,13 @@ static int tt_setvol(struct tt_device *dev, int vol) /* many more or less strange things are going on here, but hey, it works :) */ static int tt_setfreq(struct tt_device *dev, unsigned long freq1) -{ +{ int freq; int i; int p; int temp; long rest; - + unsigned char buffer[25]; /* we have to bit shift 25 registers */ freq = freq1/160; /* convert the freq. to a nice to handle value */ for(i=24;i>-1;i--) @@ -143,9 +143,9 @@ static int tt_setfreq(struct tt_device *dev, unsigned long freq1) { if (rest%temp == rest) buffer[i] = 0; - else + else { - buffer[i] = 1; + buffer[i] = 1; rest = rest-temp; } i--; @@ -154,10 +154,10 @@ static int tt_setfreq(struct tt_device *dev, unsigned long freq1) } spin_lock(&lock); - + for (i=24;i>-1;i--) /* bit shift the values to the radiocard */ { - if (buffer[i]==1) + if (buffer[i]==1) { outb(WRT_EN|DATA, BASEPORT); outb(WRT_EN|DATA|CLK_ON , BASEPORT); @@ -169,11 +169,11 @@ static int tt_setfreq(struct tt_device *dev, unsigned long freq1) outb(WRT_EN|0x00|CLK_ON , BASEPORT); } } - outb(0x00, BASEPORT); - + outb(0x00, BASEPORT); + spin_unlock(&lock); - - return 0; + + return 0; } static int tt_getsigstr(struct tt_device *dev) /* TODO */ @@ -191,7 +191,7 @@ static int tt_do_ioctl(struct inode *inode, struct file *file, { struct video_device *dev = video_devdata(file); struct tt_device *tt=dev->priv; - + switch(cmd) { case VIDIOCGCAP: @@ -207,7 +207,7 @@ static int tt_do_ioctl(struct inode *inode, struct file *file, case VIDIOCGTUNER: { struct video_tuner *v = arg; - if(v->tuner) /* Only 1 tuner */ + if(v->tuner) /* Only 1 tuner */ return -EINVAL; v->rangelow=(87*16000); v->rangehigh=(108*16000); @@ -239,21 +239,21 @@ static int tt_do_ioctl(struct inode *inode, struct file *file, return 0; } case VIDIOCGAUDIO: - { + { struct video_audio *v = arg; memset(v,0, sizeof(*v)); v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME; v->volume=tt->curvol * 6554; v->step=6554; strcpy(v->name, "Radio"); - return 0; + return 0; } case VIDIOCSAUDIO: { struct video_audio *v = arg; - if(v->audio) + if(v->audio) return -EINVAL; - if(v->flags&VIDEO_AUDIO_MUTE) + if(v->flags&VIDEO_AUDIO_MUTE) tt_mute(tt); else tt_setvol(tt,v->volume/6554); @@ -297,25 +297,25 @@ static int __init terratec_init(void) printk(KERN_ERR "You must set an I/O address with io=0x???\n"); return -EINVAL; } - if (!request_region(io, 2, "terratec")) + if (!request_region(io, 2, "terratec")) { printk(KERN_ERR "TerraTec: port 0x%x already in use\n", io); return -EBUSY; } terratec_radio.priv=&terratec_unit; - + spin_lock_init(&lock); - + if(video_register_device(&terratec_radio, VFL_TYPE_RADIO, radio_nr)==-1) { release_region(io,2); return -EINVAL; } - + printk(KERN_INFO "TERRATEC ActivRadio Standalone card driver.\n"); - /* mute card - prevents noisy bootups */ + /* mute card - prevents noisy bootups */ /* this ensures that the volume is all the way down */ cardWriteVol(0); @@ -335,7 +335,7 @@ static void __exit terratec_cleanup_module(void) { video_unregister_device(&terratec_radio); release_region(io,2); - printk(KERN_INFO "TERRATEC ActivRadio Standalone card driver unloaded.\n"); + printk(KERN_INFO "TERRATEC ActivRadio Standalone card driver unloaded.\n"); } module_init(terratec_init); diff --git a/linux/drivers/media/radio/radio-trust.c b/linux/drivers/media/radio/radio-trust.c index 448725ca9..eedadaf66 100644 --- a/linux/drivers/media/radio/radio-trust.c +++ b/linux/drivers/media/radio/radio-trust.c @@ -1,14 +1,14 @@ -/* radio-trust.c - Trust FM Radio card driver for Linux 2.2 +/* radio-trust.c - Trust FM Radio card driver for Linux 2.2 * by Eric Lammerts * * Based on radio-aztech.c. Original notes: * - * Adapted to support the Video for Linux API by + * Adapted to support the Video for Linux API by * Russell Kroll . Based on original tuner code by: * * Quay Ly * Donald Song - * Jason Lewis (jlewis@twilight.vtc.vsc.edu) + * Jason Lewis (jlewis@twilight.vtc.vsc.edu) * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) * William McGrath (wmcgrath@twilight.vtc.vsc.edu) * @@ -31,7 +31,7 @@ #define CONFIG_RADIO_TRUST_PORT -1 #endif -static int io = CONFIG_RADIO_TRUST_PORT; +static int io = CONFIG_RADIO_TRUST_PORT; static int radio_nr = -1; static int ioval = 0xf; static __u16 curvol; @@ -136,7 +136,7 @@ static void tr_setmute(int mute) static int tr_getsigstr(void) { int i, v; - + for(i = 0, v = 0; i < 100; i++) v |= inb(io); return (v & 1)? 0 : 0xffff; } @@ -176,7 +176,7 @@ static int tr_do_ioctl(struct inode *inode, struct file *file, { struct video_tuner *v = arg; - if(v->tuner) /* Only 1 tuner */ + if(v->tuner) /* Only 1 tuner */ return -EINVAL; v->rangelow = 87500 * 16; @@ -212,28 +212,28 @@ static int tr_do_ioctl(struct inode *inode, struct file *file, return 0; } case VIDIOCGAUDIO: - { + { struct video_audio *v = arg; memset(v,0, sizeof(*v)); v->flags = VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME | - VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE; + VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE; v->mode = curstereo? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO; v->volume = curvol * 2048; v->step = 2048; v->bass = curbass * 4370; v->treble = curtreble * 4370; - + strcpy(v->name, "Trust FM Radio"); - return 0; + return 0; } case VIDIOCSAUDIO: { struct video_audio *v = arg; - if(v->audio) + if(v->audio) return -EINVAL; - tr_setvol(v->volume); + tr_setvol(v->volume); tr_setbass(v->bass); tr_settreble(v->treble); tr_setstereo(v->mode & VIDEO_SOUND_STEREO); @@ -293,7 +293,7 @@ static int __init trust_init(void) write_i2c(2, TDA7318_ADDR, 0xe0); /* speaker att. RR = 0 dB */ write_i2c(2, TDA7318_ADDR, 0x40); /* stereo 1 input, gain = 18.75 dB */ - tr_setvol(0x8000); + tr_setvol(0x8000); tr_setbass(0x8000); tr_settreble(0x8000); tr_setstereo(1); diff --git a/linux/drivers/media/radio/radio-typhoon.c b/linux/drivers/media/radio/radio-typhoon.c index 8e87d0b42..541072e7c 100644 --- a/linux/drivers/media/radio/radio-typhoon.c +++ b/linux/drivers/media/radio/radio-typhoon.c @@ -362,7 +362,7 @@ static int __init typhoon_init(void) #ifdef CONFIG_RADIO_TYPHOON_PROC_FS if (!create_proc_info_entry("driver/radio-typhoon", 0, NULL, - typhoon_get_info)) + typhoon_get_info)) printk(KERN_ERR "radio-typhoon: registering /proc/driver/radio-typhoon failed\n"); #endif diff --git a/linux/drivers/media/radio/radio-zoltrix.c b/linux/drivers/media/radio/radio-zoltrix.c index 7c7760e26..d428514c7 100644 --- a/linux/drivers/media/radio/radio-zoltrix.c +++ b/linux/drivers/media/radio/radio-zoltrix.c @@ -1,7 +1,7 @@ /* zoltrix radio plus driver for Linux radio support * (c) 1998 C. van Schaik * - * BUGS + * BUGS * Due to the inconsistency in reading from the signal flags * it is difficult to get an accurate tuned signal. * @@ -14,7 +14,7 @@ * * 1999-05-06 - (C. van Schaik) * - Make signal strength and stereo scans - * kinder to cpu while in delay + * kinder to cpu while in delay * 1999-01-05 - (C. van Schaik) * - Changed tuning to 1/160Mhz accuracy * - Added stereo support @@ -106,7 +106,7 @@ static int zol_setfreq(struct zol_device *dev, unsigned long freq) i = 45; mutex_lock(&dev->lock); - + outb(0, io); outb(0, io); inb(io + 3); /* Zoltrix needs to be read to confirm */ @@ -140,8 +140,8 @@ static int zol_setfreq(struct zol_device *dev, unsigned long freq) udelay(1000); inb(io+2); - udelay(1000); - + udelay(1000); + if (dev->muted) { outb(0, io); @@ -149,12 +149,12 @@ static int zol_setfreq(struct zol_device *dev, unsigned long freq) inb(io + 3); udelay(1000); } - + mutex_unlock(&dev->lock); - + if(!dev->muted) { - zol_setvol(dev, dev->curvol); + zol_setvol(dev, dev->curvol); } return 0; } @@ -175,14 +175,14 @@ static int zol_getsigstr(struct zol_device *dev) b = inb(io); mutex_unlock(&dev->lock); - + if (a != b) return (0); - if ((a == 0xcf) || (a == 0xdf) /* I found this out by playing */ + if ((a == 0xcf) || (a == 0xdf) /* I found this out by playing */ || (a == 0xef)) /* with a binary scanner on the card io */ return (1); - return (0); + return (0); } static int zol_is_stereo (struct zol_device *dev) @@ -190,7 +190,7 @@ static int zol_is_stereo (struct zol_device *dev) int x1, x2; mutex_lock(&dev->lock); - + outb(0x00, io); outb(dev->curvol, io); msleep(20); @@ -200,7 +200,7 @@ static int zol_is_stereo (struct zol_device *dev) x2 = inb(io); mutex_unlock(&dev->lock); - + if ((x1 == x2) && (x1 == 0xcf)) return 1; return 0; @@ -227,7 +227,7 @@ static int zol_do_ioctl(struct inode *inode, struct file *file, case VIDIOCGTUNER: { struct video_tuner *v = arg; - if (v->tuner) + if (v->tuner) return -EINVAL; strcpy(v->name, "FM"); v->rangelow = (int) (88.0 * 16000); @@ -352,7 +352,7 @@ static int __init zoltrix_init(void) printk(KERN_INFO "Zoltrix Radio Plus card driver.\n"); mutex_init(&zoltrix_unit.lock); - + /* mute card - prevents noisy bootups */ /* this ensures that the volume is all the way down */ diff --git a/linux/drivers/media/video/cpia.c b/linux/drivers/media/video/cpia.c index 85d84e89d..b8d491311 100644 --- a/linux/drivers/media/video/cpia.c +++ b/linux/drivers/media/video/cpia.c @@ -67,10 +67,10 @@ MODULE_SUPPORTED_DEVICE("video"); static unsigned short colorspace_conv; module_param(colorspace_conv, ushort, 0444); MODULE_PARM_DESC(colorspace_conv, - " Colorspace conversion:" - "\n 0 = disable, 1 = enable" - "\n Default value is 0" - ); + " Colorspace conversion:" + "\n 0 = disable, 1 = enable" + "\n Default value is 0" + ); #define ABOUT "V4L-Driver for Vision CPiA based cameras" -- cgit v1.2.3 From 47d9ea56db3d35c645477ed1540e852886230d74 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Apr 2006 15:43:41 -0300 Subject: Create V4L1 config options From: Mauro Carvalho Chehab V4L1 API is depreciated and should be removed soon from kernel. This patch adds two new options, one to disable V4L1 drivers, and another to disable V4L1 compat module. This way, it would be easy to check what still depends on V4L1 stuff, allowing also to test if app works fine with V4L2 only support. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/Kconfig | 45 ++++++++++++--- linux/drivers/media/common/Kconfig | 1 + linux/drivers/media/dvb/ttpci/Kconfig | 12 ++-- linux/drivers/media/radio/Kconfig | 30 +++++----- linux/drivers/media/video/Kconfig | 67 +++++++++++----------- linux/drivers/media/video/Makefile | 3 +- linux/drivers/media/video/bt8xx/Kconfig | 2 +- linux/drivers/media/video/cx88/cx88-video.c | 2 +- linux/drivers/media/video/em28xx/Kconfig | 2 +- linux/drivers/media/video/pvrusb2/Kconfig | 2 +- linux/drivers/media/video/pvrusb2/pvrusb2-audio.c | 2 +- linux/drivers/media/video/pvrusb2/pvrusb2-demod.c | 2 +- .../media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c | 2 +- linux/drivers/media/video/pvrusb2/pvrusb2-main.c | 2 +- linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c | 2 +- linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 2 +- .../media/video/pvrusb2/pvrusb2-video-v4l.c | 2 +- linux/drivers/media/video/saa7134/saa7134-video.c | 2 +- linux/drivers/media/video/vivi.c | 3 +- 19 files changed, 110 insertions(+), 75 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/Kconfig b/linux/drivers/media/Kconfig index fffc711c2..344d83aae 100644 --- a/linux/drivers/media/Kconfig +++ b/linux/drivers/media/Kconfig @@ -8,22 +8,54 @@ config VIDEO_DEV tristate "Video For Linux" ---help--- Support for audio/video capture and overlay devices and FM radio - cards. The exact capabilities of each device vary. User tools for - this are available from - . + cards. The exact capabilities of each device vary. This kernel includes support for the new Video for Linux Two API, (V4L2) as well as the original system. Drivers and applications need to be rewritten to use V4L2, but drivers for popular cards and applications for most video capture functions already exist. - Documentation for the original API is included in the file - . Documentation for V4L2 is - available on the web at . + Additional info and docs are available on the web at + + + Documentation for V4L2 is also available on the web at + . To compile this driver as a module, choose M here: the module will be called videodev. +config VIDEO_V4L1 + boolean "Enable Video For Linux API 1 (DEPRECATED)" + depends on VIDEO_DEV + select VIDEO_V4L1_COMPAT + default y + ---help--- + Enables a compatibility API used by most V4L2 devices to allow + its usage with legacy applications that supports only V4L1 api. + + If you are unsure as to whether this is required, answer Y. + +config VIDEO_V4L1_COMPAT + boolean "Enable Video For Linux API 1 compatible Layer" + depends on VIDEO_DEV + default y + ---help--- + This api were developed to be used at Kernel 2.2 and 2.4, but + lacks support for several video standards. There are several + drivers at kernel that still depends on it. + + Documentation for the original API is included in the file + . + + User tools for this are available from + . + + If you are unsure as to whether this is required, answer Y. + +config VIDEO_V4L2 + tristate + default y + source "drivers/media/video/Kconfig" source "drivers/media/radio/Kconfig" @@ -65,4 +97,3 @@ config USB_DABUSB module will be called dabusb. endmenu - diff --git a/linux/drivers/media/common/Kconfig b/linux/drivers/media/common/Kconfig index 6a901a026..9c45b983e 100644 --- a/linux/drivers/media/common/Kconfig +++ b/linux/drivers/media/common/Kconfig @@ -4,6 +4,7 @@ config VIDEO_SAA7146 config VIDEO_SAA7146_VV tristate + select VIDEO_V4L2 select VIDEO_BUF select VIDEO_VIDEOBUF select VIDEO_SAA7146 diff --git a/linux/drivers/media/dvb/ttpci/Kconfig b/linux/drivers/media/dvb/ttpci/Kconfig index 5b2aadb83..c26e23291 100644 --- a/linux/drivers/media/dvb/ttpci/Kconfig +++ b/linux/drivers/media/dvb/ttpci/Kconfig @@ -1,8 +1,7 @@ config DVB_AV7110 tristate "AV7110 cards" - depends on DVB_CORE && PCI + depends on DVB_CORE && PCI && VIDEO_V4L1 select FW_LOADER - select VIDEO_DEV select VIDEO_SAA7146_VV select DVB_VES1820 select DVB_VES1X93 @@ -59,7 +58,7 @@ config DVB_AV7110_OSD config DVB_BUDGET tristate "Budget cards" - depends on DVB_CORE && PCI + depends on DVB_CORE && PCI && VIDEO_V4L1 select VIDEO_SAA7146 select DVB_STV0299 select DVB_VES1X93 @@ -80,7 +79,7 @@ config DVB_BUDGET config DVB_BUDGET_CI tristate "Budget cards with onboard CI connector" - depends on DVB_CORE && PCI + depends on DVB_CORE && PCI && VIDEO_V4L1 select VIDEO_SAA7146 select DVB_STV0297 select DVB_STV0299 @@ -100,8 +99,7 @@ config DVB_BUDGET_CI config DVB_BUDGET_AV tristate "Budget cards with analog video inputs" - depends on DVB_CORE && PCI - select VIDEO_DEV + depends on DVB_CORE && PCI && VIDEO_V4L1 select VIDEO_SAA7146_VV select DVB_STV0299 select DVB_TDA1004X @@ -119,7 +117,7 @@ config DVB_BUDGET_AV config DVB_BUDGET_PATCH tristate "AV7110 cards with Budget Patch" - depends on DVB_CORE && DVB_BUDGET + depends on DVB_CORE && DVB_BUDGET && VIDEO_V4L1 select DVB_AV7110 select DVB_STV0299 select DVB_VES1X93 diff --git a/linux/drivers/media/radio/Kconfig b/linux/drivers/media/radio/Kconfig index c65bbc5fd..de3128a31 100644 --- a/linux/drivers/media/radio/Kconfig +++ b/linux/drivers/media/radio/Kconfig @@ -7,7 +7,7 @@ menu "Radio Adapters" config RADIO_CADET tristate "ADS Cadet AM/FM Tuner" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these AM/FM radio cards, and then fill in the port address below. @@ -25,7 +25,7 @@ config RADIO_CADET config RADIO_RTRACK tristate "AIMSlab RadioTrack (aka RadioReveal) support" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address below. @@ -59,7 +59,7 @@ config RADIO_RTRACK_PORT config RADIO_RTRACK2 tristate "AIMSlab RadioTrack II support" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have this FM radio card, and then fill in the port address below. @@ -82,7 +82,7 @@ config RADIO_RTRACK2_PORT config RADIO_AZTECH tristate "Aztech/Packard Bell Radio" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address below. @@ -106,7 +106,7 @@ config RADIO_AZTECH_PORT config RADIO_GEMTEK tristate "GemTek Radio Card support" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have this FM radio card, and then fill in the port address below. @@ -131,7 +131,7 @@ config RADIO_GEMTEK_PORT config RADIO_GEMTEK_PCI tristate "GemTek PCI Radio Card support" - depends on VIDEO_DEV && PCI + depends on VIDEO_V4L1 && PCI ---help--- Choose Y here if you have this PCI FM radio card. @@ -145,7 +145,7 @@ config RADIO_GEMTEK_PCI config RADIO_MAXIRADIO tristate "Guillemot MAXI Radio FM 2000 radio" - depends on VIDEO_DEV && PCI + depends on VIDEO_V4L1 && PCI ---help--- Choose Y here if you have this radio card. This card may also be found as Gemtek PCI FM. @@ -160,7 +160,7 @@ config RADIO_MAXIRADIO config RADIO_MAESTRO tristate "Maestro on board radio" - depends on VIDEO_DEV + depends on VIDEO_V4L1 ---help--- Say Y here to directly support the on-board radio tuner on the Maestro 2 or 2E sound card. @@ -175,7 +175,7 @@ config RADIO_MAESTRO config RADIO_MIROPCM20 tristate "miroSOUND PCM20 radio" - depends on ISA && VIDEO_DEV && SOUND_ACI_MIXER + depends on ISA && VIDEO_V4L1 && SOUND_ACI_MIXER ---help--- Choose Y here if you have this FM radio card. You also need to say Y to "ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20 radio)" (in "Sound") @@ -208,7 +208,7 @@ config RADIO_MIROPCM20_RDS config RADIO_SF16FMI tristate "SF16FMI Radio" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these FM radio cards. If you compile the driver into the kernel and your card is not PnP one, you @@ -225,7 +225,7 @@ config RADIO_SF16FMI config RADIO_SF16FMR2 tristate "SF16FMR2 Radio" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these FM radio cards. @@ -239,7 +239,7 @@ config RADIO_SF16FMR2 config RADIO_TERRATEC tristate "TerraTec ActiveRadio ISA Standalone" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have this FM radio card, and then fill in the port address below. (TODO) @@ -268,7 +268,7 @@ config RADIO_TERRATEC_PORT config RADIO_TRUST tristate "Trust FM radio card" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 help This is a driver for the Trust FM radio cards. Say Y if you have such a card and want to use it under Linux. @@ -286,7 +286,7 @@ config RADIO_TRUST_PORT config RADIO_TYPHOON tristate "Typhoon Radio (a.k.a. EcoRadio)" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address and the frequency used for muting below. @@ -330,7 +330,7 @@ config RADIO_TYPHOON_MUTEFREQ config RADIO_ZOLTRIX tristate "Zoltrix Radio" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address below. diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index 98f7201cd..9cc7d07a0 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -2,10 +2,10 @@ # Multimedia Video device configuration # -menu "Video For Linux" +menu "Video Capture Adapters" depends on VIDEO_DEV -comment "Video Adapters" +comment "Video Capture Adapters" config VIDEO_ADV_DEBUG bool "Enable advanced debug functionality" @@ -20,7 +20,7 @@ source "drivers/media/video/bt8xx/Kconfig" config VIDEO_SAA6588 tristate "SAA6588 Radio Chip RDS decoder support on BT848 cards" - depends on VIDEO_DEV && I2C && VIDEO_BT848 + depends on I2C && VIDEO_BT848 help Support for Radio Data System (RDS) decoder. This allows seeing @@ -32,7 +32,7 @@ config VIDEO_SAA6588 config VIDEO_PMS tristate "Mediavision Pro Movie Studio Video For Linux" - depends on VIDEO_DEV && ISA + depends on ISA && VIDEO_V4L1 help Say Y if you have such a thing. @@ -41,7 +41,7 @@ config VIDEO_PMS config VIDEO_PLANB tristate "PlanB Video-In on PowerMac" - depends on PPC_PMAC && VIDEO_DEV && BROKEN + depends on PPC_PMAC && VIDEO_V4L1 && BROKEN help PlanB is the V4L driver for the PowerMac 7x00/8x00 series video input hardware. If you want to experiment with this, say Y. @@ -52,7 +52,7 @@ config VIDEO_PLANB config VIDEO_BWQCAM tristate "Quickcam BW Video For Linux" - depends on VIDEO_DEV && PARPORT + depends on PARPORT && VIDEO_V4L1 help Say Y have if you the black and white version of the QuickCam camera. See the next option for the color version. @@ -62,7 +62,7 @@ config VIDEO_BWQCAM config VIDEO_CQCAM tristate "QuickCam Colour Video For Linux (EXPERIMENTAL)" - depends on EXPERIMENTAL && VIDEO_DEV && PARPORT + depends on EXPERIMENTAL && PARPORT && VIDEO_V4L1 help This is the video4linux driver for the colour version of the Connectix QuickCam. If you have one of these cameras, say Y here, @@ -73,7 +73,7 @@ config VIDEO_CQCAM config VIDEO_W9966 tristate "W9966CF Webcam (FlyCam Supra and others) Video For Linux" - depends on PARPORT_1284 && VIDEO_DEV && PARPORT + depends on PARPORT_1284 && PARPORT && VIDEO_V4L1 help Video4linux driver for Winbond's w9966 based Webcams. Currently tested with the LifeView FlyCam Supra. @@ -86,7 +86,7 @@ config VIDEO_W9966 config VIDEO_CPIA tristate "CPiA Video For Linux" - depends on VIDEO_DEV + depends on VIDEO_V4L1 ---help--- This is the video4linux driver for cameras based on Vision's CPiA (Colour Processor Interface ASIC), such as the Creative Labs Video @@ -123,7 +123,7 @@ source "drivers/media/video/cpia2/Kconfig" config VIDEO_SAA5246A tristate "SAA5246A, SAA5281 Teletext processor" - depends on VIDEO_DEV && I2C + depends on I2C && VIDEO_V4L1 help Support for I2C bus based teletext using the SAA5246A or SAA5281 chip. Useful only if you live in Europe. @@ -150,7 +150,7 @@ config TUNER_3036 config VIDEO_VINO tristate "SGI Vino Video For Linux (EXPERIMENTAL)" - depends on VIDEO_DEV && I2C && SGI_IP22 && EXPERIMENTAL + depends on I2C && SGI_IP22 && EXPERIMENTAL && VIDEO_V4L1 select I2C_ALGO_SGI help Say Y here to build in support for the Vino video input system found @@ -158,7 +158,7 @@ config VIDEO_VINO config VIDEO_STRADIS tristate "Stradis 4:2:2 MPEG-2 video driver (EXPERIMENTAL)" - depends on EXPERIMENTAL && VIDEO_DEV && PCI + depends on EXPERIMENTAL && PCI && VIDEO_V4L1 help Say Y here to enable support for the Stradis 4:2:2 MPEG-2 video driver for PCI. There is a product page at @@ -166,7 +166,7 @@ config VIDEO_STRADIS config VIDEO_ZORAN tristate "Zoran ZR36057/36067 Video For Linux" - depends on VIDEO_DEV && PCI && I2C_ALGOBIT + depends on PCI && I2C_ALGOBIT && VIDEO_V4L1 help Say Y for support for MJPEG capture cards based on the Zoran 36057/36067 PCI controller chipset. This includes the Iomega @@ -214,7 +214,7 @@ config VIDEO_ZORAN_LML33R10 config VIDEO_ZR36120 tristate "Zoran ZR36120/36125 Video For Linux" - depends on VIDEO_DEV && PCI && I2C && BROKEN + depends on PCI && I2C && VIDEO_V4L1 && BROKEN help Support for ZR36120/ZR36125 based frame grabber/overlay boards. This includes the Victor II, WaveWatcher, Video Wonder, Maxi-TV, @@ -226,7 +226,7 @@ config VIDEO_ZR36120 config VIDEO_MEYE tristate "Sony Vaio Picturebook Motion Eye Video For Linux" - depends on VIDEO_DEV && PCI && SONYPI + depends on PCI && SONYPI && VIDEO_V4L1 ---help--- This is the video4linux driver for the Motion Eye camera found in the Vaio Picturebook laptops. Please read the material in @@ -242,7 +242,7 @@ source "drivers/media/video/saa7134/Kconfig" config VIDEO_MXB tristate "Siemens-Nixdorf 'Multimedia eXtension Board'" - depends on VIDEO_DEV && PCI + depends on PCI && VIDEO_V4L1 select VIDEO_SAA7146_VV select VIDEO_TUNER ---help--- @@ -254,8 +254,9 @@ config VIDEO_MXB config VIDEO_DPC tristate "Philips-Semiconductors 'dpc7146 demonstration board'" - depends on VIDEO_DEV && PCI + depends on PCI && VIDEO_V4L1 select VIDEO_SAA7146_VV + select VIDEO_V4L2 ---help--- This is a video4linux driver for the 'dpc7146 demonstration board' by Philips-Semiconductors. It's the reference design @@ -268,8 +269,9 @@ config VIDEO_DPC config VIDEO_HEXIUM_ORION tristate "Hexium HV-PCI6 and Orion frame grabber" - depends on VIDEO_DEV && PCI + depends on PCI && VIDEO_V4L1 select VIDEO_SAA7146_VV + select VIDEO_V4L2 ---help--- This is a video4linux driver for the Hexium HV-PCI6 and Orion frame grabber cards by Hexium. @@ -279,8 +281,9 @@ config VIDEO_HEXIUM_ORION config VIDEO_HEXIUM_GEMINI tristate "Hexium Gemini frame grabber" - depends on VIDEO_DEV && PCI + depends on PCI && VIDEO_V4L1 select VIDEO_SAA7146_VV + select VIDEO_V4L2 ---help--- This is a video4linux driver for the Hexium Gemini frame grabber card by Hexium. Please note that the Gemini Dual @@ -293,7 +296,7 @@ source "drivers/media/video/cx88/Kconfig" config VIDEO_OVCAMCHIP tristate "OmniVision Camera Chip support" - depends on VIDEO_DEV && I2C + depends on I2C && VIDEO_V4L1 ---help--- Support for the OmniVision OV6xxx and OV7xxx series of camera chips. This driver is intended to be used with the ov511 and w9968cf USB @@ -304,7 +307,7 @@ config VIDEO_OVCAMCHIP config VIDEO_M32R_AR tristate "AR devices" - depends on M32R + depends on M32R && VIDEO_V4L1 ---help--- This is a video4linux driver for the Renesas AR (Artificial Retina) camera module. @@ -365,17 +368,17 @@ config VIDEO_WM8739 source "drivers/media/video/cx25840/Kconfig" config VIDEO_SAA711X - tristate "Philips SAA7113/4/5 video decoders" - depends on VIDEO_DEV && I2C && EXPERIMENTAL + tristate "Philips SAA7113/4/5 video decoders (OBSOLETED)" + depends on VIDEO_V4L1 && I2C && EXPERIMENTAL ---help--- - Support for the Philips SAA7113/4/5 video decoders. + Old support for the Philips SAA7113/4 video decoders. To compile this driver as a module, choose M here: the module will be called saa7115. config VIDEO_SAA7127 tristate "Philips SAA7127/9 digital video encoders" - depends on VIDEO_DEV && I2C && EXPERIMENTAL + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL ---help--- Support for the Philips SAA7127/9 digital video encoders. @@ -384,7 +387,7 @@ config VIDEO_SAA7127 config VIDEO_UPD64031A tristate "NEC Electronics uPD64031A Ghost Reduction" - depends on VIDEO_DEV && I2C && EXPERIMENTAL + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL ---help--- Support for the NEC Electronics uPD64031A Ghost Reduction video chip. It is most often found in NTSC TV cards made for @@ -396,7 +399,7 @@ config VIDEO_UPD64031A config VIDEO_UPD64083 tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation" - depends on VIDEO_DEV && I2C && EXPERIMENTAL + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL ---help--- Support for the NEC Electronics uPD64083 3-Dimensional Y/C separation video chip. It is used to improve the quality of @@ -420,7 +423,7 @@ source "drivers/media/video/em28xx/Kconfig" config USB_DSBR tristate "D-Link USB FM radio support (EXPERIMENTAL)" - depends on USB && VIDEO_DEV && EXPERIMENTAL + depends on USB && VIDEO_V4L1 && EXPERIMENTAL ---help--- Say Y here if you want to connect this type of radio to your computer's USB port. Note that the audio is not digital, and @@ -436,7 +439,7 @@ source "drivers/media/video/et61x251/Kconfig" config USB_OV511 tristate "USB OV511 Camera support" - depends on USB && VIDEO_DEV + depends on USB && VIDEO_V4L1 ---help--- Say Y here if you want to connect this type of camera to your computer's USB port. See @@ -447,7 +450,7 @@ config USB_OV511 config USB_SE401 tristate "USB SE401 Camera support" - depends on USB && VIDEO_DEV + depends on USB && VIDEO_V4L1 ---help--- Say Y here if you want to connect this type of camera to your computer's USB port. See @@ -460,7 +463,7 @@ source "drivers/media/video/sn9c102/Kconfig" config USB_STV680 tristate "USB STV680 (Pencam) Camera support" - depends on USB && VIDEO_DEV + depends on USB && VIDEO_V4L1 ---help--- Say Y here if you want to connect this type of camera to your computer's USB port. This includes the Pencam line of cameras. @@ -472,7 +475,7 @@ config USB_STV680 config USB_W9968CF tristate "USB W996[87]CF JPEG Dual Mode Camera support" - depends on USB && VIDEO_DEV && I2C + depends on USB && VIDEO_V4L1 && I2C select VIDEO_OVCAMCHIP ---help--- Say Y here if you want support for cameras based on OV681 or diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index bfd5bce8e..5284ad126 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -10,7 +10,8 @@ tuner-objs := tuner-core.o tuner-types.o tuner-simple.o \ msp3400-objs := msp3400-driver.o msp3400-kthreads.o -obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o v4l1-compat.o compat_ioctl32.o +obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o compat_ioctl32.o +obj-$(CONFIG_VIDEO_V4L1_COMPAT) += v4l1-compat.o obj-$(CONFIG_VIDEO_BT848) += bt8xx/ obj-$(CONFIG_VIDEO_BT848) += tvaudio.o tda7432.o tda9875.o ir-kbd-i2c.o diff --git a/linux/drivers/media/video/bt8xx/Kconfig b/linux/drivers/media/video/bt8xx/Kconfig index 085477c12..153f6a4a9 100644 --- a/linux/drivers/media/video/bt8xx/Kconfig +++ b/linux/drivers/media/video/bt8xx/Kconfig @@ -1,6 +1,6 @@ config VIDEO_BT848 tristate "BT848 Video For Linux" - depends on VIDEO_DEV && PCI && I2C + depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L2 select I2C_ALGOBIT select FW_LOADER select VIDEO_BTCX diff --git a/linux/drivers/media/video/cx88/cx88-video.c b/linux/drivers/media/video/cx88/cx88-video.c index 1bcd20d23..095b4b1cd 100644 --- a/linux/drivers/media/video/cx88/cx88-video.c +++ b/linux/drivers/media/video/cx88/cx88-video.c @@ -39,7 +39,7 @@ #include "cx88.h" #include -#if 1 +#ifdef CONFIG_VIDEO_V4L1_COMPAT /* Include V4L1 specific functions. Should be removed soon */ #include #endif diff --git a/linux/drivers/media/video/em28xx/Kconfig b/linux/drivers/media/video/em28xx/Kconfig index 5a793ae7c..dfb15bfb8 100644 --- a/linux/drivers/media/video/em28xx/Kconfig +++ b/linux/drivers/media/video/em28xx/Kconfig @@ -1,6 +1,6 @@ config VIDEO_EM28XX tristate "Empia EM2800/2820/2840 USB video capture support" - depends on VIDEO_DEV && USB && I2C + depends on VIDEO_V4L1 && USB && I2C select VIDEO_BUF select VIDEO_TUNER select VIDEO_TVEEPROM diff --git a/linux/drivers/media/video/pvrusb2/Kconfig b/linux/drivers/media/video/pvrusb2/Kconfig index 89031a2f5..207efa6c0 100644 --- a/linux/drivers/media/video/pvrusb2/Kconfig +++ b/linux/drivers/media/video/pvrusb2/Kconfig @@ -1,6 +1,6 @@ config VIDEO_PVRUSB2 tristate "Hauppauge WinTV-PVR USB2 support" - depends on VIDEO_DEV && USB && I2C && EXPERIMENTAL + depends on VIDEO_V4L1 && USB && I2C && EXPERIMENTAL select FW_LOADER select VIDEO_TUNER select VIDEO_TVEEPROM diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c index 7f29a8821..7d5997bdc 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -24,7 +24,7 @@ #include "pvrusb2-audio.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" -#include +#include #include #include diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-demod.c b/linux/drivers/media/video/pvrusb2/pvrusb2-demod.c index dca787dfa..f082ecee2 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-demod.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-demod.c @@ -26,7 +26,7 @@ #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" #include "compat.h" -#include +#include #include #include diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c index b3d2ce926..fb83cd2c9 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c @@ -23,7 +23,7 @@ #include "pvrusb2-i2c-cmd-v4l2.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" -#include +#include #include diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c index 40fb6ae41..988f32ad4 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include "pvrusb2-hdw.h" #include "pvrusb2-context.h" diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c b/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c index f829c0acc..8bf7cc142 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c @@ -26,7 +26,7 @@ #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" #include "compat.h" -#include +#include #include #include diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 56d15c10d..51491e67c 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -30,7 +30,7 @@ #include "pvrusb2-debug.h" #include "pvrusb2-v4l2.h" #include "pvrusb2-ioread.h" -#include +#include #include diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c index c55f3c653..dde031bb8 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c @@ -34,7 +34,7 @@ #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" -#include +#include #include #include #include diff --git a/linux/drivers/media/video/saa7134/saa7134-video.c b/linux/drivers/media/video/saa7134/saa7134-video.c index 3bd76fe3a..152e8e8ab 100644 --- a/linux/drivers/media/video/saa7134/saa7134-video.c +++ b/linux/drivers/media/video/saa7134/saa7134-video.c @@ -33,7 +33,7 @@ #include "saa7134.h" #include -#if 1 +#if CONFIG_VIDEO_V4L1_COMPAT /* Include V4L1 specific functions. Should be removed soon */ #include #endif diff --git a/linux/drivers/media/video/vivi.c b/linux/drivers/media/video/vivi.c index 70721671c..843fdecbc 100644 --- a/linux/drivers/media/video/vivi.c +++ b/linux/drivers/media/video/vivi.c @@ -26,7 +26,8 @@ #include #include #include -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15) +#ifdef CONFIG_VIDEO_V4L1_COMPAT +/* Include V4L1 specific functions. Should be removed soon */ #include #endif #include -- cgit v1.2.3 From 05eaeb82005a71ead3bf3b0a207f55f9f178c131 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Apr 2006 15:51:18 -0300 Subject: Add VIVI Kconfig stuff From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/Kconfig | 11 +++++++++++ linux/drivers/media/video/Makefile | 2 ++ 2 files changed, 13 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index 9cc7d07a0..e4dd4181d 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -16,6 +16,17 @@ config VIDEO_ADV_DEBUG V4L devices. In doubt, say N. +config VIDEO_VIVI + tristate "Virtual Video Driver" + depends on VIDEO_V4L2 + default n + ---help--- + Enables a virtual video driver. This device shows a color bar + and a timestamp, as a real device would generate by using V4L2 + api. + Say Y here if you want to test video apps or debug V4L devices. + In doubt, say N. + source "drivers/media/video/bt8xx/Kconfig" config VIDEO_SAA6588 diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index 5284ad126..d61bc458f 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -86,4 +86,6 @@ obj-$(CONFIG_USB_IBMCAM) += usbvideo/ obj-$(CONFIG_USB_KONICAWC) += usbvideo/ obj-$(CONFIG_USB_VICAM) += usbvideo/ +obj-$(CONFIG_VIDEO_VIVI) += vivi.o + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -- cgit v1.2.3 From 94bf25b038d4bd8b5ef424f6447dfcf1f4304f95 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Apr 2006 15:57:54 -0300 Subject: Added usbvideo module From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/usbvideo/Kconfig | 38 + linux/drivers/media/video/usbvideo/Makefile | 4 + linux/drivers/media/video/usbvideo/ibmcam.c | 3932 +++++++++++++++++++++++++ linux/drivers/media/video/usbvideo/konicawc.c | 978 ++++++ linux/drivers/media/video/usbvideo/ultracam.c | 679 +++++ linux/drivers/media/video/usbvideo/usbvideo.c | 2190 ++++++++++++++ linux/drivers/media/video/usbvideo/usbvideo.h | 394 +++ linux/drivers/media/video/usbvideo/vicam.c | 1411 +++++++++ 8 files changed, 9626 insertions(+) create mode 100644 linux/drivers/media/video/usbvideo/Kconfig create mode 100644 linux/drivers/media/video/usbvideo/Makefile create mode 100644 linux/drivers/media/video/usbvideo/ibmcam.c create mode 100644 linux/drivers/media/video/usbvideo/konicawc.c create mode 100644 linux/drivers/media/video/usbvideo/ultracam.c create mode 100644 linux/drivers/media/video/usbvideo/usbvideo.c create mode 100644 linux/drivers/media/video/usbvideo/usbvideo.h create mode 100644 linux/drivers/media/video/usbvideo/vicam.c (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/usbvideo/Kconfig b/linux/drivers/media/video/usbvideo/Kconfig new file mode 100644 index 000000000..39269a2c5 --- /dev/null +++ b/linux/drivers/media/video/usbvideo/Kconfig @@ -0,0 +1,38 @@ +config VIDEO_USBVIDEO + tristate + +config USB_VICAM + tristate "USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)" + depends on USB && VIDEO_V4L1 && EXPERIMENTAL + select VIDEO_USBVIDEO + ---help--- + Say Y here if you have 3com homeconnect camera (vicam). + + To compile this driver as a module, choose M here: the + module will be called vicam. + +config USB_IBMCAM + tristate "USB IBM (Xirlink) C-it Camera support" + depends on USB && VIDEO_V4L1 + select VIDEO_USBVIDEO + ---help--- + Say Y here if you want to connect a IBM "C-It" camera, also known as + "Xirlink PC Camera" to your computer's USB port. + + To compile this driver as a module, choose M here: the + module will be called ibmcam. + + This camera has several configuration options which + can be specified when you load the module. Read + to learn more. + +config USB_KONICAWC + tristate "USB Konica Webcam support" + depends on USB && VIDEO_V4L1 + select VIDEO_USBVIDEO + ---help--- + Say Y here if you want support for webcams based on a Konica + chipset. This is known to work with the Intel YC76 webcam. + + To compile this driver as a module, choose M here: the + module will be called konicawc. diff --git a/linux/drivers/media/video/usbvideo/Makefile b/linux/drivers/media/video/usbvideo/Makefile new file mode 100644 index 000000000..bb52eb8dc --- /dev/null +++ b/linux/drivers/media/video/usbvideo/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_VIDEO_USBVIDEO) += usbvideo.o +obj-$(CONFIG_USB_IBMCAM) += ibmcam.o ultracam.o +obj-$(CONFIG_USB_KONICAWC) += konicawc.o +obj-$(CONFIG_USB_VICAM) += vicam.o diff --git a/linux/drivers/media/video/usbvideo/ibmcam.c b/linux/drivers/media/video/usbvideo/ibmcam.c new file mode 100644 index 000000000..76f771b6a --- /dev/null +++ b/linux/drivers/media/video/usbvideo/ibmcam.c @@ -0,0 +1,3932 @@ +/* + * USB IBM C-It Video Camera driver + * + * Supports Xirlink C-It Video Camera, IBM PC Camera, + * IBM NetCamera and Veo Stingray. + * + * This driver is based on earlier work of: + * + * (C) Copyright 1999 Johannes Erdfelt + * (C) Copyright 1999 Randy Dunlap + * + * 5/24/00 Removed optional (and unnecessary) locking of the driver while + * the device remains plugged in. Corrected race conditions in ibmcam_open + * and ibmcam_probe() routines using this as a guideline: + */ + +#include +#include +#include +#include + +#include "usbvideo.h" + +#define IBMCAM_VENDOR_ID 0x0545 +#define IBMCAM_PRODUCT_ID 0x8080 +#define NETCAM_PRODUCT_ID 0x8002 /* IBM NetCamera, close to model 2 */ +#define VEO_800C_PRODUCT_ID 0x800C /* Veo Stingray, repackaged Model 2 */ +#define VEO_800D_PRODUCT_ID 0x800D /* Veo Stingray, repackaged Model 4 */ + +#define MAX_IBMCAM 4 /* How many devices we allow to connect */ +#define USES_IBMCAM_PUTPIXEL 0 /* 0=Fast/oops 1=Slow/secure */ + +/* Header signatures */ + +/* Model 1 header: 00 FF 00 xx */ +#define HDRSIG_MODEL1_128x96 0x06 /* U Y V Y ... */ +#define HDRSIG_MODEL1_176x144 0x0e /* U Y V Y ... */ +#define HDRSIG_MODEL1_352x288 0x00 /* V Y U Y ... */ + +#define IBMCAM_MODEL_1 1 /* XVP-501, 3 interfaces, rev. 0.02 */ +#define IBMCAM_MODEL_2 2 /* KSX-X9903, 2 interfaces, rev. 3.0a */ +#define IBMCAM_MODEL_3 3 /* KSX-X9902, 2 interfaces, rev. 3.01 */ +#define IBMCAM_MODEL_4 4 /* IBM NetCamera, 0545/8002/3.0a */ + +/* Video sizes supported */ +#define VIDEOSIZE_128x96 VIDEOSIZE(128, 96) +#define VIDEOSIZE_176x144 VIDEOSIZE(176,144) +#define VIDEOSIZE_352x288 VIDEOSIZE(352,288) +#define VIDEOSIZE_320x240 VIDEOSIZE(320,240) +#define VIDEOSIZE_352x240 VIDEOSIZE(352,240) +#define VIDEOSIZE_640x480 VIDEOSIZE(640,480) +#define VIDEOSIZE_160x120 VIDEOSIZE(160,120) + +/* Video sizes supported */ +enum { + SIZE_128x96 = 0, + SIZE_160x120, + SIZE_176x144, + SIZE_320x240, + SIZE_352x240, + SIZE_352x288, + SIZE_640x480, + /* Add/remove/rearrange items before this line */ + SIZE_LastItem +}; + +/* + * This structure lives in uvd->user field. + */ +typedef struct { + int initialized; /* Had we already sent init sequence? */ + int camera_model; /* What type of IBM camera we got? */ + int has_hdr; +} ibmcam_t; +#define IBMCAM_T(uvd) ((ibmcam_t *)((uvd)->user_data)) + +static struct usbvideo *cams; + +static int debug; + +static int flags; /* = FLAGS_DISPLAY_HINTS | FLAGS_OVERLAY_STATS; */ + +static const int min_canvasWidth = 8; +static const int min_canvasHeight = 4; + +static int lighting = 1; /* Medium */ + +#define SHARPNESS_MIN 0 +#define SHARPNESS_MAX 6 +static int sharpness = 4; /* Low noise, good details */ + +#define FRAMERATE_MIN 0 +#define FRAMERATE_MAX 6 +static int framerate = -1; + +static int size = SIZE_352x288; + +/* + * Here we define several initialization variables. They may + * be used to automatically set color, hue, brightness and + * contrast to desired values. This is particularly useful in + * case of webcams (which have no controls and no on-screen + * output) and also when a client V4L software is used that + * does not have some of those controls. In any case it's + * good to have startup values as options. + * + * These values are all in [0..255] range. This simplifies + * operation. Note that actual values of V4L variables may + * be scaled up (as much as << 8). User can see that only + * on overlay output, however, or through a V4L client. + */ +static int init_brightness = 128; +static int init_contrast = 192; +static int init_color = 128; +static int init_hue = 128; +static int hue_correction = 128; + +/* Settings for camera model 2 */ +static int init_model2_rg2 = -1; +static int init_model2_sat = -1; +static int init_model2_yb = -1; + +/* 01.01.08 - Added for RCA video in support -LO */ +/* Settings for camera model 3 */ +static int init_model3_input = 0; + +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)"); +module_param(flags, int, 0); +MODULE_PARM_DESC(flags, "Bitfield: 0=VIDIOCSYNC, 1=B/W, 2=show hints, 3=show stats, 4=test pattern, 5=separate frames, 6=clean frames"); +module_param(framerate, int, 0); +MODULE_PARM_DESC(framerate, "Framerate setting: 0=slowest, 6=fastest (default=2)"); +module_param(lighting, int, 0); +MODULE_PARM_DESC(lighting, "Photosensitivity: 0=bright, 1=medium (default), 2=low light"); +module_param(sharpness, int, 0); +MODULE_PARM_DESC(sharpness, "Model1 noise reduction: 0=smooth, 6=sharp (default=4)"); +module_param(size, int, 0); +MODULE_PARM_DESC(size, "Image size: 0=128x96 1=160x120 2=176x144 3=320x240 4=352x240 5=352x288 6=640x480 (default=5)"); +module_param(init_brightness, int, 0); +MODULE_PARM_DESC(init_brightness, "Brightness preconfiguration: 0-255 (default=128)"); +module_param(init_contrast, int, 0); +MODULE_PARM_DESC(init_contrast, "Contrast preconfiguration: 0-255 (default=192)"); +module_param(init_color, int, 0); +MODULE_PARM_DESC(init_color, "Color preconfiguration: 0-255 (default=128)"); +module_param(init_hue, int, 0); +MODULE_PARM_DESC(init_hue, "Hue preconfiguration: 0-255 (default=128)"); +module_param(hue_correction, int, 0); +MODULE_PARM_DESC(hue_correction, "YUV colorspace regulation: 0-255 (default=128)"); + +module_param(init_model2_rg2, int, 0); +MODULE_PARM_DESC(init_model2_rg2, "Model2 preconfiguration: 0-255 (default=47)"); +module_param(init_model2_sat, int, 0); +MODULE_PARM_DESC(init_model2_sat, "Model2 preconfiguration: 0-255 (default=52)"); +module_param(init_model2_yb, int, 0); +MODULE_PARM_DESC(init_model2_yb, "Model2 preconfiguration: 0-255 (default=160)"); + +/* 01.01.08 - Added for RCA video in support -LO */ +module_param(init_model3_input, int, 0); +MODULE_PARM_DESC(init_model3_input, "Model3 input: 0=CCD 1=RCA"); + +MODULE_AUTHOR ("Dmitri"); +MODULE_DESCRIPTION ("IBM/Xirlink C-it USB Camera Driver for Linux (c) 2000"); +MODULE_LICENSE("GPL"); + +/* Still mysterious i2c commands */ +static const unsigned short unknown_88 = 0x0088; +static const unsigned short unknown_89 = 0x0089; +static const unsigned short bright_3x[3] = { 0x0031, 0x0032, 0x0033 }; +static const unsigned short contrast_14 = 0x0014; +static const unsigned short light_27 = 0x0027; +static const unsigned short sharp_13 = 0x0013; + +/* i2c commands for Model 2 cameras */ +static const unsigned short mod2_brightness = 0x001a; /* $5b .. $ee; default=$5a */ +static const unsigned short mod2_set_framerate = 0x001c; /* 0 (fast).. $1F (slow) */ +static const unsigned short mod2_color_balance_rg2 = 0x001e; /* 0 (red) .. $7F (green) */ +static const unsigned short mod2_saturation = 0x0020; /* 0 (b/w) - $7F (full color) */ +static const unsigned short mod2_color_balance_yb = 0x0022; /* 0..$7F, $50 is about right */ +static const unsigned short mod2_hue = 0x0024; /* 0..$7F, $70 is about right */ +static const unsigned short mod2_sensitivity = 0x0028; /* 0 (min) .. $1F (max) */ + +struct struct_initData { + unsigned char req; + unsigned short value; + unsigned short index; +}; + +/* + * ibmcam_size_to_videosize() + * + * This procedure converts module option 'size' into the actual + * videosize_t that defines the image size in pixels. We need + * simplified 'size' because user wants a simple enumerated list + * of choices, not an infinite set of possibilities. + */ +static videosize_t ibmcam_size_to_videosize(int size) +{ + videosize_t vs = VIDEOSIZE_352x288; + RESTRICT_TO_RANGE(size, 0, (SIZE_LastItem-1)); + switch (size) { + case SIZE_128x96: + vs = VIDEOSIZE_128x96; + break; + case SIZE_160x120: + vs = VIDEOSIZE_160x120; + break; + case SIZE_176x144: + vs = VIDEOSIZE_176x144; + break; + case SIZE_320x240: + vs = VIDEOSIZE_320x240; + break; + case SIZE_352x240: + vs = VIDEOSIZE_352x240; + break; + case SIZE_352x288: + vs = VIDEOSIZE_352x288; + break; + case SIZE_640x480: + vs = VIDEOSIZE_640x480; + break; + default: + err("size=%d. is not valid", size); + break; + } + return vs; +} + +/* + * ibmcam_find_header() + * + * Locate one of supported header markers in the queue. + * Once found, remove all preceding bytes AND the marker (4 bytes) + * from the data pump queue. Whatever follows must be video lines. + * + * History: + * 1/21/00 Created. + */ +static enum ParseState ibmcam_find_header(struct uvd *uvd) /* FIXME: Add frame here */ +{ + struct usbvideo_frame *frame; + ibmcam_t *icam; + + if ((uvd->curframe) < 0 || (uvd->curframe >= USBVIDEO_NUMFRAMES)) { + err("ibmcam_find_header: Illegal frame %d.", uvd->curframe); + return scan_EndParse; + } + icam = IBMCAM_T(uvd); + assert(icam != NULL); + frame = &uvd->frame[uvd->curframe]; + icam->has_hdr = 0; + switch (icam->camera_model) { + case IBMCAM_MODEL_1: + { + const int marker_len = 4; + while (RingQueue_GetLength(&uvd->dp) >= marker_len) { + if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) && + (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xFF) && + (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00)) + { +#if 0 /* This code helps to detect new frame markers */ + info("Header sig: 00 FF 00 %02X", RING_QUEUE_PEEK(&uvd->dp, 3)); +#endif + frame->header = RING_QUEUE_PEEK(&uvd->dp, 3); + if ((frame->header == HDRSIG_MODEL1_128x96) || + (frame->header == HDRSIG_MODEL1_176x144) || + (frame->header == HDRSIG_MODEL1_352x288)) + { +#if 0 + info("Header found."); +#endif + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len); + icam->has_hdr = 1; + break; + } + } + /* If we are still here then this doesn't look like a header */ + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1); + } + break; + } + case IBMCAM_MODEL_2: +case IBMCAM_MODEL_4: + { + int marker_len = 0; + switch (uvd->videosize) { + case VIDEOSIZE_176x144: + marker_len = 10; + break; + default: + marker_len = 2; + break; + } + while (RingQueue_GetLength(&uvd->dp) >= marker_len) { + if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) && + (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xFF)) + { +#if 0 + info("Header found."); +#endif + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len); + icam->has_hdr = 1; + frame->header = HDRSIG_MODEL1_176x144; + break; + } + /* If we are still here then this doesn't look like a header */ + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1); + } + break; + } + case IBMCAM_MODEL_3: + { /* + * Headers: (one precedes every frame). nc=no compression, + * bq=best quality bf=best frame rate. + * + * 176x144: 00 FF 02 { 0A=nc CA=bq EA=bf } + * 320x240: 00 FF 02 { 08=nc 28=bq 68=bf } + * 640x480: 00 FF 03 { 08=nc 28=bq 68=bf } + * + * Bytes '00 FF' seem to indicate header. Other two bytes + * encode the frame type. This is a set of bit fields that + * encode image size, compression type etc. These fields + * do NOT contain frame number because all frames carry + * the same header. + */ + const int marker_len = 4; + while (RingQueue_GetLength(&uvd->dp) >= marker_len) { + if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) && + (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xFF) && + (RING_QUEUE_PEEK(&uvd->dp, 2) != 0xFF)) + { + /* + * Combine 2 bytes of frame type into one + * easy to use value + */ + unsigned long byte3, byte4; + + byte3 = RING_QUEUE_PEEK(&uvd->dp, 2); + byte4 = RING_QUEUE_PEEK(&uvd->dp, 3); + frame->header = (byte3 << 8) | byte4; +#if 0 + info("Header found."); +#endif + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len); + icam->has_hdr = 1; + break; + } + /* If we are still here then this doesn't look like a header */ + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1); + } + break; + } + default: + break; + } + if (!icam->has_hdr) { + if (uvd->debug > 2) + info("Skipping frame, no header"); + return scan_EndParse; + } + + /* Header found */ + icam->has_hdr = 1; + uvd->stats.header_count++; + frame->scanstate = ScanState_Lines; + frame->curline = 0; + + if (flags & FLAGS_FORCE_TESTPATTERN) { + usbvideo_TestPattern(uvd, 1, 1); + return scan_NextFrame; + } + return scan_Continue; +} + +/* + * ibmcam_parse_lines() + * + * Parse one line (interlaced) from the buffer, put + * decoded RGB value into the current frame buffer + * and add the written number of bytes (RGB) to + * the *pcopylen. + * + * History: + * 21-Jan-2000 Created. + * 12-Oct-2000 Reworked to reflect interlaced nature of the data. + */ +static enum ParseState ibmcam_parse_lines( + struct uvd *uvd, + struct usbvideo_frame *frame, + long *pcopylen) +{ + unsigned char *f; + ibmcam_t *icam; + unsigned int len, scanLength, scanHeight, order_uv, order_yc; + int v4l_linesize; /* V4L line offset */ + const int hue_corr = (uvd->vpic.hue - 0x8000) >> 10; /* -32..+31 */ + const int hue2_corr = (hue_correction - 128) / 4; /* -32..+31 */ + const int ccm = 128; /* Color correction median - see below */ + int y, u, v, i, frame_done=0, color_corr; + static unsigned char lineBuffer[640*3]; + unsigned const char *chromaLine, *lumaLine; + + assert(uvd != NULL); + assert(frame != NULL); + icam = IBMCAM_T(uvd); + assert(icam != NULL); + color_corr = (uvd->vpic.colour - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ + RESTRICT_TO_RANGE(color_corr, -ccm, ccm+1); + + v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; + + if (IBMCAM_T(uvd)->camera_model == IBMCAM_MODEL_4) { + /* Model 4 frame markers do not carry image size identification */ + switch (uvd->videosize) { + case VIDEOSIZE_128x96: + case VIDEOSIZE_160x120: + case VIDEOSIZE_176x144: + scanLength = VIDEOSIZE_X(uvd->videosize); + scanHeight = VIDEOSIZE_Y(uvd->videosize); + break; + default: + err("ibmcam_parse_lines: Wrong mode."); + return scan_Out; + } + order_yc = 1; /* order_yc: true=Yc false=cY ('c'=either U or V) */ + order_uv = 1; /* Always true in this algorithm */ + } else { + switch (frame->header) { + case HDRSIG_MODEL1_128x96: + scanLength = 128; + scanHeight = 96; + order_uv = 1; /* U Y V Y ... */ + break; + case HDRSIG_MODEL1_176x144: + scanLength = 176; + scanHeight = 144; + order_uv = 1; /* U Y V Y ... */ + break; + case HDRSIG_MODEL1_352x288: + scanLength = 352; + scanHeight = 288; + order_uv = 0; /* Y V Y V ... */ + break; + default: + err("Unknown header signature 00 FF 00 %02lX", frame->header); + return scan_NextFrame; + } + /* order_yc: true=Yc false=cY ('c'=either U or V) */ + order_yc = (IBMCAM_T(uvd)->camera_model == IBMCAM_MODEL_2); + } + + len = scanLength * 3; + assert(len <= sizeof(lineBuffer)); + + /* + * Lines are organized this way: + * + * I420: + * ~~~~ + * + * ___________________________________ + * |-----Y-----|---UVUVUV...UVUV-----| \ + * |-----------+---------------------| \ + * |<-- 176 -->|<------ 176*2 ------>| Total 72. lines (interlaced) + * |... ... | ... | / + * |<-- 352 -->|<------ 352*2 ------>| Total 144. lines (interlaced) + * |___________|_____________________| / + * \ \ + * lumaLine chromaLine + */ + + /* Make sure there's enough data for the entire line */ + if (RingQueue_GetLength(&uvd->dp) < len) + return scan_Out; + + /* Suck one line out of the ring queue */ + RingQueue_Dequeue(&uvd->dp, lineBuffer, len); + + /* + * Make sure that our writing into output buffer + * will not exceed the buffer. Mind that we may write + * not into current output scanline but in several after + * it as well (if we enlarge image vertically.) + */ + if ((frame->curline + 2) >= VIDEOSIZE_Y(frame->request)) + return scan_NextFrame; + + /* + * Now we are sure that entire line (representing all 'scanLength' + * pixels from the camera) is available in the buffer. We + * start copying the line left-aligned to the V4L buffer. + * If the camera line is shorter then we should pad the V4L + * buffer with something (black) to complete the line. + */ + assert(frame->data != NULL); + f = frame->data + (v4l_linesize * frame->curline); + + /* + * To obtain chrominance data from the 'chromaLine' use this: + * v = chromaLine[0]; // 0-1:[0], 2-3:[4], 4-5:[8]... + * u = chromaLine[2]; // 0-1:[2], 2-3:[6], 4-5:[10]... + * + * Indices must be calculated this way: + * v_index = (i >> 1) << 2; + * u_index = (i >> 1) << 2 + 2; + * + * where 'i' is the column number [0..VIDEOSIZE_X(frame->request)-1] + */ + lumaLine = lineBuffer; + chromaLine = lineBuffer + scanLength; + for (i = 0; i < VIDEOSIZE_X(frame->request); i++) + { + unsigned char rv, gv, bv; /* RGB components */ + + /* Check for various visual debugging hints (colorized pixels) */ + if ((flags & FLAGS_DISPLAY_HINTS) && (icam->has_hdr)) { + /* + * This is bad and should not happen. This means that + * we somehow overshoot the line and encountered new + * frame! Obviously our camera/V4L frame size is out + * of whack. This cyan dot will help you to figure + * out where exactly the new frame arrived. + */ + if (icam->has_hdr == 1) { + bv = 0; /* Yellow marker */ + gv = 0xFF; + rv = 0xFF; + } else { + bv = 0xFF; /* Cyan marker */ + gv = 0xFF; + rv = 0; + } + icam->has_hdr = 0; + goto make_pixel; + } + + /* + * Check if we are still in range. We may be out of range if our + * V4L canvas is wider or taller than the camera "native" image. + * Then we quickly fill the remainder of the line with zeros to + * make black color and quit the horizontal scanning loop. + */ + if (((frame->curline + 2) >= scanHeight) || (i >= scanLength)) { + const int j = i * V4L_BYTES_PER_PIXEL; +#if USES_IBMCAM_PUTPIXEL + /* Refresh 'f' because we don't use it much with PUTPIXEL */ + f = frame->data + (v4l_linesize * frame->curline) + j; +#endif + memset(f, 0, v4l_linesize - j); + break; + } + + y = lumaLine[i]; + if (flags & FLAGS_MONOCHROME) /* Use monochrome for debugging */ + rv = gv = bv = y; + else { + int off_0, off_2; + + off_0 = (i >> 1) << 2; + off_2 = off_0 + 2; + + if (order_yc) { + off_0++; + off_2++; + } + if (!order_uv) { + off_0 += 2; + off_2 -= 2; + } + u = chromaLine[off_0] + hue_corr; + v = chromaLine[off_2] + hue2_corr; + + /* Apply color correction */ + if (color_corr != 0) { + /* Magnify up to 2 times, reduce down to zero saturation */ + u = 128 + ((ccm + color_corr) * (u - 128)) / ccm; + v = 128 + ((ccm + color_corr) * (v - 128)) / ccm; + } + YUV_TO_RGB_BY_THE_BOOK(y, u, v, rv, gv, bv); + } + + make_pixel: + /* + * The purpose of creating the pixel here, in one, + * dedicated place is that we may need to make the + * pixel wider and taller than it actually is. This + * may be used if camera generates small frames for + * sake of frame rate (or any other reason.) + * + * The output data consists of B, G, R bytes + * (in this order). + */ +#if USES_IBMCAM_PUTPIXEL + RGB24_PUTPIXEL(frame, i, frame->curline, rv, gv, bv); +#else + *f++ = bv; + *f++ = gv; + *f++ = rv; +#endif + /* + * Typically we do not decide within a legitimate frame + * that we want to end the frame. However debugging code + * may detect marker of new frame within the data. Then + * this condition activates. The 'data' pointer is already + * pointing at the new marker, so we'd better leave it as is. + */ + if (frame_done) + break; /* End scanning of lines */ + } + /* + * Account for number of bytes that we wrote into output V4L frame. + * We do it here, after we are done with the scanline, because we + * may fill more than one output scanline if we do vertical + * enlargement. + */ + frame->curline += 2; + if (pcopylen != NULL) + *pcopylen += 2 * v4l_linesize; + frame->deinterlace = Deinterlace_FillOddLines; + + if (frame_done || (frame->curline >= VIDEOSIZE_Y(frame->request))) + return scan_NextFrame; + else + return scan_Continue; +} + +/* + * ibmcam_model2_320x240_parse_lines() + * + * This procedure deals with a weird RGB format that is produced by IBM + * camera model 2 in modes 320x240 and above; 'x' below is 159 or 175, + * depending on horizontal size of the picture: + * + * <--- 160 or 176 pairs of RA,RB bytes -----> + * *-----------------------------------------* \ + * | RA0 | RB0 | RA1 | RB1 | ... | RAx | RBx | \ This is pair of horizontal lines, + * |-----+-----+-----+-----+ ... +-----+-----| *- or one interlaced line, total + * | B0 | G0 | B1 | G1 | ... | Bx | Gx | / 120 or 144 such pairs which yield + * |=====+=====+=====+=====+ ... +=====+=====| / 240 or 288 lines after deinterlacing. + * + * Each group of FOUR bytes (RAi, RBi, Bi, Gi) where i=0..frame_width/2-1 + * defines ONE pixel. Therefore this format yields 176x144 "decoded" + * resolution at best. I do not know why camera sends such format - the + * previous model (1) just used interlaced I420 and everyone was happy. + * + * I do not know what is the difference between RAi and RBi bytes. Both + * seemingly represent R component, but slightly vary in value (so that + * the picture looks a bit colored if one or another is used). I use + * them both as R component in attempt to at least partially recover the + * lost resolution. + */ +static enum ParseState ibmcam_model2_320x240_parse_lines( + struct uvd *uvd, + struct usbvideo_frame *frame, + long *pcopylen) +{ + unsigned char *f, *la, *lb; + unsigned int len; + int v4l_linesize; /* V4L line offset */ + int i, j, frame_done=0, color_corr; + int scanLength, scanHeight; + static unsigned char lineBuffer[352*2]; + + switch (uvd->videosize) { + case VIDEOSIZE_320x240: + case VIDEOSIZE_352x240: + case VIDEOSIZE_352x288: + scanLength = VIDEOSIZE_X(uvd->videosize); + scanHeight = VIDEOSIZE_Y(uvd->videosize); + break; + default: + err("ibmcam_model2_320x240_parse_lines: Wrong mode."); + return scan_Out; + } + + color_corr = (uvd->vpic.colour) >> 8; /* 0..+255 */ + v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; + + len = scanLength * 2; /* See explanation above */ + assert(len <= sizeof(lineBuffer)); + + /* Make sure there's enough data for the entire line */ + if (RingQueue_GetLength(&uvd->dp) < len) + return scan_Out; + + /* Suck one line out of the ring queue */ + RingQueue_Dequeue(&uvd->dp, lineBuffer, len); + + /* + * Make sure that our writing into output buffer + * will not exceed the buffer. Mind that we may write + * not into current output scanline but in several after + * it as well (if we enlarge image vertically.) + */ + if ((frame->curline + 2) >= VIDEOSIZE_Y(frame->request)) + return scan_NextFrame; + + la = lineBuffer; + lb = lineBuffer + scanLength; + + /* + * Now we are sure that entire line (representing all + * VIDEOSIZE_X(frame->request) + * pixels from the camera) is available in the scratch buffer. We + * start copying the line left-aligned to the V4L buffer (which + * might be larger - not smaller, hopefully). If the camera + * line is shorter then we should pad the V4L buffer with something + * (black in this case) to complete the line. + */ + f = frame->data + (v4l_linesize * frame->curline); + + /* Fill the 2-line strip */ + for (i = 0; i < VIDEOSIZE_X(frame->request); i++) { + int y, rv, gv, bv; /* RGB components */ + + j = i & (~1); + + /* Check for various visual debugging hints (colorized pixels) */ + if ((flags & FLAGS_DISPLAY_HINTS) && (IBMCAM_T(uvd)->has_hdr)) { + if (IBMCAM_T(uvd)->has_hdr == 1) { + bv = 0; /* Yellow marker */ + gv = 0xFF; + rv = 0xFF; + } else { + bv = 0xFF; /* Cyan marker */ + gv = 0xFF; + rv = 0; + } + IBMCAM_T(uvd)->has_hdr = 0; + goto make_pixel; + } + + /* + * Check if we are still in range. We may be out of range if our + * V4L canvas is wider or taller than the camera "native" image. + * Then we quickly fill the remainder of the line with zeros to + * make black color and quit the horizontal scanning loop. + */ + if (((frame->curline + 2) >= scanHeight) || (i >= scanLength)) { + const int j = i * V4L_BYTES_PER_PIXEL; +#if USES_IBMCAM_PUTPIXEL + /* Refresh 'f' because we don't use it much with PUTPIXEL */ + f = frame->data + (v4l_linesize * frame->curline) + j; +#endif + memset(f, 0, v4l_linesize - j); + break; + } + + /* + * Here I use RA and RB components, one per physical pixel. + * This causes fine vertical grid on the picture but may improve + * horizontal resolution. If you prefer replicating, use this: + * rv = la[j + 0]; ... or ... rv = la[j + 1]; + * then the pixel will be replicated. + */ + rv = la[i]; + gv = lb[j + 1]; + bv = lb[j + 0]; + + y = (rv + gv + bv) / 3; /* Brightness (badly calculated) */ + + if (flags & FLAGS_MONOCHROME) /* Use monochrome for debugging */ + rv = gv = bv = y; + else if (color_corr != 128) { + + /* Calculate difference between color and brightness */ + rv -= y; + gv -= y; + bv -= y; + + /* Scale differences */ + rv = (rv * color_corr) / 128; + gv = (gv * color_corr) / 128; + bv = (bv * color_corr) / 128; + + /* Reapply brightness */ + rv += y; + gv += y; + bv += y; + + /* Watch for overflows */ + RESTRICT_TO_RANGE(rv, 0, 255); + RESTRICT_TO_RANGE(gv, 0, 255); + RESTRICT_TO_RANGE(bv, 0, 255); + } + + make_pixel: + RGB24_PUTPIXEL(frame, i, frame->curline, rv, gv, bv); + } + /* + * Account for number of bytes that we wrote into output V4L frame. + * We do it here, after we are done with the scanline, because we + * may fill more than one output scanline if we do vertical + * enlargement. + */ + frame->curline += 2; + *pcopylen += v4l_linesize * 2; + frame->deinterlace = Deinterlace_FillOddLines; + + if (frame_done || (frame->curline >= VIDEOSIZE_Y(frame->request))) + return scan_NextFrame; + else + return scan_Continue; +} + +static enum ParseState ibmcam_model3_parse_lines( + struct uvd *uvd, + struct usbvideo_frame *frame, + long *pcopylen) +{ + unsigned char *data; + const unsigned char *color; + unsigned int len; + int v4l_linesize; /* V4L line offset */ + const int hue_corr = (uvd->vpic.hue - 0x8000) >> 10; /* -32..+31 */ + const int hue2_corr = (hue_correction - 128) / 4; /* -32..+31 */ + const int ccm = 128; /* Color correction median - see below */ + int i, u, v, rw, data_w=0, data_h=0, color_corr; + static unsigned char lineBuffer[640*3]; + + color_corr = (uvd->vpic.colour - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ + RESTRICT_TO_RANGE(color_corr, -ccm, ccm+1); + + v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; + + /* The header tells us what sort of data is in this frame */ + switch (frame->header) { + /* + * Uncompressed modes (that are easy to decode). + */ + case 0x0308: + data_w = 640; + data_h = 480; + break; + case 0x0208: + data_w = 320; + data_h = 240; + break; + case 0x020A: + data_w = 160; + data_h = 120; + break; + /* + * Compressed modes (ViCE - that I don't know how to decode). + */ + case 0x0328: /* 640x480, best quality compression */ + case 0x0368: /* 640x480, best frame rate compression */ + case 0x0228: /* 320x240, best quality compression */ + case 0x0268: /* 320x240, best frame rate compression */ + case 0x02CA: /* 160x120, best quality compression */ + case 0x02EA: /* 160x120, best frame rate compression */ + /* Do nothing with this - not supported */ + err("Unsupported mode $%04lx", frame->header); + return scan_NextFrame; + default: + /* Catch unknown headers, may help in learning new headers */ + err("Strange frame->header=$%08lx", frame->header); + return scan_NextFrame; + } + + /* + * Make sure that our writing into output buffer + * will not exceed the buffer. Note that we may write + * not into current output scanline but in several after + * it as well (if we enlarge image vertically.) + */ + if ((frame->curline + 1) >= data_h) { + if (uvd->debug >= 3) + info("Reached line %d. (frame is done)", frame->curline); + return scan_NextFrame; + } + + /* Make sure there's enough data for the entire line */ + len = 3 * data_w; /* */ + assert(len <= sizeof(lineBuffer)); + + /* Make sure there's enough data for the entire line */ + if (RingQueue_GetLength(&uvd->dp) < len) + return scan_Out; + + /* Suck one line out of the ring queue */ + RingQueue_Dequeue(&uvd->dp, lineBuffer, len); + + data = lineBuffer; + color = data + data_w; /* Point to where color planes begin */ + + /* Bottom-to-top scanning */ + rw = (int)VIDEOSIZE_Y(frame->request) - (int)(frame->curline) - 1; + RESTRICT_TO_RANGE(rw, 0, VIDEOSIZE_Y(frame->request)-1); + + for (i = 0; i < VIDEOSIZE_X(frame->request); i++) { + int y, rv, gv, bv; /* RGB components */ + + if (i < data_w) { + y = data[i]; /* Luminosity is the first line */ + + /* Apply static color correction */ + u = color[i*2] + hue_corr; + v = color[i*2 + 1] + hue2_corr; + + /* Apply color correction */ + if (color_corr != 0) { + /* Magnify up to 2 times, reduce down to zero saturation */ + u = 128 + ((ccm + color_corr) * (u - 128)) / ccm; + v = 128 + ((ccm + color_corr) * (v - 128)) / ccm; + } + } else + y = 0, u = v = 128; + + YUV_TO_RGB_BY_THE_BOOK(y, u, v, rv, gv, bv); + RGB24_PUTPIXEL(frame, i, rw, rv, gv, bv); /* Done by deinterlacing now */ + } + frame->deinterlace = Deinterlace_FillEvenLines; + + /* + * Account for number of bytes that we wrote into output V4L frame. + * We do it here, after we are done with the scanline, because we + * may fill more than one output scanline if we do vertical + * enlargement. + */ + frame->curline += 2; + *pcopylen += 2 * v4l_linesize; + + if (frame->curline >= VIDEOSIZE_Y(frame->request)) { + if (uvd->debug >= 3) { + info("All requested lines (%ld.) done.", + VIDEOSIZE_Y(frame->request)); + } + return scan_NextFrame; + } else + return scan_Continue; +} + +/* + * ibmcam_model4_128x96_parse_lines() + * + * This decoder is for one strange data format that is produced by Model 4 + * camera only in 128x96 mode. This is RGB format and here is its description. + * First of all, this is non-interlaced stream, meaning that all scan lines + * are present in the datastream. There are 96 consecutive blocks of data + * that describe all 96 lines of the image. Each block is 5*128 bytes long + * and carries R, G, B components. The format of the block is shown in the + * code below. First 128*2 bytes are interleaved R and G components. Then + * we have a gap (junk data) 64 bytes long. Then follow B and something + * else, also interleaved (this makes another 128*2 bytes). After that + * probably another 64 bytes of junk follow. + * + * History: + * 10-Feb-2001 Created. + */ +static enum ParseState ibmcam_model4_128x96_parse_lines( + struct uvd *uvd, + struct usbvideo_frame *frame, + long *pcopylen) +{ + const unsigned char *data_rv, *data_gv, *data_bv; + unsigned int len; + int i, v4l_linesize; /* V4L line offset */ + const int data_w=128, data_h=96; + static unsigned char lineBuffer[128*5]; + + v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; + + /* + * Make sure that our writing into output buffer + * will not exceed the buffer. Note that we may write + * not into current output scanline but in several after + * it as well (if we enlarge image vertically.) + */ + if ((frame->curline + 1) >= data_h) { + if (uvd->debug >= 3) + info("Reached line %d. (frame is done)", frame->curline); + return scan_NextFrame; + } + + /* + * RGRGRG .... RGRG_____________B?B?B? ... B?B?____________ + * <---- 128*2 ---><---- 64 ---><--- 128*2 ---><--- 64 ---> + */ + + /* Make sure there's enough data for the entire line */ + len = 5 * data_w; + assert(len <= sizeof(lineBuffer)); + + /* Make sure there's enough data for the entire line */ + if (RingQueue_GetLength(&uvd->dp) < len) + return scan_Out; + + /* Suck one line out of the ring queue */ + RingQueue_Dequeue(&uvd->dp, lineBuffer, len); + + data_rv = lineBuffer; + data_gv = lineBuffer + 1; + data_bv = lineBuffer + data_w*2 + data_w/2; + for (i = 0; i < VIDEOSIZE_X(frame->request); i++) { + int rv, gv, bv; /* RGB components */ + if (i < data_w) { + const int j = i * 2; + gv = data_rv[j]; + rv = data_gv[j]; + bv = data_bv[j]; + if (flags & FLAGS_MONOCHROME) { + unsigned long y; + y = rv + gv + bv; + y /= 3; + if (y > 0xFF) + y = 0xFF; + rv = gv = bv = (unsigned char) y; + } + } else { + rv = gv = bv = 0; + } + RGB24_PUTPIXEL(frame, i, frame->curline, rv, gv, bv); + } + frame->deinterlace = Deinterlace_None; + frame->curline++; + *pcopylen += v4l_linesize; + + if (frame->curline >= VIDEOSIZE_Y(frame->request)) { + if (uvd->debug >= 3) { + info("All requested lines (%ld.) done.", + VIDEOSIZE_Y(frame->request)); + } + return scan_NextFrame; + } else + return scan_Continue; +} + +/* + * ibmcam_ProcessIsocData() + * + * Generic routine to parse the ring queue data. It employs either + * ibmcam_find_header() or ibmcam_parse_lines() to do most + * of work. + * + * History: + * 1/21/00 Created. + */ +static void ibmcam_ProcessIsocData(struct uvd *uvd, + struct usbvideo_frame *frame) +{ + enum ParseState newstate; + long copylen = 0; + int mod = IBMCAM_T(uvd)->camera_model; + + while (1) { + newstate = scan_Out; + if (RingQueue_GetLength(&uvd->dp) > 0) { + if (frame->scanstate == ScanState_Scanning) { + newstate = ibmcam_find_header(uvd); + } else if (frame->scanstate == ScanState_Lines) { + if ((mod == IBMCAM_MODEL_2) && + ((uvd->videosize == VIDEOSIZE_352x288) || + (uvd->videosize == VIDEOSIZE_320x240) || + (uvd->videosize == VIDEOSIZE_352x240))) + { + newstate = ibmcam_model2_320x240_parse_lines( + uvd, frame, ©len); + } else if (mod == IBMCAM_MODEL_4) { + /* + * Model 4 cameras (IBM NetCamera) use Model 2 decoder (RGB) + * for 320x240 and above; 160x120 and 176x144 uses Model 1 + * decoder (YUV), and 128x96 mode uses ??? + */ + if ((uvd->videosize == VIDEOSIZE_352x288) || + (uvd->videosize == VIDEOSIZE_320x240) || + (uvd->videosize == VIDEOSIZE_352x240)) + { + newstate = ibmcam_model2_320x240_parse_lines(uvd, frame, ©len); + } else if (uvd->videosize == VIDEOSIZE_128x96) { + newstate = ibmcam_model4_128x96_parse_lines(uvd, frame, ©len); + } else { + newstate = ibmcam_parse_lines(uvd, frame, ©len); + } + } else if (mod == IBMCAM_MODEL_3) { + newstate = ibmcam_model3_parse_lines(uvd, frame, ©len); + } else { + newstate = ibmcam_parse_lines(uvd, frame, ©len); + } + } + } + if (newstate == scan_Continue) + continue; + else if ((newstate == scan_NextFrame) || (newstate == scan_Out)) + break; + else + return; /* scan_EndParse */ + } + + if (newstate == scan_NextFrame) { + frame->frameState = FrameState_Done; + uvd->curframe = -1; + uvd->stats.frame_num++; + if ((mod == IBMCAM_MODEL_2) || (mod == IBMCAM_MODEL_4)) { + /* Need software contrast adjustment for those cameras */ + frame->flags |= USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST; + } + } + + /* Update the frame's uncompressed length. */ + frame->seqRead_Length += copylen; + +#if 0 + { + static unsigned char j=0; + memset(frame->data, j++, uvd->max_frame_size); + frame->frameState = FrameState_Ready; + } +#endif +} + +/* + * ibmcam_veio() + * + * History: + * 1/27/00 Added check for dev == NULL; this happens if camera is unplugged. + */ +static int ibmcam_veio( + struct uvd *uvd, + unsigned char req, + unsigned short value, + unsigned short index) +{ + static const char proc[] = "ibmcam_veio"; + unsigned char cp[8] /* = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } */; + int i; + + if (!CAMERA_IS_OPERATIONAL(uvd)) + return 0; + + if (req == 1) { + i = usb_control_msg( + uvd->dev, + usb_rcvctrlpipe(uvd->dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + value, + index, + cp, + sizeof(cp), + 1000); +#if 0 + info("USB => %02x%02x%02x%02x%02x%02x%02x%02x " + "(req=$%02x val=$%04x ind=$%04x)", + cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7], + req, value, index); +#endif + } else { + i = usb_control_msg( + uvd->dev, + usb_sndctrlpipe(uvd->dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + value, + index, + NULL, + 0, + 1000); + } + if (i < 0) { + err("%s: ERROR=%d. Camera stopped; Reconnect or reload driver.", + proc, i); + uvd->last_error = i; + } + return i; +} + +/* + * ibmcam_calculate_fps() + * + * This procedure roughly calculates the real frame rate based + * on FPS code (framerate=NNN option). Actual FPS differs + * slightly depending on lighting conditions, so that actual frame + * rate is determined by the camera. Since I don't know how to ask + * the camera what FPS is now I have to use the FPS code instead. + * + * The FPS code is in range [0..6], 0 is slowest, 6 is fastest. + * Corresponding real FPS should be in range [3..30] frames per second. + * The conversion formula is obvious: + * + * real_fps = 3 + (fps_code * 4.5) + * + * History: + * 1/18/00 Created. + */ +static int ibmcam_calculate_fps(struct uvd *uvd) +{ + return 3 + framerate*4 + framerate/2; +} + +/* + * ibmcam_send_FF_04_02() + * + * This procedure sends magic 3-command prefix to the camera. + * The purpose of this prefix is not known. + * + * History: + * 1/2/00 Created. + */ +static void ibmcam_send_FF_04_02(struct uvd *uvd) +{ + ibmcam_veio(uvd, 0, 0x00FF, 0x0127); + ibmcam_veio(uvd, 0, 0x0004, 0x0124); + ibmcam_veio(uvd, 0, 0x0002, 0x0124); +} + +static void ibmcam_send_00_04_06(struct uvd *uvd) +{ + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x0004, 0x0124); + ibmcam_veio(uvd, 0, 0x0006, 0x0124); +} + +static void ibmcam_send_x_00(struct uvd *uvd, unsigned short x) +{ + ibmcam_veio(uvd, 0, x, 0x0127); + ibmcam_veio(uvd, 0, 0x0000, 0x0124); +} + +static void ibmcam_send_x_00_05(struct uvd *uvd, unsigned short x) +{ + ibmcam_send_x_00(uvd, x); + ibmcam_veio(uvd, 0, 0x0005, 0x0124); +} + +static void ibmcam_send_x_00_05_02(struct uvd *uvd, unsigned short x) +{ + ibmcam_veio(uvd, 0, x, 0x0127); + ibmcam_veio(uvd, 0, 0x0000, 0x0124); + ibmcam_veio(uvd, 0, 0x0005, 0x0124); + ibmcam_veio(uvd, 0, 0x0002, 0x0124); +} + +static void ibmcam_send_x_01_00_05(struct uvd *uvd, unsigned short x) +{ + ibmcam_veio(uvd, 0, x, 0x0127); + ibmcam_veio(uvd, 0, 0x0001, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0124); + ibmcam_veio(uvd, 0, 0x0005, 0x0124); +} + +static void ibmcam_send_x_00_05_02_01(struct uvd *uvd, unsigned short x) +{ + ibmcam_veio(uvd, 0, x, 0x0127); + ibmcam_veio(uvd, 0, 0x0000, 0x0124); + ibmcam_veio(uvd, 0, 0x0005, 0x0124); + ibmcam_veio(uvd, 0, 0x0002, 0x0124); + ibmcam_veio(uvd, 0, 0x0001, 0x0124); +} + +static void ibmcam_send_x_00_05_02_08_01(struct uvd *uvd, unsigned short x) +{ + ibmcam_veio(uvd, 0, x, 0x0127); + ibmcam_veio(uvd, 0, 0x0000, 0x0124); + ibmcam_veio(uvd, 0, 0x0005, 0x0124); + ibmcam_veio(uvd, 0, 0x0002, 0x0124); + ibmcam_veio(uvd, 0, 0x0008, 0x0124); + ibmcam_veio(uvd, 0, 0x0001, 0x0124); +} + +static void ibmcam_Packet_Format1(struct uvd *uvd, unsigned char fkey, unsigned char val) +{ + ibmcam_send_x_01_00_05(uvd, unknown_88); + ibmcam_send_x_00_05(uvd, fkey); + ibmcam_send_x_00_05_02_08_01(uvd, val); + ibmcam_send_x_00_05(uvd, unknown_88); + ibmcam_send_x_00_05_02_01(uvd, fkey); + ibmcam_send_x_00_05(uvd, unknown_89); + ibmcam_send_x_00(uvd, fkey); + ibmcam_send_00_04_06(uvd); + ibmcam_veio(uvd, 1, 0x0000, 0x0126); + ibmcam_send_FF_04_02(uvd); +} + +static void ibmcam_PacketFormat2(struct uvd *uvd, unsigned char fkey, unsigned char val) +{ + ibmcam_send_x_01_00_05 (uvd, unknown_88); + ibmcam_send_x_00_05 (uvd, fkey); + ibmcam_send_x_00_05_02 (uvd, val); +} + +static void ibmcam_model2_Packet2(struct uvd *uvd) +{ + ibmcam_veio(uvd, 0, 0x00ff, 0x012d); + ibmcam_veio(uvd, 0, 0xfea3, 0x0124); +} + +static void ibmcam_model2_Packet1(struct uvd *uvd, unsigned short v1, unsigned short v2) +{ + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x00ff, 0x012e); + ibmcam_veio(uvd, 0, v1, 0x012f); + ibmcam_veio(uvd, 0, 0x00ff, 0x0130); + ibmcam_veio(uvd, 0, 0xc719, 0x0124); + ibmcam_veio(uvd, 0, v2, 0x0127); + + ibmcam_model2_Packet2(uvd); +} + +/* + * ibmcam_model3_Packet1() + * + * 00_0078_012d + * 00_0097_012f + * 00_d141_0124 + * 00_0096_0127 + * 00_fea8_0124 +*/ +static void ibmcam_model3_Packet1(struct uvd *uvd, unsigned short v1, unsigned short v2) +{ + ibmcam_veio(uvd, 0, 0x0078, 0x012d); + ibmcam_veio(uvd, 0, v1, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, v2, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); +} + +static void ibmcam_model4_BrightnessPacket(struct uvd *uvd, int i) +{ + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0026, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, i, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0038, 0x012d); + ibmcam_veio(uvd, 0, 0x0004, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); +} + +/* + * ibmcam_adjust_contrast() + * + * The contrast value changes from 0 (high contrast) to 15 (low contrast). + * This is in reverse to usual order of things (such as TV controls), so + * we reverse it again here. + * + * TODO: we probably don't need to send the setup 5 times... + * + * History: + * 1/2/00 Created. + */ +static void ibmcam_adjust_contrast(struct uvd *uvd) +{ + unsigned char a_contrast = uvd->vpic.contrast >> 12; + unsigned char new_contrast; + + if (a_contrast >= 16) + a_contrast = 15; + new_contrast = 15 - a_contrast; + if (new_contrast == uvd->vpic_old.contrast) + return; + uvd->vpic_old.contrast = new_contrast; + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + { + const int ntries = 5; + int i; + for (i=0; i < ntries; i++) { + ibmcam_Packet_Format1(uvd, contrast_14, new_contrast); + ibmcam_send_FF_04_02(uvd); + } + break; + } + case IBMCAM_MODEL_2: + case IBMCAM_MODEL_4: + /* Models 2, 4 do not have this control; implemented in software. */ + break; + case IBMCAM_MODEL_3: + { /* Preset hardware values */ + static const struct { + unsigned short cv1; + unsigned short cv2; + unsigned short cv3; + } cv[7] = { + { 0x05, 0x05, 0x0f }, /* Minimum */ + { 0x04, 0x04, 0x16 }, + { 0x02, 0x03, 0x16 }, + { 0x02, 0x08, 0x16 }, + { 0x01, 0x0c, 0x16 }, + { 0x01, 0x0e, 0x16 }, + { 0x01, 0x10, 0x16 } /* Maximum */ + }; + int i = a_contrast / 2; + RESTRICT_TO_RANGE(i, 0, 6); + ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop */ + ibmcam_model3_Packet1(uvd, 0x0067, cv[i].cv1); + ibmcam_model3_Packet1(uvd, 0x005b, cv[i].cv2); + ibmcam_model3_Packet1(uvd, 0x005c, cv[i].cv3); + ibmcam_veio(uvd, 0, 0x0001, 0x0114); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go! */ + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); + break; + } + default: + break; + } +} + +/* + * ibmcam_change_lighting_conditions() + * + * Camera model 1: + * We have 3 levels of lighting conditions: 0=Bright, 1=Medium, 2=Low. + * + * Camera model 2: + * We have 16 levels of lighting, 0 for bright light and up to 15 for + * low light. But values above 5 or so are useless because camera is + * not really capable to produce anything worth viewing at such light. + * This setting may be altered only in certain camera state. + * + * Low lighting forces slower FPS. Lighting is set as a module parameter. + * + * History: + * 1/5/00 Created. + * 2/20/00 Added support for Model 2 cameras. + */ +static void ibmcam_change_lighting_conditions(struct uvd *uvd) +{ + static const char proc[] = "ibmcam_change_lighting_conditions"; + + if (debug > 0) + info("%s: Set lighting to %hu.", proc, lighting); + + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + { + const int ntries = 5; + int i; + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, light_27, (unsigned short) lighting); + break; + } + case IBMCAM_MODEL_2: +#if 0 + /* + * This command apparently requires camera to be stopped. My + * experiments showed that it -is- possible to alter the lighting + * conditions setting "on the fly", but why bother? This setting does + * not work reliably in all cases, so I decided simply to leave the + * setting where Xirlink put it - in the camera setup phase. This code + * is commented out because it does not work at -any- moment, so its + * presence makes no sense. You may use it for experiments. + */ + ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop camera */ + ibmcam_model2_Packet1(uvd, mod2_sensitivity, lighting); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Start camera */ +#endif + break; + case IBMCAM_MODEL_3: + case IBMCAM_MODEL_4: + default: + break; + } +} + +/* + * ibmcam_set_sharpness() + * + * Cameras model 1 have internal smoothing feature. It is controlled by value in + * range [0..6], where 0 is most smooth and 6 is most sharp (raw image, I guess). + * Recommended value is 4. Cameras model 2 do not have this feature at all. + */ +static void ibmcam_set_sharpness(struct uvd *uvd) +{ + static const char proc[] = "ibmcam_set_sharpness"; + + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + { + static const unsigned short sa[] = { 0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a }; + unsigned short i, sv; + + RESTRICT_TO_RANGE(sharpness, SHARPNESS_MIN, SHARPNESS_MAX); + if (debug > 0) + info("%s: Set sharpness to %hu.", proc, sharpness); + + sv = sa[sharpness - SHARPNESS_MIN]; + for (i=0; i < 2; i++) { + ibmcam_send_x_01_00_05 (uvd, unknown_88); + ibmcam_send_x_00_05 (uvd, sharp_13); + ibmcam_send_x_00_05_02 (uvd, sv); + } + break; + } + case IBMCAM_MODEL_2: + case IBMCAM_MODEL_4: + /* Models 2, 4 do not have this control */ + break; + case IBMCAM_MODEL_3: + { /* + * "Use a table of magic numbers. + * This setting doesn't really change much. + * But that's how Windows does it." + */ + static const struct { + unsigned short sv1; + unsigned short sv2; + unsigned short sv3; + unsigned short sv4; + } sv[7] = { + { 0x00, 0x00, 0x05, 0x14 }, /* Smoothest */ + { 0x01, 0x04, 0x05, 0x14 }, + { 0x02, 0x04, 0x05, 0x14 }, + { 0x03, 0x04, 0x05, 0x14 }, + { 0x03, 0x05, 0x05, 0x14 }, + { 0x03, 0x06, 0x05, 0x14 }, + { 0x03, 0x07, 0x05, 0x14 } /* Sharpest */ + }; + RESTRICT_TO_RANGE(sharpness, SHARPNESS_MIN, SHARPNESS_MAX); + RESTRICT_TO_RANGE(sharpness, 0, 6); + ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop */ + ibmcam_model3_Packet1(uvd, 0x0060, sv[sharpness].sv1); + ibmcam_model3_Packet1(uvd, 0x0061, sv[sharpness].sv2); + ibmcam_model3_Packet1(uvd, 0x0062, sv[sharpness].sv3); + ibmcam_model3_Packet1(uvd, 0x0063, sv[sharpness].sv4); + ibmcam_veio(uvd, 0, 0x0001, 0x0114); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go! */ + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); + ibmcam_veio(uvd, 0, 0x0001, 0x0113); + break; + } + default: + break; + } +} + +/* + * ibmcam_set_brightness() + * + * This procedure changes brightness of the picture. + */ +static void ibmcam_set_brightness(struct uvd *uvd) +{ + static const char proc[] = "ibmcam_set_brightness"; + static const unsigned short n = 1; + + if (debug > 0) + info("%s: Set brightness to %hu.", proc, uvd->vpic.brightness); + + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + { + unsigned short i, j, bv[3]; + bv[0] = bv[1] = bv[2] = uvd->vpic.brightness >> 10; + if (bv[0] == (uvd->vpic_old.brightness >> 10)) + return; + uvd->vpic_old.brightness = bv[0]; + for (j=0; j < 3; j++) + for (i=0; i < n; i++) + ibmcam_Packet_Format1(uvd, bright_3x[j], bv[j]); + break; + } + case IBMCAM_MODEL_2: + { + unsigned short i, j; + i = uvd->vpic.brightness >> 12; /* 0 .. 15 */ + j = 0x60 + i * ((0xee - 0x60) / 16); /* 0x60 .. 0xee or so */ + if (uvd->vpic_old.brightness == j) + break; + uvd->vpic_old.brightness = j; + ibmcam_model2_Packet1(uvd, mod2_brightness, j); + break; + } + case IBMCAM_MODEL_3: + { + /* Model 3: Brightness range 'i' in [0x0C..0x3F] */ + unsigned short i = + 0x0C + (uvd->vpic.brightness / (0xFFFF / (0x3F - 0x0C + 1))); + RESTRICT_TO_RANGE(i, 0x0C, 0x3F); + if (uvd->vpic_old.brightness == i) + break; + uvd->vpic_old.brightness = i; + ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop */ + ibmcam_model3_Packet1(uvd, 0x0036, i); + ibmcam_veio(uvd, 0, 0x0001, 0x0114); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go! */ + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); + ibmcam_veio(uvd, 0, 0x0001, 0x0113); + break; + } + case IBMCAM_MODEL_4: + { + /* Model 4: Brightness range 'i' in [0x04..0xb4] */ + unsigned short i = 0x04 + (uvd->vpic.brightness / (0xFFFF / (0xb4 - 0x04 + 1))); + RESTRICT_TO_RANGE(i, 0x04, 0xb4); + if (uvd->vpic_old.brightness == i) + break; + uvd->vpic_old.brightness = i; + ibmcam_model4_BrightnessPacket(uvd, i); + break; + } + default: + break; + } +} + +static void ibmcam_set_hue(struct uvd *uvd) +{ + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_2: + { + unsigned short hue = uvd->vpic.hue >> 9; /* 0 .. 7F */ + if (uvd->vpic_old.hue == hue) + return; + uvd->vpic_old.hue = hue; + ibmcam_model2_Packet1(uvd, mod2_hue, hue); + /* ibmcam_model2_Packet1(uvd, mod2_saturation, sat); */ + break; + } + case IBMCAM_MODEL_3: + { +#if 0 /* This seems not to work. No problem, will fix programmatically */ + unsigned short hue = 0x05 + (uvd->vpic.hue / (0xFFFF / (0x37 - 0x05 + 1))); + RESTRICT_TO_RANGE(hue, 0x05, 0x37); + if (uvd->vpic_old.hue == hue) + return; + uvd->vpic_old.hue = hue; + ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop */ + ibmcam_model3_Packet1(uvd, 0x007e, hue); + ibmcam_veio(uvd, 0, 0x0001, 0x0114); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go! */ + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); + ibmcam_veio(uvd, 0, 0x0001, 0x0113); +#endif + break; + } + case IBMCAM_MODEL_4: + { + unsigned short r_gain, g_gain, b_gain, hue; + + /* + * I am not sure r/g/b_gain variables exactly control gain + * of those channels. Most likely they subtly change some + * very internal image processing settings in the camera. + * In any case, here is what they do, and feel free to tweak: + * + * r_gain: seriously affects red gain + * g_gain: seriously affects green gain + * b_gain: seriously affects blue gain + * hue: changes average color from violet (0) to red (0xFF) + * + * These settings are preset for a decent white balance in + * 320x240, 352x288 modes. Low-res modes exhibit higher contrast + * and therefore may need different values here. + */ + hue = 20 + (uvd->vpic.hue >> 9); + switch (uvd->videosize) { + case VIDEOSIZE_128x96: + r_gain = 90; + g_gain = 166; + b_gain = 175; + break; + case VIDEOSIZE_160x120: + r_gain = 70; + g_gain = 166; + b_gain = 185; + break; + case VIDEOSIZE_176x144: + r_gain = 160; + g_gain = 175; + b_gain = 185; + break; + default: + r_gain = 120; + g_gain = 166; + b_gain = 175; + break; + } + RESTRICT_TO_RANGE(hue, 1, 0x7f); + + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x001e, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, g_gain, 0x0127); /* Green gain */ + ibmcam_veio(uvd, 0, r_gain, 0x012e); /* Red gain */ + ibmcam_veio(uvd, 0, b_gain, 0x0130); /* Blue gain */ + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, hue, 0x012d); /* Hue */ + ibmcam_veio(uvd, 0, 0xf545, 0x0124); + break; + } + default: + break; + } +} + +/* + * ibmcam_adjust_picture() + * + * This procedure gets called from V4L interface to update picture settings. + * Here we change brightness and contrast. + */ +static void ibmcam_adjust_picture(struct uvd *uvd) +{ + ibmcam_adjust_contrast(uvd); + ibmcam_set_brightness(uvd); + ibmcam_set_hue(uvd); +} + +static int ibmcam_model1_setup(struct uvd *uvd) +{ + const int ntries = 5; + int i; + + ibmcam_veio(uvd, 1, 0x00, 0x0128); + ibmcam_veio(uvd, 1, 0x00, 0x0100); + ibmcam_veio(uvd, 0, 0x01, 0x0100); /* LED On */ + ibmcam_veio(uvd, 1, 0x00, 0x0100); + ibmcam_veio(uvd, 0, 0x81, 0x0100); /* LED Off */ + ibmcam_veio(uvd, 1, 0x00, 0x0100); + ibmcam_veio(uvd, 0, 0x01, 0x0100); /* LED On */ + ibmcam_veio(uvd, 0, 0x01, 0x0108); + + ibmcam_veio(uvd, 0, 0x03, 0x0112); + ibmcam_veio(uvd, 1, 0x00, 0x0115); + ibmcam_veio(uvd, 0, 0x06, 0x0115); + ibmcam_veio(uvd, 1, 0x00, 0x0116); + ibmcam_veio(uvd, 0, 0x44, 0x0116); + ibmcam_veio(uvd, 1, 0x00, 0x0116); + ibmcam_veio(uvd, 0, 0x40, 0x0116); + ibmcam_veio(uvd, 1, 0x00, 0x0115); + ibmcam_veio(uvd, 0, 0x0e, 0x0115); + ibmcam_veio(uvd, 0, 0x19, 0x012c); + + ibmcam_Packet_Format1(uvd, 0x00, 0x1e); + ibmcam_Packet_Format1(uvd, 0x39, 0x0d); + ibmcam_Packet_Format1(uvd, 0x39, 0x09); + ibmcam_Packet_Format1(uvd, 0x3b, 0x00); + ibmcam_Packet_Format1(uvd, 0x28, 0x22); + ibmcam_Packet_Format1(uvd, light_27, 0); + ibmcam_Packet_Format1(uvd, 0x2b, 0x1f); + ibmcam_Packet_Format1(uvd, 0x39, 0x08); + + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x2c, 0x00); + + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x30, 0x14); + + ibmcam_PacketFormat2(uvd, 0x39, 0x02); + ibmcam_PacketFormat2(uvd, 0x01, 0xe1); + ibmcam_PacketFormat2(uvd, 0x02, 0xcd); + ibmcam_PacketFormat2(uvd, 0x03, 0xcd); + ibmcam_PacketFormat2(uvd, 0x04, 0xfa); + ibmcam_PacketFormat2(uvd, 0x3f, 0xff); + ibmcam_PacketFormat2(uvd, 0x39, 0x00); + + ibmcam_PacketFormat2(uvd, 0x39, 0x02); + ibmcam_PacketFormat2(uvd, 0x0a, 0x37); + ibmcam_PacketFormat2(uvd, 0x0b, 0xb8); + ibmcam_PacketFormat2(uvd, 0x0c, 0xf3); + ibmcam_PacketFormat2(uvd, 0x0d, 0xe3); + ibmcam_PacketFormat2(uvd, 0x0e, 0x0d); + ibmcam_PacketFormat2(uvd, 0x0f, 0xf2); + ibmcam_PacketFormat2(uvd, 0x10, 0xd5); + ibmcam_PacketFormat2(uvd, 0x11, 0xba); + ibmcam_PacketFormat2(uvd, 0x12, 0x53); + ibmcam_PacketFormat2(uvd, 0x3f, 0xff); + ibmcam_PacketFormat2(uvd, 0x39, 0x00); + + ibmcam_PacketFormat2(uvd, 0x39, 0x02); + ibmcam_PacketFormat2(uvd, 0x16, 0x00); + ibmcam_PacketFormat2(uvd, 0x17, 0x28); + ibmcam_PacketFormat2(uvd, 0x18, 0x7d); + ibmcam_PacketFormat2(uvd, 0x19, 0xbe); + ibmcam_PacketFormat2(uvd, 0x3f, 0xff); + ibmcam_PacketFormat2(uvd, 0x39, 0x00); + + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x00, 0x18); + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x13, 0x18); + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x14, 0x06); + + /* This is default brightness */ + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x31, 0x37); + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x32, 0x46); + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x33, 0x55); + + ibmcam_Packet_Format1(uvd, 0x2e, 0x04); + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x2d, 0x04); + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x29, 0x80); + ibmcam_Packet_Format1(uvd, 0x2c, 0x01); + ibmcam_Packet_Format1(uvd, 0x30, 0x17); + ibmcam_Packet_Format1(uvd, 0x39, 0x08); + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x34, 0x00); + + ibmcam_veio(uvd, 0, 0x00, 0x0101); + ibmcam_veio(uvd, 0, 0x00, 0x010a); + + switch (uvd->videosize) { + case VIDEOSIZE_128x96: + ibmcam_veio(uvd, 0, 0x80, 0x0103); + ibmcam_veio(uvd, 0, 0x60, 0x0105); + ibmcam_veio(uvd, 0, 0x0c, 0x010b); + ibmcam_veio(uvd, 0, 0x04, 0x011b); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x0b, 0x011d); + ibmcam_veio(uvd, 0, 0x00, 0x011e); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x00, 0x0129); + break; + case VIDEOSIZE_176x144: + ibmcam_veio(uvd, 0, 0xb0, 0x0103); + ibmcam_veio(uvd, 0, 0x8f, 0x0105); + ibmcam_veio(uvd, 0, 0x06, 0x010b); + ibmcam_veio(uvd, 0, 0x04, 0x011b); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x0d, 0x011d); + ibmcam_veio(uvd, 0, 0x00, 0x011e); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x03, 0x0129); + break; + case VIDEOSIZE_352x288: + ibmcam_veio(uvd, 0, 0xb0, 0x0103); + ibmcam_veio(uvd, 0, 0x90, 0x0105); + ibmcam_veio(uvd, 0, 0x02, 0x010b); + ibmcam_veio(uvd, 0, 0x04, 0x011b); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x05, 0x011d); + ibmcam_veio(uvd, 0, 0x00, 0x011e); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x00, 0x0129); + break; + } + + ibmcam_veio(uvd, 0, 0xff, 0x012b); + + /* This is another brightness - don't know why */ + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x31, 0xc3); + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x32, 0xd2); + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x33, 0xe1); + + /* Default contrast */ + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, contrast_14, 0x0a); + + /* Default sharpness */ + for (i=0; i < 2; i++) + ibmcam_PacketFormat2(uvd, sharp_13, 0x1a); /* Level 4 FIXME */ + + /* Default lighting conditions */ + ibmcam_Packet_Format1(uvd, light_27, lighting); /* 0=Bright 2=Low */ + + /* Assorted init */ + + switch (uvd->videosize) { + case VIDEOSIZE_128x96: + ibmcam_Packet_Format1(uvd, 0x2b, 0x1e); + ibmcam_veio(uvd, 0, 0xc9, 0x0119); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x80, 0x0109); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x36, 0x0102); + ibmcam_veio(uvd, 0, 0x1a, 0x0104); + ibmcam_veio(uvd, 0, 0x04, 0x011a); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x2b, 0x011c); + ibmcam_veio(uvd, 0, 0x23, 0x012a); /* Same everywhere */ +#if 0 + ibmcam_veio(uvd, 0, 0x00, 0x0106); + ibmcam_veio(uvd, 0, 0x38, 0x0107); +#else + ibmcam_veio(uvd, 0, 0x02, 0x0106); + ibmcam_veio(uvd, 0, 0x2a, 0x0107); +#endif + break; + case VIDEOSIZE_176x144: + ibmcam_Packet_Format1(uvd, 0x2b, 0x1e); + ibmcam_veio(uvd, 0, 0xc9, 0x0119); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x80, 0x0109); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x04, 0x0102); + ibmcam_veio(uvd, 0, 0x02, 0x0104); + ibmcam_veio(uvd, 0, 0x04, 0x011a); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x2b, 0x011c); + ibmcam_veio(uvd, 0, 0x23, 0x012a); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x01, 0x0106); + ibmcam_veio(uvd, 0, 0xca, 0x0107); + break; + case VIDEOSIZE_352x288: + ibmcam_Packet_Format1(uvd, 0x2b, 0x1f); + ibmcam_veio(uvd, 0, 0xc9, 0x0119); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x80, 0x0109); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x08, 0x0102); + ibmcam_veio(uvd, 0, 0x01, 0x0104); + ibmcam_veio(uvd, 0, 0x04, 0x011a); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x2f, 0x011c); + ibmcam_veio(uvd, 0, 0x23, 0x012a); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x03, 0x0106); + ibmcam_veio(uvd, 0, 0xf6, 0x0107); + break; + } + return (CAMERA_IS_OPERATIONAL(uvd) ? 0 : -EFAULT); +} + +static int ibmcam_model2_setup(struct uvd *uvd) +{ + ibmcam_veio(uvd, 0, 0x0000, 0x0100); /* LED on */ + ibmcam_veio(uvd, 1, 0x0000, 0x0116); + ibmcam_veio(uvd, 0, 0x0060, 0x0116); + ibmcam_veio(uvd, 0, 0x0002, 0x0112); + ibmcam_veio(uvd, 0, 0x00bc, 0x012c); + ibmcam_veio(uvd, 0, 0x0008, 0x012b); + ibmcam_veio(uvd, 0, 0x0000, 0x0108); + ibmcam_veio(uvd, 0, 0x0001, 0x0133); + ibmcam_veio(uvd, 0, 0x0001, 0x0102); + switch (uvd->videosize) { + case VIDEOSIZE_176x144: + ibmcam_veio(uvd, 0, 0x002c, 0x0103); /* All except 320x240 */ + ibmcam_veio(uvd, 0, 0x0000, 0x0104); /* Same */ + ibmcam_veio(uvd, 0, 0x0024, 0x0105); /* 176x144, 352x288 */ + ibmcam_veio(uvd, 0, 0x00b9, 0x010a); /* Unique to this mode */ + ibmcam_veio(uvd, 0, 0x0038, 0x0119); /* Unique to this mode */ + ibmcam_veio(uvd, 0, 0x0003, 0x0106); /* Same */ + ibmcam_veio(uvd, 0, 0x0090, 0x0107); /* Unique to every mode*/ + break; + case VIDEOSIZE_320x240: + ibmcam_veio(uvd, 0, 0x0028, 0x0103); /* Unique to this mode */ + ibmcam_veio(uvd, 0, 0x0000, 0x0104); /* Same */ + ibmcam_veio(uvd, 0, 0x001e, 0x0105); /* 320x240, 352x240 */ + ibmcam_veio(uvd, 0, 0x0039, 0x010a); /* All except 176x144 */ + ibmcam_veio(uvd, 0, 0x0070, 0x0119); /* All except 176x144 */ + ibmcam_veio(uvd, 0, 0x0003, 0x0106); /* Same */ + ibmcam_veio(uvd, 0, 0x0098, 0x0107); /* Unique to every mode*/ + break; + case VIDEOSIZE_352x240: + ibmcam_veio(uvd, 0, 0x002c, 0x0103); /* All except 320x240 */ + ibmcam_veio(uvd, 0, 0x0000, 0x0104); /* Same */ + ibmcam_veio(uvd, 0, 0x001e, 0x0105); /* 320x240, 352x240 */ + ibmcam_veio(uvd, 0, 0x0039, 0x010a); /* All except 176x144 */ + ibmcam_veio(uvd, 0, 0x0070, 0x0119); /* All except 176x144 */ + ibmcam_veio(uvd, 0, 0x0003, 0x0106); /* Same */ + ibmcam_veio(uvd, 0, 0x00da, 0x0107); /* Unique to every mode*/ + break; + case VIDEOSIZE_352x288: + ibmcam_veio(uvd, 0, 0x002c, 0x0103); /* All except 320x240 */ + ibmcam_veio(uvd, 0, 0x0000, 0x0104); /* Same */ + ibmcam_veio(uvd, 0, 0x0024, 0x0105); /* 176x144, 352x288 */ + ibmcam_veio(uvd, 0, 0x0039, 0x010a); /* All except 176x144 */ + ibmcam_veio(uvd, 0, 0x0070, 0x0119); /* All except 176x144 */ + ibmcam_veio(uvd, 0, 0x0003, 0x0106); /* Same */ + ibmcam_veio(uvd, 0, 0x00fe, 0x0107); /* Unique to every mode*/ + break; + } + return (CAMERA_IS_OPERATIONAL(uvd) ? 0 : -EFAULT); +} + +/* + * ibmcam_model1_setup_after_video_if() + * + * This code adds finishing touches to the video data interface. + * Here we configure the frame rate and turn on the LED. + */ +static void ibmcam_model1_setup_after_video_if(struct uvd *uvd) +{ + unsigned short internal_frame_rate; + + RESTRICT_TO_RANGE(framerate, FRAMERATE_MIN, FRAMERATE_MAX); + internal_frame_rate = FRAMERATE_MAX - framerate; /* 0=Fast 6=Slow */ + ibmcam_veio(uvd, 0, 0x01, 0x0100); /* LED On */ + ibmcam_veio(uvd, 0, internal_frame_rate, 0x0111); + ibmcam_veio(uvd, 0, 0x01, 0x0114); + ibmcam_veio(uvd, 0, 0xc0, 0x010c); +} + +static void ibmcam_model2_setup_after_video_if(struct uvd *uvd) +{ + unsigned short setup_model2_rg2, setup_model2_sat, setup_model2_yb; + + ibmcam_veio(uvd, 0, 0x0000, 0x0100); /* LED on */ + + switch (uvd->videosize) { + case VIDEOSIZE_176x144: + ibmcam_veio(uvd, 0, 0x0050, 0x0111); + ibmcam_veio(uvd, 0, 0x00d0, 0x0111); + break; + case VIDEOSIZE_320x240: + case VIDEOSIZE_352x240: + case VIDEOSIZE_352x288: + ibmcam_veio(uvd, 0, 0x0040, 0x0111); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + break; + } + ibmcam_veio(uvd, 0, 0x009b, 0x010f); + ibmcam_veio(uvd, 0, 0x00bb, 0x010f); + + /* + * Hardware settings, may affect CMOS sensor; not user controls! + * ------------------------------------------------------------- + * 0x0004: no effect + * 0x0006: hardware effect + * 0x0008: no effect + * 0x000a: stops video stream, probably important h/w setting + * 0x000c: changes color in hardware manner (not user setting) + * 0x0012: changes number of colors (does not affect speed) + * 0x002a: no effect + * 0x002c: hardware setting (related to scan lines) + * 0x002e: stops video stream, probably important h/w setting + */ + ibmcam_model2_Packet1(uvd, 0x000a, 0x005c); + ibmcam_model2_Packet1(uvd, 0x0004, 0x0000); + ibmcam_model2_Packet1(uvd, 0x0006, 0x00fb); + ibmcam_model2_Packet1(uvd, 0x0008, 0x0000); + ibmcam_model2_Packet1(uvd, 0x000c, 0x0009); + ibmcam_model2_Packet1(uvd, 0x0012, 0x000a); + ibmcam_model2_Packet1(uvd, 0x002a, 0x0000); + ibmcam_model2_Packet1(uvd, 0x002c, 0x0000); + ibmcam_model2_Packet1(uvd, 0x002e, 0x0008); + + /* + * Function 0x0030 pops up all over the place. Apparently + * it is a hardware control register, with every bit assigned to + * do something. + */ + ibmcam_model2_Packet1(uvd, 0x0030, 0x0000); + + /* + * Magic control of CMOS sensor. Only lower values like + * 0-3 work, and picture shifts left or right. Don't change. + */ + switch (uvd->videosize) { + case VIDEOSIZE_176x144: + ibmcam_model2_Packet1(uvd, 0x0014, 0x0002); + ibmcam_model2_Packet1(uvd, 0x0016, 0x0002); /* Horizontal shift */ + ibmcam_model2_Packet1(uvd, 0x0018, 0x004a); /* Another hardware setting */ + break; + case VIDEOSIZE_320x240: + ibmcam_model2_Packet1(uvd, 0x0014, 0x0009); + ibmcam_model2_Packet1(uvd, 0x0016, 0x0005); /* Horizontal shift */ + ibmcam_model2_Packet1(uvd, 0x0018, 0x0044); /* Another hardware setting */ + break; + case VIDEOSIZE_352x240: + /* This mode doesn't work as Windows programs it; changed to work */ + ibmcam_model2_Packet1(uvd, 0x0014, 0x0009); /* Windows sets this to 8 */ + ibmcam_model2_Packet1(uvd, 0x0016, 0x0003); /* Horizontal shift */ + ibmcam_model2_Packet1(uvd, 0x0018, 0x0044); /* Windows sets this to 0x0045 */ + break; + case VIDEOSIZE_352x288: + ibmcam_model2_Packet1(uvd, 0x0014, 0x0003); + ibmcam_model2_Packet1(uvd, 0x0016, 0x0002); /* Horizontal shift */ + ibmcam_model2_Packet1(uvd, 0x0018, 0x004a); /* Another hardware setting */ + break; + } + + ibmcam_model2_Packet1(uvd, mod2_brightness, 0x005a); + + /* + * We have our own frame rate setting varying from 0 (slowest) to 6 (fastest). + * The camera model 2 allows frame rate in range [0..0x1F] where 0 is also the + * slowest setting. However for all practical reasons high settings make no + * sense because USB is not fast enough to support high FPS. Be aware that + * the picture datastream will be severely disrupted if you ask for + * frame rate faster than allowed for the video size - see below: + * + * Allowable ranges (obtained experimentally on OHCI, K6-3, 450 MHz): + * ----------------------------------------------------------------- + * 176x144: [6..31] + * 320x240: [8..31] + * 352x240: [10..31] + * 352x288: [16..31] I have to raise lower threshold for stability... + * + * As usual, slower FPS provides better sensitivity. + */ + { + short hw_fps=31, i_framerate; + + RESTRICT_TO_RANGE(framerate, FRAMERATE_MIN, FRAMERATE_MAX); + i_framerate = FRAMERATE_MAX - framerate + FRAMERATE_MIN; + switch (uvd->videosize) { + case VIDEOSIZE_176x144: + hw_fps = 6 + i_framerate*4; + break; + case VIDEOSIZE_320x240: + hw_fps = 8 + i_framerate*3; + break; + case VIDEOSIZE_352x240: + hw_fps = 10 + i_framerate*2; + break; + case VIDEOSIZE_352x288: + hw_fps = 28 + i_framerate/2; + break; + } + if (uvd->debug > 0) + info("Framerate (hardware): %hd.", hw_fps); + RESTRICT_TO_RANGE(hw_fps, 0, 31); + ibmcam_model2_Packet1(uvd, mod2_set_framerate, hw_fps); + } + + /* + * This setting does not visibly affect pictures; left it here + * because it was present in Windows USB data stream. This function + * does not allow arbitrary values and apparently is a bit mask, to + * be activated only at appropriate time. Don't change it randomly! + */ + switch (uvd->videosize) { + case VIDEOSIZE_176x144: + ibmcam_model2_Packet1(uvd, 0x0026, 0x00c2); + break; + case VIDEOSIZE_320x240: + ibmcam_model2_Packet1(uvd, 0x0026, 0x0044); + break; + case VIDEOSIZE_352x240: + ibmcam_model2_Packet1(uvd, 0x0026, 0x0046); + break; + case VIDEOSIZE_352x288: + ibmcam_model2_Packet1(uvd, 0x0026, 0x0048); + break; + } + + ibmcam_model2_Packet1(uvd, mod2_sensitivity, lighting); + + if (init_model2_rg2 >= 0) { + RESTRICT_TO_RANGE(init_model2_rg2, 0, 255); + setup_model2_rg2 = init_model2_rg2; + } else + setup_model2_rg2 = 0x002f; + + if (init_model2_sat >= 0) { + RESTRICT_TO_RANGE(init_model2_sat, 0, 255); + setup_model2_sat = init_model2_sat; + } else + setup_model2_sat = 0x0034; + + if (init_model2_yb >= 0) { + RESTRICT_TO_RANGE(init_model2_yb, 0, 255); + setup_model2_yb = init_model2_yb; + } else + setup_model2_yb = 0x00a0; + + ibmcam_model2_Packet1(uvd, mod2_color_balance_rg2, setup_model2_rg2); + ibmcam_model2_Packet1(uvd, mod2_saturation, setup_model2_sat); + ibmcam_model2_Packet1(uvd, mod2_color_balance_yb, setup_model2_yb); + ibmcam_model2_Packet1(uvd, mod2_hue, uvd->vpic.hue >> 9); /* 0 .. 7F */; + + /* Hardware control command */ + ibmcam_model2_Packet1(uvd, 0x0030, 0x0004); + + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go camera, go! */ + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); +} + +static void ibmcam_model4_setup_after_video_if(struct uvd *uvd) +{ + switch (uvd->videosize) { + case VIDEOSIZE_128x96: + ibmcam_veio(uvd, 0, 0x0000, 0x0100); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x00bc, 0x012c); + ibmcam_veio(uvd, 0, 0x0080, 0x012b); + ibmcam_veio(uvd, 0, 0x0000, 0x0108); + ibmcam_veio(uvd, 0, 0x0001, 0x0133); + ibmcam_veio(uvd, 0, 0x009b, 0x010f); + ibmcam_veio(uvd, 0, 0x00bb, 0x010f); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x000a, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x005c, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0004, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00fb, 0x012e); + ibmcam_veio(uvd, 0, 0x0000, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x000c, 0x0127); + ibmcam_veio(uvd, 0, 0x0009, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0012, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0008, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x002a, 0x012d); + ibmcam_veio(uvd, 0, 0x0000, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0034, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x0070, 0x0119); + ibmcam_veio(uvd, 0, 0x00d2, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x005e, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x00d0, 0x0111); + ibmcam_veio(uvd, 0, 0x0039, 0x010a); + ibmcam_veio(uvd, 0, 0x0001, 0x0102); + ibmcam_veio(uvd, 0, 0x0028, 0x0103); + ibmcam_veio(uvd, 0, 0x0000, 0x0104); + ibmcam_veio(uvd, 0, 0x001e, 0x0105); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0016, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x000a, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0014, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012e); + ibmcam_veio(uvd, 0, 0x001a, 0x0130); + ibmcam_veio(uvd, 0, 0x8a0a, 0x0124); + ibmcam_veio(uvd, 0, 0x005a, 0x012d); + ibmcam_veio(uvd, 0, 0x9545, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x0127); + ibmcam_veio(uvd, 0, 0x0018, 0x012e); + ibmcam_veio(uvd, 0, 0x0043, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x001c, 0x0127); + ibmcam_veio(uvd, 0, 0x00eb, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0032, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0036, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x001e, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0017, 0x0127); + ibmcam_veio(uvd, 0, 0x0013, 0x012e); + ibmcam_veio(uvd, 0, 0x0031, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x0017, 0x012d); + ibmcam_veio(uvd, 0, 0x0078, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0004, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + break; + case VIDEOSIZE_160x120: + ibmcam_veio(uvd, 0, 0x0000, 0x0100); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x00bc, 0x012c); + ibmcam_veio(uvd, 0, 0x0080, 0x012b); + ibmcam_veio(uvd, 0, 0x0000, 0x0108); + ibmcam_veio(uvd, 0, 0x0001, 0x0133); + ibmcam_veio(uvd, 0, 0x009b, 0x010f); + ibmcam_veio(uvd, 0, 0x00bb, 0x010f); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x000a, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x005c, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0004, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00fb, 0x012e); + ibmcam_veio(uvd, 0, 0x0000, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x000c, 0x0127); + ibmcam_veio(uvd, 0, 0x0009, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0012, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0008, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x002a, 0x012d); + ibmcam_veio(uvd, 0, 0x0000, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0034, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x0038, 0x0119); + ibmcam_veio(uvd, 0, 0x00d8, 0x0107); + ibmcam_veio(uvd, 0, 0x0002, 0x0106); + ibmcam_veio(uvd, 0, 0x00d0, 0x0111); + ibmcam_veio(uvd, 0, 0x00b9, 0x010a); + ibmcam_veio(uvd, 0, 0x0001, 0x0102); + ibmcam_veio(uvd, 0, 0x0028, 0x0103); + ibmcam_veio(uvd, 0, 0x0000, 0x0104); + ibmcam_veio(uvd, 0, 0x001e, 0x0105); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0016, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x000b, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0014, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012e); + ibmcam_veio(uvd, 0, 0x001a, 0x0130); + ibmcam_veio(uvd, 0, 0x8a0a, 0x0124); + ibmcam_veio(uvd, 0, 0x005a, 0x012d); + ibmcam_veio(uvd, 0, 0x9545, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x0127); + ibmcam_veio(uvd, 0, 0x0018, 0x012e); + ibmcam_veio(uvd, 0, 0x0043, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x001c, 0x0127); + ibmcam_veio(uvd, 0, 0x00c7, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0032, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0025, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0036, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x001e, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0048, 0x0127); + ibmcam_veio(uvd, 0, 0x0035, 0x012e); + ibmcam_veio(uvd, 0, 0x00d0, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x0048, 0x012d); + ibmcam_veio(uvd, 0, 0x0090, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x0001, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0004, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + break; + case VIDEOSIZE_176x144: + ibmcam_veio(uvd, 0, 0x0000, 0x0100); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x00bc, 0x012c); + ibmcam_veio(uvd, 0, 0x0080, 0x012b); + ibmcam_veio(uvd, 0, 0x0000, 0x0108); + ibmcam_veio(uvd, 0, 0x0001, 0x0133); + ibmcam_veio(uvd, 0, 0x009b, 0x010f); + ibmcam_veio(uvd, 0, 0x00bb, 0x010f); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x000a, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x005c, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0004, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00fb, 0x012e); + ibmcam_veio(uvd, 0, 0x0000, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x000c, 0x0127); + ibmcam_veio(uvd, 0, 0x0009, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0012, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0008, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x002a, 0x012d); + ibmcam_veio(uvd, 0, 0x0000, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0034, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x0038, 0x0119); + ibmcam_veio(uvd, 0, 0x00d6, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x0018, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x00d0, 0x0111); + ibmcam_veio(uvd, 0, 0x00b9, 0x010a); + ibmcam_veio(uvd, 0, 0x0001, 0x0102); + ibmcam_veio(uvd, 0, 0x002c, 0x0103); + ibmcam_veio(uvd, 0, 0x0000, 0x0104); + ibmcam_veio(uvd, 0, 0x0024, 0x0105); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0016, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0007, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0014, 0x012d); + ibmcam_veio(uvd, 0, 0x0001, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012e); + ibmcam_veio(uvd, 0, 0x001a, 0x0130); + ibmcam_veio(uvd, 0, 0x8a0a, 0x0124); + ibmcam_veio(uvd, 0, 0x005e, 0x012d); + ibmcam_veio(uvd, 0, 0x9545, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x0127); + ibmcam_veio(uvd, 0, 0x0018, 0x012e); + ibmcam_veio(uvd, 0, 0x0049, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x001c, 0x0127); + ibmcam_veio(uvd, 0, 0x00c7, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0032, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0028, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0036, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x001e, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0010, 0x0127); + ibmcam_veio(uvd, 0, 0x0013, 0x012e); + ibmcam_veio(uvd, 0, 0x002a, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x0010, 0x012d); + ibmcam_veio(uvd, 0, 0x006d, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x0001, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0004, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + break; + case VIDEOSIZE_320x240: + ibmcam_veio(uvd, 0, 0x0000, 0x0100); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x00bc, 0x012c); + ibmcam_veio(uvd, 0, 0x0080, 0x012b); + ibmcam_veio(uvd, 0, 0x0000, 0x0108); + ibmcam_veio(uvd, 0, 0x0001, 0x0133); + ibmcam_veio(uvd, 0, 0x009b, 0x010f); + ibmcam_veio(uvd, 0, 0x00bb, 0x010f); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x000a, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x005c, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0004, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00fb, 0x012e); + ibmcam_veio(uvd, 0, 0x0000, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x000c, 0x0127); + ibmcam_veio(uvd, 0, 0x0009, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0012, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0008, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x002a, 0x012d); + ibmcam_veio(uvd, 0, 0x0000, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0034, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x0070, 0x0119); + ibmcam_veio(uvd, 0, 0x00d2, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x005e, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x00d0, 0x0111); + ibmcam_veio(uvd, 0, 0x0039, 0x010a); + ibmcam_veio(uvd, 0, 0x0001, 0x0102); + ibmcam_veio(uvd, 0, 0x0028, 0x0103); + ibmcam_veio(uvd, 0, 0x0000, 0x0104); + ibmcam_veio(uvd, 0, 0x001e, 0x0105); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0016, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x000a, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0014, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012e); + ibmcam_veio(uvd, 0, 0x001a, 0x0130); + ibmcam_veio(uvd, 0, 0x8a0a, 0x0124); + ibmcam_veio(uvd, 0, 0x005a, 0x012d); + ibmcam_veio(uvd, 0, 0x9545, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x0127); + ibmcam_veio(uvd, 0, 0x0018, 0x012e); + ibmcam_veio(uvd, 0, 0x0043, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x001c, 0x0127); + ibmcam_veio(uvd, 0, 0x00eb, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0032, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0036, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x001e, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0017, 0x0127); + ibmcam_veio(uvd, 0, 0x0013, 0x012e); + ibmcam_veio(uvd, 0, 0x0031, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x0017, 0x012d); + ibmcam_veio(uvd, 0, 0x0078, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0004, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + break; + case VIDEOSIZE_352x288: + ibmcam_veio(uvd, 0, 0x0000, 0x0100); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x00bc, 0x012c); + ibmcam_veio(uvd, 0, 0x0080, 0x012b); + ibmcam_veio(uvd, 0, 0x0000, 0x0108); + ibmcam_veio(uvd, 0, 0x0001, 0x0133); + ibmcam_veio(uvd, 0, 0x009b, 0x010f); + ibmcam_veio(uvd, 0, 0x00bb, 0x010f); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x000a, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x005c, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0004, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00fb, 0x012e); + ibmcam_veio(uvd, 0, 0x0000, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x000c, 0x0127); + ibmcam_veio(uvd, 0, 0x0009, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0012, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0008, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x002a, 0x012d); + ibmcam_veio(uvd, 0, 0x0000, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0034, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x0070, 0x0119); + ibmcam_veio(uvd, 0, 0x00f2, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x008c, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x0039, 0x010a); + ibmcam_veio(uvd, 0, 0x0001, 0x0102); + ibmcam_veio(uvd, 0, 0x002c, 0x0103); + ibmcam_veio(uvd, 0, 0x0000, 0x0104); + ibmcam_veio(uvd, 0, 0x0024, 0x0105); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0016, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0006, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0014, 0x012d); + ibmcam_veio(uvd, 0, 0x0002, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012e); + ibmcam_veio(uvd, 0, 0x001a, 0x0130); + ibmcam_veio(uvd, 0, 0x8a0a, 0x0124); + ibmcam_veio(uvd, 0, 0x005e, 0x012d); + ibmcam_veio(uvd, 0, 0x9545, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x0127); + ibmcam_veio(uvd, 0, 0x0018, 0x012e); + ibmcam_veio(uvd, 0, 0x0049, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x001c, 0x0127); + ibmcam_veio(uvd, 0, 0x00cf, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0032, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0036, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x001e, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0010, 0x0127); + ibmcam_veio(uvd, 0, 0x0013, 0x012e); + ibmcam_veio(uvd, 0, 0x0025, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x0010, 0x012d); + ibmcam_veio(uvd, 0, 0x0048, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0004, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + break; + } + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); +} + +static void ibmcam_model3_setup_after_video_if(struct uvd *uvd) +{ + int i; + /* + * 01.01.08 - Added for RCA video in support -LO + * This struct is used to init the Model3 cam to use the RCA video in port + * instead of the CCD sensor. + */ + static const struct struct_initData initData[] = { + {0, 0x0000, 0x010c}, + {0, 0x0006, 0x012c}, + {0, 0x0078, 0x012d}, + {0, 0x0046, 0x012f}, + {0, 0xd141, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfea8, 0x0124}, + {1, 0x0000, 0x0116}, + {0, 0x0064, 0x0116}, + {1, 0x0000, 0x0115}, + {0, 0x0003, 0x0115}, + {0, 0x0008, 0x0123}, + {0, 0x0000, 0x0117}, + {0, 0x0000, 0x0112}, + {0, 0x0080, 0x0100}, + {0, 0x0000, 0x0100}, + {1, 0x0000, 0x0116}, + {0, 0x0060, 0x0116}, + {0, 0x0002, 0x0112}, + {0, 0x0000, 0x0123}, + {0, 0x0001, 0x0117}, + {0, 0x0040, 0x0108}, + {0, 0x0019, 0x012c}, + {0, 0x0040, 0x0116}, + {0, 0x000a, 0x0115}, + {0, 0x000b, 0x0115}, + {0, 0x0078, 0x012d}, + {0, 0x0046, 0x012f}, + {0, 0xd141, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfea8, 0x0124}, + {0, 0x0064, 0x0116}, + {0, 0x0000, 0x0115}, + {0, 0x0001, 0x0115}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00aa, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00f2, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x000f, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00f8, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00fc, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00f9, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x003c, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0027, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0019, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0021, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0006, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0045, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002a, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x000e, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002b, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00f4, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002c, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0004, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002d, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0014, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002e, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0003, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002f, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0003, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0014, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0053, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0x0000, 0x0101}, + {0, 0x00a0, 0x0103}, + {0, 0x0078, 0x0105}, + {0, 0x0000, 0x010a}, + {0, 0x0024, 0x010b}, + {0, 0x0028, 0x0119}, + {0, 0x0088, 0x011b}, + {0, 0x0002, 0x011d}, + {0, 0x0003, 0x011e}, + {0, 0x0000, 0x0129}, + {0, 0x00fc, 0x012b}, + {0, 0x0008, 0x0102}, + {0, 0x0000, 0x0104}, + {0, 0x0008, 0x011a}, + {0, 0x0028, 0x011c}, + {0, 0x0021, 0x012a}, + {0, 0x0000, 0x0118}, + {0, 0x0000, 0x0132}, + {0, 0x0000, 0x0109}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0031, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00dc, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0032, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0020, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0030, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0008, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0x0003, 0x0106}, + {0, 0x0062, 0x0107}, + {0, 0x0003, 0x0111}, + }; +#define NUM_INIT_DATA + + unsigned short compression = 0; /* 0=none, 7=best frame rate */ + int f_rate; /* 0=Fastest 7=slowest */ + + if (IBMCAM_T(uvd)->initialized) + return; + + /* Internal frame rate is controlled by f_rate value */ + f_rate = 7 - framerate; + RESTRICT_TO_RANGE(f_rate, 0, 7); + + ibmcam_veio(uvd, 0, 0x0000, 0x0100); + ibmcam_veio(uvd, 1, 0x0000, 0x0116); + ibmcam_veio(uvd, 0, 0x0060, 0x0116); + ibmcam_veio(uvd, 0, 0x0002, 0x0112); + ibmcam_veio(uvd, 0, 0x0000, 0x0123); + ibmcam_veio(uvd, 0, 0x0001, 0x0117); + ibmcam_veio(uvd, 0, 0x0040, 0x0108); + ibmcam_veio(uvd, 0, 0x0019, 0x012c); + ibmcam_veio(uvd, 0, 0x0060, 0x0116); + ibmcam_veio(uvd, 0, 0x0002, 0x0115); + ibmcam_veio(uvd, 0, 0x0003, 0x0115); + ibmcam_veio(uvd, 1, 0x0000, 0x0115); + ibmcam_veio(uvd, 0, 0x000b, 0x0115); + ibmcam_model3_Packet1(uvd, 0x000a, 0x0040); + ibmcam_model3_Packet1(uvd, 0x000b, 0x00f6); + ibmcam_model3_Packet1(uvd, 0x000c, 0x0002); + ibmcam_model3_Packet1(uvd, 0x000d, 0x0020); + ibmcam_model3_Packet1(uvd, 0x000e, 0x0033); + ibmcam_model3_Packet1(uvd, 0x000f, 0x0007); + ibmcam_model3_Packet1(uvd, 0x0010, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0011, 0x0070); + ibmcam_model3_Packet1(uvd, 0x0012, 0x0030); + ibmcam_model3_Packet1(uvd, 0x0013, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0014, 0x0001); + ibmcam_model3_Packet1(uvd, 0x0015, 0x0001); + ibmcam_model3_Packet1(uvd, 0x0016, 0x0001); + ibmcam_model3_Packet1(uvd, 0x0017, 0x0001); + ibmcam_model3_Packet1(uvd, 0x0018, 0x0000); + ibmcam_model3_Packet1(uvd, 0x001e, 0x00c3); + ibmcam_model3_Packet1(uvd, 0x0020, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0028, 0x0010); + ibmcam_model3_Packet1(uvd, 0x0029, 0x0054); + ibmcam_model3_Packet1(uvd, 0x002a, 0x0013); + ibmcam_model3_Packet1(uvd, 0x002b, 0x0007); + ibmcam_model3_Packet1(uvd, 0x002d, 0x0028); + ibmcam_model3_Packet1(uvd, 0x002e, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0031, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0032, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0033, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0034, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0035, 0x0038); + ibmcam_model3_Packet1(uvd, 0x003a, 0x0001); + ibmcam_model3_Packet1(uvd, 0x003c, 0x001e); + ibmcam_model3_Packet1(uvd, 0x003f, 0x000a); + ibmcam_model3_Packet1(uvd, 0x0041, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0046, 0x003f); + ibmcam_model3_Packet1(uvd, 0x0047, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0050, 0x0005); + ibmcam_model3_Packet1(uvd, 0x0052, 0x001a); + ibmcam_model3_Packet1(uvd, 0x0053, 0x0003); + ibmcam_model3_Packet1(uvd, 0x005a, 0x006b); + ibmcam_model3_Packet1(uvd, 0x005d, 0x001e); + ibmcam_model3_Packet1(uvd, 0x005e, 0x0030); + ibmcam_model3_Packet1(uvd, 0x005f, 0x0041); + ibmcam_model3_Packet1(uvd, 0x0064, 0x0008); + ibmcam_model3_Packet1(uvd, 0x0065, 0x0015); + ibmcam_model3_Packet1(uvd, 0x0068, 0x000f); + ibmcam_model3_Packet1(uvd, 0x0079, 0x0000); + ibmcam_model3_Packet1(uvd, 0x007a, 0x0000); + ibmcam_model3_Packet1(uvd, 0x007c, 0x003f); + ibmcam_model3_Packet1(uvd, 0x0082, 0x000f); + ibmcam_model3_Packet1(uvd, 0x0085, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0099, 0x0000); + ibmcam_model3_Packet1(uvd, 0x009b, 0x0023); + ibmcam_model3_Packet1(uvd, 0x009c, 0x0022); + ibmcam_model3_Packet1(uvd, 0x009d, 0x0096); + ibmcam_model3_Packet1(uvd, 0x009e, 0x0096); + ibmcam_model3_Packet1(uvd, 0x009f, 0x000a); + + switch (uvd->videosize) { + case VIDEOSIZE_160x120: + ibmcam_veio(uvd, 0, 0x0000, 0x0101); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x00a0, 0x0103); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x0078, 0x0105); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x0000, 0x010a); /* Same */ + ibmcam_veio(uvd, 0, 0x0024, 0x010b); /* Differs everywhere */ + ibmcam_veio(uvd, 0, 0x00a9, 0x0119); + ibmcam_veio(uvd, 0, 0x0016, 0x011b); + ibmcam_veio(uvd, 0, 0x0002, 0x011d); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x0003, 0x011e); /* Same on 176x144, 640x480 */ + ibmcam_veio(uvd, 0, 0x0000, 0x0129); /* Same */ + ibmcam_veio(uvd, 0, 0x00fc, 0x012b); /* Same */ + ibmcam_veio(uvd, 0, 0x0018, 0x0102); + ibmcam_veio(uvd, 0, 0x0004, 0x0104); + ibmcam_veio(uvd, 0, 0x0004, 0x011a); + ibmcam_veio(uvd, 0, 0x0028, 0x011c); + ibmcam_veio(uvd, 0, 0x0022, 0x012a); /* Same */ + ibmcam_veio(uvd, 0, 0x0000, 0x0118); + ibmcam_veio(uvd, 0, 0x0000, 0x0132); + ibmcam_model3_Packet1(uvd, 0x0021, 0x0001); /* Same */ + ibmcam_veio(uvd, 0, compression, 0x0109); + break; + case VIDEOSIZE_320x240: + ibmcam_veio(uvd, 0, 0x0000, 0x0101); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x00a0, 0x0103); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x0078, 0x0105); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x0000, 0x010a); /* Same */ + ibmcam_veio(uvd, 0, 0x0028, 0x010b); /* Differs everywhere */ + ibmcam_veio(uvd, 0, 0x0002, 0x011d); /* Same */ + ibmcam_veio(uvd, 0, 0x0000, 0x011e); + ibmcam_veio(uvd, 0, 0x0000, 0x0129); /* Same */ + ibmcam_veio(uvd, 0, 0x00fc, 0x012b); /* Same */ + /* 4 commands from 160x120 skipped */ + ibmcam_veio(uvd, 0, 0x0022, 0x012a); /* Same */ + ibmcam_model3_Packet1(uvd, 0x0021, 0x0001); /* Same */ + ibmcam_veio(uvd, 0, compression, 0x0109); + ibmcam_veio(uvd, 0, 0x00d9, 0x0119); + ibmcam_veio(uvd, 0, 0x0006, 0x011b); + ibmcam_veio(uvd, 0, 0x0021, 0x0102); /* Same on 320x240, 640x480 */ + ibmcam_veio(uvd, 0, 0x0010, 0x0104); + ibmcam_veio(uvd, 0, 0x0004, 0x011a); + ibmcam_veio(uvd, 0, 0x003f, 0x011c); + ibmcam_veio(uvd, 0, 0x001c, 0x0118); + ibmcam_veio(uvd, 0, 0x0000, 0x0132); + break; + case VIDEOSIZE_640x480: + ibmcam_veio(uvd, 0, 0x00f0, 0x0105); + ibmcam_veio(uvd, 0, 0x0000, 0x010a); /* Same */ + ibmcam_veio(uvd, 0, 0x0038, 0x010b); /* Differs everywhere */ + ibmcam_veio(uvd, 0, 0x00d9, 0x0119); /* Same on 320x240, 640x480 */ + ibmcam_veio(uvd, 0, 0x0006, 0x011b); /* Same on 320x240, 640x480 */ + ibmcam_veio(uvd, 0, 0x0004, 0x011d); /* NC */ + ibmcam_veio(uvd, 0, 0x0003, 0x011e); /* Same on 176x144, 640x480 */ + ibmcam_veio(uvd, 0, 0x0000, 0x0129); /* Same */ + ibmcam_veio(uvd, 0, 0x00fc, 0x012b); /* Same */ + ibmcam_veio(uvd, 0, 0x0021, 0x0102); /* Same on 320x240, 640x480 */ + ibmcam_veio(uvd, 0, 0x0016, 0x0104); /* NC */ + ibmcam_veio(uvd, 0, 0x0004, 0x011a); /* Same on 320x240, 640x480 */ + ibmcam_veio(uvd, 0, 0x003f, 0x011c); /* Same on 320x240, 640x480 */ + ibmcam_veio(uvd, 0, 0x0022, 0x012a); /* Same */ + ibmcam_veio(uvd, 0, 0x001c, 0x0118); /* Same on 320x240, 640x480 */ + ibmcam_model3_Packet1(uvd, 0x0021, 0x0001); /* Same */ + ibmcam_veio(uvd, 0, compression, 0x0109); + ibmcam_veio(uvd, 0, 0x0040, 0x0101); + ibmcam_veio(uvd, 0, 0x0040, 0x0103); + ibmcam_veio(uvd, 0, 0x0000, 0x0132); /* Same on 320x240, 640x480 */ + break; + } + ibmcam_model3_Packet1(uvd, 0x007e, 0x000e); /* Hue */ + ibmcam_model3_Packet1(uvd, 0x0036, 0x0011); /* Brightness */ + ibmcam_model3_Packet1(uvd, 0x0060, 0x0002); /* Sharpness */ + ibmcam_model3_Packet1(uvd, 0x0061, 0x0004); /* Sharpness */ + ibmcam_model3_Packet1(uvd, 0x0062, 0x0005); /* Sharpness */ + ibmcam_model3_Packet1(uvd, 0x0063, 0x0014); /* Sharpness */ + ibmcam_model3_Packet1(uvd, 0x0096, 0x00a0); /* Red gain */ + ibmcam_model3_Packet1(uvd, 0x0097, 0x0096); /* Blue gain */ + ibmcam_model3_Packet1(uvd, 0x0067, 0x0001); /* Contrast */ + ibmcam_model3_Packet1(uvd, 0x005b, 0x000c); /* Contrast */ + ibmcam_model3_Packet1(uvd, 0x005c, 0x0016); /* Contrast */ + ibmcam_model3_Packet1(uvd, 0x0098, 0x000b); + ibmcam_model3_Packet1(uvd, 0x002c, 0x0003); /* Was 1, broke 640x480 */ + ibmcam_model3_Packet1(uvd, 0x002f, 0x002a); + ibmcam_model3_Packet1(uvd, 0x0030, 0x0029); + ibmcam_model3_Packet1(uvd, 0x0037, 0x0002); + ibmcam_model3_Packet1(uvd, 0x0038, 0x0059); + ibmcam_model3_Packet1(uvd, 0x003d, 0x002e); + ibmcam_model3_Packet1(uvd, 0x003e, 0x0028); + ibmcam_model3_Packet1(uvd, 0x0078, 0x0005); + ibmcam_model3_Packet1(uvd, 0x007b, 0x0011); + ibmcam_model3_Packet1(uvd, 0x007d, 0x004b); + ibmcam_model3_Packet1(uvd, 0x007f, 0x0022); + ibmcam_model3_Packet1(uvd, 0x0080, 0x000c); + ibmcam_model3_Packet1(uvd, 0x0081, 0x000b); + ibmcam_model3_Packet1(uvd, 0x0083, 0x00fd); + ibmcam_model3_Packet1(uvd, 0x0086, 0x000b); + ibmcam_model3_Packet1(uvd, 0x0087, 0x000b); + ibmcam_model3_Packet1(uvd, 0x007e, 0x000e); + ibmcam_model3_Packet1(uvd, 0x0096, 0x00a0); /* Red gain */ + ibmcam_model3_Packet1(uvd, 0x0097, 0x0096); /* Blue gain */ + ibmcam_model3_Packet1(uvd, 0x0098, 0x000b); + + switch (uvd->videosize) { + case VIDEOSIZE_160x120: + ibmcam_veio(uvd, 0, 0x0002, 0x0106); + ibmcam_veio(uvd, 0, 0x0008, 0x0107); + ibmcam_veio(uvd, 0, f_rate, 0x0111); /* Frame rate */ + ibmcam_model3_Packet1(uvd, 0x001f, 0x0000); /* Same */ + ibmcam_model3_Packet1(uvd, 0x0039, 0x001f); /* Same */ + ibmcam_model3_Packet1(uvd, 0x003b, 0x003c); /* Same */ + ibmcam_model3_Packet1(uvd, 0x0040, 0x000a); + ibmcam_model3_Packet1(uvd, 0x0051, 0x000a); + break; + case VIDEOSIZE_320x240: + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x0062, 0x0107); + ibmcam_veio(uvd, 0, f_rate, 0x0111); /* Frame rate */ + ibmcam_model3_Packet1(uvd, 0x001f, 0x0000); /* Same */ + ibmcam_model3_Packet1(uvd, 0x0039, 0x001f); /* Same */ + ibmcam_model3_Packet1(uvd, 0x003b, 0x003c); /* Same */ + ibmcam_model3_Packet1(uvd, 0x0040, 0x0008); + ibmcam_model3_Packet1(uvd, 0x0051, 0x000b); + break; + case VIDEOSIZE_640x480: + ibmcam_veio(uvd, 0, 0x0002, 0x0106); /* Adjustments */ + ibmcam_veio(uvd, 0, 0x00b4, 0x0107); /* Adjustments */ + ibmcam_veio(uvd, 0, f_rate, 0x0111); /* Frame rate */ + ibmcam_model3_Packet1(uvd, 0x001f, 0x0002); /* !Same */ + ibmcam_model3_Packet1(uvd, 0x0039, 0x003e); /* !Same */ + ibmcam_model3_Packet1(uvd, 0x0040, 0x0008); + ibmcam_model3_Packet1(uvd, 0x0051, 0x000a); + break; + } + + /* 01.01.08 - Added for RCA video in support -LO */ + if(init_model3_input) { + if (debug > 0) + info("Setting input to RCA."); + for (i=0; i < ARRAY_SIZE(initData); i++) { + ibmcam_veio(uvd, initData[i].req, initData[i].value, initData[i].index); + } + } + + ibmcam_veio(uvd, 0, 0x0001, 0x0114); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); +} + +/* + * ibmcam_video_stop() + * + * This code tells camera to stop streaming. The interface remains + * configured and bandwidth - claimed. + */ +static void ibmcam_video_stop(struct uvd *uvd) +{ + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + ibmcam_veio(uvd, 0, 0x00, 0x010c); + ibmcam_veio(uvd, 0, 0x00, 0x010c); + ibmcam_veio(uvd, 0, 0x01, 0x0114); + ibmcam_veio(uvd, 0, 0xc0, 0x010c); + ibmcam_veio(uvd, 0, 0x00, 0x010c); + ibmcam_send_FF_04_02(uvd); + ibmcam_veio(uvd, 1, 0x00, 0x0100); + ibmcam_veio(uvd, 0, 0x81, 0x0100); /* LED Off */ + break; + case IBMCAM_MODEL_2: +case IBMCAM_MODEL_4: + ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop the camera */ + + ibmcam_model2_Packet1(uvd, 0x0030, 0x0004); + + ibmcam_veio(uvd, 0, 0x0080, 0x0100); /* LED Off */ + ibmcam_veio(uvd, 0, 0x0020, 0x0111); + ibmcam_veio(uvd, 0, 0x00a0, 0x0111); + + ibmcam_model2_Packet1(uvd, 0x0030, 0x0002); + + ibmcam_veio(uvd, 0, 0x0020, 0x0111); + ibmcam_veio(uvd, 0, 0x0000, 0x0112); + break; + case IBMCAM_MODEL_3: +#if 1 + ibmcam_veio(uvd, 0, 0x0000, 0x010c); + + /* Here we are supposed to select video interface alt. setting 0 */ + ibmcam_veio(uvd, 0, 0x0006, 0x012c); + + ibmcam_model3_Packet1(uvd, 0x0046, 0x0000); + + ibmcam_veio(uvd, 1, 0x0000, 0x0116); + ibmcam_veio(uvd, 0, 0x0064, 0x0116); + ibmcam_veio(uvd, 1, 0x0000, 0x0115); + ibmcam_veio(uvd, 0, 0x0003, 0x0115); + ibmcam_veio(uvd, 0, 0x0008, 0x0123); + ibmcam_veio(uvd, 0, 0x0000, 0x0117); + ibmcam_veio(uvd, 0, 0x0000, 0x0112); + ibmcam_veio(uvd, 0, 0x0080, 0x0100); + IBMCAM_T(uvd)->initialized = 0; +#endif + break; + } /* switch */ +} + +/* + * ibmcam_reinit_iso() + * + * This procedure sends couple of commands to the camera and then + * resets the video pipe. This sequence was observed to reinit the + * camera or, at least, to initiate ISO data stream. + * + * History: + * 1/2/00 Created. + */ +static void ibmcam_reinit_iso(struct uvd *uvd, int do_stop) +{ + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + if (do_stop) + ibmcam_video_stop(uvd); + ibmcam_veio(uvd, 0, 0x0001, 0x0114); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); + ibmcam_model1_setup_after_video_if(uvd); + break; + case IBMCAM_MODEL_2: + ibmcam_model2_setup_after_video_if(uvd); + break; + case IBMCAM_MODEL_3: + ibmcam_video_stop(uvd); + ibmcam_model3_setup_after_video_if(uvd); + break; + case IBMCAM_MODEL_4: + ibmcam_model4_setup_after_video_if(uvd); + break; + } +} + +static void ibmcam_video_start(struct uvd *uvd) +{ + ibmcam_change_lighting_conditions(uvd); + ibmcam_set_sharpness(uvd); + ibmcam_reinit_iso(uvd, 0); +} + +/* + * Return negative code on failure, 0 on success. + */ +static int ibmcam_setup_on_open(struct uvd *uvd) +{ + int setup_ok = 0; /* Success by default */ + /* Send init sequence only once, it's large! */ + if (!IBMCAM_T(uvd)->initialized) { /* FIXME rename */ + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + setup_ok = ibmcam_model1_setup(uvd); + break; + case IBMCAM_MODEL_2: + setup_ok = ibmcam_model2_setup(uvd); + break; + case IBMCAM_MODEL_3: + case IBMCAM_MODEL_4: + /* We do all setup when Isoc stream is requested */ + break; + } + IBMCAM_T(uvd)->initialized = (setup_ok != 0); + } + return setup_ok; +} + +static void ibmcam_configure_video(struct uvd *uvd) +{ + if (uvd == NULL) + return; + + RESTRICT_TO_RANGE(init_brightness, 0, 255); + RESTRICT_TO_RANGE(init_contrast, 0, 255); + RESTRICT_TO_RANGE(init_color, 0, 255); + RESTRICT_TO_RANGE(init_hue, 0, 255); + RESTRICT_TO_RANGE(hue_correction, 0, 255); + + memset(&uvd->vpic, 0, sizeof(uvd->vpic)); + memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old)); + + uvd->vpic.colour = init_color << 8; + uvd->vpic.hue = init_hue << 8; + uvd->vpic.brightness = init_brightness << 8; + uvd->vpic.contrast = init_contrast << 8; + uvd->vpic.whiteness = 105 << 8; /* This one isn't used */ + uvd->vpic.depth = 24; + uvd->vpic.palette = VIDEO_PALETTE_RGB24; + + memset(&uvd->vcap, 0, sizeof(uvd->vcap)); + strcpy(uvd->vcap.name, "IBM USB Camera"); + uvd->vcap.type = VID_TYPE_CAPTURE; + uvd->vcap.channels = 1; + uvd->vcap.audios = 0; + uvd->vcap.maxwidth = VIDEOSIZE_X(uvd->canvas); + uvd->vcap.maxheight = VIDEOSIZE_Y(uvd->canvas); + uvd->vcap.minwidth = min_canvasWidth; + uvd->vcap.minheight = min_canvasHeight; + + memset(&uvd->vchan, 0, sizeof(uvd->vchan)); + uvd->vchan.flags = 0; + uvd->vchan.tuners = 0; + uvd->vchan.channel = 0; + uvd->vchan.type = VIDEO_TYPE_CAMERA; + strcpy(uvd->vchan.name, "Camera"); +} + +/* + * ibmcam_probe() + * + * This procedure queries device descriptor and accepts the interface + * if it looks like IBM C-it camera. + * + * History: + * 22-Jan-2000 Moved camera init code to ibmcam_open() + * 27=Jan-2000 Changed to use static structures, added locking. + * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). + * 03-Jul-2000 Fixed endianness bug. + * 12-Nov-2000 Reworked to comply with new probe() signature. + * 23-Jan-2001 Added compatibility with 2.2.x kernels. + */ +static int ibmcam_probe(struct usb_interface *intf, const struct usb_device_id *devid) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct uvd *uvd = NULL; + int ix, i, nas, model=0, canvasX=0, canvasY=0; + int actInterface=-1, inactInterface=-1, maxPS=0; + __u8 ifnum = intf->altsetting->desc.bInterfaceNumber; + unsigned char video_ep = 0; + + if (debug >= 1) + info("ibmcam_probe(%p,%u.)", intf, ifnum); + + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return -ENODEV; + + /* Check the version/revision */ + switch (le16_to_cpu(dev->descriptor.bcdDevice)) { + case 0x0002: + if (ifnum != 2) + return -ENODEV; + model = IBMCAM_MODEL_1; + break; + case 0x030A: + if (ifnum != 0) + return -ENODEV; + if ((le16_to_cpu(dev->descriptor.idProduct) == NETCAM_PRODUCT_ID) || + (le16_to_cpu(dev->descriptor.idProduct) == VEO_800D_PRODUCT_ID)) + model = IBMCAM_MODEL_4; + else + model = IBMCAM_MODEL_2; + break; + case 0x0301: + if (ifnum != 0) + return -ENODEV; + model = IBMCAM_MODEL_3; + break; + default: + err("IBM camera with revision 0x%04x is not supported.", + le16_to_cpu(dev->descriptor.bcdDevice)); + return -ENODEV; + } + + /* Print detailed info on what we found so far */ + do { + char *brand = NULL; + switch (le16_to_cpu(dev->descriptor.idProduct)) { + case NETCAM_PRODUCT_ID: + brand = "IBM NetCamera"; + break; + case VEO_800C_PRODUCT_ID: + brand = "Veo Stingray [800C]"; + break; + case VEO_800D_PRODUCT_ID: + brand = "Veo Stingray [800D]"; + break; + case IBMCAM_PRODUCT_ID: + default: + brand = "IBM PC Camera"; /* a.k.a. Xirlink C-It */ + break; + } + info("%s USB camera found (model %d, rev. 0x%04x)", + brand, model, le16_to_cpu(dev->descriptor.bcdDevice)); + } while (0); + + /* Validate found interface: must have one ISO endpoint */ + nas = intf->num_altsetting; + if (debug > 0) + info("Number of alternate settings=%d.", nas); + if (nas < 2) { + err("Too few alternate settings for this camera!"); + return -ENODEV; + } + /* Validate all alternate settings */ + for (ix=0; ix < nas; ix++) { + const struct usb_host_interface *interface; + const struct usb_endpoint_descriptor *endpoint; + + interface = &intf->altsetting[ix]; + i = interface->desc.bAlternateSetting; + if (interface->desc.bNumEndpoints != 1) { + err("Interface %d. has %u. endpoints!", + ifnum, (unsigned)(interface->desc.bNumEndpoints)); + return -ENODEV; + } + endpoint = &interface->endpoint[0].desc; + if (video_ep == 0) + video_ep = endpoint->bEndpointAddress; + else if (video_ep != endpoint->bEndpointAddress) { + err("Alternate settings have different endpoint addresses!"); + return -ENODEV; + } + if ((endpoint->bmAttributes & 0x03) != 0x01) { + err("Interface %d. has non-ISO endpoint!", ifnum); + return -ENODEV; + } + if ((endpoint->bEndpointAddress & 0x80) == 0) { + err("Interface %d. has ISO OUT endpoint!", ifnum); + return -ENODEV; + } + if (le16_to_cpu(endpoint->wMaxPacketSize) == 0) { + if (inactInterface < 0) + inactInterface = i; + else { + err("More than one inactive alt. setting!"); + return -ENODEV; + } + } else { + if (actInterface < 0) { + actInterface = i; + maxPS = le16_to_cpu(endpoint->wMaxPacketSize); + if (debug > 0) + info("Active setting=%d. maxPS=%d.", i, maxPS); + } else + err("More than one active alt. setting! Ignoring #%d.", i); + } + } + if ((maxPS <= 0) || (actInterface < 0) || (inactInterface < 0)) { + err("Failed to recognize the camera!"); + return -ENODEV; + } + + /* Validate options */ + switch (model) { + case IBMCAM_MODEL_1: + RESTRICT_TO_RANGE(lighting, 0, 2); + RESTRICT_TO_RANGE(size, SIZE_128x96, SIZE_352x288); + if (framerate < 0) + framerate = 2; + canvasX = 352; + canvasY = 288; + break; + case IBMCAM_MODEL_2: + RESTRICT_TO_RANGE(lighting, 0, 15); + RESTRICT_TO_RANGE(size, SIZE_176x144, SIZE_352x240); + if (framerate < 0) + framerate = 2; + canvasX = 352; + canvasY = 240; + break; + case IBMCAM_MODEL_3: + RESTRICT_TO_RANGE(lighting, 0, 15); /* FIXME */ + switch (size) { + case SIZE_160x120: + canvasX = 160; + canvasY = 120; + if (framerate < 0) + framerate = 2; + RESTRICT_TO_RANGE(framerate, 0, 5); + break; + default: + info("IBM camera: using 320x240"); + size = SIZE_320x240; + /* No break here */ + case SIZE_320x240: + canvasX = 320; + canvasY = 240; + if (framerate < 0) + framerate = 3; + RESTRICT_TO_RANGE(framerate, 0, 5); + break; + case SIZE_640x480: + canvasX = 640; + canvasY = 480; + framerate = 0; /* Slowest, and maybe even that is too fast */ + break; + } + break; + case IBMCAM_MODEL_4: + RESTRICT_TO_RANGE(lighting, 0, 2); + switch (size) { + case SIZE_128x96: + canvasX = 128; + canvasY = 96; + break; + case SIZE_160x120: + canvasX = 160; + canvasY = 120; + break; + default: + info("IBM NetCamera: using 176x144"); + size = SIZE_176x144; + /* No break here */ + case SIZE_176x144: + canvasX = 176; + canvasY = 144; + break; + case SIZE_320x240: + canvasX = 320; + canvasY = 240; + break; + case SIZE_352x288: + canvasX = 352; + canvasY = 288; + break; + } + break; + default: + err("IBM camera: Model %d. not supported!", model); + return -ENODEV; + } + + uvd = usbvideo_AllocateDevice(cams); + if (uvd != NULL) { + /* Here uvd is a fully allocated uvd object */ + uvd->flags = flags; + uvd->debug = debug; + uvd->dev = dev; + uvd->iface = ifnum; + uvd->ifaceAltInactive = inactInterface; + uvd->ifaceAltActive = actInterface; + uvd->video_endp = video_ep; + uvd->iso_packet_len = maxPS; + uvd->paletteBits = 1L << VIDEO_PALETTE_RGB24; + uvd->defaultPalette = VIDEO_PALETTE_RGB24; + uvd->canvas = VIDEOSIZE(canvasX, canvasY); + uvd->videosize = ibmcam_size_to_videosize(size); + + /* Initialize ibmcam-specific data */ + assert(IBMCAM_T(uvd) != NULL); + IBMCAM_T(uvd)->camera_model = model; + IBMCAM_T(uvd)->initialized = 0; + + ibmcam_configure_video(uvd); + + i = usbvideo_RegisterVideoDevice(uvd); + if (i != 0) { + err("usbvideo_RegisterVideoDevice() failed."); + uvd = NULL; + } + } + usb_set_intfdata (intf, uvd); + return 0; +} + + +static struct usb_device_id id_table[] = { + { USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x0002, 0x0002) }, /* Model 1 */ + { USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x030a, 0x030a) }, /* Model 2 */ + { USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x0301, 0x0301) }, /* Model 3 */ + { USB_DEVICE_VER(IBMCAM_VENDOR_ID, NETCAM_PRODUCT_ID, 0x030a, 0x030a) }, /* Model 4 */ + { USB_DEVICE_VER(IBMCAM_VENDOR_ID, VEO_800C_PRODUCT_ID, 0x030a, 0x030a) }, /* Model 2 */ + { USB_DEVICE_VER(IBMCAM_VENDOR_ID, VEO_800D_PRODUCT_ID, 0x030a, 0x030a) }, /* Model 4 */ + { } /* Terminating entry */ +}; + +/* + * ibmcam_init() + * + * This code is run to initialize the driver. + * + * History: + * 1/27/00 Reworked to use statically allocated ibmcam structures. + * 21/10/00 Completely redesigned to use usbvideo services. + */ +static int __init ibmcam_init(void) +{ + struct usbvideo_cb cbTbl; + memset(&cbTbl, 0, sizeof(cbTbl)); + cbTbl.probe = ibmcam_probe; + cbTbl.setupOnOpen = ibmcam_setup_on_open; + cbTbl.videoStart = ibmcam_video_start; + cbTbl.videoStop = ibmcam_video_stop; + cbTbl.processData = ibmcam_ProcessIsocData; + cbTbl.postProcess = usbvideo_DeinterlaceFrame; + cbTbl.adjustPicture = ibmcam_adjust_picture; + cbTbl.getFPS = ibmcam_calculate_fps; + return usbvideo_register( + &cams, + MAX_IBMCAM, + sizeof(ibmcam_t), + "ibmcam", + &cbTbl, + THIS_MODULE, + id_table); +} + +static void __exit ibmcam_cleanup(void) +{ + usbvideo_Deregister(&cams); +} + +MODULE_DEVICE_TABLE(usb, id_table); + +module_init(ibmcam_init); +module_exit(ibmcam_cleanup); diff --git a/linux/drivers/media/video/usbvideo/konicawc.c b/linux/drivers/media/video/usbvideo/konicawc.c new file mode 100644 index 000000000..c11f5d46b --- /dev/null +++ b/linux/drivers/media/video/usbvideo/konicawc.c @@ -0,0 +1,978 @@ +/* + * konicawc.c - konica webcam driver + * + * Author: Simon Evans + * + * Copyright (C) 2002 Simon Evans + * + * Licence: GPL + * + * Driver for USB webcams based on Konica chipset. This + * chipset is used in Intel YC76 camera. + * + */ + +#include +#include +#include +#include +#include + +#include "usbvideo.h" + +#define MAX_BRIGHTNESS 108 +#define MAX_CONTRAST 108 +#define MAX_SATURATION 108 +#define MAX_SHARPNESS 108 +#define MAX_WHITEBAL 372 +#define MAX_SPEED 6 + + +#define MAX_CAMERAS 1 + +#define DRIVER_VERSION "v1.4" +#define DRIVER_DESC "Konica Webcam driver" + +enum ctrl_req { + SetWhitebal = 0x01, + SetBrightness = 0x02, + SetSharpness = 0x03, + SetContrast = 0x04, + SetSaturation = 0x05, +}; + + +enum frame_sizes { + SIZE_160X120 = 0, + SIZE_160X136 = 1, + SIZE_176X144 = 2, + SIZE_320X240 = 3, + +}; + +#define MAX_FRAME_SIZE SIZE_320X240 + +static struct usbvideo *cams; + +#ifdef CONFIG_USB_DEBUG +static int debug; +#define DEBUG(n, format, arg...) \ + if (n <= debug) { \ + printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __FUNCTION__ , ## arg); \ + } +#else +#define DEBUG(n, arg...) +static const int debug = 0; +#endif + + +/* Some default values for initial camera settings, + can be set by modprobe */ + +static int size; +static int speed = 6; /* Speed (fps) 0 (slowest) to 6 (fastest) */ +static int brightness = MAX_BRIGHTNESS/2; +static int contrast = MAX_CONTRAST/2; +static int saturation = MAX_SATURATION/2; +static int sharpness = MAX_SHARPNESS/2; +static int whitebal = 3*(MAX_WHITEBAL/4); + +static const int spd_to_iface[] = { 1, 0, 3, 2, 4, 5, 6 }; + +/* These FPS speeds are from the windows config box. They are + * indexed on size (0-2) and speed (0-6). Divide by 3 to get the + * real fps. + */ + +static const int spd_to_fps[][7] = { { 24, 40, 48, 60, 72, 80, 100 }, + { 24, 40, 48, 60, 72, 80, 100 }, + { 18, 30, 36, 45, 54, 60, 75 }, + { 6, 10, 12, 15, 18, 21, 25 } }; + +struct cam_size { + u16 width; + u16 height; + u8 cmd; +}; + +static const struct cam_size camera_sizes[] = { { 160, 120, 0x7 }, + { 160, 136, 0xa }, + { 176, 144, 0x4 }, + { 320, 240, 0x5 } }; + +struct konicawc { + u8 brightness; /* camera uses 0 - 9, x11 for real value */ + u8 contrast; /* as above */ + u8 saturation; /* as above */ + u8 sharpness; /* as above */ + u8 white_bal; /* 0 - 33, x11 for real value */ + u8 speed; /* Stored as 0 - 6, used as index in spd_to_* (above) */ + u8 size; /* Frame Size */ + int height; + int width; + struct urb *sts_urb[USBVIDEO_NUMSBUF]; + u8 sts_buf[USBVIDEO_NUMSBUF][FRAMES_PER_DESC]; + struct urb *last_data_urb; + int lastframe; + int cur_frame_size; /* number of bytes in current frame size */ + int maxline; /* number of lines per frame */ + int yplanesz; /* Number of bytes in the Y plane */ + unsigned int buttonsts:1; +#ifdef CONFIG_INPUT + struct input_dev *input; + char input_physname[64]; +#endif +}; + + +#define konicawc_set_misc(uvd, req, value, index) konicawc_ctrl_msg(uvd, USB_DIR_OUT, req, value, index, NULL, 0) +#define konicawc_get_misc(uvd, req, value, index, buf, sz) konicawc_ctrl_msg(uvd, USB_DIR_IN, req, value, index, buf, sz) +#define konicawc_set_value(uvd, value, index) konicawc_ctrl_msg(uvd, USB_DIR_OUT, 2, value, index, NULL, 0) + + +static int konicawc_ctrl_msg(struct uvd *uvd, u8 dir, u8 request, u16 value, u16 index, void *buf, int len) +{ + int retval = usb_control_msg(uvd->dev, + dir ? usb_rcvctrlpipe(uvd->dev, 0) : usb_sndctrlpipe(uvd->dev, 0), + request, 0x40 | dir, value, index, buf, len, 1000); + return retval < 0 ? retval : 0; +} + + +static inline void konicawc_camera_on(struct uvd *uvd) +{ + DEBUG(0, "camera on"); + konicawc_set_misc(uvd, 0x2, 1, 0x0b); +} + + +static inline void konicawc_camera_off(struct uvd *uvd) +{ + DEBUG(0, "camera off"); + konicawc_set_misc(uvd, 0x2, 0, 0x0b); +} + + +static void konicawc_set_camera_size(struct uvd *uvd) +{ + struct konicawc *cam = (struct konicawc *)uvd->user_data; + + konicawc_set_misc(uvd, 0x2, camera_sizes[cam->size].cmd, 0x08); + cam->width = camera_sizes[cam->size].width; + cam->height = camera_sizes[cam->size].height; + cam->yplanesz = cam->height * cam->width; + cam->cur_frame_size = (cam->yplanesz * 3) / 2; + cam->maxline = cam->yplanesz / 256; + uvd->videosize = VIDEOSIZE(cam->width, cam->height); +} + + +static int konicawc_setup_on_open(struct uvd *uvd) +{ + struct konicawc *cam = (struct konicawc *)uvd->user_data; + + DEBUG(1, "setting brightness to %d (%d)", cam->brightness, + cam->brightness * 11); + konicawc_set_value(uvd, cam->brightness, SetBrightness); + DEBUG(1, "setting white balance to %d (%d)", cam->white_bal, + cam->white_bal * 11); + konicawc_set_value(uvd, cam->white_bal, SetWhitebal); + DEBUG(1, "setting contrast to %d (%d)", cam->contrast, + cam->contrast * 11); + konicawc_set_value(uvd, cam->contrast, SetContrast); + DEBUG(1, "setting saturation to %d (%d)", cam->saturation, + cam->saturation * 11); + konicawc_set_value(uvd, cam->saturation, SetSaturation); + DEBUG(1, "setting sharpness to %d (%d)", cam->sharpness, + cam->sharpness * 11); + konicawc_set_value(uvd, cam->sharpness, SetSharpness); + konicawc_set_camera_size(uvd); + cam->lastframe = -2; + cam->buttonsts = 0; + return 0; +} + + +static void konicawc_adjust_picture(struct uvd *uvd) +{ + struct konicawc *cam = (struct konicawc *)uvd->user_data; + + konicawc_camera_off(uvd); + DEBUG(1, "new brightness: %d", uvd->vpic.brightness); + uvd->vpic.brightness = (uvd->vpic.brightness > MAX_BRIGHTNESS) ? MAX_BRIGHTNESS : uvd->vpic.brightness; + if(cam->brightness != uvd->vpic.brightness / 11) { + cam->brightness = uvd->vpic.brightness / 11; + DEBUG(1, "setting brightness to %d (%d)", cam->brightness, + cam->brightness * 11); + konicawc_set_value(uvd, cam->brightness, SetBrightness); + } + + DEBUG(1, "new contrast: %d", uvd->vpic.contrast); + uvd->vpic.contrast = (uvd->vpic.contrast > MAX_CONTRAST) ? MAX_CONTRAST : uvd->vpic.contrast; + if(cam->contrast != uvd->vpic.contrast / 11) { + cam->contrast = uvd->vpic.contrast / 11; + DEBUG(1, "setting contrast to %d (%d)", cam->contrast, + cam->contrast * 11); + konicawc_set_value(uvd, cam->contrast, SetContrast); + } + konicawc_camera_on(uvd); +} + +#ifdef CONFIG_INPUT + +static void konicawc_register_input(struct konicawc *cam, struct usb_device *dev) +{ + struct input_dev *input_dev; + + usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname)); + strncat(cam->input_physname, "/input0", sizeof(cam->input_physname)); + + cam->input = input_dev = input_allocate_device(); + if (!input_dev) { + warn("Not enough memory for camera's input device\n"); + return; + } + + input_dev->name = "Konicawc snapshot button"; + input_dev->phys = cam->input_physname; + usb_to_input_id(dev, &input_dev->id); + input_dev->cdev.dev = &dev->dev; + + input_dev->evbit[0] = BIT(EV_KEY); + input_dev->keybit[LONG(BTN_0)] = BIT(BTN_0); + + input_dev->private = cam; + + input_register_device(cam->input); +} + +static void konicawc_unregister_input(struct konicawc *cam) +{ + if (cam->input) { + input_unregister_device(cam->input); + cam->input = NULL; + } +} + +static void konicawc_report_buttonstat(struct konicawc *cam) +{ + if (cam->input) { + input_report_key(cam->input, BTN_0, cam->buttonsts); + input_sync(cam->input); + } +} + +#else + +static inline void konicawc_register_input(struct konicawc *cam, struct usb_device *dev) { } +static inline void konicawc_unregister_input(struct konicawc *cam) { } +static inline void konicawc_report_buttonstat(struct konicawc *cam) { } + +#endif /* CONFIG_INPUT */ + +static int konicawc_compress_iso(struct uvd *uvd, struct urb *dataurb, struct urb *stsurb) +{ + char *cdata; + int i, totlen = 0; + unsigned char *status = stsurb->transfer_buffer; + int keep = 0, discard = 0, bad = 0; + struct konicawc *cam = (struct konicawc *)uvd->user_data; + + for (i = 0; i < dataurb->number_of_packets; i++) { + int button = cam->buttonsts; + unsigned char sts; + int n = dataurb->iso_frame_desc[i].actual_length; + int st = dataurb->iso_frame_desc[i].status; + cdata = dataurb->transfer_buffer + + dataurb->iso_frame_desc[i].offset; + + /* Detect and ignore errored packets */ + if (st < 0) { + DEBUG(1, "Data error: packet=%d. len=%d. status=%d.", + i, n, st); + uvd->stats.iso_err_count++; + continue; + } + + /* Detect and ignore empty packets */ + if (n <= 0) { + uvd->stats.iso_skip_count++; + continue; + } + + /* See what the status data said about the packet */ + sts = *(status+stsurb->iso_frame_desc[i].offset); + + /* sts: 0x80-0xff: frame start with frame number (ie 0-7f) + * otherwise: + * bit 0 0: keep packet + * 1: drop packet (padding data) + * + * bit 4 0 button not clicked + * 1 button clicked + * button is used to `take a picture' (in software) + */ + + if(sts < 0x80) { + button = !!(sts & 0x40); + sts &= ~0x40; + } + + /* work out the button status, but don't do + anything with it for now */ + + if(button != cam->buttonsts) { + DEBUG(2, "button: %sclicked", button ? "" : "un"); + cam->buttonsts = button; + konicawc_report_buttonstat(cam); + } + + if(sts == 0x01) { /* drop frame */ + discard++; + continue; + } + + if((sts > 0x01) && (sts < 0x80)) { + info("unknown status %2.2x", sts); + bad++; + continue; + } + if(!sts && cam->lastframe == -2) { + DEBUG(2, "dropping frame looking for image start"); + continue; + } + + keep++; + if(sts & 0x80) { /* frame start */ + unsigned char marker[] = { 0, 0xff, 0, 0x00 }; + + if(cam->lastframe == -2) { + DEBUG(2, "found initial image"); + cam->lastframe = -1; + } + + marker[3] = sts & 0x7F; + RingQueue_Enqueue(&uvd->dp, marker, 4); + totlen += 4; + } + + totlen += n; /* Little local accounting */ + RingQueue_Enqueue(&uvd->dp, cdata, n); + } + DEBUG(8, "finished: keep = %d discard = %d bad = %d added %d bytes", + keep, discard, bad, totlen); + return totlen; +} + + +static void resubmit_urb(struct uvd *uvd, struct urb *urb) +{ + int i, ret; + for (i = 0; i < FRAMES_PER_DESC; i++) { + urb->iso_frame_desc[i].status = 0; + } + urb->dev = uvd->dev; + urb->status = 0; + ret = usb_submit_urb(urb, GFP_ATOMIC); + DEBUG(3, "submitting urb of length %d", urb->transfer_buffer_length); + if(ret) + err("usb_submit_urb error (%d)", ret); + +} + + +static void konicawc_isoc_irq(struct urb *urb, struct pt_regs *regs) +{ + struct uvd *uvd = urb->context; + struct konicawc *cam = (struct konicawc *)uvd->user_data; + + /* We don't want to do anything if we are about to be removed! */ + if (!CAMERA_IS_OPERATIONAL(uvd)) + return; + + if (!uvd->streaming) { + DEBUG(1, "Not streaming, but interrupt!"); + return; + } + + DEBUG(3, "got frame %d len = %d buflen =%d", urb->start_frame, urb->actual_length, urb->transfer_buffer_length); + + uvd->stats.urb_count++; + + if (urb->transfer_buffer_length > 32) { + cam->last_data_urb = urb; + return; + } + /* Copy the data received into ring queue */ + if(cam->last_data_urb) { + int len = 0; + if(urb->start_frame != cam->last_data_urb->start_frame) + err("Lost sync on frames"); + else if (!urb->status && !cam->last_data_urb->status) + len = konicawc_compress_iso(uvd, cam->last_data_urb, urb); + + resubmit_urb(uvd, cam->last_data_urb); + resubmit_urb(uvd, urb); + cam->last_data_urb = NULL; + uvd->stats.urb_length = len; + uvd->stats.data_count += len; + if(len) + RingQueue_WakeUpInterruptible(&uvd->dp); + return; + } + return; +} + + +static int konicawc_start_data(struct uvd *uvd) +{ + struct usb_device *dev = uvd->dev; + int i, errFlag; + struct konicawc *cam = (struct konicawc *)uvd->user_data; + int pktsz; + struct usb_interface *intf; + struct usb_host_interface *interface = NULL; + + intf = usb_ifnum_to_if(dev, uvd->iface); + if (intf) + interface = usb_altnum_to_altsetting(intf, + spd_to_iface[cam->speed]); + if (!interface) + return -ENXIO; + pktsz = le16_to_cpu(interface->endpoint[1].desc.wMaxPacketSize); + DEBUG(1, "pktsz = %d", pktsz); + if (!CAMERA_IS_OPERATIONAL(uvd)) { + err("Camera is not operational"); + return -EFAULT; + } + uvd->curframe = -1; + konicawc_camera_on(uvd); + /* Alternate interface 1 is is the biggest frame size */ + i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive); + if (i < 0) { + err("usb_set_interface error"); + uvd->last_error = i; + return -EBUSY; + } + + /* We double buffer the Iso lists */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + int j, k; + struct urb *urb = uvd->sbuf[i].urb; + urb->dev = dev; + urb->context = uvd; + urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp); + urb->interval = 1; + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = uvd->sbuf[i].data; + urb->complete = konicawc_isoc_irq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = pktsz * FRAMES_PER_DESC; + for (j=k=0; j < FRAMES_PER_DESC; j++, k += pktsz) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = pktsz; + } + + urb = cam->sts_urb[i]; + urb->dev = dev; + urb->context = uvd; + urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp-1); + urb->interval = 1; + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = cam->sts_buf[i]; + urb->complete = konicawc_isoc_irq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = FRAMES_PER_DESC; + for (j=0; j < FRAMES_PER_DESC; j++) { + urb->iso_frame_desc[j].offset = j; + urb->iso_frame_desc[j].length = 1; + } + } + + cam->last_data_urb = NULL; + + /* Submit all URBs */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + errFlag = usb_submit_urb(cam->sts_urb[i], GFP_KERNEL); + if (errFlag) + err("usb_submit_isoc(%d) ret %d", i, errFlag); + + errFlag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL); + if (errFlag) + err ("usb_submit_isoc(%d) ret %d", i, errFlag); + } + + uvd->streaming = 1; + DEBUG(1, "streaming=1 video_endp=$%02x", uvd->video_endp); + return 0; +} + + +static void konicawc_stop_data(struct uvd *uvd) +{ + int i, j; + struct konicawc *cam; + + if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL)) + return; + + konicawc_camera_off(uvd); + uvd->streaming = 0; + cam = (struct konicawc *)uvd->user_data; + cam->last_data_urb = NULL; + + /* Unschedule all of the iso td's */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + usb_kill_urb(uvd->sbuf[i].urb); + usb_kill_urb(cam->sts_urb[i]); + } + + if (!uvd->remove_pending) { + /* Set packet size to 0 */ + j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive); + if (j < 0) { + err("usb_set_interface() error %d.", j); + uvd->last_error = j; + } + } +} + + +static void konicawc_process_isoc(struct uvd *uvd, struct usbvideo_frame *frame) +{ + struct konicawc *cam = (struct konicawc *)uvd->user_data; + int maxline = cam->maxline; + int yplanesz = cam->yplanesz; + + assert(frame != NULL); + + DEBUG(5, "maxline = %d yplanesz = %d", maxline, yplanesz); + DEBUG(3, "Frame state = %d", frame->scanstate); + + if(frame->scanstate == ScanState_Scanning) { + int drop = 0; + int curframe; + int fdrops = 0; + DEBUG(3, "Searching for marker, queue len = %d", RingQueue_GetLength(&uvd->dp)); + while(RingQueue_GetLength(&uvd->dp) >= 4) { + if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) && + (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xff) && + (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00) && + (RING_QUEUE_PEEK(&uvd->dp, 3) < 0x80)) { + curframe = RING_QUEUE_PEEK(&uvd->dp, 3); + if(cam->lastframe >= 0) { + fdrops = (0x80 + curframe - cam->lastframe) & 0x7F; + fdrops--; + if(fdrops) { + info("Dropped %d frames (%d -> %d)", fdrops, + cam->lastframe, curframe); + } + } + cam->lastframe = curframe; + frame->curline = 0; + frame->scanstate = ScanState_Lines; + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 4); + break; + } + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1); + drop++; + } + if(drop) + DEBUG(2, "dropped %d bytes looking for new frame", drop); + } + + if(frame->scanstate == ScanState_Scanning) + return; + + /* Try to move data from queue into frame buffer + * We get data in blocks of 384 bytes made up of: + * 256 Y, 64 U, 64 V. + * This needs to be written out as a Y plane, a U plane and a V plane. + */ + + while ( frame->curline < maxline && (RingQueue_GetLength(&uvd->dp) >= 384)) { + /* Y */ + RingQueue_Dequeue(&uvd->dp, frame->data + (frame->curline * 256), 256); + /* U */ + RingQueue_Dequeue(&uvd->dp, frame->data + yplanesz + (frame->curline * 64), 64); + /* V */ + RingQueue_Dequeue(&uvd->dp, frame->data + (5 * yplanesz)/4 + (frame->curline * 64), 64); + frame->seqRead_Length += 384; + frame->curline++; + } + /* See if we filled the frame */ + if (frame->curline == maxline) { + DEBUG(5, "got whole frame"); + + frame->frameState = FrameState_Done_Hold; + frame->curline = 0; + uvd->curframe = -1; + uvd->stats.frame_num++; + } +} + + +static int konicawc_find_fps(int size, int fps) +{ + int i; + + fps *= 3; + DEBUG(1, "konica_find_fps: size = %d fps = %d", size, fps); + if(fps <= spd_to_fps[size][0]) + return 0; + + if(fps >= spd_to_fps[size][MAX_SPEED]) + return MAX_SPEED; + + for(i = 0; i < MAX_SPEED; i++) { + if((fps >= spd_to_fps[size][i]) && (fps <= spd_to_fps[size][i+1])) { + DEBUG(2, "fps %d between %d and %d", fps, i, i+1); + if( (fps - spd_to_fps[size][i]) < (spd_to_fps[size][i+1] - fps)) + return i; + else + return i+1; + } + } + return MAX_SPEED+1; +} + + +static int konicawc_set_video_mode(struct uvd *uvd, struct video_window *vw) +{ + struct konicawc *cam = (struct konicawc *)uvd->user_data; + int newspeed = cam->speed; + int newsize; + int x = vw->width; + int y = vw->height; + int fps = vw->flags; + + if(x > 0 && y > 0) { + DEBUG(2, "trying to find size %d,%d", x, y); + for(newsize = 0; newsize <= MAX_FRAME_SIZE; newsize++) { + if((camera_sizes[newsize].width == x) && (camera_sizes[newsize].height == y)) + break; + } + } else { + newsize = cam->size; + } + + if(newsize > MAX_FRAME_SIZE) { + DEBUG(1, "couldn't find size %d,%d", x, y); + return -EINVAL; + } + + if(fps > 0) { + DEBUG(1, "trying to set fps to %d", fps); + newspeed = konicawc_find_fps(newsize, fps); + DEBUG(1, "find_fps returned %d (%d)", newspeed, spd_to_fps[newsize][newspeed]); + } + + if(newspeed > MAX_SPEED) + return -EINVAL; + + DEBUG(1, "setting size to %d speed to %d", newsize, newspeed); + if((newsize == cam->size) && (newspeed == cam->speed)) { + DEBUG(1, "Nothing to do"); + return 0; + } + DEBUG(0, "setting to %dx%d @ %d fps", camera_sizes[newsize].width, + camera_sizes[newsize].height, spd_to_fps[newsize][newspeed]/3); + + konicawc_stop_data(uvd); + uvd->ifaceAltActive = spd_to_iface[newspeed]; + DEBUG(1, "new interface = %d", uvd->ifaceAltActive); + cam->speed = newspeed; + + if(cam->size != newsize) { + cam->size = newsize; + konicawc_set_camera_size(uvd); + } + + /* Flush the input queue and clear any current frame in progress */ + + RingQueue_Flush(&uvd->dp); + cam->lastframe = -2; + if(uvd->curframe != -1) { + uvd->frame[uvd->curframe].curline = 0; + uvd->frame[uvd->curframe].seqRead_Length = 0; + uvd->frame[uvd->curframe].seqRead_Index = 0; + } + + konicawc_start_data(uvd); + return 0; +} + + +static int konicawc_calculate_fps(struct uvd *uvd) +{ + struct konicawc *cam = uvd->user_data; + return spd_to_fps[cam->size][cam->speed]/3; +} + + +static void konicawc_configure_video(struct uvd *uvd) +{ + struct konicawc *cam = (struct konicawc *)uvd->user_data; + u8 buf[2]; + + memset(&uvd->vpic, 0, sizeof(uvd->vpic)); + memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old)); + + RESTRICT_TO_RANGE(brightness, 0, MAX_BRIGHTNESS); + RESTRICT_TO_RANGE(contrast, 0, MAX_CONTRAST); + RESTRICT_TO_RANGE(saturation, 0, MAX_SATURATION); + RESTRICT_TO_RANGE(sharpness, 0, MAX_SHARPNESS); + RESTRICT_TO_RANGE(whitebal, 0, MAX_WHITEBAL); + + cam->brightness = brightness / 11; + cam->contrast = contrast / 11; + cam->saturation = saturation / 11; + cam->sharpness = sharpness / 11; + cam->white_bal = whitebal / 11; + + uvd->vpic.colour = 108; + uvd->vpic.hue = 108; + uvd->vpic.brightness = brightness; + uvd->vpic.contrast = contrast; + uvd->vpic.whiteness = whitebal; + uvd->vpic.depth = 6; + uvd->vpic.palette = VIDEO_PALETTE_YUV420P; + + memset(&uvd->vcap, 0, sizeof(uvd->vcap)); + strcpy(uvd->vcap.name, "Konica Webcam"); + uvd->vcap.type = VID_TYPE_CAPTURE; + uvd->vcap.channels = 1; + uvd->vcap.audios = 0; + uvd->vcap.minwidth = camera_sizes[SIZE_160X120].width; + uvd->vcap.minheight = camera_sizes[SIZE_160X120].height; + uvd->vcap.maxwidth = camera_sizes[SIZE_320X240].width; + uvd->vcap.maxheight = camera_sizes[SIZE_320X240].height; + + memset(&uvd->vchan, 0, sizeof(uvd->vchan)); + uvd->vchan.flags = 0 ; + uvd->vchan.tuners = 0; + uvd->vchan.channel = 0; + uvd->vchan.type = VIDEO_TYPE_CAMERA; + strcpy(uvd->vchan.name, "Camera"); + + /* Talk to device */ + DEBUG(1, "device init"); + if(!konicawc_get_misc(uvd, 0x3, 0, 0x10, buf, 2)) + DEBUG(2, "3,10 -> %2.2x %2.2x", buf[0], buf[1]); + if(!konicawc_get_misc(uvd, 0x3, 0, 0x10, buf, 2)) + DEBUG(2, "3,10 -> %2.2x %2.2x", buf[0], buf[1]); + if(konicawc_set_misc(uvd, 0x2, 0, 0xd)) + DEBUG(2, "2,0,d failed"); + DEBUG(1, "setting initial values"); +} + +static int konicawc_probe(struct usb_interface *intf, const struct usb_device_id *devid) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct uvd *uvd = NULL; + int ix, i, nas; + int actInterface=-1, inactInterface=-1, maxPS=0; + unsigned char video_ep = 0; + + DEBUG(1, "konicawc_probe(%p)", intf); + + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return -ENODEV; + + info("Konica Webcam (rev. 0x%04x)", le16_to_cpu(dev->descriptor.bcdDevice)); + RESTRICT_TO_RANGE(speed, 0, MAX_SPEED); + + /* Validate found interface: must have one ISO endpoint */ + nas = intf->num_altsetting; + if (nas != 8) { + err("Incorrect number of alternate settings (%d) for this camera!", nas); + return -ENODEV; + } + /* Validate all alternate settings */ + for (ix=0; ix < nas; ix++) { + const struct usb_host_interface *interface; + const struct usb_endpoint_descriptor *endpoint; + + interface = &intf->altsetting[ix]; + i = interface->desc.bAlternateSetting; + if (interface->desc.bNumEndpoints != 2) { + err("Interface %d. has %u. endpoints!", + interface->desc.bInterfaceNumber, + (unsigned)(interface->desc.bNumEndpoints)); + return -ENODEV; + } + endpoint = &interface->endpoint[1].desc; + DEBUG(1, "found endpoint: addr: 0x%2.2x maxps = 0x%4.4x", + endpoint->bEndpointAddress, le16_to_cpu(endpoint->wMaxPacketSize)); + if (video_ep == 0) + video_ep = endpoint->bEndpointAddress; + else if (video_ep != endpoint->bEndpointAddress) { + err("Alternate settings have different endpoint addresses!"); + return -ENODEV; + } + if ((endpoint->bmAttributes & 0x03) != 0x01) { + err("Interface %d. has non-ISO endpoint!", + interface->desc.bInterfaceNumber); + return -ENODEV; + } + if ((endpoint->bEndpointAddress & 0x80) == 0) { + err("Interface %d. has ISO OUT endpoint!", + interface->desc.bInterfaceNumber); + return -ENODEV; + } + if (le16_to_cpu(endpoint->wMaxPacketSize) == 0) { + if (inactInterface < 0) + inactInterface = i; + else { + err("More than one inactive alt. setting!"); + return -ENODEV; + } + } else { + if (i == spd_to_iface[speed]) { + /* This one is the requested one */ + actInterface = i; + } + } + if (le16_to_cpu(endpoint->wMaxPacketSize) > maxPS) + maxPS = le16_to_cpu(endpoint->wMaxPacketSize); + } + if(actInterface == -1) { + err("Cant find required endpoint"); + return -ENODEV; + } + + DEBUG(1, "Selecting requested active setting=%d. maxPS=%d.", actInterface, maxPS); + + uvd = usbvideo_AllocateDevice(cams); + if (uvd != NULL) { + struct konicawc *cam = (struct konicawc *)(uvd->user_data); + /* Here uvd is a fully allocated uvd object */ + for(i = 0; i < USBVIDEO_NUMSBUF; i++) { + cam->sts_urb[i] = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); + if(cam->sts_urb[i] == NULL) { + while(i--) { + usb_free_urb(cam->sts_urb[i]); + } + err("can't allocate urbs"); + return -ENOMEM; + } + } + cam->speed = speed; + RESTRICT_TO_RANGE(size, SIZE_160X120, SIZE_320X240); + cam->width = camera_sizes[size].width; + cam->height = camera_sizes[size].height; + cam->size = size; + + uvd->flags = 0; + uvd->debug = debug; + uvd->dev = dev; + uvd->iface = intf->altsetting->desc.bInterfaceNumber; + uvd->ifaceAltInactive = inactInterface; + uvd->ifaceAltActive = actInterface; + uvd->video_endp = video_ep; + uvd->iso_packet_len = maxPS; + uvd->paletteBits = 1L << VIDEO_PALETTE_YUV420P; + uvd->defaultPalette = VIDEO_PALETTE_YUV420P; + uvd->canvas = VIDEOSIZE(320, 240); + uvd->videosize = VIDEOSIZE(cam->width, cam->height); + + /* Initialize konicawc specific data */ + konicawc_configure_video(uvd); + + i = usbvideo_RegisterVideoDevice(uvd); + uvd->max_frame_size = (320 * 240 * 3)/2; + if (i != 0) { + err("usbvideo_RegisterVideoDevice() failed."); + uvd = NULL; + } + + konicawc_register_input(cam, dev); + } + + if (uvd) { + usb_set_intfdata (intf, uvd); + return 0; + } + return -EIO; +} + + +static void konicawc_free_uvd(struct uvd *uvd) +{ + int i; + struct konicawc *cam = (struct konicawc *)uvd->user_data; + + konicawc_unregister_input(cam); + + for (i = 0; i < USBVIDEO_NUMSBUF; i++) { + usb_free_urb(cam->sts_urb[i]); + cam->sts_urb[i] = NULL; + } +} + + +static struct usb_device_id id_table[] = { + { USB_DEVICE(0x04c8, 0x0720) }, /* Intel YC 76 */ + { } /* Terminating entry */ +}; + + +static int __init konicawc_init(void) +{ + struct usbvideo_cb cbTbl; + info(DRIVER_DESC " " DRIVER_VERSION); + memset(&cbTbl, 0, sizeof(cbTbl)); + cbTbl.probe = konicawc_probe; + cbTbl.setupOnOpen = konicawc_setup_on_open; + cbTbl.processData = konicawc_process_isoc; + cbTbl.getFPS = konicawc_calculate_fps; + cbTbl.setVideoMode = konicawc_set_video_mode; + cbTbl.startDataPump = konicawc_start_data; + cbTbl.stopDataPump = konicawc_stop_data; + cbTbl.adjustPicture = konicawc_adjust_picture; + cbTbl.userFree = konicawc_free_uvd; + return usbvideo_register( + &cams, + MAX_CAMERAS, + sizeof(struct konicawc), + "konicawc", + &cbTbl, + THIS_MODULE, + id_table); +} + + +static void __exit konicawc_cleanup(void) +{ + usbvideo_Deregister(&cams); +} + + +MODULE_DEVICE_TABLE(usb, id_table); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Simon Evans "); +MODULE_DESCRIPTION(DRIVER_DESC); +module_param(speed, int, 0); +MODULE_PARM_DESC(speed, "Initial speed: 0 (slowest) - 6 (fastest)"); +module_param(size, int, 0); +MODULE_PARM_DESC(size, "Initial Size 0: 160x120 1: 160x136 2: 176x144 3: 320x240"); +module_param(brightness, int, 0); +MODULE_PARM_DESC(brightness, "Initial brightness 0 - 108"); +module_param(contrast, int, 0); +MODULE_PARM_DESC(contrast, "Initial contrast 0 - 108"); +module_param(saturation, int, 0); +MODULE_PARM_DESC(saturation, "Initial saturation 0 - 108"); +module_param(sharpness, int, 0); +MODULE_PARM_DESC(sharpness, "Initial brightness 0 - 108"); +module_param(whitebal, int, 0); +MODULE_PARM_DESC(whitebal, "Initial white balance 0 - 363"); + +#ifdef CONFIG_USB_DEBUG +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)"); +#endif + +module_init(konicawc_init); +module_exit(konicawc_cleanup); diff --git a/linux/drivers/media/video/usbvideo/ultracam.c b/linux/drivers/media/video/usbvideo/ultracam.c new file mode 100644 index 000000000..10c58b4a2 --- /dev/null +++ b/linux/drivers/media/video/usbvideo/ultracam.c @@ -0,0 +1,679 @@ +/* + * USB NB Camera driver + * + * HISTORY: + * 25-Dec-2002 Dmitri Removed lighting, sharpness parameters, methods. + */ + +#include +#include +#include +#include + +#include "usbvideo.h" + +#define ULTRACAM_VENDOR_ID 0x0461 +#define ULTRACAM_PRODUCT_ID 0x0813 + +#define MAX_CAMERAS 4 /* How many devices we allow to connect */ + +/* + * This structure lives in uvd_t->user field. + */ +typedef struct { + int initialized; /* Had we already sent init sequence? */ + int camera_model; /* What type of IBM camera we got? */ + int has_hdr; +} ultracam_t; +#define ULTRACAM_T(uvd) ((ultracam_t *)((uvd)->user_data)) + +static struct usbvideo *cams = NULL; + +static int debug = 0; + +static int flags = 0; /* FLAGS_DISPLAY_HINTS | FLAGS_OVERLAY_STATS; */ + +static const int min_canvasWidth = 8; +static const int min_canvasHeight = 4; + +#define FRAMERATE_MIN 0 +#define FRAMERATE_MAX 6 +static int framerate = -1; + +/* + * Here we define several initialization variables. They may + * be used to automatically set color, hue, brightness and + * contrast to desired values. This is particularly useful in + * case of webcams (which have no controls and no on-screen + * output) and also when a client V4L software is used that + * does not have some of those controls. In any case it's + * good to have startup values as options. + * + * These values are all in [0..255] range. This simplifies + * operation. Note that actual values of V4L variables may + * be scaled up (as much as << 8). User can see that only + * on overlay output, however, or through a V4L client. + */ +static int init_brightness = 128; +static int init_contrast = 192; +static int init_color = 128; +static int init_hue = 128; +static int hue_correction = 128; + +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)"); +module_param(flags, int, 0); +MODULE_PARM_DESC(flags, + "Bitfield: 0=VIDIOCSYNC, " + "1=B/W, " + "2=show hints, " + "3=show stats, " + "4=test pattern, " + "5=separate frames, " + "6=clean frames"); +module_param(framerate, int, 0); +MODULE_PARM_DESC(framerate, "Framerate setting: 0=slowest, 6=fastest (default=2)"); + +module_param(init_brightness, int, 0); +MODULE_PARM_DESC(init_brightness, "Brightness preconfiguration: 0-255 (default=128)"); +module_param(init_contrast, int, 0); +MODULE_PARM_DESC(init_contrast, "Contrast preconfiguration: 0-255 (default=192)"); +module_param(init_color, int, 0); +MODULE_PARM_DESC(init_color, "Color preconfiguration: 0-255 (default=128)"); +module_param(init_hue, int, 0); +MODULE_PARM_DESC(init_hue, "Hue preconfiguration: 0-255 (default=128)"); +module_param(hue_correction, int, 0); +MODULE_PARM_DESC(hue_correction, "YUV colorspace regulation: 0-255 (default=128)"); + +/* + * ultracam_ProcessIsocData() + * + * Generic routine to parse the ring queue data. It employs either + * ultracam_find_header() or ultracam_parse_lines() to do most + * of work. + * + * 02-Nov-2000 First (mostly dummy) version. + * 06-Nov-2000 Rewrote to dump all data into frame. + */ +static void ultracam_ProcessIsocData(struct uvd *uvd, struct usbvideo_frame *frame) +{ + int n; + + assert(uvd != NULL); + assert(frame != NULL); + + /* Try to move data from queue into frame buffer */ + n = RingQueue_GetLength(&uvd->dp); + if (n > 0) { + int m; + /* See how much spare we have left */ + m = uvd->max_frame_size - frame->seqRead_Length; + if (n > m) + n = m; + /* Now move that much data into frame buffer */ + RingQueue_Dequeue( + &uvd->dp, + frame->data + frame->seqRead_Length, + m); + frame->seqRead_Length += m; + } + /* See if we filled the frame */ + if (frame->seqRead_Length >= uvd->max_frame_size) { + frame->frameState = FrameState_Done; + uvd->curframe = -1; + uvd->stats.frame_num++; + } +} + +/* + * ultracam_veio() + * + * History: + * 1/27/00 Added check for dev == NULL; this happens if camera is unplugged. + */ +static int ultracam_veio( + struct uvd *uvd, + unsigned char req, + unsigned short value, + unsigned short index, + int is_out) +{ + static const char proc[] = "ultracam_veio"; + unsigned char cp[8] /* = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } */; + int i; + + if (!CAMERA_IS_OPERATIONAL(uvd)) + return 0; + + if (!is_out) { + i = usb_control_msg( + uvd->dev, + usb_rcvctrlpipe(uvd->dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + cp, + sizeof(cp), + 1000); +#if 1 + info("USB => %02x%02x%02x%02x%02x%02x%02x%02x " + "(req=$%02x val=$%04x ind=$%04x)", + cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7], + req, value, index); +#endif + } else { + i = usb_control_msg( + uvd->dev, + usb_sndctrlpipe(uvd->dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + NULL, + 0, + 1000); + } + if (i < 0) { + err("%s: ERROR=%d. Camera stopped; Reconnect or reload driver.", + proc, i); + uvd->last_error = i; + } + return i; +} + +/* + * ultracam_calculate_fps() + */ +static int ultracam_calculate_fps(struct uvd *uvd) +{ + return 3 + framerate*4 + framerate/2; +} + +/* + * ultracam_adjust_contrast() + */ +static void ultracam_adjust_contrast(struct uvd *uvd) +{ +} + +/* + * ultracam_set_brightness() + * + * This procedure changes brightness of the picture. + */ +static void ultracam_set_brightness(struct uvd *uvd) +{ +} + +static void ultracam_set_hue(struct uvd *uvd) +{ +} + +/* + * ultracam_adjust_picture() + * + * This procedure gets called from V4L interface to update picture settings. + * Here we change brightness and contrast. + */ +static void ultracam_adjust_picture(struct uvd *uvd) +{ + ultracam_adjust_contrast(uvd); + ultracam_set_brightness(uvd); + ultracam_set_hue(uvd); +} + +/* + * ultracam_video_stop() + * + * This code tells camera to stop streaming. The interface remains + * configured and bandwidth - claimed. + */ +static void ultracam_video_stop(struct uvd *uvd) +{ +} + +/* + * ultracam_reinit_iso() + * + * This procedure sends couple of commands to the camera and then + * resets the video pipe. This sequence was observed to reinit the + * camera or, at least, to initiate ISO data stream. + */ +static void ultracam_reinit_iso(struct uvd *uvd, int do_stop) +{ +} + +static void ultracam_video_start(struct uvd *uvd) +{ + ultracam_reinit_iso(uvd, 0); +} + +static int ultracam_resetPipe(struct uvd *uvd) +{ + usb_clear_halt(uvd->dev, uvd->video_endp); + return 0; +} + +static int ultracam_alternateSetting(struct uvd *uvd, int setting) +{ + static const char proc[] = "ultracam_alternateSetting"; + int i; + i = usb_set_interface(uvd->dev, uvd->iface, setting); + if (i < 0) { + err("%s: usb_set_interface error", proc); + uvd->last_error = i; + return -EBUSY; + } + return 0; +} + +/* + * Return negative code on failure, 0 on success. + */ +static int ultracam_setup_on_open(struct uvd *uvd) +{ + int setup_ok = 0; /* Success by default */ + /* Send init sequence only once, it's large! */ + if (!ULTRACAM_T(uvd)->initialized) { + ultracam_alternateSetting(uvd, 0x04); + ultracam_alternateSetting(uvd, 0x00); + ultracam_veio(uvd, 0x02, 0x0004, 0x000b, 1); + ultracam_veio(uvd, 0x02, 0x0001, 0x0005, 1); + ultracam_veio(uvd, 0x02, 0x8000, 0x0000, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0000, 1); + ultracam_veio(uvd, 0x00, 0x00b0, 0x0001, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0002, 1); + ultracam_veio(uvd, 0x00, 0x000c, 0x0003, 1); + ultracam_veio(uvd, 0x00, 0x000b, 0x0004, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0005, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0006, 1); + ultracam_veio(uvd, 0x00, 0x0079, 0x0007, 1); + ultracam_veio(uvd, 0x00, 0x003b, 0x0008, 1); + ultracam_veio(uvd, 0x00, 0x0002, 0x000f, 1); + ultracam_veio(uvd, 0x00, 0x0001, 0x0010, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0011, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00bf, 1); + ultracam_veio(uvd, 0x00, 0x0001, 0x00c0, 1); + ultracam_veio(uvd, 0x00, 0x0010, 0x00cb, 1); + ultracam_veio(uvd, 0x01, 0x00a4, 0x0001, 1); + ultracam_veio(uvd, 0x01, 0x0010, 0x0002, 1); + ultracam_veio(uvd, 0x01, 0x0066, 0x0007, 1); + ultracam_veio(uvd, 0x01, 0x000b, 0x0008, 1); + ultracam_veio(uvd, 0x01, 0x0034, 0x0009, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x000a, 1); + ultracam_veio(uvd, 0x01, 0x002e, 0x000b, 1); + ultracam_veio(uvd, 0x01, 0x00d6, 0x000c, 1); + ultracam_veio(uvd, 0x01, 0x00fc, 0x000d, 1); + ultracam_veio(uvd, 0x01, 0x00f1, 0x000e, 1); + ultracam_veio(uvd, 0x01, 0x00da, 0x000f, 1); + ultracam_veio(uvd, 0x01, 0x0036, 0x0010, 1); + ultracam_veio(uvd, 0x01, 0x000b, 0x0011, 1); + ultracam_veio(uvd, 0x01, 0x0001, 0x0012, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x0013, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x0014, 1); + ultracam_veio(uvd, 0x01, 0x0087, 0x0051, 1); + ultracam_veio(uvd, 0x01, 0x0040, 0x0052, 1); + ultracam_veio(uvd, 0x01, 0x0058, 0x0053, 1); + ultracam_veio(uvd, 0x01, 0x0040, 0x0054, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x0040, 1); + ultracam_veio(uvd, 0x01, 0x0010, 0x0041, 1); + ultracam_veio(uvd, 0x01, 0x0020, 0x0042, 1); + ultracam_veio(uvd, 0x01, 0x0030, 0x0043, 1); + ultracam_veio(uvd, 0x01, 0x0040, 0x0044, 1); + ultracam_veio(uvd, 0x01, 0x0050, 0x0045, 1); + ultracam_veio(uvd, 0x01, 0x0060, 0x0046, 1); + ultracam_veio(uvd, 0x01, 0x0070, 0x0047, 1); + ultracam_veio(uvd, 0x01, 0x0080, 0x0048, 1); + ultracam_veio(uvd, 0x01, 0x0090, 0x0049, 1); + ultracam_veio(uvd, 0x01, 0x00a0, 0x004a, 1); + ultracam_veio(uvd, 0x01, 0x00b0, 0x004b, 1); + ultracam_veio(uvd, 0x01, 0x00c0, 0x004c, 1); + ultracam_veio(uvd, 0x01, 0x00d0, 0x004d, 1); + ultracam_veio(uvd, 0x01, 0x00e0, 0x004e, 1); + ultracam_veio(uvd, 0x01, 0x00f0, 0x004f, 1); + ultracam_veio(uvd, 0x01, 0x00ff, 0x0050, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x0056, 1); + ultracam_veio(uvd, 0x00, 0x0080, 0x00c1, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c2, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0080, 0x00c1, 1); + ultracam_veio(uvd, 0x00, 0x0004, 0x00c2, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0002, 0x00c1, 1); + ultracam_veio(uvd, 0x00, 0x0020, 0x00c2, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0040, 0x00c1, 1); + ultracam_veio(uvd, 0x00, 0x0017, 0x00c2, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x00c0, 0x00c1, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c2, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1); + ultracam_veio(uvd, 0x02, 0xc040, 0x0001, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x0008, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x0009, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x000a, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x000b, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x000c, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x000d, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x000e, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x000f, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x0010, 0); + ultracam_veio(uvd, 0x01, 0x000b, 0x0008, 1); + ultracam_veio(uvd, 0x01, 0x0034, 0x0009, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x000a, 1); + ultracam_veio(uvd, 0x01, 0x002e, 0x000b, 1); + ultracam_veio(uvd, 0x01, 0x00d6, 0x000c, 1); + ultracam_veio(uvd, 0x01, 0x00fc, 0x000d, 1); + ultracam_veio(uvd, 0x01, 0x00f1, 0x000e, 1); + ultracam_veio(uvd, 0x01, 0x00da, 0x000f, 1); + ultracam_veio(uvd, 0x01, 0x0036, 0x0010, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x0001, 0); + ultracam_veio(uvd, 0x01, 0x0064, 0x0001, 1); + ultracam_veio(uvd, 0x01, 0x0059, 0x0051, 1); + ultracam_veio(uvd, 0x01, 0x003f, 0x0052, 1); + ultracam_veio(uvd, 0x01, 0x0094, 0x0053, 1); + ultracam_veio(uvd, 0x01, 0x00ff, 0x0011, 1); + ultracam_veio(uvd, 0x01, 0x0003, 0x0012, 1); + ultracam_veio(uvd, 0x01, 0x00f7, 0x0013, 1); + ultracam_veio(uvd, 0x00, 0x0009, 0x0011, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0001, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0000, 1); + ultracam_veio(uvd, 0x00, 0x0020, 0x00c1, 1); + ultracam_veio(uvd, 0x00, 0x0010, 0x00c2, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1); + ultracam_alternateSetting(uvd, 0x04); + ultracam_veio(uvd, 0x02, 0x0000, 0x0001, 1); + ultracam_veio(uvd, 0x02, 0x0000, 0x0001, 1); + ultracam_veio(uvd, 0x02, 0x0000, 0x0006, 1); + ultracam_veio(uvd, 0x02, 0x9000, 0x0007, 1); + ultracam_veio(uvd, 0x02, 0x0042, 0x0001, 1); + ultracam_veio(uvd, 0x02, 0x0000, 0x000b, 0); + ultracam_resetPipe(uvd); + ULTRACAM_T(uvd)->initialized = (setup_ok != 0); + } + return setup_ok; +} + +static void ultracam_configure_video(struct uvd *uvd) +{ + if (uvd == NULL) + return; + + RESTRICT_TO_RANGE(init_brightness, 0, 255); + RESTRICT_TO_RANGE(init_contrast, 0, 255); + RESTRICT_TO_RANGE(init_color, 0, 255); + RESTRICT_TO_RANGE(init_hue, 0, 255); + RESTRICT_TO_RANGE(hue_correction, 0, 255); + + memset(&uvd->vpic, 0, sizeof(uvd->vpic)); + memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old)); + + uvd->vpic.colour = init_color << 8; + uvd->vpic.hue = init_hue << 8; + uvd->vpic.brightness = init_brightness << 8; + uvd->vpic.contrast = init_contrast << 8; + uvd->vpic.whiteness = 105 << 8; /* This one isn't used */ + uvd->vpic.depth = 24; + uvd->vpic.palette = VIDEO_PALETTE_RGB24; + + memset(&uvd->vcap, 0, sizeof(uvd->vcap)); + strcpy(uvd->vcap.name, "IBM Ultra Camera"); + uvd->vcap.type = VID_TYPE_CAPTURE; + uvd->vcap.channels = 1; + uvd->vcap.audios = 0; + uvd->vcap.maxwidth = VIDEOSIZE_X(uvd->canvas); + uvd->vcap.maxheight = VIDEOSIZE_Y(uvd->canvas); + uvd->vcap.minwidth = min_canvasWidth; + uvd->vcap.minheight = min_canvasHeight; + + memset(&uvd->vchan, 0, sizeof(uvd->vchan)); + uvd->vchan.flags = 0; + uvd->vchan.tuners = 0; + uvd->vchan.channel = 0; + uvd->vchan.type = VIDEO_TYPE_CAMERA; + strcpy(uvd->vchan.name, "Camera"); +} + +/* + * ultracam_probe() + * + * This procedure queries device descriptor and accepts the interface + * if it looks like our camera. + * + * History: + * 12-Nov-2000 Reworked to comply with new probe() signature. + * 23-Jan-2001 Added compatibility with 2.2.x kernels. + */ +static int ultracam_probe(struct usb_interface *intf, const struct usb_device_id *devid) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct uvd *uvd = NULL; + int ix, i, nas; + int actInterface=-1, inactInterface=-1, maxPS=0; + unsigned char video_ep = 0; + + if (debug >= 1) + info("ultracam_probe(%p)", intf); + + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return -ENODEV; + + info("IBM Ultra camera found (rev. 0x%04x)", + le16_to_cpu(dev->descriptor.bcdDevice)); + + /* Validate found interface: must have one ISO endpoint */ + nas = intf->num_altsetting; + if (debug > 0) + info("Number of alternate settings=%d.", nas); + if (nas < 8) { + err("Too few alternate settings for this camera!"); + return -ENODEV; + } + /* Validate all alternate settings */ + for (ix=0; ix < nas; ix++) { + const struct usb_host_interface *interface; + const struct usb_endpoint_descriptor *endpoint; + + interface = &intf->altsetting[ix]; + i = interface->desc.bAlternateSetting; + if (interface->desc.bNumEndpoints != 1) { + err("Interface %d. has %u. endpoints!", + interface->desc.bInterfaceNumber, + (unsigned)(interface->desc.bNumEndpoints)); + return -ENODEV; + } + endpoint = &interface->endpoint[0].desc; + if (video_ep == 0) + video_ep = endpoint->bEndpointAddress; + else if (video_ep != endpoint->bEndpointAddress) { + err("Alternate settings have different endpoint addresses!"); + return -ENODEV; + } + if ((endpoint->bmAttributes & 0x03) != 0x01) { + err("Interface %d. has non-ISO endpoint!", + interface->desc.bInterfaceNumber); + return -ENODEV; + } + if ((endpoint->bEndpointAddress & 0x80) == 0) { + err("Interface %d. has ISO OUT endpoint!", + interface->desc.bInterfaceNumber); + return -ENODEV; + } + if (le16_to_cpu(endpoint->wMaxPacketSize) == 0) { + if (inactInterface < 0) + inactInterface = i; + else { + err("More than one inactive alt. setting!"); + return -ENODEV; + } + } else { + if (actInterface < 0) { + actInterface = i; + maxPS = le16_to_cpu(endpoint->wMaxPacketSize); + if (debug > 0) + info("Active setting=%d. maxPS=%d.", i, maxPS); + } else { + /* Got another active alt. setting */ + if (maxPS < le16_to_cpu(endpoint->wMaxPacketSize)) { + /* This one is better! */ + actInterface = i; + maxPS = le16_to_cpu(endpoint->wMaxPacketSize); + if (debug > 0) { + info("Even better ctive setting=%d. maxPS=%d.", + i, maxPS); + } + } + } + } + } + if ((maxPS <= 0) || (actInterface < 0) || (inactInterface < 0)) { + err("Failed to recognize the camera!"); + return -ENODEV; + } + + uvd = usbvideo_AllocateDevice(cams); + if (uvd != NULL) { + /* Here uvd is a fully allocated uvd object */ + uvd->flags = flags; + uvd->debug = debug; + uvd->dev = dev; + uvd->iface = intf->altsetting->desc.bInterfaceNumber; + uvd->ifaceAltInactive = inactInterface; + uvd->ifaceAltActive = actInterface; + uvd->video_endp = video_ep; + uvd->iso_packet_len = maxPS; + uvd->paletteBits = 1L << VIDEO_PALETTE_RGB24; + uvd->defaultPalette = VIDEO_PALETTE_RGB24; + uvd->canvas = VIDEOSIZE(640, 480); /* FIXME */ + uvd->videosize = uvd->canvas; /* ultracam_size_to_videosize(size);*/ + + /* Initialize ibmcam-specific data */ + assert(ULTRACAM_T(uvd) != NULL); + ULTRACAM_T(uvd)->camera_model = 0; /* Not used yet */ + ULTRACAM_T(uvd)->initialized = 0; + + ultracam_configure_video(uvd); + + i = usbvideo_RegisterVideoDevice(uvd); + if (i != 0) { + err("usbvideo_RegisterVideoDevice() failed."); + uvd = NULL; + } + } + + if (uvd) { + usb_set_intfdata (intf, uvd); + return 0; + } + return -EIO; +} + + +static struct usb_device_id id_table[] = { + { USB_DEVICE(ULTRACAM_VENDOR_ID, ULTRACAM_PRODUCT_ID) }, + { } /* Terminating entry */ +}; + +/* + * ultracam_init() + * + * This code is run to initialize the driver. + */ +static int __init ultracam_init(void) +{ + struct usbvideo_cb cbTbl; + memset(&cbTbl, 0, sizeof(cbTbl)); + cbTbl.probe = ultracam_probe; + cbTbl.setupOnOpen = ultracam_setup_on_open; + cbTbl.videoStart = ultracam_video_start; + cbTbl.videoStop = ultracam_video_stop; + cbTbl.processData = ultracam_ProcessIsocData; + cbTbl.postProcess = usbvideo_DeinterlaceFrame; + cbTbl.adjustPicture = ultracam_adjust_picture; + cbTbl.getFPS = ultracam_calculate_fps; + return usbvideo_register( + &cams, + MAX_CAMERAS, + sizeof(ultracam_t), + "ultracam", + &cbTbl, + THIS_MODULE, + id_table); +} + +static void __exit ultracam_cleanup(void) +{ + usbvideo_Deregister(&cams); +} + +MODULE_DEVICE_TABLE(usb, id_table); +MODULE_LICENSE("GPL"); + +module_init(ultracam_init); +module_exit(ultracam_cleanup); diff --git a/linux/drivers/media/video/usbvideo/usbvideo.c b/linux/drivers/media/video/usbvideo/usbvideo.c new file mode 100644 index 000000000..13b37c8c0 --- /dev/null +++ b/linux/drivers/media/video/usbvideo/usbvideo.c @@ -0,0 +1,2190 @@ +/* + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "usbvideo.h" + +#if defined(MAP_NR) +#define virt_to_page(v) MAP_NR(v) /* Kernels 2.2.x */ +#endif + +static int video_nr = -1; +module_param(video_nr, int, 0); + +/* + * Local prototypes. + */ +static void usbvideo_Disconnect(struct usb_interface *intf); +static void usbvideo_CameraRelease(struct uvd *uvd); + +static int usbvideo_v4l_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma); +static int usbvideo_v4l_open(struct inode *inode, struct file *file); +static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos); +static int usbvideo_v4l_close(struct inode *inode, struct file *file); + +static int usbvideo_StartDataPump(struct uvd *uvd); +static void usbvideo_StopDataPump(struct uvd *uvd); +static int usbvideo_GetFrame(struct uvd *uvd, int frameNum); +static int usbvideo_NewFrame(struct uvd *uvd, int framenum); +static void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd, + struct usbvideo_frame *frame); + +/*******************************/ +/* Memory management functions */ +/*******************************/ +static void *usbvideo_rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + +static void usbvideo_rvfree(void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + +static void RingQueue_Initialize(struct RingQueue *rq) +{ + assert(rq != NULL); + init_waitqueue_head(&rq->wqh); +} + +static void RingQueue_Allocate(struct RingQueue *rq, int rqLen) +{ + /* Make sure the requested size is a power of 2 and + round up if necessary. This allows index wrapping + using masks rather than modulo */ + + int i = 1; + assert(rq != NULL); + assert(rqLen > 0); + + while(rqLen >> i) + i++; + if(rqLen != 1 << (i-1)) + rqLen = 1 << i; + + rq->length = rqLen; + rq->ri = rq->wi = 0; + rq->queue = usbvideo_rvmalloc(rq->length); + assert(rq->queue != NULL); +} + +static int RingQueue_IsAllocated(const struct RingQueue *rq) +{ + if (rq == NULL) + return 0; + return (rq->queue != NULL) && (rq->length > 0); +} + +static void RingQueue_Free(struct RingQueue *rq) +{ + assert(rq != NULL); + if (RingQueue_IsAllocated(rq)) { + usbvideo_rvfree(rq->queue, rq->length); + rq->queue = NULL; + rq->length = 0; + } +} + +int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len) +{ + int rql, toread; + + assert(rq != NULL); + assert(dst != NULL); + + rql = RingQueue_GetLength(rq); + if(!rql) + return 0; + + /* Clip requested length to available data */ + if(len > rql) + len = rql; + + toread = len; + if(rq->ri > rq->wi) { + /* Read data from tail */ + int read = (toread < (rq->length - rq->ri)) ? toread : rq->length - rq->ri; + memcpy(dst, rq->queue + rq->ri, read); + toread -= read; + dst += read; + rq->ri = (rq->ri + read) & (rq->length-1); + } + if(toread) { + /* Read data from head */ + memcpy(dst, rq->queue + rq->ri, toread); + rq->ri = (rq->ri + toread) & (rq->length-1); + } + return len; +} + +EXPORT_SYMBOL(RingQueue_Dequeue); + +int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n) +{ + int enqueued = 0; + + assert(rq != NULL); + assert(cdata != NULL); + assert(rq->length > 0); + while (n > 0) { + int m, q_avail; + + /* Calculate the largest chunk that fits the tail of the ring */ + q_avail = rq->length - rq->wi; + if (q_avail <= 0) { + rq->wi = 0; + q_avail = rq->length; + } + m = n; + assert(q_avail > 0); + if (m > q_avail) + m = q_avail; + + memcpy(rq->queue + rq->wi, cdata, m); + RING_QUEUE_ADVANCE_INDEX(rq, wi, m); + cdata += m; + enqueued += m; + n -= m; + } + return enqueued; +} + +EXPORT_SYMBOL(RingQueue_Enqueue); + +static void RingQueue_InterruptibleSleepOn(struct RingQueue *rq) +{ + assert(rq != NULL); + interruptible_sleep_on(&rq->wqh); +} + +void RingQueue_WakeUpInterruptible(struct RingQueue *rq) +{ + assert(rq != NULL); + if (waitqueue_active(&rq->wqh)) + wake_up_interruptible(&rq->wqh); +} + +EXPORT_SYMBOL(RingQueue_WakeUpInterruptible); + +void RingQueue_Flush(struct RingQueue *rq) +{ + assert(rq != NULL); + rq->ri = 0; + rq->wi = 0; +} + +EXPORT_SYMBOL(RingQueue_Flush); + + +/* + * usbvideo_VideosizeToString() + * + * This procedure converts given videosize value to readable string. + * + * History: + * 07-Aug-2000 Created. + * 19-Oct-2000 Reworked for usbvideo module. + */ +static void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs) +{ + char tmp[40]; + int n; + + n = 1 + sprintf(tmp, "%ldx%ld", VIDEOSIZE_X(vs), VIDEOSIZE_Y(vs)); + assert(n < sizeof(tmp)); + if ((buf == NULL) || (bufLen < n)) + err("usbvideo_VideosizeToString: buffer is too small."); + else + memmove(buf, tmp, n); +} + +/* + * usbvideo_OverlayChar() + * + * History: + * 01-Feb-2000 Created. + */ +static void usbvideo_OverlayChar(struct uvd *uvd, struct usbvideo_frame *frame, + int x, int y, int ch) +{ + static const unsigned short digits[16] = { + 0xF6DE, /* 0 */ + 0x2492, /* 1 */ + 0xE7CE, /* 2 */ + 0xE79E, /* 3 */ + 0xB792, /* 4 */ + 0xF39E, /* 5 */ + 0xF3DE, /* 6 */ + 0xF492, /* 7 */ + 0xF7DE, /* 8 */ + 0xF79E, /* 9 */ + 0x77DA, /* a */ + 0xD75C, /* b */ + 0xF24E, /* c */ + 0xD6DC, /* d */ + 0xF34E, /* e */ + 0xF348 /* f */ + }; + unsigned short digit; + int ix, iy; + + if ((uvd == NULL) || (frame == NULL)) + return; + + if (ch >= '0' && ch <= '9') + ch -= '0'; + else if (ch >= 'A' && ch <= 'F') + ch = 10 + (ch - 'A'); + else if (ch >= 'a' && ch <= 'f') + ch = 10 + (ch - 'a'); + else + return; + digit = digits[ch]; + + for (iy=0; iy < 5; iy++) { + for (ix=0; ix < 3; ix++) { + if (digit & 0x8000) { + if (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24)) { +/* TODO */ RGB24_PUTPIXEL(frame, x+ix, y+iy, 0xFF, 0xFF, 0xFF); + } + } + digit = digit << 1; + } + } +} + +/* + * usbvideo_OverlayString() + * + * History: + * 01-Feb-2000 Created. + */ +static void usbvideo_OverlayString(struct uvd *uvd, struct usbvideo_frame *frame, + int x, int y, const char *str) +{ + while (*str) { + usbvideo_OverlayChar(uvd, frame, x, y, *str); + str++; + x += 4; /* 3 pixels character + 1 space */ + } +} + +/* + * usbvideo_OverlayStats() + * + * Overlays important debugging information. + * + * History: + * 01-Feb-2000 Created. + */ +static void usbvideo_OverlayStats(struct uvd *uvd, struct usbvideo_frame *frame) +{ + const int y_diff = 8; + char tmp[16]; + int x = 10, y=10; + long i, j, barLength; + const int qi_x1 = 60, qi_y1 = 10; + const int qi_x2 = VIDEOSIZE_X(frame->request) - 10, qi_h = 10; + + /* Call the user callback, see if we may proceed after that */ + if (VALID_CALLBACK(uvd, overlayHook)) { + if (GET_CALLBACK(uvd, overlayHook)(uvd, frame) < 0) + return; + } + + /* + * We draw a (mostly) hollow rectangle with qi_xxx coordinates. + * Left edge symbolizes the queue index 0; right edge symbolizes + * the full capacity of the queue. + */ + barLength = qi_x2 - qi_x1 - 2; + if ((barLength > 10) && (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24))) { +/* TODO */ long u_lo, u_hi, q_used; + long m_ri, m_wi, m_lo, m_hi; + + /* + * Determine fill zones (used areas of the queue): + * 0 xxxxxxx u_lo ...... uvd->dp.ri xxxxxxxx u_hi ..... uvd->dp.length + * + * if u_lo < 0 then there is no first filler. + */ + + q_used = RingQueue_GetLength(&uvd->dp); + if ((uvd->dp.ri + q_used) >= uvd->dp.length) { + u_hi = uvd->dp.length; + u_lo = (q_used + uvd->dp.ri) & (uvd->dp.length-1); + } else { + u_hi = (q_used + uvd->dp.ri); + u_lo = -1; + } + + /* Convert byte indices into screen units */ + m_ri = qi_x1 + ((barLength * uvd->dp.ri) / uvd->dp.length); + m_wi = qi_x1 + ((barLength * uvd->dp.wi) / uvd->dp.length); + m_lo = (u_lo > 0) ? (qi_x1 + ((barLength * u_lo) / uvd->dp.length)) : -1; + m_hi = qi_x1 + ((barLength * u_hi) / uvd->dp.length); + + for (j=qi_y1; j < (qi_y1 + qi_h); j++) { + for (i=qi_x1; i < qi_x2; i++) { + /* Draw border lines */ + if ((j == qi_y1) || (j == (qi_y1 + qi_h - 1)) || + (i == qi_x1) || (i == (qi_x2 - 1))) { + RGB24_PUTPIXEL(frame, i, j, 0xFF, 0xFF, 0xFF); + continue; + } + /* For all other points the Y coordinate does not matter */ + if ((i >= m_ri) && (i <= (m_ri + 3))) { + RGB24_PUTPIXEL(frame, i, j, 0x00, 0xFF, 0x00); + } else if ((i >= m_wi) && (i <= (m_wi + 3))) { + RGB24_PUTPIXEL(frame, i, j, 0xFF, 0x00, 0x00); + } else if ((i < m_lo) || ((i > m_ri) && (i < m_hi))) + RGB24_PUTPIXEL(frame, i, j, 0x00, 0x00, 0xFF); + } + } + } + + sprintf(tmp, "%8lx", uvd->stats.frame_num); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.urb_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.urb_length); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.data_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.header_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.iso_skip_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.iso_err_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", uvd->vpic.colour); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", uvd->vpic.hue); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", uvd->vpic.brightness >> 8); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", uvd->vpic.contrast >> 12); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8d", uvd->vpic.whiteness >> 8); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; +} + +/* + * usbvideo_ReportStatistics() + * + * This procedure prints packet and transfer statistics. + * + * History: + * 14-Jan-2000 Corrected default multiplier. + */ +static void usbvideo_ReportStatistics(const struct uvd *uvd) +{ + if ((uvd != NULL) && (uvd->stats.urb_count > 0)) { + unsigned long allPackets, badPackets, goodPackets, percent; + allPackets = uvd->stats.urb_count * CAMERA_URB_FRAMES; + badPackets = uvd->stats.iso_skip_count + uvd->stats.iso_err_count; + goodPackets = allPackets - badPackets; + /* Calculate percentage wisely, remember integer limits */ + assert(allPackets != 0); + if (goodPackets < (((unsigned long)-1)/100)) + percent = (100 * goodPackets) / allPackets; + else + percent = goodPackets / (allPackets / 100); + info("Packet Statistics: Total=%lu. Empty=%lu. Usage=%lu%%", + allPackets, badPackets, percent); + if (uvd->iso_packet_len > 0) { + unsigned long allBytes, xferBytes; + char multiplier = ' '; + allBytes = allPackets * uvd->iso_packet_len; + xferBytes = uvd->stats.data_count; + assert(allBytes != 0); + if (xferBytes < (((unsigned long)-1)/100)) + percent = (100 * xferBytes) / allBytes; + else + percent = xferBytes / (allBytes / 100); + /* Scale xferBytes for easy reading */ + if (xferBytes > 10*1024) { + xferBytes /= 1024; + multiplier = 'K'; + if (xferBytes > 10*1024) { + xferBytes /= 1024; + multiplier = 'M'; + if (xferBytes > 10*1024) { + xferBytes /= 1024; + multiplier = 'G'; + if (xferBytes > 10*1024) { + xferBytes /= 1024; + multiplier = 'T'; + } + } + } + } + info("Transfer Statistics: Transferred=%lu%cB Usage=%lu%%", + xferBytes, multiplier, percent); + } + } +} + +/* + * usbvideo_TestPattern() + * + * Procedure forms a test pattern (yellow grid on blue background). + * + * Parameters: + * fullframe: if TRUE then entire frame is filled, otherwise the procedure + * continues from the current scanline. + * pmode 0: fill the frame with solid blue color (like on VCR or TV) + * 1: Draw a colored grid + * + * History: + * 01-Feb-2000 Created. + */ +void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode) +{ + struct usbvideo_frame *frame; + int num_cell = 0; + int scan_length = 0; + static int num_pass = 0; + + if (uvd == NULL) { + err("%s: uvd == NULL", __FUNCTION__); + return; + } + if ((uvd->curframe < 0) || (uvd->curframe >= USBVIDEO_NUMFRAMES)) { + err("%s: uvd->curframe=%d.", __FUNCTION__, uvd->curframe); + return; + } + + /* Grab the current frame */ + frame = &uvd->frame[uvd->curframe]; + + /* Optionally start at the beginning */ + if (fullframe) { + frame->curline = 0; + frame->seqRead_Length = 0; + } +#if 0 + { /* For debugging purposes only */ + char tmp[20]; + usbvideo_VideosizeToString(tmp, sizeof(tmp), frame->request); + info("testpattern: frame=%s", tmp); + } +#endif + /* Form every scan line */ + for (; frame->curline < VIDEOSIZE_Y(frame->request); frame->curline++) { + int i; + unsigned char *f = frame->data + + (VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL * frame->curline); + for (i=0; i < VIDEOSIZE_X(frame->request); i++) { + unsigned char cb=0x80; + unsigned char cg = 0; + unsigned char cr = 0; + + if (pmode == 1) { + if (frame->curline % 32 == 0) + cb = 0, cg = cr = 0xFF; + else if (i % 32 == 0) { + if (frame->curline % 32 == 1) + num_cell++; + cb = 0, cg = cr = 0xFF; + } else { + cb = ((num_cell*7) + num_pass) & 0xFF; + cg = ((num_cell*5) + num_pass*2) & 0xFF; + cr = ((num_cell*3) + num_pass*3) & 0xFF; + } + } else { + /* Just the blue screen */ + } + + *f++ = cb; + *f++ = cg; + *f++ = cr; + scan_length += 3; + } + } + + frame->frameState = FrameState_Done; + frame->seqRead_Length += scan_length; + ++num_pass; + + /* We do this unconditionally, regardless of FLAGS_OVERLAY_STATS */ + usbvideo_OverlayStats(uvd, frame); +} + +EXPORT_SYMBOL(usbvideo_TestPattern); + + +#ifdef DEBUG +/* + * usbvideo_HexDump() + * + * A debugging tool. Prints hex dumps. + * + * History: + * 29-Jul-2000 Added printing of offsets. + */ +void usbvideo_HexDump(const unsigned char *data, int len) +{ + const int bytes_per_line = 32; + char tmp[128]; /* 32*3 + 5 */ + int i, k; + + for (i=k=0; len > 0; i++, len--) { + if (i > 0 && ((i % bytes_per_line) == 0)) { + printk("%s\n", tmp); + k=0; + } + if ((i % bytes_per_line) == 0) + k += sprintf(&tmp[k], "%04x: ", i); + k += sprintf(&tmp[k], "%02x ", data[i]); + } + if (k > 0) + printk("%s\n", tmp); +} + +EXPORT_SYMBOL(usbvideo_HexDump); + +#endif + +/* ******************************************************************** */ + +/* XXX: this piece of crap really wants some error handling.. */ +static void usbvideo_ClientIncModCount(struct uvd *uvd) +{ + if (uvd == NULL) { + err("%s: uvd == NULL", __FUNCTION__); + return; + } + if (uvd->handle == NULL) { + err("%s: uvd->handle == NULL", __FUNCTION__); + return; + } + if (uvd->handle->md_module == NULL) { + err("%s: uvd->handle->md_module == NULL", __FUNCTION__); + return; + } + if (!try_module_get(uvd->handle->md_module)) { + err("%s: try_module_get() == 0", __FUNCTION__); + return; + } +} + +static void usbvideo_ClientDecModCount(struct uvd *uvd) +{ + if (uvd == NULL) { + err("%s: uvd == NULL", __FUNCTION__); + return; + } + if (uvd->handle == NULL) { + err("%s: uvd->handle == NULL", __FUNCTION__); + return; + } + if (uvd->handle->md_module == NULL) { + err("%s: uvd->handle->md_module == NULL", __FUNCTION__); + return; + } + module_put(uvd->handle->md_module); +} + +int usbvideo_register( + struct usbvideo **pCams, + const int num_cams, + const int num_extra, + const char *driverName, + const struct usbvideo_cb *cbTbl, + struct module *md, + const struct usb_device_id *id_table) +{ + struct usbvideo *cams; + int i, base_size, result; + + /* Check parameters for sanity */ + if ((num_cams <= 0) || (pCams == NULL) || (cbTbl == NULL)) { + err("%s: Illegal call", __FUNCTION__); + return -EINVAL; + } + + /* Check registration callback - must be set! */ + if (cbTbl->probe == NULL) { + err("%s: probe() is required!", __FUNCTION__); + return -EINVAL; + } + + base_size = num_cams * sizeof(struct uvd) + sizeof(struct usbvideo); + cams = (struct usbvideo *) kzalloc(base_size, GFP_KERNEL); + if (cams == NULL) { + err("Failed to allocate %d. bytes for usbvideo struct", base_size); + return -ENOMEM; + } + dbg("%s: Allocated $%p (%d. bytes) for %d. cameras", + __FUNCTION__, cams, base_size, num_cams); + + /* Copy callbacks, apply defaults for those that are not set */ + memmove(&cams->cb, cbTbl, sizeof(cams->cb)); + if (cams->cb.getFrame == NULL) + cams->cb.getFrame = usbvideo_GetFrame; + if (cams->cb.disconnect == NULL) + cams->cb.disconnect = usbvideo_Disconnect; + if (cams->cb.startDataPump == NULL) + cams->cb.startDataPump = usbvideo_StartDataPump; + if (cams->cb.stopDataPump == NULL) + cams->cb.stopDataPump = usbvideo_StopDataPump; + + cams->num_cameras = num_cams; + cams->cam = (struct uvd *) &cams[1]; + cams->md_module = md; + if (cams->md_module == NULL) + warn("%s: module == NULL!", __FUNCTION__); + mutex_init(&cams->lock); /* to 1 == available */ + + for (i = 0; i < num_cams; i++) { + struct uvd *up = &cams->cam[i]; + + up->handle = cams; + + /* Allocate user_data separately because of kmalloc's limits */ + if (num_extra > 0) { + up->user_size = num_cams * num_extra; + up->user_data = kmalloc(up->user_size, GFP_KERNEL); + if (up->user_data == NULL) { + err("%s: Failed to allocate user_data (%d. bytes)", + __FUNCTION__, up->user_size); + while (i) { + up = &cams->cam[--i]; + kfree(up->user_data); + } + kfree(cams); + return -ENOMEM; + } + dbg("%s: Allocated cams[%d].user_data=$%p (%d. bytes)", + __FUNCTION__, i, up->user_data, up->user_size); + } + } + + /* + * Register ourselves with USB stack. + */ + strcpy(cams->drvName, (driverName != NULL) ? driverName : "Unknown"); + cams->usbdrv.name = cams->drvName; + cams->usbdrv.probe = cams->cb.probe; + cams->usbdrv.disconnect = cams->cb.disconnect; + cams->usbdrv.id_table = id_table; + + /* + * Update global handle to usbvideo. This is very important + * because probe() can be called before usb_register() returns. + * If the handle is not yet updated then the probe() will fail. + */ + *pCams = cams; + result = usb_register(&cams->usbdrv); + if (result) { + for (i = 0; i < num_cams; i++) { + struct uvd *up = &cams->cam[i]; + kfree(up->user_data); + } + kfree(cams); + } + + return result; +} + +EXPORT_SYMBOL(usbvideo_register); + +/* + * usbvideo_Deregister() + * + * Procedure frees all usbvideo and user data structures. Be warned that + * if you had some dynamically allocated components in ->user field then + * you should free them before calling here. + */ +void usbvideo_Deregister(struct usbvideo **pCams) +{ + struct usbvideo *cams; + int i; + + if (pCams == NULL) { + err("%s: pCams == NULL", __FUNCTION__); + return; + } + cams = *pCams; + if (cams == NULL) { + err("%s: cams == NULL", __FUNCTION__); + return; + } + + dbg("%s: Deregistering %s driver.", __FUNCTION__, cams->drvName); + usb_deregister(&cams->usbdrv); + + dbg("%s: Deallocating cams=$%p (%d. cameras)", __FUNCTION__, cams, cams->num_cameras); + for (i=0; i < cams->num_cameras; i++) { + struct uvd *up = &cams->cam[i]; + int warning = 0; + + if (up->user_data != NULL) { + if (up->user_size <= 0) + ++warning; + } else { + if (up->user_size > 0) + ++warning; + } + if (warning) { + err("%s: Warning: user_data=$%p user_size=%d.", + __FUNCTION__, up->user_data, up->user_size); + } else { + dbg("%s: Freeing %d. $%p->user_data=$%p", + __FUNCTION__, i, up, up->user_data); + kfree(up->user_data); + } + } + /* Whole array was allocated in one chunk */ + dbg("%s: Freed %d uvd structures", + __FUNCTION__, cams->num_cameras); + kfree(cams); + *pCams = NULL; +} + +EXPORT_SYMBOL(usbvideo_Deregister); + +/* + * usbvideo_Disconnect() + * + * This procedure stops all driver activity. Deallocation of + * the interface-private structure (pointed by 'ptr') is done now + * (if we don't have any open files) or later, when those files + * are closed. After that driver should be removable. + * + * This code handles surprise removal. The uvd->user is a counter which + * increments on open() and decrements on close(). If we see here that + * this counter is not 0 then we have a client who still has us opened. + * We set uvd->remove_pending flag as early as possible, and after that + * all access to the camera will gracefully fail. These failures should + * prompt client to (eventually) close the video device, and then - in + * usbvideo_v4l_close() - we decrement uvd->uvd_used and usage counter. + * + * History: + * 22-Jan-2000 Added polling of MOD_IN_USE to delay removal until all users gone. + * 27-Jan-2000 Reworked to allow pending disconnects; see xxx_close() + * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). + * 19-Oct-2000 Moved to usbvideo module. + */ +static void usbvideo_Disconnect(struct usb_interface *intf) +{ + struct uvd *uvd = usb_get_intfdata (intf); + int i; + + if (uvd == NULL) { + err("%s($%p): Illegal call.", __FUNCTION__, intf); + return; + } + + usb_set_intfdata (intf, NULL); + + usbvideo_ClientIncModCount(uvd); + if (uvd->debug > 0) + info("%s(%p.)", __FUNCTION__, intf); + + mutex_lock(&uvd->lock); + uvd->remove_pending = 1; /* Now all ISO data will be ignored */ + + /* At this time we ask to cancel outstanding URBs */ + GET_CALLBACK(uvd, stopDataPump)(uvd); + + for (i=0; i < USBVIDEO_NUMSBUF; i++) + usb_free_urb(uvd->sbuf[i].urb); + + usb_put_dev(uvd->dev); + uvd->dev = NULL; /* USB device is no more */ + + video_unregister_device(&uvd->vdev); + if (uvd->debug > 0) + info("%s: Video unregistered.", __FUNCTION__); + + if (uvd->user) + info("%s: In use, disconnect pending.", __FUNCTION__); + else + usbvideo_CameraRelease(uvd); + mutex_unlock(&uvd->lock); + info("USB camera disconnected."); + + usbvideo_ClientDecModCount(uvd); +} + +/* + * usbvideo_CameraRelease() + * + * This code does final release of uvd. This happens + * after the device is disconnected -and- all clients + * closed their files. + * + * History: + * 27-Jan-2000 Created. + */ +static void usbvideo_CameraRelease(struct uvd *uvd) +{ + if (uvd == NULL) { + err("%s: Illegal call", __FUNCTION__); + return; + } + + RingQueue_Free(&uvd->dp); + if (VALID_CALLBACK(uvd, userFree)) + GET_CALLBACK(uvd, userFree)(uvd); + uvd->uvd_used = 0; /* This is atomic, no need to take mutex */ +} + +/* + * usbvideo_find_struct() + * + * This code searches the array of preallocated (static) structures + * and returns index of the first one that isn't in use. Returns -1 + * if there are no free structures. + * + * History: + * 27-Jan-2000 Created. + */ +static int usbvideo_find_struct(struct usbvideo *cams) +{ + int u, rv = -1; + + if (cams == NULL) { + err("No usbvideo handle?"); + return -1; + } + mutex_lock(&cams->lock); + for (u = 0; u < cams->num_cameras; u++) { + struct uvd *uvd = &cams->cam[u]; + if (!uvd->uvd_used) /* This one is free */ + { + uvd->uvd_used = 1; /* In use now */ + mutex_init(&uvd->lock); /* to 1 == available */ + uvd->dev = NULL; + rv = u; + break; + } + } + mutex_unlock(&cams->lock); + return rv; +} + +static struct file_operations usbvideo_fops = { + .owner = THIS_MODULE, + .open = usbvideo_v4l_open, + .release =usbvideo_v4l_close, + .read = usbvideo_v4l_read, + .mmap = usbvideo_v4l_mmap, + .ioctl = usbvideo_v4l_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; +static const struct video_device usbvideo_template = { + .owner = THIS_MODULE, + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_CPIA, + .fops = &usbvideo_fops, +}; + +struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams) +{ + int i, devnum; + struct uvd *uvd = NULL; + + if (cams == NULL) { + err("No usbvideo handle?"); + return NULL; + } + + devnum = usbvideo_find_struct(cams); + if (devnum == -1) { + err("IBM USB camera driver: Too many devices!"); + return NULL; + } + uvd = &cams->cam[devnum]; + dbg("Device entry #%d. at $%p", devnum, uvd); + + /* Not relying upon caller we increase module counter ourselves */ + usbvideo_ClientIncModCount(uvd); + + mutex_lock(&uvd->lock); + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + uvd->sbuf[i].urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); + if (uvd->sbuf[i].urb == NULL) { + err("usb_alloc_urb(%d.) failed.", FRAMES_PER_DESC); + uvd->uvd_used = 0; + uvd = NULL; + goto allocate_done; + } + } + uvd->user=0; + uvd->remove_pending = 0; + uvd->last_error = 0; + RingQueue_Initialize(&uvd->dp); + + /* Initialize video device structure */ + uvd->vdev = usbvideo_template; + sprintf(uvd->vdev.name, "%.20s USB Camera", cams->drvName); + /* + * The client is free to overwrite those because we + * return control to the client's probe function right now. + */ +allocate_done: + mutex_unlock(&uvd->lock); + usbvideo_ClientDecModCount(uvd); + return uvd; +} + +EXPORT_SYMBOL(usbvideo_AllocateDevice); + +int usbvideo_RegisterVideoDevice(struct uvd *uvd) +{ + char tmp1[20], tmp2[20]; /* Buffers for printing */ + + if (uvd == NULL) { + err("%s: Illegal call.", __FUNCTION__); + return -EINVAL; + } + if (uvd->video_endp == 0) { + info("%s: No video endpoint specified; data pump disabled.", __FUNCTION__); + } + if (uvd->paletteBits == 0) { + err("%s: No palettes specified!", __FUNCTION__); + return -EINVAL; + } + if (uvd->defaultPalette == 0) { + info("%s: No default palette!", __FUNCTION__); + } + + uvd->max_frame_size = VIDEOSIZE_X(uvd->canvas) * + VIDEOSIZE_Y(uvd->canvas) * V4L_BYTES_PER_PIXEL; + usbvideo_VideosizeToString(tmp1, sizeof(tmp1), uvd->videosize); + usbvideo_VideosizeToString(tmp2, sizeof(tmp2), uvd->canvas); + + if (uvd->debug > 0) { + info("%s: iface=%d. endpoint=$%02x paletteBits=$%08lx", + __FUNCTION__, uvd->iface, uvd->video_endp, uvd->paletteBits); + } + if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { + err("%s: video_register_device failed", __FUNCTION__); + return -EPIPE; + } + if (uvd->debug > 1) { + info("%s: video_register_device() successful", __FUNCTION__); + } + if (uvd->dev == NULL) { + err("%s: uvd->dev == NULL", __FUNCTION__); + return -EINVAL; + } + + info("%s on /dev/video%d: canvas=%s videosize=%s", + (uvd->handle != NULL) ? uvd->handle->drvName : "???", + uvd->vdev.minor, tmp2, tmp1); + + usb_get_dev(uvd->dev); + return 0; +} + +EXPORT_SYMBOL(usbvideo_RegisterVideoDevice); + +/* ******************************************************************** */ + +static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct uvd *uvd = file->private_data; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end-vma->vm_start; + unsigned long page, pos; + + if (!CAMERA_IS_OPERATIONAL(uvd)) + return -EFAULT; + + if (size > (((USBVIDEO_NUMFRAMES * uvd->max_frame_size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) + return -EINVAL; + + pos = (unsigned long) uvd->fbuf; + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + +/* + * usbvideo_v4l_open() + * + * This is part of Video 4 Linux API. The driver can be opened by one + * client only (checks internal counter 'uvdser'). The procedure + * then allocates buffers needed for video processing. + * + * History: + * 22-Jan-2000 Rewrote, moved scratch buffer allocation here. Now the + * camera is also initialized here (once per connect), at + * expense of V4L client (it waits on open() call). + * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers. + * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). + */ +static int usbvideo_v4l_open(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct uvd *uvd = (struct uvd *) dev; + const int sb_size = FRAMES_PER_DESC * uvd->iso_packet_len; + int i, errCode = 0; + + if (uvd->debug > 1) + info("%s($%p)", __FUNCTION__, dev); + + usbvideo_ClientIncModCount(uvd); + mutex_lock(&uvd->lock); + + if (uvd->user) { + err("%s: Someone tried to open an already opened device!", __FUNCTION__); + errCode = -EBUSY; + } else { + /* Clear statistics */ + memset(&uvd->stats, 0, sizeof(uvd->stats)); + + /* Clean pointers so we know if we allocated something */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) + uvd->sbuf[i].data = NULL; + + /* Allocate memory for the frame buffers */ + uvd->fbuf_size = USBVIDEO_NUMFRAMES * uvd->max_frame_size; + uvd->fbuf = usbvideo_rvmalloc(uvd->fbuf_size); + RingQueue_Allocate(&uvd->dp, RING_QUEUE_SIZE); + if ((uvd->fbuf == NULL) || + (!RingQueue_IsAllocated(&uvd->dp))) { + err("%s: Failed to allocate fbuf or dp", __FUNCTION__); + errCode = -ENOMEM; + } else { + /* Allocate all buffers */ + for (i=0; i < USBVIDEO_NUMFRAMES; i++) { + uvd->frame[i].frameState = FrameState_Unused; + uvd->frame[i].data = uvd->fbuf + i*(uvd->max_frame_size); + /* + * Set default sizes in case IOCTL (VIDIOCMCAPTURE) + * is not used (using read() instead). + */ + uvd->frame[i].canvas = uvd->canvas; + uvd->frame[i].seqRead_Index = 0; + } + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + uvd->sbuf[i].data = kmalloc(sb_size, GFP_KERNEL); + if (uvd->sbuf[i].data == NULL) { + errCode = -ENOMEM; + break; + } + } + } + if (errCode != 0) { + /* Have to free all that memory */ + if (uvd->fbuf != NULL) { + usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size); + uvd->fbuf = NULL; + } + RingQueue_Free(&uvd->dp); + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + kfree(uvd->sbuf[i].data); + uvd->sbuf[i].data = NULL; + } + } + } + + /* If so far no errors then we shall start the camera */ + if (errCode == 0) { + /* Start data pump if we have valid endpoint */ + if (uvd->video_endp != 0) + errCode = GET_CALLBACK(uvd, startDataPump)(uvd); + if (errCode == 0) { + if (VALID_CALLBACK(uvd, setupOnOpen)) { + if (uvd->debug > 1) + info("%s: setupOnOpen callback", __FUNCTION__); + errCode = GET_CALLBACK(uvd, setupOnOpen)(uvd); + if (errCode < 0) { + err("%s: setupOnOpen callback failed (%d.).", + __FUNCTION__, errCode); + } else if (uvd->debug > 1) { + info("%s: setupOnOpen callback successful", __FUNCTION__); + } + } + if (errCode == 0) { + uvd->settingsAdjusted = 0; + if (uvd->debug > 1) + info("%s: Open succeeded.", __FUNCTION__); + uvd->user++; + file->private_data = uvd; + } + } + } + mutex_unlock(&uvd->lock); + if (errCode != 0) + usbvideo_ClientDecModCount(uvd); + if (uvd->debug > 0) + info("%s: Returning %d.", __FUNCTION__, errCode); + return errCode; +} + +/* + * usbvideo_v4l_close() + * + * This is part of Video 4 Linux API. The procedure + * stops streaming and deallocates all buffers that were earlier + * allocated in usbvideo_v4l_open(). + * + * History: + * 22-Jan-2000 Moved scratch buffer deallocation here. + * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers. + * 24-May-2000 Moved MOD_DEC_USE_COUNT outside of code that can sleep. + */ +static int usbvideo_v4l_close(struct inode *inode, struct file *file) +{ + struct video_device *dev = file->private_data; + struct uvd *uvd = (struct uvd *) dev; + int i; + + if (uvd->debug > 1) + info("%s($%p)", __FUNCTION__, dev); + + mutex_lock(&uvd->lock); + GET_CALLBACK(uvd, stopDataPump)(uvd); + usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size); + uvd->fbuf = NULL; + RingQueue_Free(&uvd->dp); + + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + kfree(uvd->sbuf[i].data); + uvd->sbuf[i].data = NULL; + } + +#if USBVIDEO_REPORT_STATS + usbvideo_ReportStatistics(uvd); +#endif + + uvd->user--; + if (uvd->remove_pending) { + if (uvd->debug > 0) + info("usbvideo_v4l_close: Final disconnect."); + usbvideo_CameraRelease(uvd); + } + mutex_unlock(&uvd->lock); + usbvideo_ClientDecModCount(uvd); + + if (uvd->debug > 1) + info("%s: Completed.", __FUNCTION__); + file->private_data = NULL; + return 0; +} + +/* + * usbvideo_v4l_ioctl() + * + * This is part of Video 4 Linux API. The procedure handles ioctl() calls. + * + * History: + * 22-Jan-2000 Corrected VIDIOCSPICT to reject unsupported settings. + */ +static int usbvideo_v4l_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct uvd *uvd = file->private_data; + + if (!CAMERA_IS_OPERATIONAL(uvd)) + return -EIO; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability *b = arg; + *b = uvd->vcap; + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + *v = uvd->vchan; + return 0; + } + case VIDIOCSCHAN: + { + struct video_channel *v = arg; + if (v->channel != 0) + return -EINVAL; + return 0; + } + case VIDIOCGPICT: + { + struct video_picture *pic = arg; + *pic = uvd->vpic; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture *pic = arg; + /* + * Use temporary 'video_picture' structure to preserve our + * own settings (such as color depth, palette) that we + * aren't allowing everyone (V4L client) to change. + */ + uvd->vpic.brightness = pic->brightness; + uvd->vpic.hue = pic->hue; + uvd->vpic.colour = pic->colour; + uvd->vpic.contrast = pic->contrast; + uvd->settingsAdjusted = 0; /* Will force new settings */ + return 0; + } + case VIDIOCSWIN: + { + struct video_window *vw = arg; + + if(VALID_CALLBACK(uvd, setVideoMode)) { + return GET_CALLBACK(uvd, setVideoMode)(uvd, vw); + } + + if (vw->flags) + return -EINVAL; + if (vw->clipcount) + return -EINVAL; + if (vw->width != VIDEOSIZE_X(uvd->canvas)) + return -EINVAL; + if (vw->height != VIDEOSIZE_Y(uvd->canvas)) + return -EINVAL; + + return 0; + } + case VIDIOCGWIN: + { + struct video_window *vw = arg; + + vw->x = 0; + vw->y = 0; + vw->width = VIDEOSIZE_X(uvd->videosize); + vw->height = VIDEOSIZE_Y(uvd->videosize); + vw->chromakey = 0; + if (VALID_CALLBACK(uvd, getFPS)) + vw->flags = GET_CALLBACK(uvd, getFPS)(uvd); + else + vw->flags = 10; /* FIXME: do better! */ + return 0; + } + case VIDIOCGMBUF: + { + struct video_mbuf *vm = arg; + int i; + + memset(vm, 0, sizeof(*vm)); + vm->size = uvd->max_frame_size * USBVIDEO_NUMFRAMES; + vm->frames = USBVIDEO_NUMFRAMES; + for(i = 0; i < USBVIDEO_NUMFRAMES; i++) + vm->offsets[i] = i * uvd->max_frame_size; + + return 0; + } + case VIDIOCMCAPTURE: + { + struct video_mmap *vm = arg; + + if (uvd->debug >= 1) { + info("VIDIOCMCAPTURE: frame=%d. size=%dx%d, format=%d.", + vm->frame, vm->width, vm->height, vm->format); + } + /* + * Check if the requested size is supported. If the requestor + * requests too big a frame then we may be tricked into accessing + * outside of own preallocated frame buffer (in uvd->frame). + * This will cause oops or a security hole. Theoretically, we + * could only clamp the size down to acceptable bounds, but then + * we'd need to figure out how to insert our smaller buffer into + * larger caller's buffer... this is not an easy question. So we + * here just flatly reject too large requests, assuming that the + * caller will resubmit with smaller size. Callers should know + * what size we support (returned by VIDIOCGCAP). However vidcat, + * for one, does not care and allows to ask for any size. + */ + if ((vm->width > VIDEOSIZE_X(uvd->canvas)) || + (vm->height > VIDEOSIZE_Y(uvd->canvas))) { + if (uvd->debug > 0) { + info("VIDIOCMCAPTURE: Size=%dx%d too large; " + "allowed only up to %ldx%ld", vm->width, vm->height, + VIDEOSIZE_X(uvd->canvas), VIDEOSIZE_Y(uvd->canvas)); + } + return -EINVAL; + } + /* Check if the palette is supported */ + if (((1L << vm->format) & uvd->paletteBits) == 0) { + if (uvd->debug > 0) { + info("VIDIOCMCAPTURE: format=%d. not supported" + " (paletteBits=$%08lx)", + vm->format, uvd->paletteBits); + } + return -EINVAL; + } + if ((vm->frame < 0) || (vm->frame >= USBVIDEO_NUMFRAMES)) { + err("VIDIOCMCAPTURE: vm.frame=%d. !E [0-%d]", vm->frame, USBVIDEO_NUMFRAMES-1); + return -EINVAL; + } + if (uvd->frame[vm->frame].frameState == FrameState_Grabbing) { + /* Not an error - can happen */ + } + uvd->frame[vm->frame].request = VIDEOSIZE(vm->width, vm->height); + uvd->frame[vm->frame].palette = vm->format; + + /* Mark it as ready */ + uvd->frame[vm->frame].frameState = FrameState_Ready; + + return usbvideo_NewFrame(uvd, vm->frame); + } + case VIDIOCSYNC: + { + int *frameNum = arg; + int ret; + + if (*frameNum < 0 || *frameNum >= USBVIDEO_NUMFRAMES) + return -EINVAL; + + if (uvd->debug >= 1) + info("VIDIOCSYNC: syncing to frame %d.", *frameNum); + if (uvd->flags & FLAGS_NO_DECODING) + ret = usbvideo_GetFrame(uvd, *frameNum); + else if (VALID_CALLBACK(uvd, getFrame)) { + ret = GET_CALLBACK(uvd, getFrame)(uvd, *frameNum); + if ((ret < 0) && (uvd->debug >= 1)) { + err("VIDIOCSYNC: getFrame() returned %d.", ret); + } + } else { + err("VIDIOCSYNC: getFrame is not set"); + ret = -EFAULT; + } + + /* + * The frame is in FrameState_Done_Hold state. Release it + * right now because its data is already mapped into + * the user space and it's up to the application to + * make use of it until it asks for another frame. + */ + uvd->frame[*frameNum].frameState = FrameState_Unused; + return ret; + } + case VIDIOCGFBUF: + { + struct video_buffer *vb = arg; + + memset(vb, 0, sizeof(*vb)); + return 0; + } + case VIDIOCKEY: + return 0; + + case VIDIOCCAPTURE: + return -EINVAL; + + case VIDIOCSFBUF: + + case VIDIOCGTUNER: + case VIDIOCSTUNER: + + case VIDIOCGFREQ: + case VIDIOCSFREQ: + + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int usbvideo_v4l_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, usbvideo_v4l_do_ioctl); +} + +/* + * usbvideo_v4l_read() + * + * This is mostly boring stuff. We simply ask for a frame and when it + * arrives copy all the video data from it into user space. There is + * no obvious need to override this method. + * + * History: + * 20-Oct-2000 Created. + * 01-Nov-2000 Added mutex (uvd->lock). + */ +static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct uvd *uvd = file->private_data; + int noblock = file->f_flags & O_NONBLOCK; + int frmx = -1, i; + struct usbvideo_frame *frame; + + if (!CAMERA_IS_OPERATIONAL(uvd) || (buf == NULL)) + return -EFAULT; + + if (uvd->debug >= 1) + info("%s: %Zd. bytes, noblock=%d.", __FUNCTION__, count, noblock); + + mutex_lock(&uvd->lock); + + /* See if a frame is completed, then use it. */ + for(i = 0; i < USBVIDEO_NUMFRAMES; i++) { + if ((uvd->frame[i].frameState == FrameState_Done) || + (uvd->frame[i].frameState == FrameState_Done_Hold) || + (uvd->frame[i].frameState == FrameState_Error)) { + frmx = i; + break; + } + } + + /* FIXME: If we don't start a frame here then who ever does? */ + if (noblock && (frmx == -1)) { + count = -EAGAIN; + goto read_done; + } + + /* + * If no FrameState_Done, look for a FrameState_Grabbing state. + * See if a frame is in process (grabbing), then use it. + * We will need to wait until it becomes cooked, of course. + */ + if (frmx == -1) { + for(i = 0; i < USBVIDEO_NUMFRAMES; i++) { + if (uvd->frame[i].frameState == FrameState_Grabbing) { + frmx = i; + break; + } + } + } + + /* + * If no frame is active, start one. We don't care which one + * it will be, so #0 is as good as any. + * In read access mode we don't have convenience of VIDIOCMCAPTURE + * to specify the requested palette (video format) on per-frame + * basis. This means that we have to return data in -some- format + * and just hope that the client knows what to do with it. + * The default format is configured in uvd->defaultPalette field + * as one of VIDEO_PALETTE_xxx values. We stuff it into the new + * frame and initiate the frame filling process. + */ + if (frmx == -1) { + if (uvd->defaultPalette == 0) { + err("%s: No default palette; don't know what to do!", __FUNCTION__); + count = -EFAULT; + goto read_done; + } + frmx = 0; + /* + * We have no per-frame control over video size. + * Therefore we only can use whatever size was + * specified as default. + */ + uvd->frame[frmx].request = uvd->videosize; + uvd->frame[frmx].palette = uvd->defaultPalette; + uvd->frame[frmx].frameState = FrameState_Ready; + usbvideo_NewFrame(uvd, frmx); + /* Now frame 0 is supposed to start filling... */ + } + + /* + * Get a pointer to the active frame. It is either previously + * completed frame or frame in progress but not completed yet. + */ + frame = &uvd->frame[frmx]; + + /* + * Sit back & wait until the frame gets filled and postprocessed. + * If we fail to get the picture [in time] then return the error. + * In this call we specify that we want the frame to be waited for, + * postprocessed and switched into FrameState_Done_Hold state. This + * state is used to hold the frame as "fully completed" between + * subsequent partial reads of the same frame. + */ + if (frame->frameState != FrameState_Done_Hold) { + long rv = -EFAULT; + if (uvd->flags & FLAGS_NO_DECODING) + rv = usbvideo_GetFrame(uvd, frmx); + else if (VALID_CALLBACK(uvd, getFrame)) + rv = GET_CALLBACK(uvd, getFrame)(uvd, frmx); + else + err("getFrame is not set"); + if ((rv != 0) || (frame->frameState != FrameState_Done_Hold)) { + count = rv; + goto read_done; + } + } + + /* + * Copy bytes to user space. We allow for partial reads, which + * means that the user application can request read less than + * the full frame size. It is up to the application to issue + * subsequent calls until entire frame is read. + * + * First things first, make sure we don't copy more than we + * have - even if the application wants more. That would be + * a big security embarassment! + */ + if ((count + frame->seqRead_Index) > frame->seqRead_Length) + count = frame->seqRead_Length - frame->seqRead_Index; + + /* + * Copy requested amount of data to user space. We start + * copying from the position where we last left it, which + * will be zero for a new frame (not read before). + */ + if (copy_to_user(buf, frame->data + frame->seqRead_Index, count)) { + count = -EFAULT; + goto read_done; + } + + /* Update last read position */ + frame->seqRead_Index += count; + if (uvd->debug >= 1) { + err("%s: {copy} count used=%Zd, new seqRead_Index=%ld", + __FUNCTION__, count, frame->seqRead_Index); + } + + /* Finally check if the frame is done with and "release" it */ + if (frame->seqRead_Index >= frame->seqRead_Length) { + /* All data has been read */ + frame->seqRead_Index = 0; + + /* Mark it as available to be used again. */ + uvd->frame[frmx].frameState = FrameState_Unused; + if (usbvideo_NewFrame(uvd, (frmx + 1) % USBVIDEO_NUMFRAMES)) { + err("%s: usbvideo_NewFrame failed.", __FUNCTION__); + } + } +read_done: + mutex_unlock(&uvd->lock); + return count; +} + +/* + * Make all of the blocks of data contiguous + */ +static int usbvideo_CompressIsochronous(struct uvd *uvd, struct urb *urb) +{ + char *cdata; + int i, totlen = 0; + + for (i = 0; i < urb->number_of_packets; i++) { + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + + cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + /* Detect and ignore errored packets */ + if (st < 0) { + if (uvd->debug >= 1) + err("Data error: packet=%d. len=%d. status=%d.", i, n, st); + uvd->stats.iso_err_count++; + continue; + } + + /* Detect and ignore empty packets */ + if (n <= 0) { + uvd->stats.iso_skip_count++; + continue; + } + totlen += n; /* Little local accounting */ + RingQueue_Enqueue(&uvd->dp, cdata, n); + } + return totlen; +} + +static void usbvideo_IsocIrq(struct urb *urb, struct pt_regs *regs) +{ + int i, ret, len; + struct uvd *uvd = urb->context; + + /* We don't want to do anything if we are about to be removed! */ + if (!CAMERA_IS_OPERATIONAL(uvd)) + return; +#if 0 + if (urb->actual_length > 0) { + info("urb=$%p status=%d. errcount=%d. length=%d.", + urb, urb->status, urb->error_count, urb->actual_length); + } else { + static int c = 0; + if (c++ % 100 == 0) + info("No Isoc data"); + } +#endif + + if (!uvd->streaming) { + if (uvd->debug >= 1) + info("Not streaming, but interrupt!"); + return; + } + + uvd->stats.urb_count++; + if (urb->actual_length <= 0) + goto urb_done_with; + + /* Copy the data received into ring queue */ + len = usbvideo_CompressIsochronous(uvd, urb); + uvd->stats.urb_length = len; + if (len <= 0) + goto urb_done_with; + + /* Here we got some data */ + uvd->stats.data_count += len; + RingQueue_WakeUpInterruptible(&uvd->dp); + +urb_done_with: + for (i = 0; i < FRAMES_PER_DESC; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + urb->status = 0; + urb->dev = uvd->dev; + ret = usb_submit_urb (urb, GFP_KERNEL); + if(ret) + err("usb_submit_urb error (%d)", ret); + return; +} + +/* + * usbvideo_StartDataPump() + * + * History: + * 27-Jan-2000 Used ibmcam->iface, ibmcam->ifaceAltActive instead + * of hardcoded values. Simplified by using for loop, + * allowed any number of URBs. + */ +static int usbvideo_StartDataPump(struct uvd *uvd) +{ + struct usb_device *dev = uvd->dev; + int i, errFlag; + + if (uvd->debug > 1) + info("%s($%p)", __FUNCTION__, uvd); + + if (!CAMERA_IS_OPERATIONAL(uvd)) { + err("%s: Camera is not operational", __FUNCTION__); + return -EFAULT; + } + uvd->curframe = -1; + + /* Alternate interface 1 is is the biggest frame size */ + i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive); + if (i < 0) { + err("%s: usb_set_interface error", __FUNCTION__); + uvd->last_error = i; + return -EBUSY; + } + if (VALID_CALLBACK(uvd, videoStart)) + GET_CALLBACK(uvd, videoStart)(uvd); + else + err("%s: videoStart not set", __FUNCTION__); + + /* We double buffer the Iso lists */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + int j, k; + struct urb *urb = uvd->sbuf[i].urb; + urb->dev = dev; + urb->context = uvd; + urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp); + urb->interval = 1; + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = uvd->sbuf[i].data; + urb->complete = usbvideo_IsocIrq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = uvd->iso_packet_len * FRAMES_PER_DESC; + for (j=k=0; j < FRAMES_PER_DESC; j++, k += uvd->iso_packet_len) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = uvd->iso_packet_len; + } + } + + /* Submit all URBs */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + errFlag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL); + if (errFlag) + err("%s: usb_submit_isoc(%d) ret %d", __FUNCTION__, i, errFlag); + } + + uvd->streaming = 1; + if (uvd->debug > 1) + info("%s: streaming=1 video_endp=$%02x", __FUNCTION__, uvd->video_endp); + return 0; +} + +/* + * usbvideo_StopDataPump() + * + * This procedure stops streaming and deallocates URBs. Then it + * activates zero-bandwidth alt. setting of the video interface. + * + * History: + * 22-Jan-2000 Corrected order of actions to work after surprise removal. + * 27-Jan-2000 Used uvd->iface, uvd->ifaceAltInactive instead of hardcoded values. + */ +static void usbvideo_StopDataPump(struct uvd *uvd) +{ + int i, j; + + if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL)) + return; + + if (uvd->debug > 1) + info("%s($%p)", __FUNCTION__, uvd); + + /* Unschedule all of the iso td's */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + usb_kill_urb(uvd->sbuf[i].urb); + } + if (uvd->debug > 1) + info("%s: streaming=0", __FUNCTION__); + uvd->streaming = 0; + + if (!uvd->remove_pending) { + /* Invoke minidriver's magic to stop the camera */ + if (VALID_CALLBACK(uvd, videoStop)) + GET_CALLBACK(uvd, videoStop)(uvd); + else + err("%s: videoStop not set", __FUNCTION__); + + /* Set packet size to 0 */ + j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive); + if (j < 0) { + err("%s: usb_set_interface() error %d.", __FUNCTION__, j); + uvd->last_error = j; + } + } +} + +/* + * usbvideo_NewFrame() + * + * History: + * 29-Mar-00 Added copying of previous frame into the current one. + * 6-Aug-00 Added model 3 video sizes, removed redundant width, height. + */ +static int usbvideo_NewFrame(struct uvd *uvd, int framenum) +{ + struct usbvideo_frame *frame; + int n; + + if (uvd->debug > 1) + info("usbvideo_NewFrame($%p,%d.)", uvd, framenum); + + /* If we're not grabbing a frame right now and the other frame is */ + /* ready to be grabbed into, then use it instead */ + if (uvd->curframe != -1) + return 0; + + /* If necessary we adjust picture settings between frames */ + if (!uvd->settingsAdjusted) { + if (VALID_CALLBACK(uvd, adjustPicture)) + GET_CALLBACK(uvd, adjustPicture)(uvd); + uvd->settingsAdjusted = 1; + } + + n = (framenum + 1) % USBVIDEO_NUMFRAMES; + if (uvd->frame[n].frameState == FrameState_Ready) + framenum = n; + + frame = &uvd->frame[framenum]; + + frame->frameState = FrameState_Grabbing; + frame->scanstate = ScanState_Scanning; + frame->seqRead_Length = 0; /* Accumulated in xxx_parse_data() */ + frame->deinterlace = Deinterlace_None; + frame->flags = 0; /* No flags yet, up to minidriver (or us) to set them */ + uvd->curframe = framenum; + + /* + * Normally we would want to copy previous frame into the current one + * before we even start filling it with data; this allows us to stop + * filling at any moment; top portion of the frame will be new and + * bottom portion will stay as it was in previous frame. If we don't + * do that then missing chunks of video stream will result in flickering + * portions of old data whatever it was before. + * + * If we choose not to copy previous frame (to, for example, save few + * bus cycles - the frame can be pretty large!) then we have an option + * to clear the frame before using. If we experience losses in this + * mode then missing picture will be black (no flickering). + * + * Finally, if user chooses not to clean the current frame before + * filling it with data then the old data will be visible if we fail + * to refill entire frame with new data. + */ + if (!(uvd->flags & FLAGS_SEPARATE_FRAMES)) { + /* This copies previous frame into this one to mask losses */ + int prev = (framenum - 1 + USBVIDEO_NUMFRAMES) % USBVIDEO_NUMFRAMES; + memmove(frame->data, uvd->frame[prev].data, uvd->max_frame_size); + } else { + if (uvd->flags & FLAGS_CLEAN_FRAMES) { + /* This provides a "clean" frame but slows things down */ + memset(frame->data, 0, uvd->max_frame_size); + } + } + return 0; +} + +/* + * usbvideo_CollectRawData() + * + * This procedure can be used instead of 'processData' callback if you + * only want to dump the raw data from the camera into the output + * device (frame buffer). You can look at it with V4L client, but the + * image will be unwatchable. The main purpose of this code and of the + * mode FLAGS_NO_DECODING is debugging and capturing of datastreams from + * new, unknown cameras. This procedure will be automatically invoked + * instead of the specified callback handler when uvd->flags has bit + * FLAGS_NO_DECODING set. Therefore, any regular build of any driver + * based on usbvideo can use this feature at any time. + */ +static void usbvideo_CollectRawData(struct uvd *uvd, struct usbvideo_frame *frame) +{ + int n; + + assert(uvd != NULL); + assert(frame != NULL); + + /* Try to move data from queue into frame buffer */ + n = RingQueue_GetLength(&uvd->dp); + if (n > 0) { + int m; + /* See how much space we have left */ + m = uvd->max_frame_size - frame->seqRead_Length; + if (n > m) + n = m; + /* Now move that much data into frame buffer */ + RingQueue_Dequeue( + &uvd->dp, + frame->data + frame->seqRead_Length, + m); + frame->seqRead_Length += m; + } + /* See if we filled the frame */ + if (frame->seqRead_Length >= uvd->max_frame_size) { + frame->frameState = FrameState_Done; + uvd->curframe = -1; + uvd->stats.frame_num++; + } +} + +static int usbvideo_GetFrame(struct uvd *uvd, int frameNum) +{ + struct usbvideo_frame *frame = &uvd->frame[frameNum]; + + if (uvd->debug >= 2) + info("%s($%p,%d.)", __FUNCTION__, uvd, frameNum); + + switch (frame->frameState) { + case FrameState_Unused: + if (uvd->debug >= 2) + info("%s: FrameState_Unused", __FUNCTION__); + return -EINVAL; + case FrameState_Ready: + case FrameState_Grabbing: + case FrameState_Error: + { + int ntries, signalPending; + redo: + if (!CAMERA_IS_OPERATIONAL(uvd)) { + if (uvd->debug >= 2) + info("%s: Camera is not operational (1)", __FUNCTION__); + return -EIO; + } + ntries = 0; + do { + RingQueue_InterruptibleSleepOn(&uvd->dp); + signalPending = signal_pending(current); + if (!CAMERA_IS_OPERATIONAL(uvd)) { + if (uvd->debug >= 2) + info("%s: Camera is not operational (2)", __FUNCTION__); + return -EIO; + } + assert(uvd->fbuf != NULL); + if (signalPending) { + if (uvd->debug >= 2) + info("%s: Signal=$%08x", __FUNCTION__, signalPending); + if (uvd->flags & FLAGS_RETRY_VIDIOCSYNC) { + usbvideo_TestPattern(uvd, 1, 0); + uvd->curframe = -1; + uvd->stats.frame_num++; + if (uvd->debug >= 2) + info("%s: Forced test pattern screen", __FUNCTION__); + return 0; + } else { + /* Standard answer: Interrupted! */ + if (uvd->debug >= 2) + info("%s: Interrupted!", __FUNCTION__); + return -EINTR; + } + } else { + /* No signals - we just got new data in dp queue */ + if (uvd->flags & FLAGS_NO_DECODING) + usbvideo_CollectRawData(uvd, frame); + else if (VALID_CALLBACK(uvd, processData)) + GET_CALLBACK(uvd, processData)(uvd, frame); + else + err("%s: processData not set", __FUNCTION__); + } + } while (frame->frameState == FrameState_Grabbing); + if (uvd->debug >= 2) { + info("%s: Grabbing done; state=%d. (%lu. bytes)", + __FUNCTION__, frame->frameState, frame->seqRead_Length); + } + if (frame->frameState == FrameState_Error) { + int ret = usbvideo_NewFrame(uvd, frameNum); + if (ret < 0) { + err("%s: usbvideo_NewFrame() failed (%d.)", __FUNCTION__, ret); + return ret; + } + goto redo; + } + /* Note that we fall through to meet our destiny below */ + } + case FrameState_Done: + /* + * Do all necessary postprocessing of data prepared in + * "interrupt" code and the collecting code above. The + * frame gets marked as FrameState_Done by queue parsing code. + * This status means that we collected enough data and + * most likely processed it as we went through. However + * the data may need postprocessing, such as deinterlacing + * or picture adjustments implemented in software (horror!) + * + * As soon as the frame becomes "final" it gets promoted to + * FrameState_Done_Hold status where it will remain until the + * caller consumed all the video data from the frame. Then + * the empty shell of ex-frame is thrown out for dogs to eat. + * But we, worried about pets, will recycle the frame! + */ + uvd->stats.frame_num++; + if ((uvd->flags & FLAGS_NO_DECODING) == 0) { + if (VALID_CALLBACK(uvd, postProcess)) + GET_CALLBACK(uvd, postProcess)(uvd, frame); + if (frame->flags & USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST) + usbvideo_SoftwareContrastAdjustment(uvd, frame); + } + frame->frameState = FrameState_Done_Hold; + if (uvd->debug >= 2) + info("%s: Entered FrameState_Done_Hold state.", __FUNCTION__); + return 0; + + case FrameState_Done_Hold: + /* + * We stay in this state indefinitely until someone external, + * like ioctl() or read() call finishes digesting the frame + * data. Then it will mark the frame as FrameState_Unused and + * it will be released back into the wild to roam freely. + */ + if (uvd->debug >= 2) + info("%s: FrameState_Done_Hold state.", __FUNCTION__); + return 0; + } + + /* Catch-all for other cases. We shall not be here. */ + err("%s: Invalid state %d.", __FUNCTION__, frame->frameState); + frame->frameState = FrameState_Unused; + return 0; +} + +/* + * usbvideo_DeinterlaceFrame() + * + * This procedure deinterlaces the given frame. Some cameras produce + * only half of scanlines - sometimes only even lines, sometimes only + * odd lines. The deinterlacing method is stored in frame->deinterlace + * variable. + * + * Here we scan the frame vertically and replace missing scanlines with + * average between surrounding ones - before and after. If we have no + * line above then we just copy next line. Similarly, if we need to + * create a last line then preceding line is used. + */ +void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame) +{ + if ((uvd == NULL) || (frame == NULL)) + return; + + if ((frame->deinterlace == Deinterlace_FillEvenLines) || + (frame->deinterlace == Deinterlace_FillOddLines)) + { + const int v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; + int i = (frame->deinterlace == Deinterlace_FillEvenLines) ? 0 : 1; + + for (; i < VIDEOSIZE_Y(frame->request); i += 2) { + const unsigned char *fs1, *fs2; + unsigned char *fd; + int ip, in, j; /* Previous and next lines */ + + /* + * Need to average lines before and after 'i'. + * If we go out of bounds seeking those lines then + * we point back to existing line. + */ + ip = i - 1; /* First, get rough numbers */ + in = i + 1; + + /* Now validate */ + if (ip < 0) + ip = in; + if (in >= VIDEOSIZE_Y(frame->request)) + in = ip; + + /* Sanity check */ + if ((ip < 0) || (in < 0) || + (ip >= VIDEOSIZE_Y(frame->request)) || + (in >= VIDEOSIZE_Y(frame->request))) + { + err("Error: ip=%d. in=%d. req.height=%ld.", + ip, in, VIDEOSIZE_Y(frame->request)); + break; + } + + /* Now we need to average lines 'ip' and 'in' to produce line 'i' */ + fs1 = frame->data + (v4l_linesize * ip); + fs2 = frame->data + (v4l_linesize * in); + fd = frame->data + (v4l_linesize * i); + + /* Average lines around destination */ + for (j=0; j < v4l_linesize; j++) { + fd[j] = (unsigned char)((((unsigned) fs1[j]) + + ((unsigned)fs2[j])) >> 1); + } + } + } + + /* Optionally display statistics on the screen */ + if (uvd->flags & FLAGS_OVERLAY_STATS) + usbvideo_OverlayStats(uvd, frame); +} + +EXPORT_SYMBOL(usbvideo_DeinterlaceFrame); + +/* + * usbvideo_SoftwareContrastAdjustment() + * + * This code adjusts the contrast of the frame, assuming RGB24 format. + * As most software image processing, this job is CPU-intensive. + * Get a camera that supports hardware adjustment! + * + * History: + * 09-Feb-2001 Created. + */ +static void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd, + struct usbvideo_frame *frame) +{ + int i, j, v4l_linesize; + signed long adj; + const int ccm = 128; /* Color correction median - see below */ + + if ((uvd == NULL) || (frame == NULL)) { + err("%s: Illegal call.", __FUNCTION__); + return; + } + adj = (uvd->vpic.contrast - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ + RESTRICT_TO_RANGE(adj, -ccm, ccm+1); + if (adj == 0) { + /* In rare case of no adjustment */ + return; + } + v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; + for (i=0; i < VIDEOSIZE_Y(frame->request); i++) { + unsigned char *fd = frame->data + (v4l_linesize * i); + for (j=0; j < v4l_linesize; j++) { + signed long v = (signed long) fd[j]; + /* Magnify up to 2 times, reduce down to zero */ + v = 128 + ((ccm + adj) * (v - 128)) / ccm; + RESTRICT_TO_RANGE(v, 0, 0xFF); /* Must flatten tails */ + fd[j] = (unsigned char) v; + } + } +} + +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/usbvideo/usbvideo.h b/linux/drivers/media/video/usbvideo/usbvideo.h new file mode 100644 index 000000000..3cbf4fc49 --- /dev/null +++ b/linux/drivers/media/video/usbvideo/usbvideo.h @@ -0,0 +1,394 @@ +/* + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef usbvideo_h +#define usbvideo_h + +#include +#include +#include +#include + +/* Most helpful debugging aid */ +#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__)))) + +#define USBVIDEO_REPORT_STATS 1 /* Set to 0 to block statistics on close */ + +/* Bit flags (options) */ +#define FLAGS_RETRY_VIDIOCSYNC (1 << 0) +#define FLAGS_MONOCHROME (1 << 1) +#define FLAGS_DISPLAY_HINTS (1 << 2) +#define FLAGS_OVERLAY_STATS (1 << 3) +#define FLAGS_FORCE_TESTPATTERN (1 << 4) +#define FLAGS_SEPARATE_FRAMES (1 << 5) +#define FLAGS_CLEAN_FRAMES (1 << 6) +#define FLAGS_NO_DECODING (1 << 7) + +/* Bit flags for frames (apply to the frame where they are specified) */ +#define USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST (1 << 0) + +/* Camera capabilities (maximum) */ +#define CAMERA_URB_FRAMES 32 +#define CAMERA_MAX_ISO_PACKET 1023 /* 1022 actually sent by camera */ +#define FRAMES_PER_DESC (CAMERA_URB_FRAMES) +#define FRAME_SIZE_PER_DESC (CAMERA_MAX_ISO_PACKET) + +/* This macro restricts an int variable to an inclusive range */ +#define RESTRICT_TO_RANGE(v,mi,ma) { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); } + +#define V4L_BYTES_PER_PIXEL 3 /* Because we produce RGB24 */ + +/* + * Use this macro to construct constants for different video sizes. + * We have to deal with different video sizes that have to be + * configured in the device or compared against when we receive + * a data. Normally one would define a bunch of VIDEOSIZE_x_by_y + * #defines and that's the end of story. However this solution + * does not allow to convert between real pixel sizes and the + * constant (integer) value that may be used to tag a frame or + * whatever. The set of macros below constructs videosize constants + * from the pixel size and allows to reconstruct the pixel size + * from the combined value later. + */ +#define VIDEOSIZE(x,y) (((x) & 0xFFFFL) | (((y) & 0xFFFFL) << 16)) +#define VIDEOSIZE_X(vs) ((vs) & 0xFFFFL) +#define VIDEOSIZE_Y(vs) (((vs) >> 16) & 0xFFFFL) +typedef unsigned long videosize_t; + +/* + * This macro checks if the camera is still operational. The 'uvd' + * pointer must be valid, uvd->dev must be valid, we are not + * removing the device and the device has not erred on us. + */ +#define CAMERA_IS_OPERATIONAL(uvd) (\ + (uvd != NULL) && \ + ((uvd)->dev != NULL) && \ + ((uvd)->last_error == 0) && \ + (!(uvd)->remove_pending)) + +/* + * We use macros to do YUV -> RGB conversion because this is + * very important for speed and totally unimportant for size. + * + * YUV -> RGB Conversion + * --------------------- + * + * B = 1.164*(Y-16) + 2.018*(V-128) + * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128) + * R = 1.164*(Y-16) + 1.596*(U-128) + * + * If you fancy integer arithmetics (as you should), hear this: + * + * 65536*B = 76284*(Y-16) + 132252*(V-128) + * 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128) + * 65536*R = 76284*(Y-16) + 104595*(U-128) + * + * Make sure the output values are within [0..255] range. + */ +#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x))) +#define YUV_TO_RGB_BY_THE_BOOK(my,mu,mv,mr,mg,mb) { \ + int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \ + mm_y = (my) - 16; \ + mm_u = (mu) - 128; \ + mm_v = (mv) - 128; \ + mm_yc= mm_y * 76284; \ + mm_b = (mm_yc + 132252*mm_v ) >> 16; \ + mm_g = (mm_yc - 53281*mm_u - 25625*mm_v ) >> 16; \ + mm_r = (mm_yc + 104595*mm_u ) >> 16; \ + mb = LIMIT_RGB(mm_b); \ + mg = LIMIT_RGB(mm_g); \ + mr = LIMIT_RGB(mm_r); \ +} + +#define RING_QUEUE_SIZE (128*1024) /* Must be a power of 2 */ +#define RING_QUEUE_ADVANCE_INDEX(rq,ind,n) (rq)->ind = ((rq)->ind + (n)) & ((rq)->length-1) +#define RING_QUEUE_DEQUEUE_BYTES(rq,n) RING_QUEUE_ADVANCE_INDEX(rq,ri,n) +#define RING_QUEUE_PEEK(rq,ofs) ((rq)->queue[((ofs) + (rq)->ri) & ((rq)->length-1)]) + +struct RingQueue { + unsigned char *queue; /* Data from the Isoc data pump */ + int length; /* How many bytes allocated for the queue */ + int wi; /* That's where we write */ + int ri; /* Read from here until you hit write index */ + wait_queue_head_t wqh; /* Processes waiting */ +}; + +enum ScanState { + ScanState_Scanning, /* Scanning for header */ + ScanState_Lines /* Parsing lines */ +}; + +/* Completion states of the data parser */ +enum ParseState { + scan_Continue, /* Just parse next item */ + scan_NextFrame, /* Frame done, send it to V4L */ + scan_Out, /* Not enough data for frame */ + scan_EndParse /* End parsing */ +}; + +enum FrameState { + FrameState_Unused, /* Unused (no MCAPTURE) */ + FrameState_Ready, /* Ready to start grabbing */ + FrameState_Grabbing, /* In the process of being grabbed into */ + FrameState_Done, /* Finished grabbing, but not been synced yet */ + FrameState_Done_Hold, /* Are syncing or reading */ + FrameState_Error, /* Something bad happened while processing */ +}; + +/* + * Some frames may contain only even or odd lines. This type + * specifies what type of deinterlacing is required. + */ +enum Deinterlace { + Deinterlace_None=0, + Deinterlace_FillOddLines, + Deinterlace_FillEvenLines +}; + +#define USBVIDEO_NUMFRAMES 2 /* How many frames we work with */ +#define USBVIDEO_NUMSBUF 2 /* How many URBs linked in a ring */ + +/* This structure represents one Isoc request - URB and buffer */ +struct usbvideo_sbuf { + char *data; + struct urb *urb; +}; + +struct usbvideo_frame { + char *data; /* Frame buffer */ + unsigned long header; /* Significant bits from the header */ + + videosize_t canvas; /* The canvas (max. image) allocated */ + videosize_t request; /* That's what the application asked for */ + unsigned short palette; /* The desired format */ + + enum FrameState frameState;/* State of grabbing */ + enum ScanState scanstate; /* State of scanning */ + enum Deinterlace deinterlace; + int flags; /* USBVIDEO_FRAME_FLAG_xxx bit flags */ + + int curline; /* Line of frame we're working on */ + + long seqRead_Length; /* Raw data length of frame */ + long seqRead_Index; /* Amount of data that has been already read */ + + void *user; /* Additional data that user may need */ +}; + +/* Statistics that can be overlaid on screen */ +struct usbvideo_statistics { + unsigned long frame_num; /* Sequential number of the frame */ + unsigned long urb_count; /* How many URBs we received so far */ + unsigned long urb_length; /* Length of last URB */ + unsigned long data_count; /* How many bytes we received */ + unsigned long header_count; /* How many frame headers we found */ + unsigned long iso_skip_count; /* How many empty ISO packets received */ + unsigned long iso_err_count; /* How many bad ISO packets received */ +}; + +struct usbvideo; + +struct uvd { + struct video_device vdev; /* Must be the first field! */ + struct usb_device *dev; + struct usbvideo *handle; /* Points back to the struct usbvideo */ + void *user_data; /* Camera-dependent data */ + int user_size; /* Size of that camera-dependent data */ + int debug; /* Debug level for usbvideo */ + unsigned char iface; /* Video interface number */ + unsigned char video_endp; + unsigned char ifaceAltActive; + unsigned char ifaceAltInactive; /* Alt settings */ + unsigned long flags; /* FLAGS_USBVIDEO_xxx */ + unsigned long paletteBits; /* Which palettes we accept? */ + unsigned short defaultPalette; /* What palette to use for read() */ + struct mutex lock; + int user; /* user count for exclusive use */ + + videosize_t videosize; /* Current setting */ + videosize_t canvas; /* This is the width,height of the V4L canvas */ + int max_frame_size; /* Bytes in one video frame */ + + int uvd_used; /* Is this structure in use? */ + int streaming; /* Are we streaming Isochronous? */ + int grabbing; /* Are we grabbing? */ + int settingsAdjusted; /* Have we adjusted contrast etc.? */ + int last_error; /* What calamity struck us? */ + + char *fbuf; /* Videodev buffer area */ + int fbuf_size; /* Videodev buffer size */ + + int curframe; + int iso_packet_len; /* Videomode-dependent, saves bus bandwidth */ + + struct RingQueue dp; /* Isoc data pump */ + struct usbvideo_frame frame[USBVIDEO_NUMFRAMES]; + struct usbvideo_sbuf sbuf[USBVIDEO_NUMSBUF]; + + volatile int remove_pending; /* If set then about to exit */ + + struct video_picture vpic, vpic_old; /* Picture settings */ + struct video_capability vcap; /* Video capabilities */ + struct video_channel vchan; /* May be used for tuner support */ + struct usbvideo_statistics stats; + char videoName[32]; /* Holds name like "video7" */ +}; + +/* + * usbvideo callbacks (virtual methods). They are set when usbvideo + * services are registered. All of these default to NULL, except those + * that default to usbvideo-provided methods. + */ +struct usbvideo_cb { + int (*probe)(struct usb_interface *, const struct usb_device_id *); + void (*userFree)(struct uvd *); + void (*disconnect)(struct usb_interface *); + int (*setupOnOpen)(struct uvd *); + void (*videoStart)(struct uvd *); + void (*videoStop)(struct uvd *); + void (*processData)(struct uvd *, struct usbvideo_frame *); + void (*postProcess)(struct uvd *, struct usbvideo_frame *); + void (*adjustPicture)(struct uvd *); + int (*getFPS)(struct uvd *); + int (*overlayHook)(struct uvd *, struct usbvideo_frame *); + int (*getFrame)(struct uvd *, int); + int (*startDataPump)(struct uvd *uvd); + void (*stopDataPump)(struct uvd *uvd); + int (*setVideoMode)(struct uvd *uvd, struct video_window *vw); +}; + +struct usbvideo { + int num_cameras; /* As allocated */ + struct usb_driver usbdrv; /* Interface to the USB stack */ + char drvName[80]; /* Driver name */ + struct mutex lock; /* Mutex protecting camera structures */ + struct usbvideo_cb cb; /* Table of callbacks (virtual methods) */ + struct video_device vdt; /* Video device template */ + struct uvd *cam; /* Array of camera structures */ + struct module *md_module; /* Minidriver module */ +}; + + +/* + * This macro retrieves callback address from the struct uvd object. + * No validity checks are done here, so be sure to check the + * callback beforehand with VALID_CALLBACK. + */ +#define GET_CALLBACK(uvd,cbName) ((uvd)->handle->cb.cbName) + +/* + * This macro returns either callback pointer or NULL. This is safe + * macro, meaning that most of components of data structures involved + * may be NULL - this only results in NULL being returned. You may + * wish to use this macro to make sure that the callback is callable. + * However keep in mind that those checks take time. + */ +#define VALID_CALLBACK(uvd,cbName) ((((uvd) != NULL) && \ + ((uvd)->handle != NULL)) ? GET_CALLBACK(uvd,cbName) : NULL) + +int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len); +int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n); +void RingQueue_WakeUpInterruptible(struct RingQueue *rq); +void RingQueue_Flush(struct RingQueue *rq); + +static inline int RingQueue_GetLength(const struct RingQueue *rq) +{ + return (rq->wi - rq->ri + rq->length) & (rq->length-1); +} + +static inline int RingQueue_GetFreeSpace(const struct RingQueue *rq) +{ + return rq->length - RingQueue_GetLength(rq); +} + +void usbvideo_DrawLine( + struct usbvideo_frame *frame, + int x1, int y1, + int x2, int y2, + unsigned char cr, unsigned char cg, unsigned char cb); +void usbvideo_HexDump(const unsigned char *data, int len); +void usbvideo_SayAndWait(const char *what); +void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode); + +/* Memory allocation routines */ +unsigned long usbvideo_kvirt_to_pa(unsigned long adr); + +int usbvideo_register( + struct usbvideo **pCams, + const int num_cams, + const int num_extra, + const char *driverName, + const struct usbvideo_cb *cbTable, + struct module *md, + const struct usb_device_id *id_table); +struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams); +int usbvideo_RegisterVideoDevice(struct uvd *uvd); +void usbvideo_Deregister(struct usbvideo **uvt); + +int usbvideo_v4l_initialize(struct video_device *dev); + +void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame); + +/* + * This code performs bounds checking - use it when working with + * new formats, or else you may get oopses all over the place. + * If pixel falls out of bounds then it gets shoved back (as close + * to place of offence as possible) and is painted bright red. + * + * There are two important concepts: frame width, height and + * V4L canvas width, height. The former is the area requested by + * the application -for this very frame-. The latter is the largest + * possible frame that we can serve (we advertise that via V4L ioctl). + * The frame data is expected to be formatted as lines of length + * VIDEOSIZE_X(fr->request), total VIDEOSIZE_Y(frame->request) lines. + */ +static inline void RGB24_PUTPIXEL( + struct usbvideo_frame *fr, + int ix, int iy, + unsigned char vr, + unsigned char vg, + unsigned char vb) +{ + register unsigned char *pf; + int limiter = 0, mx, my; + mx = ix; + my = iy; + if (mx < 0) { + mx=0; + limiter++; + } else if (mx >= VIDEOSIZE_X((fr)->request)) { + mx= VIDEOSIZE_X((fr)->request) - 1; + limiter++; + } + if (my < 0) { + my = 0; + limiter++; + } else if (my >= VIDEOSIZE_Y((fr)->request)) { + my = VIDEOSIZE_Y((fr)->request) - 1; + limiter++; + } + pf = (fr)->data + V4L_BYTES_PER_PIXEL*((iy)*VIDEOSIZE_X((fr)->request) + (ix)); + if (limiter) { + *pf++ = 0; + *pf++ = 0; + *pf++ = 0xFF; + } else { + *pf++ = (vb); + *pf++ = (vg); + *pf++ = (vr); + } +} + +#endif /* usbvideo_h */ diff --git a/linux/drivers/media/video/usbvideo/vicam.c b/linux/drivers/media/video/usbvideo/vicam.c new file mode 100644 index 000000000..90d48e851 --- /dev/null +++ b/linux/drivers/media/video/usbvideo/vicam.c @@ -0,0 +1,1411 @@ +/* + * USB ViCam WebCam driver + * Copyright (c) 2002 Joe Burks (jburks@wavicle.org), + * Christopher L Cheney (ccheney@cheney.cx), + * Pavel Machek (pavel@suse.cz), + * John Tyner (jtyner@cs.ucr.edu), + * Monroe Williams (monroe@pobox.com) + * + * Supports 3COM HomeConnect PC Digital WebCam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This source code is based heavily on the CPiA webcam driver which was + * written by Peter Pregler, Scott J. Bertin and Johannes Erdfelt + * + * Portions of this code were also copied from usbvideo.c + * + * Special thanks to the the whole team at Sourceforge for help making + * this driver become a reality. Notably: + * Andy Armstrong who reverse engineered the color encoding and + * Pavel Machek and Chris Cheney who worked on reverse engineering the + * camera controls and wrote the first generation driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "usbvideo.h" + +// #define VICAM_DEBUG + +#ifdef VICAM_DEBUG +#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, __FUNCTION__, lineno, ##args) +#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):%s (%d):"fmt,##args) +#else +#define DBG(fmn,args...) do {} while(0) +#endif + +#define DRIVER_AUTHOR "Joe Burks, jburks@wavicle.org" +#define DRIVER_DESC "ViCam WebCam Driver" + +/* Define these values to match your device */ +#define USB_VICAM_VENDOR_ID 0x04c1 +#define USB_VICAM_PRODUCT_ID 0x009d + +#define VICAM_BYTES_PER_PIXEL 3 +#define VICAM_MAX_READ_SIZE (512*242+128) +#define VICAM_MAX_FRAME_SIZE (VICAM_BYTES_PER_PIXEL*320*240) +#define VICAM_FRAMES 2 + +#define VICAM_HEADER_SIZE 64 + +#define clamp( x, l, h ) max_t( __typeof__( x ), \ + ( l ), \ + min_t( __typeof__( x ), \ + ( h ), \ + ( x ) ) ) + +/* Not sure what all the bytes in these char + * arrays do, but they're necessary to make + * the camera work. + */ + +static unsigned char setup1[] = { + 0xB6, 0xC3, 0x1F, 0x00, 0x02, 0x64, 0xE7, 0x67, + 0xFD, 0xFF, 0x0E, 0xC0, 0xE7, 0x09, 0xDE, 0x00, + 0x8E, 0x00, 0xC0, 0x09, 0x40, 0x03, 0xC0, 0x17, + 0x44, 0x03, 0x4B, 0xAF, 0xC0, 0x07, 0x00, 0x00, + 0x4B, 0xAF, 0x97, 0xCF, 0x00, 0x00 +}; + +static unsigned char setup2[] = { + 0xB6, 0xC3, 0x03, 0x00, 0x03, 0x64, 0x18, 0x00, + 0x00, 0x00 +}; + +static unsigned char setup3[] = { + 0xB6, 0xC3, 0x01, 0x00, 0x06, 0x64, 0x00, 0x00 +}; + +static unsigned char setup4[] = { + 0xB6, 0xC3, 0x8F, 0x06, 0x02, 0x64, 0xE7, 0x07, + 0x00, 0x00, 0x08, 0xC0, 0xE7, 0x07, 0x00, 0x00, + 0x3E, 0xC0, 0xE7, 0x07, 0x54, 0x01, 0xAA, 0x00, + 0xE7, 0x07, 0xC8, 0x05, 0xB6, 0x00, 0xE7, 0x07, + 0x42, 0x01, 0xD2, 0x00, 0xE7, 0x07, 0x7C, 0x00, + 0x16, 0x00, 0xE7, 0x07, 0x56, 0x00, 0x18, 0x00, + 0xE7, 0x07, 0x06, 0x00, 0x92, 0xC0, 0xE7, 0x07, + 0x00, 0x00, 0x1E, 0xC0, 0xE7, 0x07, 0xFF, 0xFF, + 0x22, 0xC0, 0xE7, 0x07, 0x04, 0x00, 0x24, 0xC0, + 0xE7, 0x07, 0xEC, 0x27, 0x28, 0xC0, 0xE7, 0x07, + 0x16, 0x01, 0x8E, 0x00, 0xE7, 0x87, 0x01, 0x00, + 0x0E, 0xC0, 0x97, 0xCF, 0xD7, 0x09, 0x00, 0xC0, + 0xE7, 0x77, 0x01, 0x00, 0x92, 0xC0, 0x09, 0xC1, + 0xE7, 0x09, 0xFE, 0x05, 0x24, 0x01, 0xE7, 0x09, + 0x04, 0x06, 0x26, 0x01, 0xE7, 0x07, 0x07, 0x00, + 0x92, 0xC0, 0xE7, 0x05, 0x00, 0xC0, 0xC0, 0xDF, + 0x97, 0xCF, 0x17, 0x00, 0x57, 0x00, 0x17, 0x02, + 0xD7, 0x09, 0x00, 0xC0, 0xE7, 0x77, 0x01, 0x00, + 0x92, 0xC0, 0x0A, 0xC1, 0xE7, 0x57, 0xFF, 0xFF, + 0xFA, 0x05, 0x0D, 0xC0, 0xE7, 0x57, 0x00, 0x00, + 0xFA, 0x05, 0x0F, 0xC0, 0x9F, 0xAF, 0xC6, 0x00, + 0xE7, 0x05, 0x00, 0xC0, 0xC8, 0x05, 0xC1, 0x05, + 0xC0, 0x05, 0xC0, 0xDF, 0x97, 0xCF, 0x27, 0xDA, + 0xFA, 0x05, 0xEF, 0x07, 0x01, 0x00, 0x0B, 0x06, + 0x73, 0xCF, 0x9F, 0xAF, 0x78, 0x01, 0x9F, 0xAF, + 0x1A, 0x03, 0x6E, 0xCF, 0xE7, 0x09, 0xFC, 0x05, + 0x24, 0x01, 0xE7, 0x09, 0x02, 0x06, 0x26, 0x01, + 0xE7, 0x07, 0x07, 0x00, 0x92, 0xC0, 0xE7, 0x09, + 0xFC, 0x05, 0xFE, 0x05, 0xE7, 0x09, 0x02, 0x06, + 0x04, 0x06, 0xE7, 0x09, 0x00, 0x06, 0xFC, 0x05, + 0xE7, 0x09, 0xFE, 0x05, 0x00, 0x06, 0x27, 0xDA, + 0xFA, 0x05, 0xE7, 0x57, 0x01, 0x00, 0xFA, 0x05, + 0x02, 0xCA, 0x04, 0xC0, 0x97, 0xCF, 0x9F, 0xAF, + 0x66, 0x05, 0x97, 0xCF, 0xE7, 0x07, 0x40, 0x00, + 0x02, 0x06, 0xC8, 0x09, 0xFC, 0x05, 0x9F, 0xAF, + 0xDA, 0x02, 0x97, 0xCF, 0xCF, 0x17, 0x02, 0x00, + 0xEF, 0x57, 0x81, 0x00, 0x09, 0x06, 0x9F, 0xA0, + 0xB6, 0x01, 0xEF, 0x57, 0x80, 0x00, 0x09, 0x06, + 0x9F, 0xA0, 0x40, 0x02, 0xEF, 0x57, 0x01, 0x00, + 0x0B, 0x06, 0x9F, 0xA0, 0x46, 0x03, 0xE7, 0x07, + 0x01, 0x00, 0x0A, 0xC0, 0x46, 0xAF, 0x47, 0xAF, + 0x9F, 0xAF, 0x40, 0x02, 0xE7, 0x07, 0x2E, 0x00, + 0x0A, 0xC0, 0xEF, 0x87, 0x80, 0x00, 0x09, 0x06, + 0x97, 0xCF, 0x00, 0x0E, 0x01, 0x00, 0xC0, 0x57, + 0x51, 0x00, 0x9F, 0xC0, 0x9E, 0x02, 0xC0, 0x57, + 0x50, 0x00, 0x20, 0xC0, 0xC0, 0x57, 0x55, 0x00, + 0x12, 0xC0, 0xC0, 0x57, 0x56, 0x00, 0x9F, 0xC0, + 0x72, 0x02, 0x9F, 0xCF, 0xD6, 0x02, 0xC1, 0x0B, + 0x08, 0x06, 0x01, 0xD0, 0x6F, 0x90, 0x08, 0x06, + 0xC0, 0x07, 0x08, 0x00, 0xC1, 0x0B, 0x08, 0x06, + 0x9F, 0xAF, 0x28, 0x05, 0x97, 0xCF, 0x2F, 0x0E, + 0x02, 0x00, 0x08, 0x06, 0xC0, 0x07, 0x08, 0x00, + 0xC1, 0x0B, 0x08, 0x06, 0x9F, 0xAF, 0x28, 0x05, + 0x9F, 0xCF, 0xD6, 0x02, 0x2F, 0x0E, 0x02, 0x00, + 0x09, 0x06, 0xEF, 0x87, 0x80, 0x00, 0x09, 0x06, + 0x9F, 0xCF, 0xD6, 0x02, 0xEF, 0x67, 0x7F, 0xFF, + 0x09, 0x06, 0xE7, 0x67, 0xFF, 0xFD, 0x22, 0xC0, + 0xE7, 0x67, 0xEF, 0xFF, 0x24, 0xC0, 0xE7, 0x87, + 0x10, 0x00, 0x28, 0xC0, 0x9F, 0xAF, 0xB8, 0x05, + 0xE7, 0x87, 0xE0, 0x21, 0x24, 0xC0, 0x9F, 0xAF, + 0xA8, 0x05, 0xE7, 0x87, 0x08, 0x00, 0x24, 0xC0, + 0xE7, 0x67, 0xDF, 0xFF, 0x24, 0xC0, 0xC8, 0x07, + 0x0A, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xC1, 0x07, + 0x01, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0x9F, 0xAF, + 0xB8, 0x05, 0xC0, 0x07, 0x9E, 0x00, 0x9F, 0xAF, + 0x44, 0x05, 0xE7, 0x67, 0xFF, 0xFE, 0x24, 0xC0, + 0xC0, 0x09, 0x20, 0xC0, 0xE7, 0x87, 0x00, 0x01, + 0x24, 0xC0, 0xC0, 0x77, 0x00, 0x02, 0x0F, 0xC1, + 0xE7, 0x67, 0xF7, 0xFF, 0x24, 0xC0, 0xE7, 0x67, + 0xF7, 0xFF, 0x24, 0xC0, 0xE7, 0x87, 0x08, 0x00, + 0x24, 0xC0, 0x08, 0xDA, 0x5E, 0xC1, 0xEF, 0x07, + 0x80, 0x00, 0x09, 0x06, 0x97, 0xCF, 0xEF, 0x07, + 0x01, 0x00, 0x0A, 0x06, 0x97, 0xCF, 0xEF, 0x07, + 0x00, 0x00, 0x0B, 0x06, 0xEF, 0x07, 0x00, 0x00, + 0x0A, 0x06, 0xEF, 0x67, 0x7F, 0xFF, 0x09, 0x06, + 0xEF, 0x07, 0x00, 0x00, 0x0D, 0x06, 0xE7, 0x67, + 0xEF, 0xFF, 0x28, 0xC0, 0xE7, 0x67, 0x17, 0xD8, + 0x24, 0xC0, 0xE7, 0x07, 0x00, 0x00, 0x1E, 0xC0, + 0xE7, 0x07, 0xFF, 0xFF, 0x22, 0xC0, 0x97, 0xCF, + 0xC8, 0x07, 0x0E, 0x06, 0x9F, 0xAF, 0xDA, 0x02, + 0xE7, 0x07, 0x00, 0x00, 0xF2, 0x05, 0xE7, 0x07, + 0x10, 0x00, 0xF6, 0x05, 0xE7, 0x07, 0x0E, 0x06, + 0xF4, 0x05, 0xE7, 0x07, 0xD6, 0x02, 0xF8, 0x05, + 0xC8, 0x07, 0xF2, 0x05, 0xC1, 0x07, 0x00, 0x80, + 0x50, 0xAF, 0x97, 0xCF, 0x2F, 0x0C, 0x02, 0x00, + 0x07, 0x06, 0x2F, 0x0C, 0x04, 0x00, 0x06, 0x06, + 0xE7, 0x07, 0x00, 0x00, 0xF2, 0x05, 0xE7, 0x07, + 0x10, 0x00, 0xF6, 0x05, 0xE7, 0x07, 0xE2, 0x05, + 0xF4, 0x05, 0xE7, 0x07, 0xCE, 0x02, 0xF8, 0x05, + 0xC8, 0x07, 0xF2, 0x05, 0xC1, 0x07, 0x00, 0x80, + 0x51, 0xAF, 0x97, 0xCF, 0x9F, 0xAF, 0x66, 0x04, + 0x9F, 0xAF, 0x1A, 0x03, 0x59, 0xAF, 0x97, 0xCF, + 0xC0, 0x07, 0x0E, 0x00, 0xC1, 0x0B, 0x0C, 0x06, + 0x41, 0xD1, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, + 0x3C, 0x00, 0x9F, 0xAF, 0x44, 0x05, 0x68, 0x00, + 0xC0, 0x07, 0x3B, 0x00, 0x9F, 0xAF, 0x44, 0x05, + 0x6F, 0x00, 0x0C, 0x06, 0x68, 0x00, 0xE0, 0x07, + 0x04, 0x01, 0xE8, 0x0B, 0x0A, 0x06, 0xE8, 0x07, + 0x00, 0x00, 0xE0, 0x07, 0x00, 0x02, 0xE0, 0x07, + 0xEC, 0x01, 0xE0, 0x07, 0xFC, 0xFF, 0x97, 0xCF, + 0xE7, 0x07, 0xFF, 0xFF, 0xFA, 0x05, 0xEF, 0x07, + 0x00, 0x00, 0x0B, 0x06, 0xE7, 0x07, 0x0E, 0x06, + 0x24, 0x01, 0xE7, 0x07, 0x0E, 0x06, 0xFE, 0x05, + 0xE7, 0x07, 0x40, 0x00, 0x26, 0x01, 0xE7, 0x07, + 0x40, 0x00, 0x04, 0x06, 0xE7, 0x07, 0x07, 0x00, + 0x92, 0xC0, 0x97, 0xCF, 0xEF, 0x07, 0x02, 0x00, + 0x0B, 0x06, 0x9F, 0xAF, 0x78, 0x01, 0xEF, 0x77, + 0x80, 0x00, 0x07, 0x06, 0x9F, 0xC0, 0x14, 0x04, + 0xEF, 0x77, 0x01, 0x00, 0x07, 0x06, 0x37, 0xC0, + 0xEF, 0x77, 0x01, 0x00, 0x0D, 0x06, 0x0F, 0xC1, + 0xEF, 0x07, 0x01, 0x00, 0x0D, 0x06, 0xC0, 0x07, + 0x02, 0x00, 0xC1, 0x07, 0x30, 0x00, 0x9F, 0xAF, + 0x28, 0x05, 0xC0, 0x07, 0x01, 0x00, 0xC1, 0x07, + 0x02, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0xC8, 0x07, + 0xFF, 0x4F, 0x9F, 0xAF, 0xA8, 0x05, 0xC0, 0x07, + 0x38, 0x00, 0x9F, 0xAF, 0x44, 0x05, 0xC1, 0x77, + 0x03, 0x00, 0x02, 0xC1, 0x08, 0xDA, 0x75, 0xC1, + 0xC1, 0x77, 0x01, 0x00, 0x0A, 0xC1, 0xC0, 0x07, + 0x01, 0x00, 0xC1, 0x07, 0x02, 0x00, 0x9F, 0xAF, + 0x28, 0x05, 0xEF, 0x07, 0x01, 0x00, 0x06, 0x06, + 0x2C, 0xCF, 0xC0, 0x07, 0x01, 0x00, 0xC1, 0x07, + 0x04, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0xEF, 0x07, + 0x00, 0x00, 0x06, 0x06, 0x22, 0xCF, 0xEF, 0x07, + 0x00, 0x00, 0x0D, 0x06, 0xEF, 0x57, 0x01, 0x00, + 0x06, 0x06, 0x1B, 0xC0, 0xC0, 0x07, 0x01, 0x00, + 0xC1, 0x07, 0x01, 0x00, 0x9F, 0xAF, 0x28, 0x05, + 0xC0, 0x07, 0x02, 0x00, 0xC1, 0x07, 0x30, 0x00, + 0x9F, 0xAF, 0x28, 0x05, 0xC8, 0x07, 0xFF, 0x4F, + 0x9F, 0xAF, 0xA8, 0x05, 0xC0, 0x07, 0x38, 0x00, + 0x9F, 0xAF, 0x44, 0x05, 0xC1, 0x67, 0x03, 0x00, + 0xC1, 0x57, 0x03, 0x00, 0x02, 0xC0, 0x08, 0xDA, + 0x73, 0xC1, 0xC0, 0x07, 0x02, 0x00, 0xC1, 0x07, + 0x12, 0x00, 0xEF, 0x57, 0x00, 0x00, 0x06, 0x06, + 0x02, 0xC0, 0xC1, 0x07, 0x23, 0x00, 0x9F, 0xAF, + 0x28, 0x05, 0xC0, 0x07, 0x14, 0x00, 0xC1, 0x0B, + 0xEA, 0x05, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, + 0x3E, 0x00, 0x9F, 0xAF, 0x0A, 0x05, 0xE7, 0x09, + 0xE4, 0x05, 0xFA, 0x05, 0x27, 0xD8, 0xFA, 0x05, + 0xE7, 0x07, 0x0E, 0x06, 0xFC, 0x05, 0xE7, 0x07, + 0x4E, 0x06, 0x00, 0x06, 0xE7, 0x07, 0x40, 0x00, + 0x02, 0x06, 0x9F, 0xAF, 0x66, 0x05, 0x9F, 0xAF, + 0xC6, 0x00, 0x97, 0xCF, 0xC1, 0x0B, 0xE2, 0x05, + 0x41, 0xD0, 0x01, 0xD2, 0xC1, 0x17, 0x23, 0x00, + 0x9F, 0xAF, 0xDC, 0x04, 0xC0, 0x07, 0x04, 0x00, + 0xC1, 0x0B, 0xE3, 0x05, 0x9F, 0xAF, 0x28, 0x05, + 0xC0, 0x07, 0x06, 0x00, 0xC1, 0x09, 0xE6, 0x05, + 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x07, 0x00, + 0xC1, 0x09, 0xE6, 0x05, 0xC1, 0xD1, 0x9F, 0xAF, + 0x28, 0x05, 0xC0, 0x07, 0x0B, 0x00, 0xC1, 0x09, + 0xE8, 0x05, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, + 0x0C, 0x00, 0xC1, 0x09, 0xE8, 0x05, 0xC1, 0xD1, + 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x0D, 0x00, + 0xC1, 0x07, 0x09, 0x00, 0x9F, 0xAF, 0x28, 0x05, + 0xC0, 0x07, 0x03, 0x00, 0xC1, 0x07, 0x32, 0x00, + 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x0F, 0x00, + 0xC1, 0x07, 0x00, 0x00, 0x9F, 0xAF, 0x28, 0x05, + 0x97, 0xCF, 0xE7, 0x67, 0xFF, 0xD9, 0x24, 0xC0, + 0xC8, 0x07, 0x0A, 0x00, 0x40, 0x00, 0xC0, 0x67, + 0x00, 0x02, 0x27, 0x80, 0x24, 0xC0, 0xE7, 0x87, + 0x00, 0x04, 0x24, 0xC0, 0xE7, 0x67, 0xFF, 0xF9, + 0x24, 0xC0, 0x01, 0xD2, 0x08, 0xDA, 0x72, 0xC1, + 0xE7, 0x87, 0x00, 0x20, 0x24, 0xC0, 0x97, 0xCF, + 0x27, 0x00, 0x1E, 0xC0, 0xE7, 0x87, 0xFF, 0x00, + 0x22, 0xC0, 0xE7, 0x67, 0x7F, 0xFF, 0x24, 0xC0, + 0xE7, 0x87, 0x80, 0x00, 0x24, 0xC0, 0xE7, 0x87, + 0x80, 0x00, 0x24, 0xC0, 0x97, 0xCF, 0x9F, 0xAF, + 0x0A, 0x05, 0x67, 0x00, 0x1E, 0xC0, 0xE7, 0x67, + 0xBF, 0xFF, 0x24, 0xC0, 0xE7, 0x87, 0x40, 0x00, + 0x24, 0xC0, 0xE7, 0x87, 0x40, 0x00, 0x24, 0xC0, + 0x97, 0xCF, 0x9F, 0xAF, 0x0A, 0x05, 0xE7, 0x67, + 0x00, 0xFF, 0x22, 0xC0, 0xE7, 0x67, 0xFF, 0xFE, + 0x24, 0xC0, 0xE7, 0x67, 0xFF, 0xFE, 0x24, 0xC0, + 0xC1, 0x09, 0x20, 0xC0, 0xE7, 0x87, 0x00, 0x01, + 0x24, 0xC0, 0x97, 0xCF, 0xC0, 0x07, 0x40, 0x00, + 0xC8, 0x09, 0xFC, 0x05, 0xE7, 0x67, 0x00, 0xFF, + 0x22, 0xC0, 0xE7, 0x67, 0xFF, 0xFE, 0x24, 0xC0, + 0xE7, 0x67, 0xBF, 0xFF, 0x24, 0xC0, 0xE7, 0x67, + 0xBF, 0xFF, 0x24, 0xC0, 0x00, 0xDA, 0xE8, 0x09, + 0x20, 0xC0, 0xE7, 0x87, 0x40, 0x00, 0x24, 0xC0, + 0xE7, 0x87, 0x40, 0x00, 0x24, 0xC0, 0x00, 0xDA, + 0xE8, 0x09, 0x20, 0xC0, 0x6D, 0xC1, 0xE7, 0x87, + 0x00, 0x01, 0x24, 0xC0, 0x97, 0xCF, 0xE7, 0x07, + 0x32, 0x00, 0x12, 0xC0, 0xE7, 0x77, 0x00, 0x80, + 0x12, 0xC0, 0x7C, 0xC0, 0x97, 0xCF, 0xE7, 0x07, + 0x20, 0x4E, 0x12, 0xC0, 0xE7, 0x77, 0x00, 0x80, + 0x12, 0xC0, 0x7C, 0xC0, 0x97, 0xCF, 0x09, 0x02, + 0x19, 0x00, 0x01, 0x01, 0x00, 0x80, 0x96, 0x09, + 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x05, 0x81, 0x02, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static unsigned char setup5[] = { + 0xB6, 0xC3, 0x2F, 0x01, 0x03, 0x64, 0x0E, 0x00, + 0x14, 0x00, 0x1A, 0x00, 0x20, 0x00, 0x26, 0x00, + 0x4A, 0x00, 0x64, 0x00, 0x6A, 0x00, 0x92, 0x00, + 0x9A, 0x00, 0xA0, 0x00, 0xB2, 0x00, 0xB8, 0x00, + 0xBE, 0x00, 0xC2, 0x00, 0xC8, 0x00, 0xCE, 0x00, + 0xDC, 0x00, 0xDA, 0x00, 0xE2, 0x00, 0xE0, 0x00, + 0xE8, 0x00, 0xE6, 0x00, 0xEE, 0x00, 0xEC, 0x00, + 0xF2, 0x00, 0xF8, 0x00, 0x02, 0x01, 0x0A, 0x01, + 0x0E, 0x01, 0x12, 0x01, 0x1E, 0x01, 0x22, 0x01, + 0x28, 0x01, 0x2C, 0x01, 0x32, 0x01, 0x36, 0x01, + 0x44, 0x01, 0x50, 0x01, 0x5E, 0x01, 0x72, 0x01, + 0x76, 0x01, 0x7A, 0x01, 0x80, 0x01, 0x88, 0x01, + 0x8C, 0x01, 0x94, 0x01, 0x9C, 0x01, 0xA0, 0x01, + 0xA4, 0x01, 0xAA, 0x01, 0xB0, 0x01, 0xB4, 0x01, + 0xBA, 0x01, 0xD0, 0x01, 0xDA, 0x01, 0xF6, 0x01, + 0xFA, 0x01, 0x02, 0x02, 0x34, 0x02, 0x3C, 0x02, + 0x44, 0x02, 0x4A, 0x02, 0x50, 0x02, 0x56, 0x02, + 0x74, 0x02, 0x78, 0x02, 0x7E, 0x02, 0x84, 0x02, + 0x8A, 0x02, 0x88, 0x02, 0x90, 0x02, 0x8E, 0x02, + 0x94, 0x02, 0xA2, 0x02, 0xA8, 0x02, 0xAE, 0x02, + 0xB4, 0x02, 0xBA, 0x02, 0xB8, 0x02, 0xC0, 0x02, + 0xBE, 0x02, 0xC4, 0x02, 0xD0, 0x02, 0xD4, 0x02, + 0xE0, 0x02, 0xE6, 0x02, 0xEE, 0x02, 0xF8, 0x02, + 0xFC, 0x02, 0x06, 0x03, 0x1E, 0x03, 0x24, 0x03, + 0x28, 0x03, 0x30, 0x03, 0x2E, 0x03, 0x3C, 0x03, + 0x4A, 0x03, 0x4E, 0x03, 0x54, 0x03, 0x58, 0x03, + 0x5E, 0x03, 0x66, 0x03, 0x6E, 0x03, 0x7A, 0x03, + 0x86, 0x03, 0x8E, 0x03, 0x96, 0x03, 0xB2, 0x03, + 0xB8, 0x03, 0xC6, 0x03, 0xCC, 0x03, 0xD4, 0x03, + 0xDA, 0x03, 0xE8, 0x03, 0xF4, 0x03, 0xFC, 0x03, + 0x04, 0x04, 0x20, 0x04, 0x2A, 0x04, 0x32, 0x04, + 0x36, 0x04, 0x3E, 0x04, 0x44, 0x04, 0x42, 0x04, + 0x48, 0x04, 0x4E, 0x04, 0x4C, 0x04, 0x54, 0x04, + 0x52, 0x04, 0x5A, 0x04, 0x5E, 0x04, 0x62, 0x04, + 0x68, 0x04, 0x74, 0x04, 0x7C, 0x04, 0x80, 0x04, + 0x88, 0x04, 0x8C, 0x04, 0x94, 0x04, 0x9A, 0x04, + 0xA2, 0x04, 0xA6, 0x04, 0xAE, 0x04, 0xB4, 0x04, + 0xC0, 0x04, 0xCC, 0x04, 0xD8, 0x04, 0x2A, 0x05, + 0x46, 0x05, 0x6C, 0x05, 0x00, 0x00 +}; + +/* rvmalloc / rvfree copied from usbvideo.c + * + * Not sure why these are not yet non-statics which I can reference through + * usbvideo.h the same as it is in 2.4.20. I bet this will get fixed sometime + * in the future. + * +*/ +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + +static void rvfree(void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + +struct vicam_camera { + u16 shutter_speed; // capture shutter speed + u16 gain; // capture gain + + u8 *raw_image; // raw data captured from the camera + u8 *framebuf; // processed data in RGB24 format + u8 *cntrlbuf; // area used to send control msgs + + struct video_device vdev; // v4l video device + struct usb_device *udev; // usb device + + /* guard against simultaneous accesses to the camera */ + struct mutex cam_lock; + + int is_initialized; + u8 open_count; + u8 bulkEndpoint; + int needsDummyRead; + +#if defined(CONFIG_VIDEO_PROC_FS) + struct proc_dir_entry *proc_dir; +#endif + +}; + +static int vicam_probe( struct usb_interface *intf, const struct usb_device_id *id); +static void vicam_disconnect(struct usb_interface *intf); +static void read_frame(struct vicam_camera *cam, int framenum); +static void vicam_decode_color(const u8 *, u8 *); + +static int __send_control_msg(struct vicam_camera *cam, + u8 request, + u16 value, + u16 index, + unsigned char *cp, + u16 size) +{ + int status; + + /* cp must be memory that has been allocated by kmalloc */ + + status = usb_control_msg(cam->udev, + usb_sndctrlpipe(cam->udev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, + cp, size, 1000); + + status = min(status, 0); + + if (status < 0) { + printk(KERN_INFO "Failed sending control message, error %d.\n", + status); + } + + return status; +} + +static int send_control_msg(struct vicam_camera *cam, + u8 request, + u16 value, + u16 index, + unsigned char *cp, + u16 size) +{ + int status = -ENODEV; + mutex_lock(&cam->cam_lock); + if (cam->udev) { + status = __send_control_msg(cam, request, value, + index, cp, size); + } + mutex_unlock(&cam->cam_lock); + return status; +} +static int +initialize_camera(struct vicam_camera *cam) +{ + const struct { + u8 *data; + u32 size; + } firmware[] = { + { .data = setup1, .size = sizeof(setup1) }, + { .data = setup2, .size = sizeof(setup2) }, + { .data = setup3, .size = sizeof(setup3) }, + { .data = setup4, .size = sizeof(setup4) }, + { .data = setup5, .size = sizeof(setup5) }, + { .data = setup3, .size = sizeof(setup3) }, + { .data = NULL, .size = 0 } + }; + + int err, i; + + for (i = 0, err = 0; firmware[i].data && !err; i++) { + memcpy(cam->cntrlbuf, firmware[i].data, firmware[i].size); + + err = send_control_msg(cam, 0xff, 0, 0, + cam->cntrlbuf, firmware[i].size); + } + + return err; +} + +static int +set_camera_power(struct vicam_camera *cam, int state) +{ + int status; + + if ((status = send_control_msg(cam, 0x50, state, 0, NULL, 0)) < 0) + return status; + + if (state) { + send_control_msg(cam, 0x55, 1, 0, NULL, 0); + } + + return 0; +} + +static int +vicam_ioctl(struct inode *inode, struct file *file, unsigned int ioctlnr, unsigned long arg) +{ + void __user *user_arg = (void __user *)arg; + struct vicam_camera *cam = file->private_data; + int retval = 0; + + if (!cam) + return -ENODEV; + + switch (ioctlnr) { + /* query capabilities */ + case VIDIOCGCAP: + { + struct video_capability b; + + DBG("VIDIOCGCAP\n"); + memset(&b, 0, sizeof(b)); + strcpy(b.name, "ViCam-based Camera"); + b.type = VID_TYPE_CAPTURE; + b.channels = 1; + b.audios = 0; + b.maxwidth = 320; /* VIDEOSIZE_CIF */ + b.maxheight = 240; + b.minwidth = 320; /* VIDEOSIZE_48_48 */ + b.minheight = 240; + + if (copy_to_user(user_arg, &b, sizeof(b))) + retval = -EFAULT; + + break; + } + /* get/set video source - we are a camera and nothing else */ + case VIDIOCGCHAN: + { + struct video_channel v; + + DBG("VIDIOCGCHAN\n"); + if (copy_from_user(&v, user_arg, sizeof(v))) { + retval = -EFAULT; + break; + } + if (v.channel != 0) { + retval = -EINVAL; + break; + } + + v.channel = 0; + strcpy(v.name, "Camera"); + v.tuners = 0; + v.flags = 0; + v.type = VIDEO_TYPE_CAMERA; + v.norm = 0; + + if (copy_to_user(user_arg, &v, sizeof(v))) + retval = -EFAULT; + break; + } + + case VIDIOCSCHAN: + { + int v; + + if (copy_from_user(&v, user_arg, sizeof(v))) + retval = -EFAULT; + DBG("VIDIOCSCHAN %d\n", v); + + if (retval == 0 && v != 0) + retval = -EINVAL; + + break; + } + + /* image properties */ + case VIDIOCGPICT: + { + struct video_picture vp; + DBG("VIDIOCGPICT\n"); + memset(&vp, 0, sizeof (struct video_picture)); + vp.brightness = cam->gain << 8; + vp.depth = 24; + vp.palette = VIDEO_PALETTE_RGB24; + if (copy_to_user(user_arg, &vp, sizeof (struct video_picture))) + retval = -EFAULT; + break; + } + + case VIDIOCSPICT: + { + struct video_picture vp; + + if (copy_from_user(&vp, user_arg, sizeof(vp))) { + retval = -EFAULT; + break; + } + + DBG("VIDIOCSPICT depth = %d, pal = %d\n", vp.depth, + vp.palette); + + cam->gain = vp.brightness >> 8; + + if (vp.depth != 24 + || vp.palette != VIDEO_PALETTE_RGB24) + retval = -EINVAL; + + break; + } + + /* get/set capture window */ + case VIDIOCGWIN: + { + struct video_window vw; + vw.x = 0; + vw.y = 0; + vw.width = 320; + vw.height = 240; + vw.chromakey = 0; + vw.flags = 0; + vw.clips = NULL; + vw.clipcount = 0; + + DBG("VIDIOCGWIN\n"); + + if (copy_to_user(user_arg, (void *)&vw, sizeof(vw))) + retval = -EFAULT; + + // I'm not sure what the deal with a capture window is, it is very poorly described + // in the doc. So I won't support it now. + break; + } + + case VIDIOCSWIN: + { + + struct video_window vw; + + if (copy_from_user(&vw, user_arg, sizeof(vw))) { + retval = -EFAULT; + break; + } + + DBG("VIDIOCSWIN %d x %d\n", vw.width, vw.height); + + if ( vw.width != 320 || vw.height != 240 ) + retval = -EFAULT; + + break; + } + + /* mmap interface */ + case VIDIOCGMBUF: + { + struct video_mbuf vm; + int i; + + DBG("VIDIOCGMBUF\n"); + memset(&vm, 0, sizeof (vm)); + vm.size = + VICAM_MAX_FRAME_SIZE * VICAM_FRAMES; + vm.frames = VICAM_FRAMES; + for (i = 0; i < VICAM_FRAMES; i++) + vm.offsets[i] = VICAM_MAX_FRAME_SIZE * i; + + if (copy_to_user(user_arg, (void *)&vm, sizeof(vm))) + retval = -EFAULT; + + break; + } + + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + // int video_size; + + if (copy_from_user((void *)&vm, user_arg, sizeof(vm))) { + retval = -EFAULT; + break; + } + + DBG("VIDIOCMCAPTURE frame=%d, height=%d, width=%d, format=%d.\n",vm.frame,vm.width,vm.height,vm.format); + + if ( vm.frame >= VICAM_FRAMES || vm.format != VIDEO_PALETTE_RGB24 ) + retval = -EINVAL; + + // in theory right here we'd start the image capturing + // (fill in a bulk urb and submit it asynchronously) + // + // Instead we're going to do a total hack job for now and + // retrieve the frame in VIDIOCSYNC + + break; + } + + case VIDIOCSYNC: + { + int frame; + + if (copy_from_user((void *)&frame, user_arg, sizeof(int))) { + retval = -EFAULT; + break; + } + DBG("VIDIOCSYNC: %d\n", frame); + + read_frame(cam, frame); + vicam_decode_color(cam->raw_image, + cam->framebuf + + frame * VICAM_MAX_FRAME_SIZE ); + + break; + } + + /* pointless to implement overlay with this camera */ + case VIDIOCCAPTURE: + case VIDIOCGFBUF: + case VIDIOCSFBUF: + case VIDIOCKEY: + retval = -EINVAL; + break; + + /* tuner interface - we have none */ + case VIDIOCGTUNER: + case VIDIOCSTUNER: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + retval = -EINVAL; + break; + + /* audio interface - we have none */ + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + retval = -EINVAL; + break; + default: + retval = -ENOIOCTLCMD; + break; + } + + return retval; +} + +static int +vicam_open(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct vicam_camera *cam = + (struct vicam_camera *) dev->priv; + DBG("open\n"); + + if (!cam) { + printk(KERN_ERR + "vicam video_device improperly initialized"); + return -EINVAL; + } + + /* the videodev_lock held above us protects us from + * simultaneous opens...for now. we probably shouldn't + * rely on this fact forever. + */ + + if (cam->open_count > 0) { + printk(KERN_INFO + "vicam_open called on already opened camera"); + return -EBUSY; + } + + cam->raw_image = kmalloc(VICAM_MAX_READ_SIZE, GFP_KERNEL); + if (!cam->raw_image) { + return -ENOMEM; + } + + cam->framebuf = rvmalloc(VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); + if (!cam->framebuf) { + kfree(cam->raw_image); + return -ENOMEM; + } + + cam->cntrlbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!cam->cntrlbuf) { + kfree(cam->raw_image); + rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); + return -ENOMEM; + } + + // First upload firmware, then turn the camera on + + if (!cam->is_initialized) { + initialize_camera(cam); + + cam->is_initialized = 1; + } + + set_camera_power(cam, 1); + + cam->needsDummyRead = 1; + cam->open_count++; + + file->private_data = cam; + + return 0; +} + +static int +vicam_close(struct inode *inode, struct file *file) +{ + struct vicam_camera *cam = file->private_data; + int open_count; + struct usb_device *udev; + + DBG("close\n"); + + /* it's not the end of the world if + * we fail to turn the camera off. + */ + + set_camera_power(cam, 0); + + kfree(cam->raw_image); + rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); + kfree(cam->cntrlbuf); + + mutex_lock(&cam->cam_lock); + + cam->open_count--; + open_count = cam->open_count; + udev = cam->udev; + + mutex_unlock(&cam->cam_lock); + + if (!open_count && !udev) { + kfree(cam); + } + + return 0; +} + +static void vicam_decode_color(const u8 *data, u8 *rgb) +{ + /* vicam_decode_color - Convert from Vicam Y-Cr-Cb to RGB + * Copyright (C) 2002 Monroe Williams (monroe@pobox.com) + */ + + int i, prevY, nextY; + + prevY = 512; + nextY = 512; + + data += VICAM_HEADER_SIZE; + + for( i = 0; i < 240; i++, data += 512 ) { + const int y = ( i * 242 ) / 240; + + int j, prevX, nextX; + int Y, Cr, Cb; + + if ( y == 242 - 1 ) { + nextY = -512; + } + + prevX = 1; + nextX = 1; + + for ( j = 0; j < 320; j++, rgb += 3 ) { + const int x = ( j * 512 ) / 320; + const u8 * const src = &data[x]; + + if ( x == 512 - 1 ) { + nextX = -1; + } + + Cr = ( src[prevX] - src[0] ) + + ( src[nextX] - src[0] ); + Cr /= 2; + + Cb = ( src[prevY] - src[prevX + prevY] ) + + ( src[prevY] - src[nextX + prevY] ) + + ( src[nextY] - src[prevX + nextY] ) + + ( src[nextY] - src[nextX + nextY] ); + Cb /= 4; + + Y = 1160 * ( src[0] + ( Cr / 2 ) - 16 ); + + if ( i & 1 ) { + int Ct = Cr; + Cr = Cb; + Cb = Ct; + } + + if ( ( x ^ i ) & 1 ) { + Cr = -Cr; + Cb = -Cb; + } + + rgb[0] = clamp( ( ( Y + ( 2017 * Cb ) ) + + 500 ) / 900, 0, 255 ); + rgb[1] = clamp( ( ( Y - ( 392 * Cb ) - + ( 813 * Cr ) ) + + 500 ) / 1000, 0, 255 ); + rgb[2] = clamp( ( ( Y + ( 1594 * Cr ) ) + + 500 ) / 1300, 0, 255 ); + + prevX = -1; + } + + prevY = -512; + } +} + +static void +read_frame(struct vicam_camera *cam, int framenum) +{ + unsigned char *request = cam->cntrlbuf; + int realShutter; + int n; + int actual_length; + + if (cam->needsDummyRead) { + cam->needsDummyRead = 0; + read_frame(cam, framenum); + } + + memset(request, 0, 16); + request[0] = cam->gain; // 0 = 0% gain, FF = 100% gain + + request[1] = 0; // 512x242 capture + + request[2] = 0x90; // the function of these two bytes + request[3] = 0x07; // is not yet understood + + if (cam->shutter_speed > 60) { + // Short exposure + realShutter = + ((-15631900 / cam->shutter_speed) + 260533) / 1000; + request[4] = realShutter & 0xFF; + request[5] = (realShutter >> 8) & 0xFF; + request[6] = 0x03; + request[7] = 0x01; + } else { + // Long exposure + realShutter = 15600 / cam->shutter_speed - 1; + request[4] = 0; + request[5] = 0; + request[6] = realShutter & 0xFF; + request[7] = realShutter >> 8; + } + + // Per John Markus Bjørndalen, byte at index 8 causes problems if it isn't 0 + request[8] = 0; + // bytes 9-15 do not seem to affect exposure or image quality + + mutex_lock(&cam->cam_lock); + + if (!cam->udev) { + goto done; + } + + n = __send_control_msg(cam, 0x51, 0x80, 0, request, 16); + + if (n < 0) { + printk(KERN_ERR + " Problem sending frame capture control message"); + goto done; + } + + n = usb_bulk_msg(cam->udev, + usb_rcvbulkpipe(cam->udev, cam->bulkEndpoint), + cam->raw_image, + 512 * 242 + 128, &actual_length, 10000); + + if (n < 0) { + printk(KERN_ERR "Problem during bulk read of frame data: %d\n", + n); + } + + done: + mutex_unlock(&cam->cam_lock); +} + +static ssize_t +vicam_read( struct file *file, char __user *buf, size_t count, loff_t *ppos ) +{ + struct vicam_camera *cam = file->private_data; + + DBG("read %d bytes.\n", (int) count); + + if (*ppos >= VICAM_MAX_FRAME_SIZE) { + *ppos = 0; + return 0; + } + + if (*ppos == 0) { + read_frame(cam, 0); + vicam_decode_color(cam->raw_image, + cam->framebuf + + 0 * VICAM_MAX_FRAME_SIZE); + } + + count = min_t(size_t, count, VICAM_MAX_FRAME_SIZE - *ppos); + + if (copy_to_user(buf, &cam->framebuf[*ppos], count)) { + count = -EFAULT; + } else { + *ppos += count; + } + + if (count == VICAM_MAX_FRAME_SIZE) { + *ppos = 0; + } + + return count; +} + + +static int +vicam_mmap(struct file *file, struct vm_area_struct *vma) +{ + // TODO: allocate the raw frame buffer if necessary + unsigned long page, pos; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end-vma->vm_start; + struct vicam_camera *cam = file->private_data; + + if (!cam) + return -ENODEV; + + DBG("vicam_mmap: %ld\n", size); + + /* We let mmap allocate as much as it wants because Linux was adding 2048 bytes + * to the size the application requested for mmap and it was screwing apps up. + if (size > VICAM_FRAMES*VICAM_MAX_FRAME_SIZE) + return -EINVAL; + */ + + pos = (unsigned long)cam->framebuf; + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + +#if defined(CONFIG_VIDEO_PROC_FS) + +static struct proc_dir_entry *vicam_proc_root = NULL; + +static int vicam_read_helper(char *page, char **start, off_t off, + int count, int *eof, int value) +{ + char *out = page; + int len; + + out += sprintf(out, "%d",value); + + len = out - page; + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + + *start = page + off; + return len; +} + +static int vicam_read_proc_shutter(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + return vicam_read_helper(page,start,off,count,eof, + ((struct vicam_camera *)data)->shutter_speed); +} + +static int vicam_read_proc_gain(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + return vicam_read_helper(page,start,off,count,eof, + ((struct vicam_camera *)data)->gain); +} + +static int +vicam_write_proc_shutter(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + u16 stmp; + char kbuf[8]; + struct vicam_camera *cam = (struct vicam_camera *) data; + + if (count > 6) + return -EINVAL; + + if (copy_from_user(kbuf, buffer, count)) + return -EFAULT; + + stmp = (u16) simple_strtoul(kbuf, NULL, 10); + if (stmp < 4 || stmp > 32000) + return -EINVAL; + + cam->shutter_speed = stmp; + + return count; +} + +static int +vicam_write_proc_gain(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + u16 gtmp; + char kbuf[8]; + + struct vicam_camera *cam = (struct vicam_camera *) data; + + if (count > 4) + return -EINVAL; + + if (copy_from_user(kbuf, buffer, count)) + return -EFAULT; + + gtmp = (u16) simple_strtoul(kbuf, NULL, 10); + if (gtmp > 255) + return -EINVAL; + cam->gain = gtmp; + + return count; +} + +static void +vicam_create_proc_root(void) +{ + vicam_proc_root = proc_mkdir("video/vicam", NULL); + + if (vicam_proc_root) + vicam_proc_root->owner = THIS_MODULE; + else + printk(KERN_ERR + "could not create /proc entry for vicam!"); +} + +static void +vicam_destroy_proc_root(void) +{ + if (vicam_proc_root) + remove_proc_entry("video/vicam", 0); +} + +static void +vicam_create_proc_entry(struct vicam_camera *cam) +{ + char name[64]; + struct proc_dir_entry *ent; + + DBG(KERN_INFO "vicam: creating proc entry\n"); + + if (!vicam_proc_root || !cam) { + printk(KERN_INFO + "vicam: could not create proc entry, %s pointer is null.\n", + (!cam ? "camera" : "root")); + return; + } + + sprintf(name, "video%d", cam->vdev.minor); + + cam->proc_dir = proc_mkdir(name, vicam_proc_root); + + if ( !cam->proc_dir ) + return; // FIXME: We should probably return an error here + + ent = create_proc_entry("shutter", S_IFREG | S_IRUGO | S_IWUSR, + cam->proc_dir); + if (ent) { + ent->data = cam; + ent->read_proc = vicam_read_proc_shutter; + ent->write_proc = vicam_write_proc_shutter; + ent->size = 64; + } + + ent = create_proc_entry("gain", S_IFREG | S_IRUGO | S_IWUSR, + cam->proc_dir); + if (ent) { + ent->data = cam; + ent->read_proc = vicam_read_proc_gain; + ent->write_proc = vicam_write_proc_gain; + ent->size = 64; + } +} + +static void +vicam_destroy_proc_entry(void *ptr) +{ + struct vicam_camera *cam = (struct vicam_camera *) ptr; + char name[16]; + + if ( !cam->proc_dir ) + return; + + sprintf(name, "video%d", cam->vdev.minor); + remove_proc_entry("shutter", cam->proc_dir); + remove_proc_entry("gain", cam->proc_dir); + remove_proc_entry(name,vicam_proc_root); + cam->proc_dir = NULL; + +} + +#else +static inline void vicam_create_proc_root(void) { } +static inline void vicam_destroy_proc_root(void) { } +static inline void vicam_create_proc_entry(struct vicam_camera *cam) { } +static inline void vicam_destroy_proc_entry(void *ptr) { } +#endif + +static struct file_operations vicam_fops = { + .owner = THIS_MODULE, + .open = vicam_open, + .release = vicam_close, + .read = vicam_read, + .mmap = vicam_mmap, + .ioctl = vicam_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device vicam_template = { + .owner = THIS_MODULE, + .name = "ViCam-based USB Camera", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_VICAM, + .fops = &vicam_fops, + .minor = -1, +}; + +/* table of devices that work with this driver */ +static struct usb_device_id vicam_table[] = { + {USB_DEVICE(USB_VICAM_VENDOR_ID, USB_VICAM_PRODUCT_ID)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, vicam_table); + +static struct usb_driver vicam_driver = { + .name = "vicam", + .probe = vicam_probe, + .disconnect = vicam_disconnect, + .id_table = vicam_table +}; + +/** + * vicam_probe + * @intf: the interface + * @id: the device id + * + * Called by the usb core when a new device is connected that it thinks + * this driver might be interested in. + */ +static int +vicam_probe( struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + int bulkEndpoint = 0; + const struct usb_host_interface *interface; + const struct usb_endpoint_descriptor *endpoint; + struct vicam_camera *cam; + + printk(KERN_INFO "ViCam based webcam connected\n"); + + interface = intf->cur_altsetting; + + DBG(KERN_DEBUG "Interface %d. has %u. endpoints!\n", + interface->desc.bInterfaceNumber, (unsigned) (interface->desc.bNumEndpoints)); + endpoint = &interface->endpoint[0].desc; + + if ((endpoint->bEndpointAddress & 0x80) && + ((endpoint->bmAttributes & 3) == 0x02)) { + /* we found a bulk in endpoint */ + bulkEndpoint = endpoint->bEndpointAddress; + } else { + printk(KERN_ERR + "No bulk in endpoint was found ?! (this is bad)\n"); + } + + if ((cam = + kmalloc(sizeof (struct vicam_camera), GFP_KERNEL)) == NULL) { + printk(KERN_WARNING + "could not allocate kernel memory for vicam_camera struct\n"); + return -ENOMEM; + } + + memset(cam, 0, sizeof (struct vicam_camera)); + + cam->shutter_speed = 15; + + mutex_init(&cam->cam_lock); + + memcpy(&cam->vdev, &vicam_template, + sizeof (vicam_template)); + cam->vdev.priv = cam; // sort of a reverse mapping for those functions that get vdev only + + cam->udev = dev; + cam->bulkEndpoint = bulkEndpoint; + + if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1) == -1) { + kfree(cam); + printk(KERN_WARNING "video_register_device failed\n"); + return -EIO; + } + + vicam_create_proc_entry(cam); + + printk(KERN_INFO "ViCam webcam driver now controlling video device %d\n",cam->vdev.minor); + + usb_set_intfdata (intf, cam); + + return 0; +} + +static void +vicam_disconnect(struct usb_interface *intf) +{ + int open_count; + struct vicam_camera *cam = usb_get_intfdata (intf); + usb_set_intfdata (intf, NULL); + + /* we must unregister the device before taking its + * cam_lock. This is because the video open call + * holds the same lock as video unregister. if we + * unregister inside of the cam_lock and open also + * uses the cam_lock, we get deadlock. + */ + + video_unregister_device(&cam->vdev); + + /* stop the camera from being used */ + + mutex_lock(&cam->cam_lock); + + /* mark the camera as gone */ + + cam->udev = NULL; + + vicam_destroy_proc_entry(cam); + + /* the only thing left to do is synchronize with + * our close/release function on who should release + * the camera memory. if there are any users using the + * camera, it's their job. if there are no users, + * it's ours. + */ + + open_count = cam->open_count; + + mutex_unlock(&cam->cam_lock); + + if (!open_count) { + kfree(cam); + } + + printk(KERN_DEBUG "ViCam-based WebCam disconnected\n"); +} + +/* + */ +static int __init +usb_vicam_init(void) +{ + int retval; + DBG(KERN_INFO "ViCam-based WebCam driver startup\n"); + vicam_create_proc_root(); + retval = usb_register(&vicam_driver); + if (retval) + printk(KERN_WARNING "usb_register failed!\n"); + return retval; +} + +static void __exit +usb_vicam_exit(void) +{ + DBG(KERN_INFO + "ViCam-based WebCam driver shutdown\n"); + + usb_deregister(&vicam_driver); + vicam_destroy_proc_root(); +} + +module_init(usb_vicam_init); +module_exit(usb_vicam_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From c9e76e2e8fe15c5c8300f25ab69877b8478b076f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Apr 2006 16:06:04 -0300 Subject: Fix compilation inside the tree. From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/usbvideo/usbvideo.h | 1 + linux/drivers/media/video/usbvideo/vicam.c | 1 + 2 files changed, 2 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/usbvideo/usbvideo.h b/linux/drivers/media/video/usbvideo/usbvideo.h index 3cbf4fc49..0d2066b7d 100644 --- a/linux/drivers/media/video/usbvideo/usbvideo.h +++ b/linux/drivers/media/video/usbvideo/usbvideo.h @@ -17,6 +17,7 @@ #define usbvideo_h #include +#include "compat.h" #include #include #include diff --git a/linux/drivers/media/video/usbvideo/vicam.c b/linux/drivers/media/video/usbvideo/vicam.c index 90d48e851..98cb58e89 100644 --- a/linux/drivers/media/video/usbvideo/vicam.c +++ b/linux/drivers/media/video/usbvideo/vicam.c @@ -37,6 +37,7 @@ #include #include #include +#include "compat.h" #include #include #include -- cgit v1.2.3 From 73161b8d82145c318ff26c1f62c49f21a95e66cb Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Apr 2006 16:07:45 -0300 Subject: Added ET61X251 support at the tree From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/et61x251/Kconfig | 14 + linux/drivers/media/video/et61x251/Makefile | 4 + linux/drivers/media/video/et61x251/et61x251.h | 235 ++ linux/drivers/media/video/et61x251/et61x251_core.c | 2630 ++++++++++++++++++++ .../drivers/media/video/et61x251/et61x251_sensor.h | 117 + .../media/video/et61x251/et61x251_tas5130d1b.c | 141 ++ 6 files changed, 3141 insertions(+) create mode 100644 linux/drivers/media/video/et61x251/Kconfig create mode 100644 linux/drivers/media/video/et61x251/Makefile create mode 100644 linux/drivers/media/video/et61x251/et61x251.h create mode 100644 linux/drivers/media/video/et61x251/et61x251_core.c create mode 100644 linux/drivers/media/video/et61x251/et61x251_sensor.h create mode 100644 linux/drivers/media/video/et61x251/et61x251_tas5130d1b.c (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/et61x251/Kconfig b/linux/drivers/media/video/et61x251/Kconfig new file mode 100644 index 000000000..c6bff7056 --- /dev/null +++ b/linux/drivers/media/video/et61x251/Kconfig @@ -0,0 +1,14 @@ +config USB_ET61X251 + tristate "USB ET61X[12]51 PC Camera Controller support" + depends on USB && VIDEO_V4L1 + ---help--- + Say Y here if you want support for cameras based on Etoms ET61X151 + or ET61X251 PC Camera Controllers. + + See for more info. + + This driver uses the Video For Linux API. You must say Y or M to + "Video For Linux" to use this driver. + + To compile this driver as a module, choose M here: the + module will be called et61x251. diff --git a/linux/drivers/media/video/et61x251/Makefile b/linux/drivers/media/video/et61x251/Makefile new file mode 100644 index 000000000..2ff4db9ec --- /dev/null +++ b/linux/drivers/media/video/et61x251/Makefile @@ -0,0 +1,4 @@ +et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o + +obj-$(CONFIG_USB_ET61X251) += et61x251.o + diff --git a/linux/drivers/media/video/et61x251/et61x251.h b/linux/drivers/media/video/et61x251/et61x251.h new file mode 100644 index 000000000..3ac2f508e --- /dev/null +++ b/linux/drivers/media/video/et61x251/et61x251.h @@ -0,0 +1,235 @@ +/*************************************************************************** + * V4L2 driver for ET61X[12]51 PC Camera Controllers * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _ET61X251_H_ +#define _ET61X251_H_ + +#include +#include +#include "compat.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "et61x251_sensor.h" + +/*****************************************************************************/ + +#define ET61X251_DEBUG +#define ET61X251_DEBUG_LEVEL 2 +#define ET61X251_MAX_DEVICES 64 +#define ET61X251_PRESERVE_IMGSCALE 0 +#define ET61X251_FORCE_MUNMAP 0 +#define ET61X251_MAX_FRAMES 32 +#define ET61X251_COMPRESSION_QUALITY 0 +#define ET61X251_URBS 2 +#define ET61X251_ISO_PACKETS 7 +#define ET61X251_ALTERNATE_SETTING 13 +#define ET61X251_URB_TIMEOUT msecs_to_jiffies(2 * ET61X251_ISO_PACKETS) +#define ET61X251_CTRL_TIMEOUT 100 +#define ET61X251_FRAME_TIMEOUT 2 + +/*****************************************************************************/ + +static const struct usb_device_id et61x251_id_table[] = { + { USB_DEVICE(0x102c, 0x6151), }, + { USB_DEVICE(0x102c, 0x6251), }, + { USB_DEVICE(0x102c, 0x6253), }, + { USB_DEVICE(0x102c, 0x6254), }, + { USB_DEVICE(0x102c, 0x6255), }, + { USB_DEVICE(0x102c, 0x6256), }, + { USB_DEVICE(0x102c, 0x6257), }, + { USB_DEVICE(0x102c, 0x6258), }, + { USB_DEVICE(0x102c, 0x6259), }, + { USB_DEVICE(0x102c, 0x625a), }, + { USB_DEVICE(0x102c, 0x625b), }, + { USB_DEVICE(0x102c, 0x625c), }, + { USB_DEVICE(0x102c, 0x625d), }, + { USB_DEVICE(0x102c, 0x625e), }, + { USB_DEVICE(0x102c, 0x625f), }, + { USB_DEVICE(0x102c, 0x6260), }, + { USB_DEVICE(0x102c, 0x6261), }, + { USB_DEVICE(0x102c, 0x6262), }, + { USB_DEVICE(0x102c, 0x6263), }, + { USB_DEVICE(0x102c, 0x6264), }, + { USB_DEVICE(0x102c, 0x6265), }, + { USB_DEVICE(0x102c, 0x6266), }, + { USB_DEVICE(0x102c, 0x6267), }, + { USB_DEVICE(0x102c, 0x6268), }, + { USB_DEVICE(0x102c, 0x6269), }, + { } +}; + +ET61X251_SENSOR_TABLE + +/*****************************************************************************/ + +enum et61x251_frame_state { + F_UNUSED, + F_QUEUED, + F_GRABBING, + F_DONE, + F_ERROR, +}; + +struct et61x251_frame_t { + void* bufmem; + struct v4l2_buffer buf; + enum et61x251_frame_state state; + struct list_head frame; + unsigned long vma_use_count; +}; + +enum et61x251_dev_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +enum et61x251_io_method { + IO_NONE, + IO_READ, + IO_MMAP, +}; + +enum et61x251_stream_state { + STREAM_OFF, + STREAM_INTERRUPT, + STREAM_ON, +}; + +struct et61x251_sysfs_attr { + u8 reg, i2c_reg; +}; + +struct et61x251_module_param { + u8 force_munmap; + u16 frame_timeout; +}; + +static DEFINE_MUTEX(et61x251_sysfs_lock); +static DECLARE_RWSEM(et61x251_disconnect); + +struct et61x251_device { + struct video_device* v4ldev; + + struct et61x251_sensor sensor; + + struct usb_device* usbdev; + struct urb* urb[ET61X251_URBS]; + void* transfer_buffer[ET61X251_URBS]; + u8* control_buffer; + + struct et61x251_frame_t *frame_current, frame[ET61X251_MAX_FRAMES]; + struct list_head inqueue, outqueue; + u32 frame_count, nbuffers, nreadbuffers; + + enum et61x251_io_method io; + enum et61x251_stream_state stream; + + struct v4l2_jpegcompression compression; + + struct et61x251_sysfs_attr sysfs; + struct et61x251_module_param module_param; + + enum et61x251_dev_state state; + u8 users; + + struct mutex dev_mutex, fileop_mutex; + spinlock_t queue_lock; + wait_queue_head_t open, wait_frame, wait_stream; +}; + +/*****************************************************************************/ + +struct et61x251_device* +et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id) +{ + if (usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id)) + return cam; + + return NULL; +} + + +void +et61x251_attach_sensor(struct et61x251_device* cam, + struct et61x251_sensor* sensor) +{ + memcpy(&cam->sensor, sensor, sizeof(struct et61x251_sensor)); +} + +/*****************************************************************************/ + +#undef DBG +#undef KDBG +#ifdef ET61X251_DEBUG +# define DBG(level, fmt, args...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1) \ + dev_err(&cam->usbdev->dev, fmt "\n", ## args); \ + else if ((level) == 2) \ + dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ + else if ((level) >= 3) \ + dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args); \ + } \ +} while (0) +# define KDBG(level, fmt, args...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1 || (level) == 2) \ + pr_info("et61x251: " fmt "\n", ## args); \ + else if ((level) == 3) \ + pr_debug("et61x251: [%s:%d] " fmt "\n", __FUNCTION__, \ + __LINE__ , ## args); \ + } \ +} while (0) +# define V4LDBG(level, name, cmd) \ +do { \ + if (debug >= (level)) \ + v4l_print_ioctl(name, cmd); \ +} while (0) +#else +# define DBG(level, fmt, args...) do {;} while(0) +# define KDBG(level, fmt, args...) do {;} while(0) +# define V4LDBG(level, name, cmd) do {;} while(0) +#endif + +#undef PDBG +#define PDBG(fmt, args...) \ +dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args) + +#undef PDBGG +#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ + +#endif /* _ET61X251_H_ */ diff --git a/linux/drivers/media/video/et61x251/et61x251_core.c b/linux/drivers/media/video/et61x251/et61x251_core.c new file mode 100644 index 000000000..dfc9dd732 --- /dev/null +++ b/linux/drivers/media/video/et61x251/et61x251_core.c @@ -0,0 +1,2630 @@ +/*************************************************************************** + * V4L2 driver for ET61X[12]51 PC Camera Controllers * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "et61x251.h" + +/*****************************************************************************/ + +#define ET61X251_MODULE_NAME "V4L2 driver for ET61X[12]51 " \ + "PC Camera Controllers" +#define ET61X251_MODULE_AUTHOR "(C) 2006 Luca Risolia" +#define ET61X251_AUTHOR_EMAIL "" +#define ET61X251_MODULE_LICENSE "GPL" +#define ET61X251_MODULE_VERSION "1:1.02" +#define ET61X251_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 2) + +/*****************************************************************************/ + +MODULE_DEVICE_TABLE(usb, et61x251_id_table); + +MODULE_AUTHOR(ET61X251_MODULE_AUTHOR " " ET61X251_AUTHOR_EMAIL); +MODULE_DESCRIPTION(ET61X251_MODULE_NAME); +MODULE_VERSION(ET61X251_MODULE_VERSION); +MODULE_LICENSE(ET61X251_MODULE_LICENSE); + +static short video_nr[] = {[0 ... ET61X251_MAX_DEVICES-1] = -1}; +module_param_array(video_nr, short, NULL, 0444); +MODULE_PARM_DESC(video_nr, + "\n<-1|n[,...]> Specify V4L2 minor mode number." + "\n -1 = use next available (default)" + "\n n = use minor number n (integer >= 0)" + "\nYou can specify up to " + __MODULE_STRING(ET61X251_MAX_DEVICES) " cameras this way." + "\nFor example:" + "\nvideo_nr=-1,2,-1 would assign minor number 2 to" + "\nthe second registered camera and use auto for the first" + "\none and for every other camera." + "\n"); + +static short force_munmap[] = {[0 ... ET61X251_MAX_DEVICES-1] = + ET61X251_FORCE_MUNMAP}; +module_param_array(force_munmap, bool, NULL, 0444); +MODULE_PARM_DESC(force_munmap, + "\n<0|1[,...]> Force the application to unmap previously" + "\nmapped buffer memory before calling any VIDIOC_S_CROP or" + "\nVIDIOC_S_FMT ioctl's. Not all the applications support" + "\nthis feature. This parameter is specific for each" + "\ndetected camera." + "\n 0 = do not force memory unmapping" + "\n 1 = force memory unmapping (save memory)" + "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." + "\n"); + +static unsigned int frame_timeout[] = {[0 ... ET61X251_MAX_DEVICES-1] = + ET61X251_FRAME_TIMEOUT}; +module_param_array(frame_timeout, uint, NULL, 0644); +MODULE_PARM_DESC(frame_timeout, + "\n Timeout for a video frame in seconds." + "\nThis parameter is specific for each detected camera." + "\nDefault value is " + __MODULE_STRING(ET61X251_FRAME_TIMEOUT)"." + "\n"); + +#ifdef ET61X251_DEBUG +static unsigned short debug = ET61X251_DEBUG_LEVEL; +module_param(debug, ushort, 0644); +MODULE_PARM_DESC(debug, + "\n Debugging information level, from 0 to 3:" + "\n0 = none (use carefully)" + "\n1 = critical errors" + "\n2 = significant informations" + "\n3 = more verbose messages" + "\nLevel 3 is useful for testing only, when only " + "one device is used." + "\nDefault value is "__MODULE_STRING(ET61X251_DEBUG_LEVEL)"." + "\n"); +#endif + +/*****************************************************************************/ + +static u32 +et61x251_request_buffers(struct et61x251_device* cam, u32 count, + enum et61x251_io_method io) +{ + struct v4l2_pix_format* p = &(cam->sensor.pix_format); + struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); + const size_t imagesize = cam->module_param.force_munmap || + io == IO_READ ? + (p->width * p->height * p->priv) / 8 : + (r->width * r->height * p->priv) / 8; + void* buff = NULL; + u32 i; + + if (count > ET61X251_MAX_FRAMES) + count = ET61X251_MAX_FRAMES; + + cam->nbuffers = count; + while (cam->nbuffers > 0) { + if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize)))) + break; + cam->nbuffers--; + } + + for (i = 0; i < cam->nbuffers; i++) { + cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); + cam->frame[i].buf.index = i; + cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); + cam->frame[i].buf.length = imagesize; + cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->frame[i].buf.sequence = 0; + cam->frame[i].buf.field = V4L2_FIELD_NONE; + cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; + cam->frame[i].buf.flags = 0; + } + + return cam->nbuffers; +} + + +static void et61x251_release_buffers(struct et61x251_device* cam) +{ + if (cam->nbuffers) { + vfree(cam->frame[0].bufmem); + cam->nbuffers = 0; + } + cam->frame_current = NULL; +} + + +static void et61x251_empty_framequeues(struct et61x251_device* cam) +{ + u32 i; + + INIT_LIST_HEAD(&cam->inqueue); + INIT_LIST_HEAD(&cam->outqueue); + + for (i = 0; i < ET61X251_MAX_FRAMES; i++) { + cam->frame[i].state = F_UNUSED; + cam->frame[i].buf.bytesused = 0; + } +} + + +static void et61x251_requeue_outqueue(struct et61x251_device* cam) +{ + struct et61x251_frame_t *i; + + list_for_each_entry(i, &cam->outqueue, frame) { + i->state = F_QUEUED; + list_add(&i->frame, &cam->inqueue); + } + + INIT_LIST_HEAD(&cam->outqueue); +} + + +static void et61x251_queue_unusedframes(struct et61x251_device* cam) +{ + unsigned long lock_flags; + u32 i; + + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].state == F_UNUSED) { + cam->frame[i].state = F_QUEUED; + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_add_tail(&cam->frame[i].frame, &cam->inqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + } +} + +/*****************************************************************************/ + +int et61x251_write_reg(struct et61x251_device* cam, u8 value, u16 index) +{ + struct usb_device* udev = cam->usbdev; + u8* buff = cam->control_buffer; + int res; + + *buff = value; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, + 0, index, buff, 1, ET61X251_CTRL_TIMEOUT); + if (res < 0) { + DBG(3, "Failed to write a register (value 0x%02X, index " + "0x%02X, error %d)", value, index, res); + return -1; + } + + return 0; +} + + +int et61x251_read_reg(struct et61x251_device* cam, u16 index) +{ + struct usb_device* udev = cam->usbdev; + u8* buff = cam->control_buffer; + int res; + + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, + 0, index, buff, 1, ET61X251_CTRL_TIMEOUT); + if (res < 0) + DBG(3, "Failed to read a register (index 0x%02X, error %d)", + index, res); + + return (res >= 0) ? (int)(*buff) : -1; +} + + +static int +et61x251_i2c_wait(struct et61x251_device* cam, struct et61x251_sensor* sensor) +{ + int i, r; + + for (i = 1; i <= 8; i++) { + if (sensor->interface == ET61X251_I2C_3WIRES) { + r = et61x251_read_reg(cam, 0x8e); + if (!(r & 0x02) && (r >= 0)) + return 0; + } else { + r = et61x251_read_reg(cam, 0x8b); + if (!(r & 0x01) && (r >= 0)) + return 0; + } + if (r < 0) + return -EIO; + udelay(8*8); /* minimum for sensors at 400kHz */ + } + + return -EBUSY; +} + + +int +et61x251_i2c_try_read(struct et61x251_device* cam, + struct et61x251_sensor* sensor, u8 address) +{ + struct usb_device* udev = cam->usbdev; + u8* data = cam->control_buffer; + int err = 0, res; + + data[0] = address; + data[1] = cam->sensor.i2c_slave_id; + data[2] = cam->sensor.rsta | 0x10; + data[3] = !(et61x251_read_reg(cam, 0x8b) & 0x02); + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, + 0, 0x88, data, 4, ET61X251_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += et61x251_i2c_wait(cam, sensor); + + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, + 0, 0x80, data, 8, ET61X251_CTRL_TIMEOUT); + if (res < 0) + err += res; + + if (err) + DBG(3, "I2C read failed for %s image sensor", sensor->name); + + PDBGG("I2C read: address 0x%02X, value: 0x%02X", address, data[0]); + + return err ? -1 : (int)data[0]; +} + + +int +et61x251_i2c_try_write(struct et61x251_device* cam, + struct et61x251_sensor* sensor, u8 address, u8 value) +{ + struct usb_device* udev = cam->usbdev; + u8* data = cam->control_buffer; + int err = 0, res; + + data[0] = address; + data[1] = cam->sensor.i2c_slave_id; + data[2] = cam->sensor.rsta | 0x12; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, + 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT); + if (res < 0) + err += res; + + data[0] = value; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, + 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += et61x251_i2c_wait(cam, sensor); + + if (err) + DBG(3, "I2C write failed for %s image sensor", sensor->name); + + PDBGG("I2C write: address 0x%02X, value: 0x%02X", address, value); + + return err ? -1 : 0; +} + + +int +et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2, + u8 data3, u8 data4, u8 data5, u8 data6, u8 data7, + u8 data8, u8 address) +{ + struct usb_device* udev = cam->usbdev; + u8* data = cam->control_buffer; + int err = 0, res; + + data[0] = data2; + data[1] = data3; + data[2] = data4; + data[3] = data5; + data[4] = data6; + data[5] = data7; + data[6] = data8; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, + 0, 0x81, data, n-1, ET61X251_CTRL_TIMEOUT); + if (res < 0) + err += res; + + data[0] = address; + data[1] = cam->sensor.i2c_slave_id; + data[2] = cam->sensor.rsta | 0x02 | (n << 4); + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, + 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT); + if (res < 0) + err += res; + + /* Start writing through the serial interface */ + data[0] = data1; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, + 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += et61x251_i2c_wait(cam, &cam->sensor); + + if (err) + DBG(3, "I2C raw write failed for %s image sensor", + cam->sensor.name); + + PDBGG("I2C raw write: %u bytes, address = 0x%02X, data1 = 0x%02X, " + "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X," + " data6 = 0x%02X, data7 = 0x%02X, data8 = 0x%02X", n, address, + data1, data2, data3, data4, data5, data6, data7, data8); + + return err ? -1 : 0; + +} + + +int et61x251_i2c_read(struct et61x251_device* cam, u8 address) +{ + return et61x251_i2c_try_read(cam, &cam->sensor, address); +} + + +int et61x251_i2c_write(struct et61x251_device* cam, u8 address, u8 value) +{ + return et61x251_i2c_try_write(cam, &cam->sensor, address, value); +} + +/*****************************************************************************/ + +static void et61x251_urb_complete(struct urb *urb, struct pt_regs* regs) +{ + struct et61x251_device* cam = urb->context; + struct et61x251_frame_t** f; + size_t imagesize; + u8 i; + int err = 0; + + if (urb->status == -ENOENT) + return; + + f = &cam->frame_current; + + if (cam->stream == STREAM_INTERRUPT) { + cam->stream = STREAM_OFF; + if ((*f)) + (*f)->state = F_QUEUED; + DBG(3, "Stream interrupted"); + wake_up(&cam->wait_stream); + } + + if (cam->state & DEV_DISCONNECTED) + return; + + if (cam->state & DEV_MISCONFIGURED) { + wake_up_interruptible(&cam->wait_frame); + return; + } + + if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) + goto resubmit_urb; + + if (!(*f)) + (*f) = list_entry(cam->inqueue.next, struct et61x251_frame_t, + frame); + + imagesize = (cam->sensor.pix_format.width * + cam->sensor.pix_format.height * + cam->sensor.pix_format.priv) / 8; + + for (i = 0; i < urb->number_of_packets; i++) { + unsigned int len, status; + void *pos; + u8* b1, * b2, sof; + const u8 VOID_BYTES = 6; + size_t imglen; + + len = urb->iso_frame_desc[i].actual_length; + status = urb->iso_frame_desc[i].status; + pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; + + if (status) { + DBG(3, "Error in isochronous frame"); + (*f)->state = F_ERROR; + continue; + } + + b1 = pos++; + b2 = pos++; + sof = ((*b1 & 0x3f) == 63); + imglen = ((*b1 & 0xc0) << 2) | *b2; + + PDBGG("Isochrnous frame: length %u, #%u i, image length %zu", + len, i, imglen); + + if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) +start_of_frame: + if (sof) { + (*f)->state = F_GRABBING; + (*f)->buf.bytesused = 0; + do_gettimeofday(&(*f)->buf.timestamp); + pos += 22; + DBG(3, "SOF detected: new video frame"); + } + + if ((*f)->state == F_GRABBING) { + if (sof && (*f)->buf.bytesused) { + if (cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_ET61X251) + goto end_of_frame; + else { + DBG(3, "Not expected SOF detected " + "after %lu bytes", + (unsigned long)(*f)->buf.bytesused); + (*f)->state = F_ERROR; + continue; + } + } + + if ((*f)->buf.bytesused + imglen > imagesize) { + DBG(3, "Video frame size exceeded"); + (*f)->state = F_ERROR; + continue; + } + + pos += VOID_BYTES; + + memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, imglen); + (*f)->buf.bytesused += imglen; + + if ((*f)->buf.bytesused == imagesize) { + u32 b; +end_of_frame: + b = (*f)->buf.bytesused; + (*f)->state = F_DONE; + (*f)->buf.sequence= ++cam->frame_count; + spin_lock(&cam->queue_lock); + list_move_tail(&(*f)->frame, &cam->outqueue); + if (!list_empty(&cam->inqueue)) + (*f) = list_entry(cam->inqueue.next, + struct et61x251_frame_t, + frame); + else + (*f) = NULL; + spin_unlock(&cam->queue_lock); + DBG(3, "Video frame captured: : %lu bytes", + (unsigned long)(b)); + + if (!(*f)) + goto resubmit_urb; + + if (sof && + cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_ET61X251) + goto start_of_frame; + } + } + } + +resubmit_urb: + urb->dev = cam->usbdev; + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0 && err != -EPERM) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "usb_submit_urb() failed"); + } + + wake_up_interruptible(&cam->wait_frame); +} + + +static int et61x251_start_transfer(struct et61x251_device* cam) +{ + struct usb_device *udev = cam->usbdev; + struct urb* urb; + const unsigned int wMaxPacketSize[] = {0, 256, 384, 512, 640, 768, 832, + 864, 896, 920, 956, 980, 1000, + 1022}; + const unsigned int psz = wMaxPacketSize[ET61X251_ALTERNATE_SETTING]; + s8 i, j; + int err = 0; + + for (i = 0; i < ET61X251_URBS; i++) { + cam->transfer_buffer[i] = kzalloc(ET61X251_ISO_PACKETS * psz, + GFP_KERNEL); + if (!cam->transfer_buffer[i]) { + err = -ENOMEM; + DBG(1, "Not enough memory"); + goto free_buffers; + } + } + + for (i = 0; i < ET61X251_URBS; i++) { + urb = usb_alloc_urb(ET61X251_ISO_PACKETS, GFP_KERNEL); + cam->urb[i] = urb; + if (!urb) { + err = -ENOMEM; + DBG(1, "usb_alloc_urb() failed"); + goto free_urbs; + } + urb->dev = udev; + urb->context = cam; + urb->pipe = usb_rcvisocpipe(udev, 1); + urb->transfer_flags = URB_ISO_ASAP; + urb->number_of_packets = ET61X251_ISO_PACKETS; + urb->complete = et61x251_urb_complete; + urb->transfer_buffer = cam->transfer_buffer[i]; + urb->transfer_buffer_length = psz * ET61X251_ISO_PACKETS; + urb->interval = 1; + for (j = 0; j < ET61X251_ISO_PACKETS; j++) { + urb->iso_frame_desc[j].offset = psz * j; + urb->iso_frame_desc[j].length = psz; + } + } + + err = et61x251_write_reg(cam, 0x01, 0x03); + err = et61x251_write_reg(cam, 0x00, 0x03); + err = et61x251_write_reg(cam, 0x08, 0x03); + if (err) { + err = -EIO; + DBG(1, "I/O hardware error"); + goto free_urbs; + } + + err = usb_set_interface(udev, 0, ET61X251_ALTERNATE_SETTING); + if (err) { + DBG(1, "usb_set_interface() failed"); + goto free_urbs; + } + + cam->frame_current = NULL; + + for (i = 0; i < ET61X251_URBS; i++) { + err = usb_submit_urb(cam->urb[i], GFP_KERNEL); + if (err) { + for (j = i-1; j >= 0; j--) + usb_kill_urb(cam->urb[j]); + DBG(1, "usb_submit_urb() failed, error %d", err); + goto free_urbs; + } + } + + return 0; + +free_urbs: + for (i = 0; (i < ET61X251_URBS) && cam->urb[i]; i++) + usb_free_urb(cam->urb[i]); + +free_buffers: + for (i = 0; (i < ET61X251_URBS) && cam->transfer_buffer[i]; i++) + kfree(cam->transfer_buffer[i]); + + return err; +} + + +static int et61x251_stop_transfer(struct et61x251_device* cam) +{ + struct usb_device *udev = cam->usbdev; + s8 i; + int err = 0; + + if (cam->state & DEV_DISCONNECTED) + return 0; + + for (i = ET61X251_URBS-1; i >= 0; i--) { + usb_kill_urb(cam->urb[i]); + usb_free_urb(cam->urb[i]); + kfree(cam->transfer_buffer[i]); + } + + err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ + if (err) + DBG(3, "usb_set_interface() failed"); + + return err; +} + + +static int et61x251_stream_interrupt(struct et61x251_device* cam) +{ + long timeout; + + cam->stream = STREAM_INTERRUPT; + timeout = wait_event_timeout(cam->wait_stream, + (cam->stream == STREAM_OFF) || + (cam->state & DEV_DISCONNECTED), + ET61X251_URB_TIMEOUT); + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + else if (cam->stream != STREAM_OFF) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "URB timeout reached. The camera is misconfigured. To " + "use it, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + return 0; +} + +/*****************************************************************************/ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static u8 et61x251_strtou8(const char* buff, size_t len, ssize_t* count) +{ + char str[5]; + char* endp; + unsigned long val; + + if (len < 4) { + strncpy(str, buff, len); + str[len+1] = '\0'; + } else { + strncpy(str, buff, 4); + str[4] = '\0'; + } + + val = simple_strtoul(str, &endp, 0); + + *count = 0; + if (val <= 0xff) + *count = (ssize_t)(endp - str); + if ((*count) && (len == *count+1) && (buff[*count] == '\n')) + *count += 1; + + return (u8)val; +} + +/* + NOTE 1: being inside one of the following methods implies that the v4l + device exists for sure (see kobjects and reference counters) + NOTE 2: buffers are PAGE_SIZE long +*/ + +static ssize_t et61x251_show_reg(struct class_device* cd, char* buf) +{ + struct et61x251_device* cam; + ssize_t count; + + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENODEV; + } + + count = sprintf(buf, "%u\n", cam->sysfs.reg); + + mutex_unlock(&et61x251_sysfs_lock); + + return count; +} + + +static ssize_t +et61x251_store_reg(struct class_device* cd, const char* buf, size_t len) +{ + struct et61x251_device* cam; + u8 index; + ssize_t count; + + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENODEV; + } + + index = et61x251_strtou8(buf, len, &count); + if (index > 0x8e || !count) { + mutex_unlock(&et61x251_sysfs_lock); + return -EINVAL; + } + + cam->sysfs.reg = index; + + DBG(2, "Moved ET61X[12]51 register index to 0x%02X", cam->sysfs.reg); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&et61x251_sysfs_lock); + + return count; +} + + +static ssize_t et61x251_show_val(struct class_device* cd, char* buf) +{ + struct et61x251_device* cam; + ssize_t count; + int val; + + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENODEV; + } + + if ((val = et61x251_read_reg(cam, cam->sysfs.reg)) < 0) { + mutex_unlock(&et61x251_sysfs_lock); + return -EIO; + } + + count = sprintf(buf, "%d\n", val); + + DBG(3, "Read bytes: %zd", count); + + mutex_unlock(&et61x251_sysfs_lock); + + return count; +} + + +static ssize_t +et61x251_store_val(struct class_device* cd, const char* buf, size_t len) +{ + struct et61x251_device* cam; + u8 value; + ssize_t count; + int err; + + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENODEV; + } + + value = et61x251_strtou8(buf, len, &count); + if (!count) { + mutex_unlock(&et61x251_sysfs_lock); + return -EINVAL; + } + + err = et61x251_write_reg(cam, value, cam->sysfs.reg); + if (err) { + mutex_unlock(&et61x251_sysfs_lock); + return -EIO; + } + + DBG(2, "Written ET61X[12]51 reg. 0x%02X, val. 0x%02X", + cam->sysfs.reg, value); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&et61x251_sysfs_lock); + + return count; +} + + +static ssize_t et61x251_show_i2c_reg(struct class_device* cd, char* buf) +{ + struct et61x251_device* cam; + ssize_t count; + + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENODEV; + } + + count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg); + + DBG(3, "Read bytes: %zd", count); + + mutex_unlock(&et61x251_sysfs_lock); + + return count; +} + + +static ssize_t +et61x251_store_i2c_reg(struct class_device* cd, const char* buf, size_t len) +{ + struct et61x251_device* cam; + u8 index; + ssize_t count; + + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENODEV; + } + + index = et61x251_strtou8(buf, len, &count); + if (!count) { + mutex_unlock(&et61x251_sysfs_lock); + return -EINVAL; + } + + cam->sysfs.i2c_reg = index; + + DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&et61x251_sysfs_lock); + + return count; +} + + +static ssize_t et61x251_show_i2c_val(struct class_device* cd, char* buf) +{ + struct et61x251_device* cam; + ssize_t count; + int val; + + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENODEV; + } + + if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENOSYS; + } + + if ((val = et61x251_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) { + mutex_unlock(&et61x251_sysfs_lock); + return -EIO; + } + + count = sprintf(buf, "%d\n", val); + + DBG(3, "Read bytes: %zd", count); + + mutex_unlock(&et61x251_sysfs_lock); + + return count; +} + + +static ssize_t +et61x251_store_i2c_val(struct class_device* cd, const char* buf, size_t len) +{ + struct et61x251_device* cam; + u8 value; + ssize_t count; + int err; + + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENODEV; + } + + if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENOSYS; + } + + value = et61x251_strtou8(buf, len, &count); + if (!count) { + mutex_unlock(&et61x251_sysfs_lock); + return -EINVAL; + } + + err = et61x251_i2c_write(cam, cam->sysfs.i2c_reg, value); + if (err) { + mutex_unlock(&et61x251_sysfs_lock); + return -EIO; + } + + DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X", + cam->sysfs.i2c_reg, value); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&et61x251_sysfs_lock); + + return count; +} + + +static CLASS_DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, + et61x251_show_reg, et61x251_store_reg); +static CLASS_DEVICE_ATTR(val, S_IRUGO | S_IWUSR, + et61x251_show_val, et61x251_store_val); +static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, + et61x251_show_i2c_reg, et61x251_store_i2c_reg); +static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, + et61x251_show_i2c_val, et61x251_store_i2c_val); + + +static void et61x251_create_sysfs(struct et61x251_device* cam) +{ + struct video_device *v4ldev = cam->v4ldev; + + video_device_create_file(v4ldev, &class_device_attr_reg); + video_device_create_file(v4ldev, &class_device_attr_val); + if (cam->sensor.sysfs_ops) { + video_device_create_file(v4ldev, &class_device_attr_i2c_reg); + video_device_create_file(v4ldev, &class_device_attr_i2c_val); + } +} +#endif /* CONFIG_VIDEO_ADV_DEBUG */ + +/*****************************************************************************/ + +static int +et61x251_set_pix_format(struct et61x251_device* cam, + struct v4l2_pix_format* pix) +{ + int r, err = 0; + + if ((r = et61x251_read_reg(cam, 0x12)) < 0) + err += r; + if (pix->pixelformat == V4L2_PIX_FMT_ET61X251) + err += et61x251_write_reg(cam, r & 0xfd, 0x12); + else + err += et61x251_write_reg(cam, r | 0x02, 0x12); + + return err ? -EIO : 0; +} + + +static int +et61x251_set_compression(struct et61x251_device* cam, + struct v4l2_jpegcompression* compression) +{ + int r, err = 0; + + if ((r = et61x251_read_reg(cam, 0x12)) < 0) + err += r; + if (compression->quality == 0) + err += et61x251_write_reg(cam, r & 0xfb, 0x12); + else + err += et61x251_write_reg(cam, r | 0x04, 0x12); + + return err ? -EIO : 0; +} + + +static int et61x251_set_scale(struct et61x251_device* cam, u8 scale) +{ + int r = 0, err = 0; + + r = et61x251_read_reg(cam, 0x12); + if (r < 0) + err += r; + + if (scale == 1) + err += et61x251_write_reg(cam, r & ~0x01, 0x12); + else if (scale == 2) + err += et61x251_write_reg(cam, r | 0x01, 0x12); + + if (err) + return -EIO; + + PDBGG("Scaling factor: %u", scale); + + return 0; +} + + +static int +et61x251_set_crop(struct et61x251_device* cam, struct v4l2_rect* rect) +{ + struct et61x251_sensor* s = &cam->sensor; + u16 fmw_sx = (u16)(rect->left - s->cropcap.bounds.left + + s->active_pixel.left), + fmw_sy = (u16)(rect->top - s->cropcap.bounds.top + + s->active_pixel.top), + fmw_length = (u16)(rect->width), + fmw_height = (u16)(rect->height); + int err = 0; + + err += et61x251_write_reg(cam, fmw_sx & 0xff, 0x69); + err += et61x251_write_reg(cam, fmw_sy & 0xff, 0x6a); + err += et61x251_write_reg(cam, fmw_length & 0xff, 0x6b); + err += et61x251_write_reg(cam, fmw_height & 0xff, 0x6c); + err += et61x251_write_reg(cam, (fmw_sx >> 8) | ((fmw_sy & 0x300) >> 6) + | ((fmw_length & 0x300) >> 4) + | ((fmw_height & 0x300) >> 2), 0x6d); + if (err) + return -EIO; + + PDBGG("fmw_sx, fmw_sy, fmw_length, fmw_height: %u %u %u %u", + fmw_sx, fmw_sy, fmw_length, fmw_height); + + return 0; +} + + +static int et61x251_init(struct et61x251_device* cam) +{ + struct et61x251_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + struct v4l2_queryctrl *qctrl; + struct v4l2_rect* rect; + u8 i = 0; + int err = 0; + + if (!(cam->state & DEV_INITIALIZED)) { + init_waitqueue_head(&cam->open); + qctrl = s->qctrl; + rect = &(s->cropcap.defrect); + cam->compression.quality = ET61X251_COMPRESSION_QUALITY; + } else { /* use current values */ + qctrl = s->_qctrl; + rect = &(s->_rect); + } + + err += et61x251_set_scale(cam, rect->width / s->pix_format.width); + err += et61x251_set_crop(cam, rect); + if (err) + return err; + + if (s->init) { + err = s->init(cam); + if (err) { + DBG(3, "Sensor initialization failed"); + return err; + } + } + + err += et61x251_set_compression(cam, &cam->compression); + err += et61x251_set_pix_format(cam, &s->pix_format); + if (s->set_pix_format) + err += s->set_pix_format(cam, &s->pix_format); + if (err) + return err; + + if (s->pix_format.pixelformat == V4L2_PIX_FMT_ET61X251) + DBG(3, "Compressed video format is active, quality %d", + cam->compression.quality); + else + DBG(3, "Uncompressed video format is active"); + + if (s->set_crop) + if ((err = s->set_crop(cam, rect))) { + DBG(3, "set_crop() failed"); + return err; + } + + if (s->set_ctrl) { + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (s->qctrl[i].id != 0 && + !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { + ctrl.id = s->qctrl[i].id; + ctrl.value = qctrl[i].default_value; + err = s->set_ctrl(cam, &ctrl); + if (err) { + DBG(3, "Set %s control failed", + s->qctrl[i].name); + return err; + } + DBG(3, "Image sensor supports '%s' control", + s->qctrl[i].name); + } + } + + if (!(cam->state & DEV_INITIALIZED)) { + mutex_init(&cam->fileop_mutex); + spin_lock_init(&cam->queue_lock); + init_waitqueue_head(&cam->wait_frame); + init_waitqueue_head(&cam->wait_stream); + cam->nreadbuffers = 2; + memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); + memcpy(&(s->_rect), &(s->cropcap.defrect), + sizeof(struct v4l2_rect)); + cam->state |= DEV_INITIALIZED; + } + + DBG(2, "Initialization succeeded"); + return 0; +} + + +static void et61x251_release_resources(struct et61x251_device* cam) +{ + mutex_lock(&et61x251_sysfs_lock); + + DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor); + video_set_drvdata(cam->v4ldev, NULL); + video_unregister_device(cam->v4ldev); + + usb_put_dev(cam->usbdev); + + mutex_unlock(&et61x251_sysfs_lock); + + kfree(cam->control_buffer); +} + +/*****************************************************************************/ + +static int et61x251_open(struct inode* inode, struct file* filp) +{ + struct et61x251_device* cam; + int err = 0; + + /* + This is the only safe way to prevent race conditions with + disconnect + */ + if (!down_read_trylock(&et61x251_disconnect)) + return -ERESTARTSYS; + + cam = video_get_drvdata(video_devdata(filp)); + + if (mutex_lock_interruptible(&cam->dev_mutex)) { + up_read(&et61x251_disconnect); + return -ERESTARTSYS; + } + + if (cam->users) { + DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor); + if ((filp->f_flags & O_NONBLOCK) || + (filp->f_flags & O_NDELAY)) { + err = -EWOULDBLOCK; + goto out; + } + mutex_unlock(&cam->dev_mutex); + err = wait_event_interruptible_exclusive(cam->open, + cam->state & DEV_DISCONNECTED + || !cam->users); + if (err) { + up_read(&et61x251_disconnect); + return err; + } + if (cam->state & DEV_DISCONNECTED) { + up_read(&et61x251_disconnect); + return -ENODEV; + } + mutex_lock(&cam->dev_mutex); + } + + + if (cam->state & DEV_MISCONFIGURED) { + err = et61x251_init(cam); + if (err) { + DBG(1, "Initialization failed again. " + "I will retry on next open()."); + goto out; + } + cam->state &= ~DEV_MISCONFIGURED; + } + + if ((err = et61x251_start_transfer(cam))) + goto out; + + filp->private_data = cam; + cam->users++; + cam->io = IO_NONE; + cam->stream = STREAM_OFF; + cam->nbuffers = 0; + cam->frame_count = 0; + et61x251_empty_framequeues(cam); + + DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor); + +out: + mutex_unlock(&cam->dev_mutex); + up_read(&et61x251_disconnect); + return err; +} + + +static int et61x251_release(struct inode* inode, struct file* filp) +{ + struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + + mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */ + + et61x251_stop_transfer(cam); + + et61x251_release_buffers(cam); + + if (cam->state & DEV_DISCONNECTED) { + et61x251_release_resources(cam); + mutex_unlock(&cam->dev_mutex); + kfree(cam); + return 0; + } + + cam->users--; + wake_up_interruptible_nr(&cam->open, 1); + + DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor); + + mutex_unlock(&cam->dev_mutex); + + return 0; +} + + +static ssize_t +et61x251_read(struct file* filp, char __user * buf, + size_t count, loff_t* f_pos) +{ + struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + struct et61x251_frame_t* f, * i; + unsigned long lock_flags; + long timeout; + int err = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + if (cam->io == IO_MMAP) { + DBG(3, "Close and open the device again to choose the read " + "method"); + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + if (cam->io == IO_NONE) { + if (!et61x251_request_buffers(cam, cam->nreadbuffers, + IO_READ)) { + DBG(1, "read() failed, not enough memory"); + mutex_unlock(&cam->fileop_mutex); + return -ENOMEM; + } + cam->io = IO_READ; + cam->stream = STREAM_ON; + } + + if (list_empty(&cam->inqueue)) { + if (!list_empty(&cam->outqueue)) + et61x251_empty_framequeues(cam); + et61x251_queue_unusedframes(cam); + } + + if (!count) { + mutex_unlock(&cam->fileop_mutex); + return 0; + } + + if (list_empty(&cam->outqueue)) { + if (filp->f_flags & O_NONBLOCK) { + mutex_unlock(&cam->fileop_mutex); + return -EAGAIN; + } + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) { + mutex_unlock(&cam->fileop_mutex); + return timeout; + } + if (cam->state & DEV_DISCONNECTED) { + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + if (!timeout || (cam->state & DEV_MISCONFIGURED)) { + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + } + + f = list_entry(cam->outqueue.prev, struct et61x251_frame_t, frame); + + if (count > f->buf.bytesused) + count = f->buf.bytesused; + + if (copy_to_user(buf, f->bufmem, count)) { + err = -EFAULT; + goto exit; + } + *f_pos += count; + +exit: + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_for_each_entry(i, &cam->outqueue, frame) + i->state = F_UNUSED; + INIT_LIST_HEAD(&cam->outqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + et61x251_queue_unusedframes(cam); + + PDBGG("Frame #%lu, bytes read: %zu", + (unsigned long)f->buf.index, count); + + mutex_unlock(&cam->fileop_mutex); + + return err ? err : count; +} + + +static unsigned int et61x251_poll(struct file *filp, poll_table *wait) +{ + struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + struct et61x251_frame_t* f; + unsigned long lock_flags; + unsigned int mask = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return POLLERR; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + goto error; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + goto error; + } + + if (cam->io == IO_NONE) { + if (!et61x251_request_buffers(cam, cam->nreadbuffers, + IO_READ)) { + DBG(1, "poll() failed, not enough memory"); + goto error; + } + cam->io = IO_READ; + cam->stream = STREAM_ON; + } + + if (cam->io == IO_READ) { + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_for_each_entry(f, &cam->outqueue, frame) + f->state = F_UNUSED; + INIT_LIST_HEAD(&cam->outqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + et61x251_queue_unusedframes(cam); + } + + poll_wait(filp, &cam->wait_frame, wait); + + if (!list_empty(&cam->outqueue)) + mask |= POLLIN | POLLRDNORM; + + mutex_unlock(&cam->fileop_mutex); + + return mask; + +error: + mutex_unlock(&cam->fileop_mutex); + return POLLERR; +} + + +static void et61x251_vm_open(struct vm_area_struct* vma) +{ + struct et61x251_frame_t* f = vma->vm_private_data; + f->vma_use_count++; +} + + +static void et61x251_vm_close(struct vm_area_struct* vma) +{ + /* NOTE: buffers are not freed here */ + struct et61x251_frame_t* f = vma->vm_private_data; + f->vma_use_count--; +} + + +static struct vm_operations_struct et61x251_vm_ops = { + .open = et61x251_vm_open, + .close = et61x251_vm_close, +}; + + +static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma) +{ + struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + unsigned long size = vma->vm_end - vma->vm_start, + start = vma->vm_start; + void *pos; + u32 i; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || + size != PAGE_ALIGN(cam->frame[0].buf.length)) { + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + for (i = 0; i < cam->nbuffers; i++) { + if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff) + break; + } + if (i == cam->nbuffers) { + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_RESERVED; + + pos = cam->frame[i].bufmem; + while (size > 0) { /* size is page-aligned */ + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { + mutex_unlock(&cam->fileop_mutex); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + size -= PAGE_SIZE; + } + + vma->vm_ops = &et61x251_vm_ops; + vma->vm_private_data = &cam->frame[i]; + + et61x251_vm_open(vma); + + mutex_unlock(&cam->fileop_mutex); + + return 0; +} + +/*****************************************************************************/ + +static int +et61x251_vidioc_querycap(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_capability cap = { + .driver = "et61x251", + .version = ET61X251_MODULE_VERSION_CODE, + .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING, + }; + + strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); + if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) + strlcpy(cap.bus_info, cam->usbdev->dev.bus_id, + sizeof(cap.bus_info)); + + if (copy_to_user(arg, &cap, sizeof(cap))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_enuminput(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_input i; + + if (copy_from_user(&i, arg, sizeof(i))) + return -EFAULT; + + if (i.index) + return -EINVAL; + + memset(&i, 0, sizeof(i)); + strcpy(i.name, "Camera"); + i.type = V4L2_INPUT_TYPE_CAMERA; + + if (copy_to_user(arg, &i, sizeof(i))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_g_input(struct et61x251_device* cam, void __user * arg) +{ + int index = 0; + + if (copy_to_user(arg, &index, sizeof(index))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_s_input(struct et61x251_device* cam, void __user * arg) +{ + int index; + + if (copy_from_user(&index, arg, sizeof(index))) + return -EFAULT; + + if (index != 0) + return -EINVAL; + + return 0; +} + + +static int +et61x251_vidioc_query_ctrl(struct et61x251_device* cam, void __user * arg) +{ + struct et61x251_sensor* s = &cam->sensor; + struct v4l2_queryctrl qc; + u8 i; + + if (copy_from_user(&qc, arg, sizeof(qc))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (qc.id && qc.id == s->qctrl[i].id) { + memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); + if (copy_to_user(arg, &qc, sizeof(qc))) + return -EFAULT; + return 0; + } + + return -EINVAL; +} + + +static int +et61x251_vidioc_g_ctrl(struct et61x251_device* cam, void __user * arg) +{ + struct et61x251_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + int err = 0; + u8 i; + + if (!s->get_ctrl && !s->set_ctrl) + return -EINVAL; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + + if (!s->get_ctrl) { + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (ctrl.id == s->qctrl[i].id) { + ctrl.value = s->_qctrl[i].default_value; + goto exit; + } + return -EINVAL; + } else + err = s->get_ctrl(cam, &ctrl); + +exit: + if (copy_to_user(arg, &ctrl, sizeof(ctrl))) + return -EFAULT; + + return err; +} + + +static int +et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg) +{ + struct et61x251_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + u8 i; + int err = 0; + + if (!s->set_ctrl) + return -EINVAL; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (ctrl.id == s->qctrl[i].id) { + if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) + return -EINVAL; + if (ctrl.value < s->qctrl[i].minimum || + ctrl.value > s->qctrl[i].maximum) + return -ERANGE; + ctrl.value -= ctrl.value % s->qctrl[i].step; + break; + } + + if ((err = s->set_ctrl(cam, &ctrl))) + return err; + + s->_qctrl[i].default_value = ctrl.value; + + return 0; +} + + +static int +et61x251_vidioc_cropcap(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_cropcap* cc = &(cam->sensor.cropcap); + + cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cc->pixelaspect.numerator = 1; + cc->pixelaspect.denominator = 1; + + if (copy_to_user(arg, cc, sizeof(*cc))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_g_crop(struct et61x251_device* cam, void __user * arg) +{ + struct et61x251_sensor* s = &cam->sensor; + struct v4l2_crop crop = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + }; + + memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect)); + + if (copy_to_user(arg, &crop, sizeof(crop))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg) +{ + struct et61x251_sensor* s = &cam->sensor; + struct v4l2_crop crop; + struct v4l2_rect* rect; + struct v4l2_rect* bounds = &(s->cropcap.bounds); + struct v4l2_pix_format* pix_format = &(s->pix_format); + u8 scale; + const enum et61x251_stream_state stream = cam->stream; + const u32 nbuffers = cam->nbuffers; + u32 i; + int err = 0; + + if (copy_from_user(&crop, arg, sizeof(crop))) + return -EFAULT; + + rect = &(crop.c); + + if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (cam->module_param.force_munmap) + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_S_CROP failed. " + "Unmap the buffers first."); + return -EINVAL; + } + + /* Preserve R,G or B origin */ + rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L; + rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L; + + if (rect->width < 4) + rect->width = 4; + if (rect->height < 4) + rect->height = 4; + if (rect->width > bounds->width) + rect->width = bounds->width; + if (rect->height > bounds->height) + rect->height = bounds->height; + if (rect->left < bounds->left) + rect->left = bounds->left; + if (rect->top < bounds->top) + rect->top = bounds->top; + if (rect->left + rect->width > bounds->left + bounds->width) + rect->left = bounds->left+bounds->width - rect->width; + if (rect->top + rect->height > bounds->top + bounds->height) + rect->top = bounds->top+bounds->height - rect->height; + + rect->width &= ~3L; + rect->height &= ~3L; + + if (ET61X251_PRESERVE_IMGSCALE) { + /* Calculate the actual scaling factor */ + u32 a, b; + a = rect->width * rect->height; + b = pix_format->width * pix_format->height; + scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1; + } else + scale = 1; + + if (cam->stream == STREAM_ON) + if ((err = et61x251_stream_interrupt(cam))) + return err; + + if (copy_to_user(arg, &crop, sizeof(crop))) { + cam->stream = stream; + return -EFAULT; + } + + if (cam->module_param.force_munmap || cam->io == IO_READ) + et61x251_release_buffers(cam); + + err = et61x251_set_crop(cam, rect); + if (s->set_crop) + err += s->set_crop(cam, rect); + err += et61x251_set_scale(cam, scale); + + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + s->pix_format.width = rect->width/scale; + s->pix_format.height = rect->height/scale; + memcpy(&(s->_rect), rect, sizeof(*rect)); + + if ((cam->module_param.force_munmap || cam->io == IO_READ) && + nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -ENOMEM; + } + + if (cam->io == IO_READ) + et61x251_empty_framequeues(cam); + else if (cam->module_param.force_munmap) + et61x251_requeue_outqueue(cam); + + cam->stream = stream; + + return 0; +} + + +static int +et61x251_vidioc_enum_fmt(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_fmtdesc fmtd; + + if (copy_from_user(&fmtd, arg, sizeof(fmtd))) + return -EFAULT; + + if (fmtd.index == 0) { + strcpy(fmtd.description, "bayer rgb"); + fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8; + } else if (fmtd.index == 1) { + strcpy(fmtd.description, "compressed"); + fmtd.pixelformat = V4L2_PIX_FMT_ET61X251; + fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; + } else + return -EINVAL; + + fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + memset(&fmtd.reserved, 0, sizeof(fmtd.reserved)); + + if (copy_to_user(arg, &fmtd, sizeof(fmtd))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_g_fmt(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_format format; + struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format); + + if (copy_from_user(&format, arg, sizeof(format))) + return -EFAULT; + + if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_ET61X251) + ? 0 : (pfmt->width * pfmt->priv) / 8; + pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); + pfmt->field = V4L2_FIELD_NONE; + memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); + + if (copy_to_user(arg, &format, sizeof(format))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd, + void __user * arg) +{ + struct et61x251_sensor* s = &cam->sensor; + struct v4l2_format format; + struct v4l2_pix_format* pix; + struct v4l2_pix_format* pfmt = &(s->pix_format); + struct v4l2_rect* bounds = &(s->cropcap.bounds); + struct v4l2_rect rect; + u8 scale; + const enum et61x251_stream_state stream = cam->stream; + const u32 nbuffers = cam->nbuffers; + u32 i; + int err = 0; + + if (copy_from_user(&format, arg, sizeof(format))) + return -EFAULT; + + pix = &(format.fmt.pix); + + if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memcpy(&rect, &(s->_rect), sizeof(rect)); + + { /* calculate the actual scaling factor */ + u32 a, b; + a = rect.width * rect.height; + b = pix->width * pix->height; + scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1; + } + + rect.width = scale * pix->width; + rect.height = scale * pix->height; + + if (rect.width < 4) + rect.width = 4; + if (rect.height < 4) + rect.height = 4; + if (rect.width > bounds->left + bounds->width - rect.left) + rect.width = bounds->left + bounds->width - rect.left; + if (rect.height > bounds->top + bounds->height - rect.top) + rect.height = bounds->top + bounds->height - rect.top; + + rect.width &= ~3L; + rect.height &= ~3L; + + { /* adjust the scaling factor */ + u32 a, b; + a = rect.width * rect.height; + b = pix->width * pix->height; + scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1; + } + + pix->width = rect.width / scale; + pix->height = rect.height / scale; + + if (pix->pixelformat != V4L2_PIX_FMT_ET61X251 && + pix->pixelformat != V4L2_PIX_FMT_SBGGR8) + pix->pixelformat = pfmt->pixelformat; + pix->priv = pfmt->priv; /* bpp */ + pix->colorspace = pfmt->colorspace; + pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_ET61X251) + ? 0 : (pix->width * pix->priv) / 8; + pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); + pix->field = V4L2_FIELD_NONE; + + if (cmd == VIDIOC_TRY_FMT) { + if (copy_to_user(arg, &format, sizeof(format))) + return -EFAULT; + return 0; + } + + if (cam->module_param.force_munmap) + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_S_FMT failed. " + "Unmap the buffers first."); + return -EINVAL; + } + + if (cam->stream == STREAM_ON) + if ((err = et61x251_stream_interrupt(cam))) + return err; + + if (copy_to_user(arg, &format, sizeof(format))) { + cam->stream = stream; + return -EFAULT; + } + + if (cam->module_param.force_munmap || cam->io == IO_READ) + et61x251_release_buffers(cam); + + err += et61x251_set_pix_format(cam, pix); + err += et61x251_set_crop(cam, &rect); + if (s->set_pix_format) + err += s->set_pix_format(cam, pix); + if (s->set_crop) + err += s->set_crop(cam, &rect); + err += et61x251_set_scale(cam, scale); + + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + memcpy(pfmt, pix, sizeof(*pix)); + memcpy(&(s->_rect), &rect, sizeof(rect)); + + if ((cam->module_param.force_munmap || cam->io == IO_READ) && + nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -ENOMEM; + } + + if (cam->io == IO_READ) + et61x251_empty_framequeues(cam); + else if (cam->module_param.force_munmap) + et61x251_requeue_outqueue(cam); + + cam->stream = stream; + + return 0; +} + + +static int +et61x251_vidioc_g_jpegcomp(struct et61x251_device* cam, void __user * arg) +{ + if (copy_to_user(arg, &cam->compression, + sizeof(cam->compression))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_s_jpegcomp(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_jpegcompression jc; + const enum et61x251_stream_state stream = cam->stream; + int err = 0; + + if (copy_from_user(&jc, arg, sizeof(jc))) + return -EFAULT; + + if (jc.quality != 0 && jc.quality != 1) + return -EINVAL; + + if (cam->stream == STREAM_ON) + if ((err = et61x251_stream_interrupt(cam))) + return err; + + err += et61x251_set_compression(cam, &jc); + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware " + "problems. To use the camera, close and open " + "/dev/video%d again.", cam->v4ldev->minor); + return -EIO; + } + + cam->compression.quality = jc.quality; + + cam->stream = stream; + + return 0; +} + + +static int +et61x251_vidioc_reqbufs(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_requestbuffers rb; + u32 i; + int err; + + if (copy_from_user(&rb, arg, sizeof(rb))) + return -EFAULT; + + if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + rb.memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (cam->io == IO_READ) { + DBG(3, "Close and open the device again to choose the mmap " + "I/O method"); + return -EINVAL; + } + + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_REQBUFS failed. " + "Previous buffers are still mapped."); + return -EINVAL; + } + + if (cam->stream == STREAM_ON) + if ((err = et61x251_stream_interrupt(cam))) + return err; + + et61x251_empty_framequeues(cam); + + et61x251_release_buffers(cam); + if (rb.count) + rb.count = et61x251_request_buffers(cam, rb.count, IO_MMAP); + + if (copy_to_user(arg, &rb, sizeof(rb))) { + et61x251_release_buffers(cam); + cam->io = IO_NONE; + return -EFAULT; + } + + cam->io = rb.count ? IO_MMAP : IO_NONE; + + return 0; +} + + +static int +et61x251_vidioc_querybuf(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_buffer b; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + b.index >= cam->nbuffers || cam->io != IO_MMAP) + return -EINVAL; + + memcpy(&b, &cam->frame[b.index].buf, sizeof(b)); + + if (cam->frame[b.index].vma_use_count) + b.flags |= V4L2_BUF_FLAG_MAPPED; + + if (cam->frame[b.index].state == F_DONE) + b.flags |= V4L2_BUF_FLAG_DONE; + else if (cam->frame[b.index].state != F_UNUSED) + b.flags |= V4L2_BUF_FLAG_QUEUED; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_qbuf(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_buffer b; + unsigned long lock_flags; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + b.index >= cam->nbuffers || cam->io != IO_MMAP) + return -EINVAL; + + if (cam->frame[b.index].state != F_UNUSED) + return -EINVAL; + + cam->frame[b.index].state = F_QUEUED; + + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_add_tail(&cam->frame[b.index].frame, &cam->inqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + PDBGG("Frame #%lu queued", (unsigned long)b.index); + + return 0; +} + + +static int +et61x251_vidioc_dqbuf(struct et61x251_device* cam, struct file* filp, + void __user * arg) +{ + struct v4l2_buffer b; + struct et61x251_frame_t *f; + unsigned long lock_flags; + long timeout; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP) + return -EINVAL; + + if (list_empty(&cam->outqueue)) { + if (cam->stream == STREAM_OFF) + return -EINVAL; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) + return timeout; + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + if (!timeout || (cam->state & DEV_MISCONFIGURED)) + return -EIO; + } + + spin_lock_irqsave(&cam->queue_lock, lock_flags); + f = list_entry(cam->outqueue.next, struct et61x251_frame_t, frame); + list_del(cam->outqueue.next); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + f->state = F_UNUSED; + + memcpy(&b, &f->buf, sizeof(b)); + if (f->vma_use_count) + b.flags |= V4L2_BUF_FLAG_MAPPED; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index); + + return 0; +} + + +static int +et61x251_vidioc_streamon(struct et61x251_device* cam, void __user * arg) +{ + int type; + + if (copy_from_user(&type, arg, sizeof(type))) + return -EFAULT; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + if (list_empty(&cam->inqueue)) + return -EINVAL; + + cam->stream = STREAM_ON; + + DBG(3, "Stream on"); + + return 0; +} + + +static int +et61x251_vidioc_streamoff(struct et61x251_device* cam, void __user * arg) +{ + int type, err; + + if (copy_from_user(&type, arg, sizeof(type))) + return -EFAULT; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + if (cam->stream == STREAM_ON) + if ((err = et61x251_stream_interrupt(cam))) + return err; + + et61x251_empty_framequeues(cam); + + DBG(3, "Stream off"); + + return 0; +} + + +static int +et61x251_vidioc_g_parm(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_streamparm sp; + + if (copy_from_user(&sp, arg, sizeof(sp))) + return -EFAULT; + + if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + sp.parm.capture.extendedmode = 0; + sp.parm.capture.readbuffers = cam->nreadbuffers; + + if (copy_to_user(arg, &sp, sizeof(sp))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_s_parm(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_streamparm sp; + + if (copy_from_user(&sp, arg, sizeof(sp))) + return -EFAULT; + + if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + sp.parm.capture.extendedmode = 0; + + if (sp.parm.capture.readbuffers == 0) + sp.parm.capture.readbuffers = cam->nreadbuffers; + + if (sp.parm.capture.readbuffers > ET61X251_MAX_FRAMES) + sp.parm.capture.readbuffers = ET61X251_MAX_FRAMES; + + if (copy_to_user(arg, &sp, sizeof(sp))) + return -EFAULT; + + cam->nreadbuffers = sp.parm.capture.readbuffers; + + return 0; +} + + +static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp, + unsigned int cmd, void __user * arg) +{ + struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + + switch (cmd) { + + case VIDIOC_QUERYCAP: + return et61x251_vidioc_querycap(cam, arg); + + case VIDIOC_ENUMINPUT: + return et61x251_vidioc_enuminput(cam, arg); + + case VIDIOC_G_INPUT: + return et61x251_vidioc_g_input(cam, arg); + + case VIDIOC_S_INPUT: + return et61x251_vidioc_s_input(cam, arg); + + case VIDIOC_QUERYCTRL: + return et61x251_vidioc_query_ctrl(cam, arg); + + case VIDIOC_G_CTRL: + return et61x251_vidioc_g_ctrl(cam, arg); + + case VIDIOC_S_CTRL_OLD: + case VIDIOC_S_CTRL: + return et61x251_vidioc_s_ctrl(cam, arg); + + case VIDIOC_CROPCAP_OLD: + case VIDIOC_CROPCAP: + return et61x251_vidioc_cropcap(cam, arg); + + case VIDIOC_G_CROP: + return et61x251_vidioc_g_crop(cam, arg); + + case VIDIOC_S_CROP: + return et61x251_vidioc_s_crop(cam, arg); + + case VIDIOC_ENUM_FMT: + return et61x251_vidioc_enum_fmt(cam, arg); + + case VIDIOC_G_FMT: + return et61x251_vidioc_g_fmt(cam, arg); + + case VIDIOC_TRY_FMT: + case VIDIOC_S_FMT: + return et61x251_vidioc_try_s_fmt(cam, cmd, arg); + + case VIDIOC_G_JPEGCOMP: + return et61x251_vidioc_g_jpegcomp(cam, arg); + + case VIDIOC_S_JPEGCOMP: + return et61x251_vidioc_s_jpegcomp(cam, arg); + + case VIDIOC_REQBUFS: + return et61x251_vidioc_reqbufs(cam, arg); + + case VIDIOC_QUERYBUF: + return et61x251_vidioc_querybuf(cam, arg); + + case VIDIOC_QBUF: + return et61x251_vidioc_qbuf(cam, arg); + + case VIDIOC_DQBUF: + return et61x251_vidioc_dqbuf(cam, filp, arg); + + case VIDIOC_STREAMON: + return et61x251_vidioc_streamon(cam, arg); + + case VIDIOC_STREAMOFF: + return et61x251_vidioc_streamoff(cam, arg); + + case VIDIOC_G_PARM: + return et61x251_vidioc_g_parm(cam, arg); + + case VIDIOC_S_PARM_OLD: + case VIDIOC_S_PARM: + return et61x251_vidioc_s_parm(cam, arg); + + case VIDIOC_G_STD: + case VIDIOC_S_STD: + case VIDIOC_QUERYSTD: + case VIDIOC_ENUMSTD: + case VIDIOC_QUERYMENU: + return -EINVAL; + + default: + return -EINVAL; + + } +} + + +static int et61x251_ioctl(struct inode* inode, struct file* filp, + unsigned int cmd, unsigned long arg) +{ + struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + int err = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + V4LDBG(3, "et61x251", cmd); + + err = et61x251_ioctl_v4l2(inode, filp, cmd, (void __user *)arg); + + mutex_unlock(&cam->fileop_mutex); + + return err; +} + + +static struct file_operations et61x251_fops = { + .owner = THIS_MODULE, + .open = et61x251_open, + .release = et61x251_release, + .ioctl = et61x251_ioctl, + .read = et61x251_read, + .poll = et61x251_poll, + .mmap = et61x251_mmap, + .llseek = no_llseek, +}; + +/*****************************************************************************/ + +/* It exists a single interface only. We do not need to validate anything. */ +static int +et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct et61x251_device* cam; + static unsigned int dev_nr = 0; + unsigned int i; + int err = 0; + + if (!(cam = kzalloc(sizeof(struct et61x251_device), GFP_KERNEL))) + return -ENOMEM; + + cam->usbdev = udev; + + if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) { + DBG(1, "kmalloc() failed"); + err = -ENOMEM; + goto fail; + } + + if (!(cam->v4ldev = video_device_alloc())) { + DBG(1, "video_device_alloc() failed"); + err = -ENOMEM; + goto fail; + } + + mutex_init(&cam->dev_mutex); + + DBG(2, "ET61X[12]51 PC Camera Controller detected " + "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct); + + for (i = 0; et61x251_sensor_table[i]; i++) { + err = et61x251_sensor_table[i](cam); + if (!err) + break; + } + + if (!err) + DBG(2, "%s image sensor detected", cam->sensor.name); + else { + DBG(1, "No supported image sensor detected"); + err = -ENODEV; + goto fail; + } + + if (et61x251_init(cam)) { + DBG(1, "Initialization failed. I will retry on open()."); + cam->state |= DEV_MISCONFIGURED; + } + + strcpy(cam->v4ldev->name, "ET61X[12]51 PC Camera"); + cam->v4ldev->owner = THIS_MODULE; + cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; + cam->v4ldev->hardware = 0; + cam->v4ldev->fops = &et61x251_fops; + cam->v4ldev->minor = video_nr[dev_nr]; + cam->v4ldev->release = video_device_release; + video_set_drvdata(cam->v4ldev, cam); + + mutex_lock(&cam->dev_mutex); + + err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, + video_nr[dev_nr]); + if (err) { + DBG(1, "V4L2 device registration failed"); + if (err == -ENFILE && video_nr[dev_nr] == -1) + DBG(1, "Free /dev/videoX node not found"); + video_nr[dev_nr] = -1; + dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0; + mutex_unlock(&cam->dev_mutex); + goto fail; + } + + DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor); + + cam->module_param.force_munmap = force_munmap[dev_nr]; + cam->module_param.frame_timeout = frame_timeout[dev_nr]; + + dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + et61x251_create_sysfs(cam); + DBG(2, "Optional device control through 'sysfs' interface ready"); +#endif + + usb_set_intfdata(intf, cam); + + mutex_unlock(&cam->dev_mutex); + + return 0; + +fail: + if (cam) { + kfree(cam->control_buffer); + if (cam->v4ldev) + video_device_release(cam->v4ldev); + kfree(cam); + } + return err; +} + + +static void et61x251_usb_disconnect(struct usb_interface* intf) +{ + struct et61x251_device* cam = usb_get_intfdata(intf); + + if (!cam) + return; + + down_write(&et61x251_disconnect); + + mutex_lock(&cam->dev_mutex); + + DBG(2, "Disconnecting %s...", cam->v4ldev->name); + + wake_up_interruptible_all(&cam->open); + + if (cam->users) { + DBG(2, "Device /dev/video%d is open! Deregistration and " + "memory deallocation are deferred on close.", + cam->v4ldev->minor); + cam->state |= DEV_MISCONFIGURED; + et61x251_stop_transfer(cam); + cam->state |= DEV_DISCONNECTED; + wake_up_interruptible(&cam->wait_frame); + wake_up(&cam->wait_stream); + usb_get_dev(cam->usbdev); + } else { + cam->state |= DEV_DISCONNECTED; + et61x251_release_resources(cam); + } + + mutex_unlock(&cam->dev_mutex); + + if (!cam->users) + kfree(cam); + + up_write(&et61x251_disconnect); +} + + +static struct usb_driver et61x251_usb_driver = { + .name = "et61x251", + .id_table = et61x251_id_table, + .probe = et61x251_usb_probe, + .disconnect = et61x251_usb_disconnect, +}; + +/*****************************************************************************/ + +static int __init et61x251_module_init(void) +{ + int err = 0; + + KDBG(2, ET61X251_MODULE_NAME " v" ET61X251_MODULE_VERSION); + KDBG(3, ET61X251_MODULE_AUTHOR); + + if ((err = usb_register(&et61x251_usb_driver))) + KDBG(1, "usb_register() failed"); + + return err; +} + + +static void __exit et61x251_module_exit(void) +{ + usb_deregister(&et61x251_usb_driver); +} + + +module_init(et61x251_module_init); +module_exit(et61x251_module_exit); diff --git a/linux/drivers/media/video/et61x251/et61x251_sensor.h b/linux/drivers/media/video/et61x251/et61x251_sensor.h new file mode 100644 index 000000000..2adfc29ba --- /dev/null +++ b/linux/drivers/media/video/et61x251/et61x251_sensor.h @@ -0,0 +1,117 @@ +/*************************************************************************** + * API for image sensors connected to ET61X[12]51 PC Camera Controllers * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _ET61X251_SENSOR_H_ +#define _ET61X251_SENSOR_H_ + +#include +#include "compat.h" +#include +#include +#include +#include +#include + +struct et61x251_device; +struct et61x251_sensor; + +/*****************************************************************************/ + +extern int et61x251_probe_tas5130d1b(struct et61x251_device* cam); + +#define ET61X251_SENSOR_TABLE \ +/* Weak detections must go at the end of the list */ \ +static int (*et61x251_sensor_table[])(struct et61x251_device*) = { \ + &et61x251_probe_tas5130d1b, \ + NULL, \ +}; + +extern struct et61x251_device* +et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id); + +extern void +et61x251_attach_sensor(struct et61x251_device* cam, + struct et61x251_sensor* sensor); + +/*****************************************************************************/ + +extern int et61x251_write_reg(struct et61x251_device*, u8 value, u16 index); +extern int et61x251_read_reg(struct et61x251_device*, u16 index); +extern int et61x251_i2c_write(struct et61x251_device*, u8 address, u8 value); +extern int et61x251_i2c_read(struct et61x251_device*, u8 address); +extern int et61x251_i2c_try_write(struct et61x251_device*, + struct et61x251_sensor*, u8 address, + u8 value); +extern int et61x251_i2c_try_read(struct et61x251_device*, + struct et61x251_sensor*, u8 address); +extern int et61x251_i2c_raw_write(struct et61x251_device*, u8 n, u8 data1, + u8 data2, u8 data3, u8 data4, u8 data5, + u8 data6, u8 data7, u8 data8, u8 address); + +/*****************************************************************************/ + +enum et61x251_i2c_sysfs_ops { + ET61X251_I2C_READ = 0x01, + ET61X251_I2C_WRITE = 0x02, +}; + +enum et61x251_i2c_interface { + ET61X251_I2C_2WIRES, + ET61X251_I2C_3WIRES, +}; + +/* Repeat start condition when RSTA is high */ +enum et61x251_i2c_rsta { + ET61X251_I2C_RSTA_STOP = 0x00, /* stop then start */ + ET61X251_I2C_RSTA_REPEAT = 0x01, /* repeat start */ +}; + +#define ET61X251_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10 + +struct et61x251_sensor { + char name[32]; + + enum et61x251_i2c_sysfs_ops sysfs_ops; + + enum et61x251_i2c_interface interface; + u8 i2c_slave_id; + enum et61x251_i2c_rsta rsta; + struct v4l2_rect active_pixel; /* left and top define FVSX and FVSY */ + + struct v4l2_queryctrl qctrl[ET61X251_MAX_CTRLS]; + struct v4l2_cropcap cropcap; + struct v4l2_pix_format pix_format; + + int (*init)(struct et61x251_device* cam); + int (*get_ctrl)(struct et61x251_device* cam, + struct v4l2_control* ctrl); + int (*set_ctrl)(struct et61x251_device* cam, + const struct v4l2_control* ctrl); + int (*set_crop)(struct et61x251_device* cam, + const struct v4l2_rect* rect); + int (*set_pix_format)(struct et61x251_device* cam, + const struct v4l2_pix_format* pix); + + /* Private */ + struct v4l2_queryctrl _qctrl[ET61X251_MAX_CTRLS]; + struct v4l2_rect _rect; +}; + +#endif /* _ET61X251_SENSOR_H_ */ diff --git a/linux/drivers/media/video/et61x251/et61x251_tas5130d1b.c b/linux/drivers/media/video/et61x251/et61x251_tas5130d1b.c new file mode 100644 index 000000000..a7d65b82b --- /dev/null +++ b/linux/drivers/media/video/et61x251/et61x251_tas5130d1b.c @@ -0,0 +1,141 @@ +/*************************************************************************** + * Plug-in for TAS5130D1B image sensor connected to the ET61X[12]51 * + * PC Camera Controllers * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "et61x251_sensor.h" + + +static int tas5130d1b_init(struct et61x251_device* cam) +{ + int err = 0; + + err += et61x251_write_reg(cam, 0x14, 0x01); + err += et61x251_write_reg(cam, 0x1b, 0x02); + err += et61x251_write_reg(cam, 0x02, 0x12); + err += et61x251_write_reg(cam, 0x0e, 0x60); + err += et61x251_write_reg(cam, 0x80, 0x61); + err += et61x251_write_reg(cam, 0xf0, 0x62); + err += et61x251_write_reg(cam, 0x03, 0x63); + err += et61x251_write_reg(cam, 0x14, 0x64); + err += et61x251_write_reg(cam, 0xf4, 0x65); + err += et61x251_write_reg(cam, 0x01, 0x66); + err += et61x251_write_reg(cam, 0x05, 0x67); + err += et61x251_write_reg(cam, 0x8f, 0x68); + err += et61x251_write_reg(cam, 0x0f, 0x8d); + err += et61x251_write_reg(cam, 0x08, 0x8e); + + return err; +} + + +static int tas5130d1b_set_ctrl(struct et61x251_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err += et61x251_i2c_raw_write(cam, 2, 0x20, + 0xf6-ctrl->value, 0, 0, 0, + 0, 0, 0, 0); + break; + case V4L2_CID_EXPOSURE: + err += et61x251_i2c_raw_write(cam, 2, 0x40, + 0x47-ctrl->value, 0, 0, 0, + 0, 0, 0, 0); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static struct et61x251_sensor tas5130d1b = { + .name = "TAS5130D1B", + .interface = ET61X251_I2C_3WIRES, + .rsta = ET61X251_I2C_RSTA_STOP, + .active_pixel = { + .left = 106, + .top = 13, + }, + .init = &tas5130d1b_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xf6, + .step = 0x02, + .default_value = 0x0d, + .flags = 0, + }, + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x47, + .step = 0x01, + .default_value = 0x23, + .flags = 0, + }, + }, + .set_ctrl = &tas5130d1b_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, +}; + + +int et61x251_probe_tas5130d1b(struct et61x251_device* cam) +{ + const struct usb_device_id tas5130d1b_id_table[] = { + { USB_DEVICE(0x102c, 0x6251), }, + { } + }; + + /* Sensor detection is based on USB pid/vid */ + if (!et61x251_match_id(cam, tas5130d1b_id_table)) + return -ENODEV; + + et61x251_attach_sensor(cam, &tas5130d1b); + + return 0; +} -- cgit v1.2.3 From 8851aa8cf1228ded68417be72940824c2b528fba Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Apr 2006 16:08:28 -0300 Subject: Add support for ovcamchip at the tree From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/ovcamchip/Makefile | 4 + linux/drivers/media/video/ovcamchip/ov6x20.c | 414 +++++++++++++++++++ linux/drivers/media/video/ovcamchip/ov6x30.c | 373 +++++++++++++++++ linux/drivers/media/video/ovcamchip/ov76be.c | 302 ++++++++++++++ linux/drivers/media/video/ovcamchip/ov7x10.c | 334 +++++++++++++++ linux/drivers/media/video/ovcamchip/ov7x20.c | 454 +++++++++++++++++++++ .../drivers/media/video/ovcamchip/ovcamchip_core.c | 443 ++++++++++++++++++++ .../drivers/media/video/ovcamchip/ovcamchip_priv.h | 87 ++++ 8 files changed, 2411 insertions(+) create mode 100644 linux/drivers/media/video/ovcamchip/Makefile create mode 100644 linux/drivers/media/video/ovcamchip/ov6x20.c create mode 100644 linux/drivers/media/video/ovcamchip/ov6x30.c create mode 100644 linux/drivers/media/video/ovcamchip/ov76be.c create mode 100644 linux/drivers/media/video/ovcamchip/ov7x10.c create mode 100644 linux/drivers/media/video/ovcamchip/ov7x20.c create mode 100644 linux/drivers/media/video/ovcamchip/ovcamchip_core.c create mode 100644 linux/drivers/media/video/ovcamchip/ovcamchip_priv.h (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/ovcamchip/Makefile b/linux/drivers/media/video/ovcamchip/Makefile new file mode 100644 index 000000000..cba4cdf20 --- /dev/null +++ b/linux/drivers/media/video/ovcamchip/Makefile @@ -0,0 +1,4 @@ +ovcamchip-objs := ovcamchip_core.o ov6x20.o ov6x30.o ov7x10.o ov7x20.o \ + ov76be.o + +obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip.o diff --git a/linux/drivers/media/video/ovcamchip/ov6x20.c b/linux/drivers/media/video/ovcamchip/ov6x20.c new file mode 100644 index 000000000..c04130dab --- /dev/null +++ b/linux/drivers/media/video/ovcamchip/ov6x20.c @@ -0,0 +1,414 @@ +/* OmniVision OV6620/OV6120 Camera Chip Support Code + * + * Copyright (c) 1999-2004 Mark McClelland + * http://alpha.dyndns.org/ov511/ + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. + */ + +#define DEBUG + +#include +#include "ovcamchip_priv.h" + +/* Registers */ +#define REG_GAIN 0x00 /* gain [5:0] */ +#define REG_BLUE 0x01 /* blue gain */ +#define REG_RED 0x02 /* red gain */ +#define REG_SAT 0x03 /* saturation */ +#define REG_CNT 0x05 /* Y contrast */ +#define REG_BRT 0x06 /* Y brightness */ +#define REG_WB_BLUE 0x0C /* WB blue ratio [5:0] */ +#define REG_WB_RED 0x0D /* WB red ratio [5:0] */ +#define REG_EXP 0x10 /* exposure */ + +/* Window parameters */ +#define HWSBASE 0x38 +#define HWEBASE 0x3A +#define VWSBASE 0x05 +#define VWEBASE 0x06 + +struct ov6x20 { + int auto_brt; + int auto_exp; + int backlight; + int bandfilt; + int mirror; +}; + +/* Initial values for use with OV511/OV511+ cameras */ +static struct ovcamchip_regvals regvals_init_6x20_511[] = { + { 0x12, 0x80 }, /* reset */ + { 0x11, 0x01 }, + { 0x03, 0x60 }, + { 0x05, 0x7f }, /* For when autoadjust is off */ + { 0x07, 0xa8 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0x0f, 0x15 }, /* COMS */ + { 0x10, 0x75 }, /* AEC Exposure time */ + { 0x12, 0x24 }, /* Enable AGC and AWB */ + { 0x14, 0x04 }, + { 0x16, 0x03 }, + { 0x26, 0xb2 }, /* BLC enable */ + /* 0x28: 0x05 Selects RGB format if RGB on */ + { 0x28, 0x05 }, + { 0x2a, 0x04 }, /* Disable framerate adjust */ + { 0x2d, 0x99 }, + { 0x33, 0xa0 }, /* Color Processing Parameter */ + { 0x34, 0xd2 }, /* Max A/D range */ + { 0x38, 0x8b }, + { 0x39, 0x40 }, + + { 0x3c, 0x39 }, /* Enable AEC mode changing */ + { 0x3c, 0x3c }, /* Change AEC mode */ + { 0x3c, 0x24 }, /* Disable AEC mode changing */ + + { 0x3d, 0x80 }, + /* These next two registers (0x4a, 0x4b) are undocumented. They + * control the color balance */ + { 0x4a, 0x80 }, + { 0x4b, 0x80 }, + { 0x4d, 0xd2 }, /* This reduces noise a bit */ + { 0x4e, 0xc1 }, + { 0x4f, 0x04 }, + { 0xff, 0xff }, /* END MARKER */ +}; + +/* Initial values for use with OV518 cameras */ +static struct ovcamchip_regvals regvals_init_6x20_518[] = { + { 0x12, 0x80 }, /* Do a reset */ + { 0x03, 0xc0 }, /* Saturation */ + { 0x05, 0x8a }, /* Contrast */ + { 0x0c, 0x24 }, /* AWB blue */ + { 0x0d, 0x24 }, /* AWB red */ + { 0x0e, 0x8d }, /* Additional 2x gain */ + { 0x0f, 0x25 }, /* Black expanding level = 1.3V */ + { 0x11, 0x01 }, /* Clock div. */ + { 0x12, 0x24 }, /* Enable AGC and AWB */ + { 0x13, 0x01 }, /* (default) */ + { 0x14, 0x80 }, /* Set reserved bit 7 */ + { 0x15, 0x01 }, /* (default) */ + { 0x16, 0x03 }, /* (default) */ + { 0x17, 0x38 }, /* (default) */ + { 0x18, 0xea }, /* (default) */ + { 0x19, 0x04 }, + { 0x1a, 0x93 }, + { 0x1b, 0x00 }, /* (default) */ + { 0x1e, 0xc4 }, /* (default) */ + { 0x1f, 0x04 }, /* (default) */ + { 0x20, 0x20 }, /* Enable 1st stage aperture correction */ + { 0x21, 0x10 }, /* Y offset */ + { 0x22, 0x88 }, /* U offset */ + { 0x23, 0xc0 }, /* Set XTAL power level */ + { 0x24, 0x53 }, /* AEC bright ratio */ + { 0x25, 0x7a }, /* AEC black ratio */ + { 0x26, 0xb2 }, /* BLC enable */ + { 0x27, 0xa2 }, /* Full output range */ + { 0x28, 0x01 }, /* (default) */ + { 0x29, 0x00 }, /* (default) */ + { 0x2a, 0x84 }, /* (default) */ + { 0x2b, 0xa8 }, /* Set custom frame rate */ + { 0x2c, 0xa0 }, /* (reserved) */ + { 0x2d, 0x95 }, /* Enable banding filter */ + { 0x2e, 0x88 }, /* V offset */ + { 0x33, 0x22 }, /* Luminance gamma on */ + { 0x34, 0xc7 }, /* A/D bias */ + { 0x36, 0x12 }, /* (reserved) */ + { 0x37, 0x63 }, /* (reserved) */ + { 0x38, 0x8b }, /* Quick AEC/AEB */ + { 0x39, 0x00 }, /* (default) */ + { 0x3a, 0x0f }, /* (default) */ + { 0x3b, 0x3c }, /* (default) */ + { 0x3c, 0x5c }, /* AEC controls */ + { 0x3d, 0x80 }, /* Drop 1 (bad) frame when AEC change */ + { 0x3e, 0x80 }, /* (default) */ + { 0x3f, 0x02 }, /* (default) */ + { 0x40, 0x10 }, /* (reserved) */ + { 0x41, 0x10 }, /* (reserved) */ + { 0x42, 0x00 }, /* (reserved) */ + { 0x43, 0x7f }, /* (reserved) */ + { 0x44, 0x80 }, /* (reserved) */ + { 0x45, 0x1c }, /* (reserved) */ + { 0x46, 0x1c }, /* (reserved) */ + { 0x47, 0x80 }, /* (reserved) */ + { 0x48, 0x5f }, /* (reserved) */ + { 0x49, 0x00 }, /* (reserved) */ + { 0x4a, 0x00 }, /* Color balance (undocumented) */ + { 0x4b, 0x80 }, /* Color balance (undocumented) */ + { 0x4c, 0x58 }, /* (reserved) */ + { 0x4d, 0xd2 }, /* U *= .938, V *= .838 */ + { 0x4e, 0xa0 }, /* (default) */ + { 0x4f, 0x04 }, /* UV 3-point average */ + { 0x50, 0xff }, /* (reserved) */ + { 0x51, 0x58 }, /* (reserved) */ + { 0x52, 0xc0 }, /* (reserved) */ + { 0x53, 0x42 }, /* (reserved) */ + { 0x27, 0xa6 }, /* Enable manual offset adj. (reg 21 & 22) */ + { 0x12, 0x20 }, + { 0x12, 0x24 }, + + { 0xff, 0xff }, /* END MARKER */ +}; + +/* This initializes the OV6x20 camera chip and relevant variables. */ +static int ov6x20_init(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov6x20 *s; + int rc; + + DDEBUG(4, &c->dev, "entered"); + + switch (c->adapter->id) { + case I2C_HW_SMBUS_OV511: + rc = ov_write_regvals(c, regvals_init_6x20_511); + break; + case I2C_HW_SMBUS_OV518: + rc = ov_write_regvals(c, regvals_init_6x20_518); + break; + default: + dev_err(&c->dev, "ov6x20: Unsupported adapter\n"); + rc = -ENODEV; + } + + if (rc < 0) + return rc; + + ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); + if (!s) + return -ENOMEM; + + s->auto_brt = 1; + s->auto_exp = 1; + + return rc; +} + +static int ov6x20_free(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + + kfree(ov->spriv); + return 0; +} + +static int ov6x20_set_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov6x20 *s = ov->spriv; + int rc; + int v = ctl->value; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_write(c, REG_CNT, v >> 8); + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_write(c, REG_BRT, v >> 8); + break; + case OVCAMCHIP_CID_SAT: + rc = ov_write(c, REG_SAT, v >> 8); + break; + case OVCAMCHIP_CID_HUE: + rc = ov_write(c, REG_RED, 0xFF - (v >> 8)); + if (rc < 0) + goto out; + + rc = ov_write(c, REG_BLUE, v >> 8); + break; + case OVCAMCHIP_CID_EXP: + rc = ov_write(c, REG_EXP, v); + break; + case OVCAMCHIP_CID_FREQ: + { + int sixty = (v == 60); + + rc = ov_write(c, 0x2b, sixty?0xa8:0x28); + if (rc < 0) + goto out; + + rc = ov_write(c, 0x2a, sixty?0x84:0xa4); + break; + } + case OVCAMCHIP_CID_BANDFILT: + rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); + s->bandfilt = v; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); + s->auto_brt = v; + break; + case OVCAMCHIP_CID_AUTOEXP: + rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01); + s->auto_exp = v; + break; + case OVCAMCHIP_CID_BACKLIGHT: + { + rc = ov_write_mask(c, 0x4e, v?0xe0:0xc0, 0xe0); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x0e, v?0x80:0x00, 0x80); + s->backlight = v; + break; + } + case OVCAMCHIP_CID_MIRROR: + rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); + s->mirror = v; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + +out: + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); + return rc; +} + +static int ov6x20_get_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov6x20 *s = ov->spriv; + int rc = 0; + unsigned char val = 0; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_read(c, REG_CNT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_read(c, REG_BRT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_SAT: + rc = ov_read(c, REG_SAT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_HUE: + rc = ov_read(c, REG_BLUE, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_EXP: + rc = ov_read(c, REG_EXP, &val); + ctl->value = val; + break; + case OVCAMCHIP_CID_BANDFILT: + ctl->value = s->bandfilt; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + ctl->value = s->auto_brt; + break; + case OVCAMCHIP_CID_AUTOEXP: + ctl->value = s->auto_exp; + break; + case OVCAMCHIP_CID_BACKLIGHT: + ctl->value = s->backlight; + break; + case OVCAMCHIP_CID_MIRROR: + ctl->value = s->mirror; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); + return rc; +} + +static int ov6x20_mode_init(struct i2c_client *c, struct ovcamchip_window *win) +{ + /******** QCIF-specific regs ********/ + + ov_write(c, 0x14, win->quarter?0x24:0x04); + + /******** Palette-specific regs ********/ + + /* OV518 needs 8 bit multiplexed in color mode, and 16 bit in B&W */ + if (c->adapter->id == I2C_HW_SMBUS_OV518) { + if (win->format == VIDEO_PALETTE_GREY) + ov_write_mask(c, 0x13, 0x00, 0x20); + else + ov_write_mask(c, 0x13, 0x20, 0x20); + } else { + if (win->format == VIDEO_PALETTE_GREY) + ov_write_mask(c, 0x13, 0x20, 0x20); + else + ov_write_mask(c, 0x13, 0x00, 0x20); + } + + /******** Clock programming ********/ + + /* The OV6620 needs special handling. This prevents the + * severe banding that normally occurs */ + + /* Clock down */ + ov_write(c, 0x2a, 0x04); + + ov_write(c, 0x11, win->clockdiv); + + ov_write(c, 0x2a, 0x84); + /* This next setting is critical. It seems to improve + * the gain or the contrast. The "reserved" bits seem + * to have some effect in this case. */ + ov_write(c, 0x2d, 0x85); /* FIXME: This messes up banding filter */ + + return 0; +} + +static int ov6x20_set_window(struct i2c_client *c, struct ovcamchip_window *win) +{ + int ret, hwscale, vwscale; + + ret = ov6x20_mode_init(c, win); + if (ret < 0) + return ret; + + if (win->quarter) { + hwscale = 0; + vwscale = 0; + } else { + hwscale = 1; + vwscale = 1; /* The datasheet says 0; it's wrong */ + } + + ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); + ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); + ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); + ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); + + return 0; +} + +static int ov6x20_command(struct i2c_client *c, unsigned int cmd, void *arg) +{ + switch (cmd) { + case OVCAMCHIP_CMD_S_CTRL: + return ov6x20_set_control(c, arg); + case OVCAMCHIP_CMD_G_CTRL: + return ov6x20_get_control(c, arg); + case OVCAMCHIP_CMD_S_MODE: + return ov6x20_set_window(c, arg); + default: + DDEBUG(2, &c->dev, "command not supported: %d", cmd); + return -ENOIOCTLCMD; + } +} + +struct ovcamchip_ops ov6x20_ops = { + .init = ov6x20_init, + .free = ov6x20_free, + .command = ov6x20_command, +}; diff --git a/linux/drivers/media/video/ovcamchip/ov6x30.c b/linux/drivers/media/video/ovcamchip/ov6x30.c new file mode 100644 index 000000000..73b94f51a --- /dev/null +++ b/linux/drivers/media/video/ovcamchip/ov6x30.c @@ -0,0 +1,373 @@ +/* OmniVision OV6630/OV6130 Camera Chip Support Code + * + * Copyright (c) 1999-2004 Mark McClelland + * http://alpha.dyndns.org/ov511/ + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. + */ + +#define DEBUG + +#include +#include "ovcamchip_priv.h" + +/* Registers */ +#define REG_GAIN 0x00 /* gain [5:0] */ +#define REG_BLUE 0x01 /* blue gain */ +#define REG_RED 0x02 /* red gain */ +#define REG_SAT 0x03 /* saturation [7:3] */ +#define REG_CNT 0x05 /* Y contrast [3:0] */ +#define REG_BRT 0x06 /* Y brightness */ +#define REG_SHARP 0x07 /* sharpness */ +#define REG_WB_BLUE 0x0C /* WB blue ratio [5:0] */ +#define REG_WB_RED 0x0D /* WB red ratio [5:0] */ +#define REG_EXP 0x10 /* exposure */ + +/* Window parameters */ +#define HWSBASE 0x38 +#define HWEBASE 0x3A +#define VWSBASE 0x05 +#define VWEBASE 0x06 + +struct ov6x30 { + int auto_brt; + int auto_exp; + int backlight; + int bandfilt; + int mirror; +}; + +static struct ovcamchip_regvals regvals_init_6x30[] = { + { 0x12, 0x80 }, /* reset */ + { 0x00, 0x1f }, /* Gain */ + { 0x01, 0x99 }, /* Blue gain */ + { 0x02, 0x7c }, /* Red gain */ + { 0x03, 0xc0 }, /* Saturation */ + { 0x05, 0x0a }, /* Contrast */ + { 0x06, 0x95 }, /* Brightness */ + { 0x07, 0x2d }, /* Sharpness */ + { 0x0c, 0x20 }, + { 0x0d, 0x20 }, + { 0x0e, 0x20 }, + { 0x0f, 0x05 }, + { 0x10, 0x9a }, /* "exposure check" */ + { 0x11, 0x00 }, /* Pixel clock = fastest */ + { 0x12, 0x24 }, /* Enable AGC and AWB */ + { 0x13, 0x21 }, + { 0x14, 0x80 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x38 }, + { 0x18, 0xea }, + { 0x19, 0x04 }, + { 0x1a, 0x93 }, + { 0x1b, 0x00 }, + { 0x1e, 0xc4 }, + { 0x1f, 0x04 }, + { 0x20, 0x20 }, + { 0x21, 0x10 }, + { 0x22, 0x88 }, + { 0x23, 0xc0 }, /* Crystal circuit power level */ + { 0x25, 0x9a }, /* Increase AEC black pixel ratio */ + { 0x26, 0xb2 }, /* BLC enable */ + { 0x27, 0xa2 }, + { 0x28, 0x00 }, + { 0x29, 0x00 }, + { 0x2a, 0x84 }, /* (keep) */ + { 0x2b, 0xa8 }, /* (keep) */ + { 0x2c, 0xa0 }, + { 0x2d, 0x95 }, /* Enable auto-brightness */ + { 0x2e, 0x88 }, + { 0x33, 0x26 }, + { 0x34, 0x03 }, + { 0x36, 0x8f }, + { 0x37, 0x80 }, + { 0x38, 0x83 }, + { 0x39, 0x80 }, + { 0x3a, 0x0f }, + { 0x3b, 0x3c }, + { 0x3c, 0x1a }, + { 0x3d, 0x80 }, + { 0x3e, 0x80 }, + { 0x3f, 0x0e }, + { 0x40, 0x00 }, /* White bal */ + { 0x41, 0x00 }, /* White bal */ + { 0x42, 0x80 }, + { 0x43, 0x3f }, /* White bal */ + { 0x44, 0x80 }, + { 0x45, 0x20 }, + { 0x46, 0x20 }, + { 0x47, 0x80 }, + { 0x48, 0x7f }, + { 0x49, 0x00 }, + { 0x4a, 0x00 }, + { 0x4b, 0x80 }, + { 0x4c, 0xd0 }, + { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */ + { 0x4e, 0x40 }, + { 0x4f, 0x07 }, /* UV average mode, color killer: strongest */ + { 0x50, 0xff }, + { 0x54, 0x23 }, /* Max AGC gain: 18dB */ + { 0x55, 0xff }, + { 0x56, 0x12 }, + { 0x57, 0x81 }, /* (default) */ + { 0x58, 0x75 }, + { 0x59, 0x01 }, /* AGC dark current compensation: +1 */ + { 0x5a, 0x2c }, + { 0x5b, 0x0f }, /* AWB chrominance levels */ + { 0x5c, 0x10 }, + { 0x3d, 0x80 }, + { 0x27, 0xa6 }, + /* Toggle AWB off and on */ + { 0x12, 0x20 }, + { 0x12, 0x24 }, + + { 0xff, 0xff }, /* END MARKER */ +}; + +/* This initializes the OV6x30 camera chip and relevant variables. */ +static int ov6x30_init(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov6x30 *s; + int rc; + + DDEBUG(4, &c->dev, "entered"); + + rc = ov_write_regvals(c, regvals_init_6x30); + if (rc < 0) + return rc; + + ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); + if (!s) + return -ENOMEM; + + s->auto_brt = 1; + s->auto_exp = 1; + + return rc; +} + +static int ov6x30_free(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + + kfree(ov->spriv); + return 0; +} + +static int ov6x30_set_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov6x30 *s = ov->spriv; + int rc; + int v = ctl->value; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_write_mask(c, REG_CNT, v >> 12, 0x0f); + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_write(c, REG_BRT, v >> 8); + break; + case OVCAMCHIP_CID_SAT: + rc = ov_write(c, REG_SAT, v >> 8); + break; + case OVCAMCHIP_CID_HUE: + rc = ov_write(c, REG_RED, 0xFF - (v >> 8)); + if (rc < 0) + goto out; + + rc = ov_write(c, REG_BLUE, v >> 8); + break; + case OVCAMCHIP_CID_EXP: + rc = ov_write(c, REG_EXP, v); + break; + case OVCAMCHIP_CID_FREQ: + { + int sixty = (v == 60); + + rc = ov_write(c, 0x2b, sixty?0xa8:0x28); + if (rc < 0) + goto out; + + rc = ov_write(c, 0x2a, sixty?0x84:0xa4); + break; + } + case OVCAMCHIP_CID_BANDFILT: + rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); + s->bandfilt = v; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); + s->auto_brt = v; + break; + case OVCAMCHIP_CID_AUTOEXP: + rc = ov_write_mask(c, 0x28, v?0x00:0x10, 0x10); + s->auto_exp = v; + break; + case OVCAMCHIP_CID_BACKLIGHT: + { + rc = ov_write_mask(c, 0x4e, v?0x80:0x60, 0xe0); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x28, v?0x02:0x00, 0x02); + s->backlight = v; + break; + } + case OVCAMCHIP_CID_MIRROR: + rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); + s->mirror = v; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + +out: + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); + return rc; +} + +static int ov6x30_get_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov6x30 *s = ov->spriv; + int rc = 0; + unsigned char val = 0; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_read(c, REG_CNT, &val); + ctl->value = (val & 0x0f) << 12; + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_read(c, REG_BRT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_SAT: + rc = ov_read(c, REG_SAT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_HUE: + rc = ov_read(c, REG_BLUE, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_EXP: + rc = ov_read(c, REG_EXP, &val); + ctl->value = val; + break; + case OVCAMCHIP_CID_BANDFILT: + ctl->value = s->bandfilt; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + ctl->value = s->auto_brt; + break; + case OVCAMCHIP_CID_AUTOEXP: + ctl->value = s->auto_exp; + break; + case OVCAMCHIP_CID_BACKLIGHT: + ctl->value = s->backlight; + break; + case OVCAMCHIP_CID_MIRROR: + ctl->value = s->mirror; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); + return rc; +} + +static int ov6x30_mode_init(struct i2c_client *c, struct ovcamchip_window *win) +{ + /******** QCIF-specific regs ********/ + + ov_write_mask(c, 0x14, win->quarter?0x20:0x00, 0x20); + + /******** Palette-specific regs ********/ + + if (win->format == VIDEO_PALETTE_GREY) { + if (c->adapter->id == I2C_HW_SMBUS_OV518) { + /* Do nothing - we're already in 8-bit mode */ + } else { + ov_write_mask(c, 0x13, 0x20, 0x20); + } + } else { + /* The OV518 needs special treatment. Although both the OV518 + * and the OV6630 support a 16-bit video bus, only the 8 bit Y + * bus is actually used. The UV bus is tied to ground. + * Therefore, the OV6630 needs to be in 8-bit multiplexed + * output mode */ + + if (c->adapter->id == I2C_HW_SMBUS_OV518) { + /* Do nothing - we want to stay in 8-bit mode */ + /* Warning: Messing with reg 0x13 breaks OV518 color */ + } else { + ov_write_mask(c, 0x13, 0x00, 0x20); + } + } + + /******** Clock programming ********/ + + ov_write(c, 0x11, win->clockdiv); + + return 0; +} + +static int ov6x30_set_window(struct i2c_client *c, struct ovcamchip_window *win) +{ + int ret, hwscale, vwscale; + + ret = ov6x30_mode_init(c, win); + if (ret < 0) + return ret; + + if (win->quarter) { + hwscale = 0; + vwscale = 0; + } else { + hwscale = 1; + vwscale = 1; /* The datasheet says 0; it's wrong */ + } + + ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); + ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); + ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); + ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); + + return 0; +} + +static int ov6x30_command(struct i2c_client *c, unsigned int cmd, void *arg) +{ + switch (cmd) { + case OVCAMCHIP_CMD_S_CTRL: + return ov6x30_set_control(c, arg); + case OVCAMCHIP_CMD_G_CTRL: + return ov6x30_get_control(c, arg); + case OVCAMCHIP_CMD_S_MODE: + return ov6x30_set_window(c, arg); + default: + DDEBUG(2, &c->dev, "command not supported: %d", cmd); + return -ENOIOCTLCMD; + } +} + +struct ovcamchip_ops ov6x30_ops = { + .init = ov6x30_init, + .free = ov6x30_free, + .command = ov6x30_command, +}; diff --git a/linux/drivers/media/video/ovcamchip/ov76be.c b/linux/drivers/media/video/ovcamchip/ov76be.c new file mode 100644 index 000000000..11f6be924 --- /dev/null +++ b/linux/drivers/media/video/ovcamchip/ov76be.c @@ -0,0 +1,302 @@ +/* OmniVision OV76BE Camera Chip Support Code + * + * Copyright (c) 1999-2004 Mark McClelland + * http://alpha.dyndns.org/ov511/ + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. + */ + +#define DEBUG + +#include +#include "ovcamchip_priv.h" + +/* OV7610 registers: Since the OV76BE is undocumented, we'll settle for these + * for now. */ +#define REG_GAIN 0x00 /* gain [5:0] */ +#define REG_BLUE 0x01 /* blue channel balance */ +#define REG_RED 0x02 /* red channel balance */ +#define REG_SAT 0x03 /* saturation */ +#define REG_CNT 0x05 /* Y contrast */ +#define REG_BRT 0x06 /* Y brightness */ +#define REG_BLUE_BIAS 0x0C /* blue channel bias [5:0] */ +#define REG_RED_BIAS 0x0D /* red channel bias [5:0] */ +#define REG_GAMMA_COEFF 0x0E /* gamma settings */ +#define REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */ +#define REG_EXP 0x10 /* manual exposure setting */ +#define REG_CLOCK 0x11 /* polarity/clock prescaler */ +#define REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */ +#define REG_HWIN_START 0x17 /* horizontal window start */ +#define REG_HWIN_END 0x18 /* horizontal window end */ +#define REG_VWIN_START 0x19 /* vertical window start */ +#define REG_VWIN_END 0x1A /* vertical window end */ +#define REG_PIXEL_SHIFT 0x1B /* pixel shift */ +#define REG_YOFFSET 0x21 /* Y channel offset */ +#define REG_UOFFSET 0x22 /* U channel offset */ +#define REG_ECW 0x24 /* exposure white level for AEC */ +#define REG_ECB 0x25 /* exposure black level for AEC */ +#define REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */ +#define REG_FRAMERATE_L 0x2B /* frame rate LSB */ +#define REG_ALC 0x2C /* Auto Level Control settings */ +#define REG_VOFFSET 0x2E /* V channel offset adjustment */ +#define REG_ARRAY_BIAS 0x2F /* array bias -- don't change */ +#define REG_YGAMMA 0x33 /* misc gamma settings [7:6] */ +#define REG_BIAS_ADJUST 0x34 /* misc bias settings */ + +/* Window parameters */ +#define HWSBASE 0x38 +#define HWEBASE 0x3a +#define VWSBASE 0x05 +#define VWEBASE 0x05 + +struct ov76be { + int auto_brt; + int auto_exp; + int bandfilt; + int mirror; +}; + +/* NOTE: These are the same as the 7x10 settings, but should eventually be + * optimized for the OV76BE */ +static struct ovcamchip_regvals regvals_init_76be[] = { + { 0x10, 0xff }, + { 0x16, 0x03 }, + { 0x28, 0x24 }, + { 0x2b, 0xac }, + { 0x12, 0x00 }, + { 0x38, 0x81 }, + { 0x28, 0x24 }, /* 0c */ + { 0x0f, 0x85 }, /* lg's setting */ + { 0x15, 0x01 }, + { 0x20, 0x1c }, + { 0x23, 0x2a }, + { 0x24, 0x10 }, + { 0x25, 0x8a }, + { 0x26, 0xa2 }, + { 0x27, 0xc2 }, + { 0x2a, 0x04 }, + { 0x2c, 0xfe }, + { 0x2d, 0x93 }, + { 0x30, 0x71 }, + { 0x31, 0x60 }, + { 0x32, 0x26 }, + { 0x33, 0x20 }, + { 0x34, 0x48 }, + { 0x12, 0x24 }, + { 0x11, 0x01 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0xff, 0xff }, /* END MARKER */ +}; + +/* This initializes the OV76be camera chip and relevant variables. */ +static int ov76be_init(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov76be *s; + int rc; + + DDEBUG(4, &c->dev, "entered"); + + rc = ov_write_regvals(c, regvals_init_76be); + if (rc < 0) + return rc; + + ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); + if (!s) + return -ENOMEM; + + s->auto_brt = 1; + s->auto_exp = 1; + + return rc; +} + +static int ov76be_free(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + + kfree(ov->spriv); + return 0; +} + +static int ov76be_set_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov76be *s = ov->spriv; + int rc; + int v = ctl->value; + + switch (ctl->id) { + case OVCAMCHIP_CID_BRIGHT: + rc = ov_write(c, REG_BRT, v >> 8); + break; + case OVCAMCHIP_CID_SAT: + rc = ov_write(c, REG_SAT, v >> 8); + break; + case OVCAMCHIP_CID_EXP: + rc = ov_write(c, REG_EXP, v); + break; + case OVCAMCHIP_CID_FREQ: + { + int sixty = (v == 60); + + rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80); + if (rc < 0) + goto out; + + rc = ov_write(c, 0x2b, sixty?0x00:0xac); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x76, 0x01, 0x01); + break; + } + case OVCAMCHIP_CID_BANDFILT: + rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); + s->bandfilt = v; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); + s->auto_brt = v; + break; + case OVCAMCHIP_CID_AUTOEXP: + rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01); + s->auto_exp = v; + break; + case OVCAMCHIP_CID_MIRROR: + rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); + s->mirror = v; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + +out: + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); + return rc; +} + +static int ov76be_get_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov76be *s = ov->spriv; + int rc = 0; + unsigned char val = 0; + + switch (ctl->id) { + case OVCAMCHIP_CID_BRIGHT: + rc = ov_read(c, REG_BRT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_SAT: + rc = ov_read(c, REG_SAT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_EXP: + rc = ov_read(c, REG_EXP, &val); + ctl->value = val; + break; + case OVCAMCHIP_CID_BANDFILT: + ctl->value = s->bandfilt; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + ctl->value = s->auto_brt; + break; + case OVCAMCHIP_CID_AUTOEXP: + ctl->value = s->auto_exp; + break; + case OVCAMCHIP_CID_MIRROR: + ctl->value = s->mirror; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); + return rc; +} + +static int ov76be_mode_init(struct i2c_client *c, struct ovcamchip_window *win) +{ + int qvga = win->quarter; + + /******** QVGA-specific regs ********/ + + ov_write(c, 0x14, qvga?0xa4:0x84); + + /******** Palette-specific regs ********/ + + if (win->format == VIDEO_PALETTE_GREY) { + ov_write_mask(c, 0x0e, 0x40, 0x40); + ov_write_mask(c, 0x13, 0x20, 0x20); + } else { + ov_write_mask(c, 0x0e, 0x00, 0x40); + ov_write_mask(c, 0x13, 0x00, 0x20); + } + + /******** Clock programming ********/ + + ov_write(c, 0x11, win->clockdiv); + + /******** Resolution-specific ********/ + + if (win->width == 640 && win->height == 480) + ov_write(c, 0x35, 0x9e); + else + ov_write(c, 0x35, 0x1e); + + return 0; +} + +static int ov76be_set_window(struct i2c_client *c, struct ovcamchip_window *win) +{ + int ret, hwscale, vwscale; + + ret = ov76be_mode_init(c, win); + if (ret < 0) + return ret; + + if (win->quarter) { + hwscale = 1; + vwscale = 0; + } else { + hwscale = 2; + vwscale = 1; + } + + ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); + ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); + ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); + ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); + + return 0; +} + +static int ov76be_command(struct i2c_client *c, unsigned int cmd, void *arg) +{ + switch (cmd) { + case OVCAMCHIP_CMD_S_CTRL: + return ov76be_set_control(c, arg); + case OVCAMCHIP_CMD_G_CTRL: + return ov76be_get_control(c, arg); + case OVCAMCHIP_CMD_S_MODE: + return ov76be_set_window(c, arg); + default: + DDEBUG(2, &c->dev, "command not supported: %d", cmd); + return -ENOIOCTLCMD; + } +} + +struct ovcamchip_ops ov76be_ops = { + .init = ov76be_init, + .free = ov76be_free, + .command = ov76be_command, +}; diff --git a/linux/drivers/media/video/ovcamchip/ov7x10.c b/linux/drivers/media/video/ovcamchip/ov7x10.c new file mode 100644 index 000000000..5206e7913 --- /dev/null +++ b/linux/drivers/media/video/ovcamchip/ov7x10.c @@ -0,0 +1,334 @@ +/* OmniVision OV7610/OV7110 Camera Chip Support Code + * + * Copyright (c) 1999-2004 Mark McClelland + * http://alpha.dyndns.org/ov511/ + * + * Color fixes by by Orion Sky Lawlor (2/26/2000) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. + */ + +#define DEBUG + +#include +#include "ovcamchip_priv.h" + +/* Registers */ +#define REG_GAIN 0x00 /* gain [5:0] */ +#define REG_BLUE 0x01 /* blue channel balance */ +#define REG_RED 0x02 /* red channel balance */ +#define REG_SAT 0x03 /* saturation */ +#define REG_CNT 0x05 /* Y contrast */ +#define REG_BRT 0x06 /* Y brightness */ +#define REG_BLUE_BIAS 0x0C /* blue channel bias [5:0] */ +#define REG_RED_BIAS 0x0D /* red channel bias [5:0] */ +#define REG_GAMMA_COEFF 0x0E /* gamma settings */ +#define REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */ +#define REG_EXP 0x10 /* manual exposure setting */ +#define REG_CLOCK 0x11 /* polarity/clock prescaler */ +#define REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */ +#define REG_HWIN_START 0x17 /* horizontal window start */ +#define REG_HWIN_END 0x18 /* horizontal window end */ +#define REG_VWIN_START 0x19 /* vertical window start */ +#define REG_VWIN_END 0x1A /* vertical window end */ +#define REG_PIXEL_SHIFT 0x1B /* pixel shift */ +#define REG_YOFFSET 0x21 /* Y channel offset */ +#define REG_UOFFSET 0x22 /* U channel offset */ +#define REG_ECW 0x24 /* exposure white level for AEC */ +#define REG_ECB 0x25 /* exposure black level for AEC */ +#define REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */ +#define REG_FRAMERATE_L 0x2B /* frame rate LSB */ +#define REG_ALC 0x2C /* Auto Level Control settings */ +#define REG_VOFFSET 0x2E /* V channel offset adjustment */ +#define REG_ARRAY_BIAS 0x2F /* array bias -- don't change */ +#define REG_YGAMMA 0x33 /* misc gamma settings [7:6] */ +#define REG_BIAS_ADJUST 0x34 /* misc bias settings */ + +/* Window parameters */ +#define HWSBASE 0x38 +#define HWEBASE 0x3a +#define VWSBASE 0x05 +#define VWEBASE 0x05 + +struct ov7x10 { + int auto_brt; + int auto_exp; + int bandfilt; + int mirror; +}; + +/* Lawrence Glaister reports: + * + * Register 0x0f in the 7610 has the following effects: + * + * 0x85 (AEC method 1): Best overall, good contrast range + * 0x45 (AEC method 2): Very overexposed + * 0xa5 (spec sheet default): Ok, but the black level is + * shifted resulting in loss of contrast + * 0x05 (old driver setting): very overexposed, too much + * contrast + */ +static struct ovcamchip_regvals regvals_init_7x10[] = { + { 0x10, 0xff }, + { 0x16, 0x03 }, + { 0x28, 0x24 }, + { 0x2b, 0xac }, + { 0x12, 0x00 }, + { 0x38, 0x81 }, + { 0x28, 0x24 }, /* 0c */ + { 0x0f, 0x85 }, /* lg's setting */ + { 0x15, 0x01 }, + { 0x20, 0x1c }, + { 0x23, 0x2a }, + { 0x24, 0x10 }, + { 0x25, 0x8a }, + { 0x26, 0xa2 }, + { 0x27, 0xc2 }, + { 0x2a, 0x04 }, + { 0x2c, 0xfe }, + { 0x2d, 0x93 }, + { 0x30, 0x71 }, + { 0x31, 0x60 }, + { 0x32, 0x26 }, + { 0x33, 0x20 }, + { 0x34, 0x48 }, + { 0x12, 0x24 }, + { 0x11, 0x01 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0xff, 0xff }, /* END MARKER */ +}; + +/* This initializes the OV7x10 camera chip and relevant variables. */ +static int ov7x10_init(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov7x10 *s; + int rc; + + DDEBUG(4, &c->dev, "entered"); + + rc = ov_write_regvals(c, regvals_init_7x10); + if (rc < 0) + return rc; + + ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); + if (!s) + return -ENOMEM; + + s->auto_brt = 1; + s->auto_exp = 1; + + return rc; +} + +static int ov7x10_free(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + + kfree(ov->spriv); + return 0; +} + +static int ov7x10_set_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov7x10 *s = ov->spriv; + int rc; + int v = ctl->value; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_write(c, REG_CNT, v >> 8); + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_write(c, REG_BRT, v >> 8); + break; + case OVCAMCHIP_CID_SAT: + rc = ov_write(c, REG_SAT, v >> 8); + break; + case OVCAMCHIP_CID_HUE: + rc = ov_write(c, REG_RED, 0xFF - (v >> 8)); + if (rc < 0) + goto out; + + rc = ov_write(c, REG_BLUE, v >> 8); + break; + case OVCAMCHIP_CID_EXP: + rc = ov_write(c, REG_EXP, v); + break; + case OVCAMCHIP_CID_FREQ: + { + int sixty = (v == 60); + + rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80); + if (rc < 0) + goto out; + + rc = ov_write(c, 0x2b, sixty?0x00:0xac); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x13, 0x10, 0x10); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x13, 0x00, 0x10); + break; + } + case OVCAMCHIP_CID_BANDFILT: + rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); + s->bandfilt = v; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); + s->auto_brt = v; + break; + case OVCAMCHIP_CID_AUTOEXP: + rc = ov_write_mask(c, 0x29, v?0x00:0x80, 0x80); + s->auto_exp = v; + break; + case OVCAMCHIP_CID_MIRROR: + rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); + s->mirror = v; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + +out: + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); + return rc; +} + +static int ov7x10_get_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov7x10 *s = ov->spriv; + int rc = 0; + unsigned char val = 0; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_read(c, REG_CNT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_read(c, REG_BRT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_SAT: + rc = ov_read(c, REG_SAT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_HUE: + rc = ov_read(c, REG_BLUE, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_EXP: + rc = ov_read(c, REG_EXP, &val); + ctl->value = val; + break; + case OVCAMCHIP_CID_BANDFILT: + ctl->value = s->bandfilt; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + ctl->value = s->auto_brt; + break; + case OVCAMCHIP_CID_AUTOEXP: + ctl->value = s->auto_exp; + break; + case OVCAMCHIP_CID_MIRROR: + ctl->value = s->mirror; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); + return rc; +} + +static int ov7x10_mode_init(struct i2c_client *c, struct ovcamchip_window *win) +{ + int qvga = win->quarter; + + /******** QVGA-specific regs ********/ + + ov_write(c, 0x14, qvga?0x24:0x04); + + /******** Palette-specific regs ********/ + + if (win->format == VIDEO_PALETTE_GREY) { + ov_write_mask(c, 0x0e, 0x40, 0x40); + ov_write_mask(c, 0x13, 0x20, 0x20); + } else { + ov_write_mask(c, 0x0e, 0x00, 0x40); + ov_write_mask(c, 0x13, 0x00, 0x20); + } + + /******** Clock programming ********/ + + ov_write(c, 0x11, win->clockdiv); + + /******** Resolution-specific ********/ + + if (win->width == 640 && win->height == 480) + ov_write(c, 0x35, 0x9e); + else + ov_write(c, 0x35, 0x1e); + + return 0; +} + +static int ov7x10_set_window(struct i2c_client *c, struct ovcamchip_window *win) +{ + int ret, hwscale, vwscale; + + ret = ov7x10_mode_init(c, win); + if (ret < 0) + return ret; + + if (win->quarter) { + hwscale = 1; + vwscale = 0; + } else { + hwscale = 2; + vwscale = 1; + } + + ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); + ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); + ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); + ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); + + return 0; +} + +static int ov7x10_command(struct i2c_client *c, unsigned int cmd, void *arg) +{ + switch (cmd) { + case OVCAMCHIP_CMD_S_CTRL: + return ov7x10_set_control(c, arg); + case OVCAMCHIP_CMD_G_CTRL: + return ov7x10_get_control(c, arg); + case OVCAMCHIP_CMD_S_MODE: + return ov7x10_set_window(c, arg); + default: + DDEBUG(2, &c->dev, "command not supported: %d", cmd); + return -ENOIOCTLCMD; + } +} + +struct ovcamchip_ops ov7x10_ops = { + .init = ov7x10_init, + .free = ov7x10_free, + .command = ov7x10_command, +}; diff --git a/linux/drivers/media/video/ovcamchip/ov7x20.c b/linux/drivers/media/video/ovcamchip/ov7x20.c new file mode 100644 index 000000000..8e26ae338 --- /dev/null +++ b/linux/drivers/media/video/ovcamchip/ov7x20.c @@ -0,0 +1,454 @@ +/* OmniVision OV7620/OV7120 Camera Chip Support Code + * + * Copyright (c) 1999-2004 Mark McClelland + * http://alpha.dyndns.org/ov511/ + * + * OV7620 fixes by Charl P. Botha + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. + */ + +#define DEBUG + +#include +#include "ovcamchip_priv.h" + +/* Registers */ +#define REG_GAIN 0x00 /* gain [5:0] */ +#define REG_BLUE 0x01 /* blue gain */ +#define REG_RED 0x02 /* red gain */ +#define REG_SAT 0x03 /* saturation */ +#define REG_BRT 0x06 /* Y brightness */ +#define REG_SHARP 0x07 /* analog sharpness */ +#define REG_BLUE_BIAS 0x0C /* WB blue ratio [5:0] */ +#define REG_RED_BIAS 0x0D /* WB red ratio [5:0] */ +#define REG_EXP 0x10 /* exposure */ + +/* Default control settings. Values are in terms of V4L2 controls. */ +#define OV7120_DFL_BRIGHT 0x60 +#define OV7620_DFL_BRIGHT 0x60 +#define OV7120_DFL_SAT 0xb0 +#define OV7620_DFL_SAT 0xc0 +#define DFL_AUTO_EXP 1 +#define DFL_AUTO_GAIN 1 +#define OV7120_DFL_GAIN 0x00 +#define OV7620_DFL_GAIN 0x00 +/* NOTE: Since autoexposure is the default, these aren't programmed into the + * OV7x20 chip. They are just here because V4L2 expects a default */ +#define OV7120_DFL_EXP 0x7f +#define OV7620_DFL_EXP 0x7f + +/* Window parameters */ +#define HWSBASE 0x2F /* From 7620.SET (spec is wrong) */ +#define HWEBASE 0x2F +#define VWSBASE 0x05 +#define VWEBASE 0x05 + +struct ov7x20 { + int auto_brt; + int auto_exp; + int auto_gain; + int backlight; + int bandfilt; + int mirror; +}; + +/* Contrast look-up table */ +static unsigned char ctab[] = { + 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57, + 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff +}; + +/* Settings for (Black & White) OV7120 camera chip */ +static struct ovcamchip_regvals regvals_init_7120[] = { + { 0x12, 0x80 }, /* reset */ + { 0x13, 0x00 }, /* Autoadjust off */ + { 0x12, 0x20 }, /* Disable AWB */ + { 0x13, DFL_AUTO_GAIN?0x01:0x00 }, /* Autoadjust on (if desired) */ + { 0x00, OV7120_DFL_GAIN }, + { 0x01, 0x80 }, + { 0x02, 0x80 }, + { 0x03, OV7120_DFL_SAT }, + { 0x06, OV7120_DFL_BRIGHT }, + { 0x07, 0x00 }, + { 0x0c, 0x20 }, + { 0x0d, 0x20 }, + { 0x11, 0x01 }, + { 0x14, 0x84 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x2f }, + { 0x18, 0xcf }, + { 0x19, 0x06 }, + { 0x1a, 0xf5 }, + { 0x1b, 0x00 }, + { 0x20, 0x08 }, + { 0x21, 0x80 }, + { 0x22, 0x80 }, + { 0x23, 0x00 }, + { 0x26, 0xa0 }, + { 0x27, 0xfa }, + { 0x28, 0x20 }, /* DON'T set bit 6. It is for the OV7620 only */ + { 0x29, DFL_AUTO_EXP?0x00:0x80 }, + { 0x2a, 0x10 }, + { 0x2b, 0x00 }, + { 0x2c, 0x88 }, + { 0x2d, 0x95 }, + { 0x2e, 0x80 }, + { 0x2f, 0x44 }, + { 0x60, 0x20 }, + { 0x61, 0x02 }, + { 0x62, 0x5f }, + { 0x63, 0xd5 }, + { 0x64, 0x57 }, + { 0x65, 0x83 }, /* OV says "don't change this value" */ + { 0x66, 0x55 }, + { 0x67, 0x92 }, + { 0x68, 0xcf }, + { 0x69, 0x76 }, + { 0x6a, 0x22 }, + { 0x6b, 0xe2 }, + { 0x6c, 0x40 }, + { 0x6d, 0x48 }, + { 0x6e, 0x80 }, + { 0x6f, 0x0d }, + { 0x70, 0x89 }, + { 0x71, 0x00 }, + { 0x72, 0x14 }, + { 0x73, 0x54 }, + { 0x74, 0xa0 }, + { 0x75, 0x8e }, + { 0x76, 0x00 }, + { 0x77, 0xff }, + { 0x78, 0x80 }, + { 0x79, 0x80 }, + { 0x7a, 0x80 }, + { 0x7b, 0xe6 }, + { 0x7c, 0x00 }, + { 0x24, 0x3a }, + { 0x25, 0x60 }, + { 0xff, 0xff }, /* END MARKER */ +}; + +/* Settings for (color) OV7620 camera chip */ +static struct ovcamchip_regvals regvals_init_7620[] = { + { 0x12, 0x80 }, /* reset */ + { 0x00, OV7620_DFL_GAIN }, + { 0x01, 0x80 }, + { 0x02, 0x80 }, + { 0x03, OV7620_DFL_SAT }, + { 0x06, OV7620_DFL_BRIGHT }, + { 0x07, 0x00 }, + { 0x0c, 0x24 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0x11, 0x01 }, + { 0x12, 0x24 }, + { 0x13, DFL_AUTO_GAIN?0x01:0x00 }, + { 0x14, 0x84 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x2f }, + { 0x18, 0xcf }, + { 0x19, 0x06 }, + { 0x1a, 0xf5 }, + { 0x1b, 0x00 }, + { 0x20, 0x18 }, + { 0x21, 0x80 }, + { 0x22, 0x80 }, + { 0x23, 0x00 }, + { 0x26, 0xa2 }, + { 0x27, 0xea }, + { 0x28, 0x20 }, + { 0x29, DFL_AUTO_EXP?0x00:0x80 }, + { 0x2a, 0x10 }, + { 0x2b, 0x00 }, + { 0x2c, 0x88 }, + { 0x2d, 0x91 }, + { 0x2e, 0x80 }, + { 0x2f, 0x44 }, + { 0x60, 0x27 }, + { 0x61, 0x02 }, + { 0x62, 0x5f }, + { 0x63, 0xd5 }, + { 0x64, 0x57 }, + { 0x65, 0x83 }, + { 0x66, 0x55 }, + { 0x67, 0x92 }, + { 0x68, 0xcf }, + { 0x69, 0x76 }, + { 0x6a, 0x22 }, + { 0x6b, 0x00 }, + { 0x6c, 0x02 }, + { 0x6d, 0x44 }, + { 0x6e, 0x80 }, + { 0x6f, 0x1d }, + { 0x70, 0x8b }, + { 0x71, 0x00 }, + { 0x72, 0x14 }, + { 0x73, 0x54 }, + { 0x74, 0x00 }, + { 0x75, 0x8e }, + { 0x76, 0x00 }, + { 0x77, 0xff }, + { 0x78, 0x80 }, + { 0x79, 0x80 }, + { 0x7a, 0x80 }, + { 0x7b, 0xe2 }, + { 0x7c, 0x00 }, + { 0xff, 0xff }, /* END MARKER */ +}; + +/* Returns index into the specified look-up table, with 'n' elements, for which + * the value is greater than or equal to "val". If a match isn't found, (n-1) + * is returned. The entries in the table must be in ascending order. */ +static inline int ov7x20_lut_find(unsigned char lut[], int n, unsigned char val) +{ + int i = 0; + + while (lut[i] < val && i < n) + i++; + + return i; +} + +/* This initializes the OV7x20 camera chip and relevant variables. */ +static int ov7x20_init(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov7x20 *s; + int rc; + + DDEBUG(4, &c->dev, "entered"); + + if (ov->mono) + rc = ov_write_regvals(c, regvals_init_7120); + else + rc = ov_write_regvals(c, regvals_init_7620); + + if (rc < 0) + return rc; + + ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); + if (!s) + return -ENOMEM; + + s->auto_brt = 1; + s->auto_exp = DFL_AUTO_EXP; + s->auto_gain = DFL_AUTO_GAIN; + + return 0; +} + +static int ov7x20_free(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + + kfree(ov->spriv); + return 0; +} + +static int ov7x20_set_v4l1_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov7x20 *s = ov->spriv; + int rc; + int v = ctl->value; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + { + /* Use Y gamma control instead. Bit 0 enables it. */ + rc = ov_write(c, 0x64, ctab[v >> 12]); + break; + } + case OVCAMCHIP_CID_BRIGHT: + /* 7620 doesn't like manual changes when in auto mode */ + if (!s->auto_brt) + rc = ov_write(c, REG_BRT, v >> 8); + else + rc = 0; + break; + case OVCAMCHIP_CID_SAT: + rc = ov_write(c, REG_SAT, v >> 8); + break; + case OVCAMCHIP_CID_EXP: + if (!s->auto_exp) + rc = ov_write(c, REG_EXP, v); + else + rc = -EBUSY; + break; + case OVCAMCHIP_CID_FREQ: + { + int sixty = (v == 60); + + rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80); + if (rc < 0) + goto out; + + rc = ov_write(c, 0x2b, sixty?0x00:0xac); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x76, 0x01, 0x01); + break; + } + case OVCAMCHIP_CID_BANDFILT: + rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); + s->bandfilt = v; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); + s->auto_brt = v; + break; + case OVCAMCHIP_CID_AUTOEXP: + rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01); + s->auto_exp = v; + break; + case OVCAMCHIP_CID_BACKLIGHT: + { + rc = ov_write_mask(c, 0x68, v?0xe0:0xc0, 0xe0); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x28, v?0x02:0x00, 0x02); + s->backlight = v; + break; + } + case OVCAMCHIP_CID_MIRROR: + rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); + s->mirror = v; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + +out: + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); + return rc; +} + +static int ov7x20_get_v4l1_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov7x20 *s = ov->spriv; + int rc = 0; + unsigned char val = 0; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_read(c, 0x64, &val); + ctl->value = ov7x20_lut_find(ctab, 16, val) << 12; + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_read(c, REG_BRT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_SAT: + rc = ov_read(c, REG_SAT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_EXP: + rc = ov_read(c, REG_EXP, &val); + ctl->value = val; + break; + case OVCAMCHIP_CID_BANDFILT: + ctl->value = s->bandfilt; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + ctl->value = s->auto_brt; + break; + case OVCAMCHIP_CID_AUTOEXP: + ctl->value = s->auto_exp; + break; + case OVCAMCHIP_CID_BACKLIGHT: + ctl->value = s->backlight; + break; + case OVCAMCHIP_CID_MIRROR: + ctl->value = s->mirror; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); + return rc; +} + +static int ov7x20_mode_init(struct i2c_client *c, struct ovcamchip_window *win) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + int qvga = win->quarter; + + /******** QVGA-specific regs ********/ + ov_write_mask(c, 0x14, qvga?0x20:0x00, 0x20); + ov_write_mask(c, 0x28, qvga?0x00:0x20, 0x20); + ov_write(c, 0x24, qvga?0x20:0x3a); + ov_write(c, 0x25, qvga?0x30:0x60); + ov_write_mask(c, 0x2d, qvga?0x40:0x00, 0x40); + if (!ov->mono) + ov_write_mask(c, 0x67, qvga?0xf0:0x90, 0xf0); + ov_write_mask(c, 0x74, qvga?0x20:0x00, 0x20); + + /******** Clock programming ********/ + + ov_write(c, 0x11, win->clockdiv); + + return 0; +} + +static int ov7x20_set_window(struct i2c_client *c, struct ovcamchip_window *win) +{ + int ret, hwscale, vwscale; + + ret = ov7x20_mode_init(c, win); + if (ret < 0) + return ret; + + if (win->quarter) { + hwscale = 1; + vwscale = 0; + } else { + hwscale = 2; + vwscale = 1; + } + + ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); + ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); + ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); + ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); + + return 0; +} + +static int ov7x20_command(struct i2c_client *c, unsigned int cmd, void *arg) +{ + switch (cmd) { + case OVCAMCHIP_CMD_S_CTRL: + return ov7x20_set_v4l1_control(c, arg); + case OVCAMCHIP_CMD_G_CTRL: + return ov7x20_get_v4l1_control(c, arg); + case OVCAMCHIP_CMD_S_MODE: + return ov7x20_set_window(c, arg); + default: + DDEBUG(2, &c->dev, "command not supported: %d", cmd); + return -ENOIOCTLCMD; + } +} + +struct ovcamchip_ops ov7x20_ops = { + .init = ov7x20_init, + .free = ov7x20_free, + .command = ov7x20_command, +}; diff --git a/linux/drivers/media/video/ovcamchip/ovcamchip_core.c b/linux/drivers/media/video/ovcamchip/ovcamchip_core.c new file mode 100644 index 000000000..3fe9fa04c --- /dev/null +++ b/linux/drivers/media/video/ovcamchip/ovcamchip_core.c @@ -0,0 +1,443 @@ +/* Shared Code for OmniVision Camera Chip Drivers + * + * Copyright (c) 2004 Mark McClelland + * http://alpha.dyndns.org/ov511/ + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include "ovcamchip_priv.h" + +#define DRIVER_VERSION "v2.27 for Linux 2.6" +#define DRIVER_AUTHOR "Mark McClelland " +#define DRIVER_DESC "OV camera chip I2C driver" + +#define PINFO(fmt, args...) printk(KERN_INFO "ovcamchip: " fmt "\n" , ## args); +#define PERROR(fmt, args...) printk(KERN_ERR "ovcamchip: " fmt "\n" , ## args); + +#ifdef DEBUG +int ovcamchip_debug = 0; +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, + "Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=all"); +#endif + +/* By default, let bridge driver tell us if chip is monochrome. mono=0 + * will ignore that and always treat chips as color. mono=1 will force + * monochrome mode for all chips. */ +static int mono = -1; +module_param(mono, int, 0); +MODULE_PARM_DESC(mono, + "1=chips are monochrome (OVx1xx), 0=force color, -1=autodetect (default)"); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* Registers common to all chips, that are needed for detection */ +#define GENERIC_REG_ID_HIGH 0x1C /* manufacturer ID MSB */ +#define GENERIC_REG_ID_LOW 0x1D /* manufacturer ID LSB */ +#define GENERIC_REG_COM_I 0x29 /* misc ID bits */ + +extern struct ovcamchip_ops ov6x20_ops; +extern struct ovcamchip_ops ov6x30_ops; +extern struct ovcamchip_ops ov7x10_ops; +extern struct ovcamchip_ops ov7x20_ops; +extern struct ovcamchip_ops ov76be_ops; + +static char *chip_names[NUM_CC_TYPES] = { + [CC_UNKNOWN] = "Unknown chip", + [CC_OV76BE] = "OV76BE", + [CC_OV7610] = "OV7610", + [CC_OV7620] = "OV7620", + [CC_OV7620AE] = "OV7620AE", + [CC_OV6620] = "OV6620", + [CC_OV6630] = "OV6630", + [CC_OV6630AE] = "OV6630AE", + [CC_OV6630AF] = "OV6630AF", +}; + +/* Forward declarations */ +static struct i2c_driver driver; +static struct i2c_client client_template; + +/* ----------------------------------------------------------------------- */ + +int ov_write_regvals(struct i2c_client *c, struct ovcamchip_regvals *rvals) +{ + int rc; + + while (rvals->reg != 0xff) { + rc = ov_write(c, rvals->reg, rvals->val); + if (rc < 0) + return rc; + rvals++; + } + + return 0; +} + +/* Writes bits at positions specified by mask to an I2C reg. Bits that are in + * the same position as 1's in "mask" are cleared and set to "value". Bits + * that are in the same position as 0's in "mask" are preserved, regardless + * of their respective state in "value". + */ +int ov_write_mask(struct i2c_client *c, + unsigned char reg, + unsigned char value, + unsigned char mask) +{ + int rc; + unsigned char oldval, newval; + + if (mask == 0xff) { + newval = value; + } else { + rc = ov_read(c, reg, &oldval); + if (rc < 0) + return rc; + + oldval &= (~mask); /* Clear the masked bits */ + value &= mask; /* Enforce mask on value */ + newval = oldval | value; /* Set the desired bits */ + } + + return ov_write(c, reg, newval); +} + +/* ----------------------------------------------------------------------- */ + +/* Reset the chip and ensure that I2C is synchronized. Returns <0 if failure. + */ +static int init_camchip(struct i2c_client *c) +{ + int i, success; + unsigned char high, low; + + /* Reset the chip */ + ov_write(c, 0x12, 0x80); + + /* Wait for it to initialize */ + msleep(150); + + for (i = 0, success = 0; i < I2C_DETECT_RETRIES && !success; i++) { + if (ov_read(c, GENERIC_REG_ID_HIGH, &high) >= 0) { + if (ov_read(c, GENERIC_REG_ID_LOW, &low) >= 0) { + if (high == 0x7F && low == 0xA2) { + success = 1; + continue; + } + } + } + + /* Reset the chip */ + ov_write(c, 0x12, 0x80); + + /* Wait for it to initialize */ + msleep(150); + + /* Dummy read to sync I2C */ + ov_read(c, 0x00, &low); + } + + if (!success) + return -EIO; + + PDEBUG(1, "I2C synced in %d attempt(s)", i); + + return 0; +} + +/* This detects the OV7610, OV7620, or OV76BE chip. */ +static int ov7xx0_detect(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + int rc; + unsigned char val; + + PDEBUG(4, ""); + + /* Detect chip (sub)type */ + rc = ov_read(c, GENERIC_REG_COM_I, &val); + if (rc < 0) { + PERROR("Error detecting ov7xx0 type"); + return rc; + } + + if ((val & 3) == 3) { + PINFO("Camera chip is an OV7610"); + ov->subtype = CC_OV7610; + } else if ((val & 3) == 1) { + rc = ov_read(c, 0x15, &val); + if (rc < 0) { + PERROR("Error detecting ov7xx0 type"); + return rc; + } + + if (val & 1) { + PINFO("Camera chip is an OV7620AE"); + /* OV7620 is a close enough match for now. There are + * some definite differences though, so this should be + * fixed */ + ov->subtype = CC_OV7620; + } else { + PINFO("Camera chip is an OV76BE"); + ov->subtype = CC_OV76BE; + } + } else if ((val & 3) == 0) { + PINFO("Camera chip is an OV7620"); + ov->subtype = CC_OV7620; + } else { + PERROR("Unknown camera chip version: %d", val & 3); + return -ENOSYS; + } + + if (ov->subtype == CC_OV76BE) + ov->sops = &ov76be_ops; + else if (ov->subtype == CC_OV7620) + ov->sops = &ov7x20_ops; + else + ov->sops = &ov7x10_ops; + + return 0; +} + +/* This detects the OV6620, OV6630, OV6630AE, or OV6630AF chip. */ +static int ov6xx0_detect(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + int rc; + unsigned char val; + + PDEBUG(4, ""); + + /* Detect chip (sub)type */ + rc = ov_read(c, GENERIC_REG_COM_I, &val); + if (rc < 0) { + PERROR("Error detecting ov6xx0 type"); + return -1; + } + + if ((val & 3) == 0) { + ov->subtype = CC_OV6630; + PINFO("Camera chip is an OV6630"); + } else if ((val & 3) == 1) { + ov->subtype = CC_OV6620; + PINFO("Camera chip is an OV6620"); + } else if ((val & 3) == 2) { + ov->subtype = CC_OV6630; + PINFO("Camera chip is an OV6630AE"); + } else if ((val & 3) == 3) { + ov->subtype = CC_OV6630; + PINFO("Camera chip is an OV6630AF"); + } + + if (ov->subtype == CC_OV6620) + ov->sops = &ov6x20_ops; + else + ov->sops = &ov6x30_ops; + + return 0; +} + +static int ovcamchip_detect(struct i2c_client *c) +{ + /* Ideally we would just try a single register write and see if it NAKs. + * That isn't possible since the OV518 can't report I2C transaction + * failures. So, we have to try to initialize the chip (i.e. reset it + * and check the ID registers) to detect its presence. */ + + /* Test for 7xx0 */ + PDEBUG(3, "Testing for 0V7xx0"); + c->addr = OV7xx0_SID; + if (init_camchip(c) < 0) { + /* Test for 6xx0 */ + PDEBUG(3, "Testing for 0V6xx0"); + c->addr = OV6xx0_SID; + if (init_camchip(c) < 0) { + return -ENODEV; + } else { + if (ov6xx0_detect(c) < 0) { + PERROR("Failed to init OV6xx0"); + return -EIO; + } + } + } else { + if (ov7xx0_detect(c) < 0) { + PERROR("Failed to init OV7xx0"); + return -EIO; + } + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int ovcamchip_attach(struct i2c_adapter *adap) +{ + int rc = 0; + struct ovcamchip *ov; + struct i2c_client *c; + + /* I2C is not a PnP bus, so we can never be certain that we're talking + * to the right chip. To prevent damage to EEPROMS and such, only + * attach to adapters that are known to contain OV camera chips. */ + + switch (adap->id) { + case I2C_HW_SMBUS_OV511: + case I2C_HW_SMBUS_OV518: + case I2C_HW_SMBUS_OVFX2: + case I2C_HW_SMBUS_W9968CF: + PDEBUG(1, "Adapter ID 0x%06x accepted", adap->id); + break; + default: + PDEBUG(1, "Adapter ID 0x%06x rejected", adap->id); + return -ENODEV; + } + + c = kmalloc(sizeof *c, GFP_KERNEL); + if (!c) { + rc = -ENOMEM; + goto no_client; + } + memcpy(c, &client_template, sizeof *c); + c->adapter = adap; + strcpy(c->name, "OV????"); + + ov = kzalloc(sizeof *ov, GFP_KERNEL); + if (!ov) { + rc = -ENOMEM; + goto no_ov; + } + i2c_set_clientdata(c, ov); + + rc = ovcamchip_detect(c); + if (rc < 0) + goto error; + + strcpy(c->name, chip_names[ov->subtype]); + + PDEBUG(1, "Camera chip detection complete"); + + i2c_attach_client(c); + + return rc; +error: + kfree(ov); +no_ov: + kfree(c); +no_client: + PDEBUG(1, "returning %d", rc); + return rc; +} + +static int ovcamchip_detach(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + int rc; + + rc = ov->sops->free(c); + if (rc < 0) + return rc; + + i2c_detach_client(c); + + kfree(ov); + kfree(c); + return 0; +} + +static int ovcamchip_command(struct i2c_client *c, unsigned int cmd, void *arg) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + + if (!ov->initialized && + cmd != OVCAMCHIP_CMD_Q_SUBTYPE && + cmd != OVCAMCHIP_CMD_INITIALIZE) { + dev_err(&c->dev, "ERROR: Camera chip not initialized yet!\n"); + return -EPERM; + } + + switch (cmd) { + case OVCAMCHIP_CMD_Q_SUBTYPE: + { + *(int *)arg = ov->subtype; + return 0; + } + case OVCAMCHIP_CMD_INITIALIZE: + { + int rc; + + if (mono == -1) + ov->mono = *(int *)arg; + else + ov->mono = mono; + + if (ov->mono) { + if (ov->subtype != CC_OV7620) + dev_warn(&c->dev, "Warning: Monochrome not " + "implemented for this chip\n"); + else + dev_info(&c->dev, "Initializing chip as " + "monochrome\n"); + } + + rc = ov->sops->init(c); + if (rc < 0) + return rc; + + ov->initialized = 1; + return 0; + } + default: + return ov->sops->command(c, cmd, arg); + } +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver driver = { + .driver = { + .name = "ovcamchip", + }, + .id = I2C_DRIVERID_OVCAMCHIP, + .class = I2C_CLASS_CAM_DIGITAL, + .attach_adapter = ovcamchip_attach, + .detach_client = ovcamchip_detach, + .command = ovcamchip_command, +}; + +static struct i2c_client client_template = { + .name = "(unset)", + .driver = &driver, +}; + +static int __init ovcamchip_init(void) +{ +#ifdef DEBUG + ovcamchip_debug = debug; +#endif + + PINFO(DRIVER_VERSION " : " DRIVER_DESC); + return i2c_add_driver(&driver); +} + +static void __exit ovcamchip_exit(void) +{ + i2c_del_driver(&driver); +} + +module_init(ovcamchip_init); +module_exit(ovcamchip_exit); diff --git a/linux/drivers/media/video/ovcamchip/ovcamchip_priv.h b/linux/drivers/media/video/ovcamchip/ovcamchip_priv.h new file mode 100644 index 000000000..1231335a9 --- /dev/null +++ b/linux/drivers/media/video/ovcamchip/ovcamchip_priv.h @@ -0,0 +1,87 @@ +/* OmniVision* camera chip driver private definitions for core code and + * chip-specific code + * + * Copyright (c) 1999-2004 Mark McClelland + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. + * + * * OmniVision is a trademark of OmniVision Technologies, Inc. This driver + * is not sponsored or developed by them. + */ + +#ifndef __LINUX_OVCAMCHIP_PRIV_H +#define __LINUX_OVCAMCHIP_PRIV_H + +#include + +#ifdef DEBUG +extern int ovcamchip_debug; +#endif + +#define PDEBUG(level, fmt, args...) \ + if (ovcamchip_debug >= (level)) pr_debug("[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args) + +#define DDEBUG(level, dev, fmt, args...) \ + if (ovcamchip_debug >= (level)) dev_dbg(dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args) + +/* Number of times to retry chip detection. Increase this if you are getting + * "Failed to init camera chip" */ +#define I2C_DETECT_RETRIES 10 + +struct ovcamchip_regvals { + unsigned char reg; + unsigned char val; +}; + +struct ovcamchip_ops { + int (*init)(struct i2c_client *); + int (*free)(struct i2c_client *); + int (*command)(struct i2c_client *, unsigned int, void *); +}; + +struct ovcamchip { + struct ovcamchip_ops *sops; + void *spriv; /* Private data for OV7x10.c etc... */ + int subtype; /* = SEN_OV7610 etc... */ + int mono; /* Monochrome chip? (invalid until init) */ + int initialized; /* OVCAMCHIP_CMD_INITIALIZE was successful */ +}; + +/* --------------------------------- */ +/* I2C I/O */ +/* --------------------------------- */ + +static inline int ov_read(struct i2c_client *c, unsigned char reg, + unsigned char *value) +{ + int rc; + + rc = i2c_smbus_read_byte_data(c, reg); + *value = (unsigned char) rc; + return rc; +} + +static inline int ov_write(struct i2c_client *c, unsigned char reg, + unsigned char value ) +{ + return i2c_smbus_write_byte_data(c, reg, value); +} + +/* --------------------------------- */ +/* FUNCTION PROTOTYPES */ +/* --------------------------------- */ + +/* Functions in ovcamchip_core.c */ + +extern int ov_write_regvals(struct i2c_client *c, + struct ovcamchip_regvals *rvals); + +extern int ov_write_mask(struct i2c_client *c, unsigned char reg, + unsigned char value, unsigned char mask); + +#endif -- cgit v1.2.3 From 630e2390dde5eb76e89b9c4393f6a3bb96560d7b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Apr 2006 16:14:40 -0300 Subject: Fix test for V4L1_COMPAT config option From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/saa7134/saa7134-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/saa7134/saa7134-video.c b/linux/drivers/media/video/saa7134/saa7134-video.c index 152e8e8ab..9491fdaad 100644 --- a/linux/drivers/media/video/saa7134/saa7134-video.c +++ b/linux/drivers/media/video/saa7134/saa7134-video.c @@ -33,7 +33,7 @@ #include "saa7134.h" #include -#if CONFIG_VIDEO_V4L1_COMPAT +#ifdef CONFIG_VIDEO_V4L1_COMPAT /* Include V4L1 specific functions. Should be removed soon */ #include #endif -- cgit v1.2.3 From 468e72158ba85f7339895fbd7dc5f6e525357326 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Apr 2006 16:16:07 -0300 Subject: Added support for pwc at the tree From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/pwc/Kconfig | 28 + linux/drivers/media/video/pwc/Makefile | 20 + linux/drivers/media/video/pwc/philips.txt | 236 +++ linux/drivers/media/video/pwc/pwc-ctrl.c | 1541 +++++++++++++++++ linux/drivers/media/video/pwc/pwc-if.c | 2205 ++++++++++++++++++++++++ linux/drivers/media/video/pwc/pwc-ioctl.h | 292 ++++ linux/drivers/media/video/pwc/pwc-kiara.c | 318 ++++ linux/drivers/media/video/pwc/pwc-kiara.h | 45 + linux/drivers/media/video/pwc/pwc-misc.c | 140 ++ linux/drivers/media/video/pwc/pwc-nala.h | 66 + linux/drivers/media/video/pwc/pwc-timon.c | 316 ++++ linux/drivers/media/video/pwc/pwc-timon.h | 61 + linux/drivers/media/video/pwc/pwc-uncompress.c | 146 ++ linux/drivers/media/video/pwc/pwc-uncompress.h | 41 + linux/drivers/media/video/pwc/pwc.h | 272 +++ 15 files changed, 5727 insertions(+) create mode 100644 linux/drivers/media/video/pwc/Kconfig create mode 100644 linux/drivers/media/video/pwc/Makefile create mode 100644 linux/drivers/media/video/pwc/philips.txt create mode 100644 linux/drivers/media/video/pwc/pwc-ctrl.c create mode 100644 linux/drivers/media/video/pwc/pwc-if.c create mode 100644 linux/drivers/media/video/pwc/pwc-ioctl.h create mode 100644 linux/drivers/media/video/pwc/pwc-kiara.c create mode 100644 linux/drivers/media/video/pwc/pwc-kiara.h create mode 100644 linux/drivers/media/video/pwc/pwc-misc.c create mode 100644 linux/drivers/media/video/pwc/pwc-nala.h create mode 100644 linux/drivers/media/video/pwc/pwc-timon.c create mode 100644 linux/drivers/media/video/pwc/pwc-timon.h create mode 100644 linux/drivers/media/video/pwc/pwc-uncompress.c create mode 100644 linux/drivers/media/video/pwc/pwc-uncompress.h create mode 100644 linux/drivers/media/video/pwc/pwc.h (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pwc/Kconfig b/linux/drivers/media/video/pwc/Kconfig new file mode 100644 index 000000000..53cbc950f --- /dev/null +++ b/linux/drivers/media/video/pwc/Kconfig @@ -0,0 +1,28 @@ +config USB_PWC + tristate "USB Philips Cameras" + depends on USB && VIDEO_V4L1 + ---help--- + Say Y or M here if you want to use one of these Philips & OEM + webcams: + * Philips PCA645, PCA646 + * Philips PCVC675, PCVC680, PCVC690 + * Philips PCVC720/40, PCVC730, PCVC740, PCVC750 + * Askey VC010 + * Logitech QuickCam Pro 3000, 4000, 'Zoom', 'Notebook Pro' + and 'Orbit'/'Sphere' + * Samsung MPC-C10, MPC-C30 + * Creative Webcam 5, Pro Ex + * SOTEC Afina Eye + * Visionite VCS-UC300, VCS-UM100 + + The PCA635, PCVC665 and PCVC720/20 are not supported by this driver + and never will be, but the 665 and 720/20 are supported by other + drivers. + + See for more information and + installation instructions. + + The built-in microphone is enabled by selecting USB Audio support. + + To compile this driver as a module, choose M here: the + module will be called pwc. diff --git a/linux/drivers/media/video/pwc/Makefile b/linux/drivers/media/video/pwc/Makefile new file mode 100644 index 000000000..8326684f4 --- /dev/null +++ b/linux/drivers/media/video/pwc/Makefile @@ -0,0 +1,20 @@ +ifneq ($(KERNELRELEASE),) + +pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o pwc-timon.o pwc-kiara.o + +obj-$(CONFIG_USB_PWC) += pwc.o + +else + +KDIR := /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +default: + $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules + +endif + +clean: + rm -f *.[oas] .*.flags *.ko .*.cmd .*.d .*.tmp *.mod.c + rm -rf .tmp_versions + diff --git a/linux/drivers/media/video/pwc/philips.txt b/linux/drivers/media/video/pwc/philips.txt new file mode 100644 index 000000000..11f751a6b --- /dev/null +++ b/linux/drivers/media/video/pwc/philips.txt @@ -0,0 +1,236 @@ +This file contains some additional information for the Philips and OEM webcams. +E-mail: webcam@smcc.demon.nl Last updated: 2004-01-19 +Site: http://www.smcc.demon.nl/webcam/ + +As of this moment, the following cameras are supported: + * Philips PCA645 + * Philips PCA646 + * Philips PCVC675 + * Philips PCVC680 + * Philips PCVC690 + * Philips PCVC720/40 + * Philips PCVC730 + * Philips PCVC740 + * Philips PCVC750 + * Askey VC010 + * Creative Labs Webcam 5 + * Creative Labs Webcam Pro Ex + * Logitech QuickCam 3000 Pro + * Logitech QuickCam 4000 Pro + * Logitech QuickCam Notebook Pro + * Logitech QuickCam Zoom + * Logitech QuickCam Orbit + * Logitech QuickCam Sphere + * Samsung MPC-C10 + * Samsung MPC-C30 + * Sotec Afina Eye + * AME CU-001 + * Visionite VCS-UM100 + * Visionite VCS-UC300 + +The main webpage for the Philips driver is at the address above. It contains +a lot of extra information, a FAQ, and the binary plugin 'PWCX'. This plugin +contains decompression routines that allow you to use higher image sizes and +framerates; in addition the webcam uses less bandwidth on the USB bus (handy +if you want to run more than 1 camera simultaneously). These routines fall +under a NDA, and may therefor not be distributed as source; however, its use +is completely optional. + +You can build this code either into your kernel, or as a module. I recommend +the latter, since it makes troubleshooting a lot easier. The built-in +microphone is supported through the USB Audio class. + +When you load the module you can set some default settings for the +camera; some programs depend on a particular image-size or -format and +don't know how to set it properly in the driver. The options are: + +size + Can be one of 'sqcif', 'qsif', 'qcif', 'sif', 'cif' or + 'vga', for an image size of resp. 128x96, 160x120, 176x144, + 320x240, 352x288 and 640x480 (of course, only for those cameras that + support these resolutions). + +fps + Specifies the desired framerate. Is an integer in the range of 4-30. + +fbufs + This paramter specifies the number of internal buffers to use for storing + frames from the cam. This will help if the process that reads images from + the cam is a bit slow or momentarely busy. However, on slow machines it + only introduces lag, so choose carefully. The default is 3, which is + reasonable. You can set it between 2 and 5. + +mbufs + This is an integer between 1 and 10. It will tell the module the number of + buffers to reserve for mmap(), VIDIOCCGMBUF, VIDIOCMCAPTURE and friends. + The default is 2, which is adequate for most applications (double + buffering). + + Should you experience a lot of 'Dumping frame...' messages during + grabbing with a tool that uses mmap(), you might want to increase if. + However, it doesn't really buffer images, it just gives you a bit more + slack when your program is behind. But you need a multi-threaded or + forked program to really take advantage of these buffers. + + The absolute maximum is 10, but don't set it too high! Every buffer takes + up 460 KB of RAM, so unless you have a lot of memory setting this to + something more than 4 is an absolute waste. This memory is only + allocated during open(), so nothing is wasted when the camera is not in + use. + +power_save + When power_save is enabled (set to 1), the module will try to shut down + the cam on close() and re-activate on open(). This will save power and + turn off the LED. Not all cameras support this though (the 645 and 646 + don't have power saving at all), and some models don't work either (they + will shut down, but never wake up). Consider this experimental. By + default this option is disabled. + +compression (only useful with the plugin) + With this option you can control the compression factor that the camera + uses to squeeze the image through the USB bus. You can set the + parameter between 0 and 3: + 0 = prefer uncompressed images; if the requested mode is not available + in an uncompressed format, the driver will silently switch to low + compression. + 1 = low compression. + 2 = medium compression. + 3 = high compression. + + High compression takes less bandwidth of course, but it could also + introduce some unwanted artefacts. The default is 2, medium compression. + See the FAQ on the website for an overview of which modes require + compression. + + The compression parameter does not apply to the 645 and 646 cameras + and OEM models derived from those (only a few). Most cams honour this + parameter. + +leds + This settings takes 2 integers, that define the on/off time for the LED + (in milliseconds). One of the interesting things that you can do with + this is let the LED blink while the camera is in use. This: + + leds=500,500 + + will blink the LED once every second. But with: + + leds=0,0 + + the LED never goes on, making it suitable for silent surveillance. + + By default the camera's LED is on solid while in use, and turned off + when the camera is not used anymore. + + This parameter works only with the ToUCam range of cameras (720, 730, 740, + 750) and OEMs. For other cameras this command is silently ignored, and + the LED cannot be controlled. + + Finally: this parameters does not take effect UNTIL the first time you + open the camera device. Until then, the LED remains on. + +dev_hint + A long standing problem with USB devices is their dynamic nature: you + never know what device a camera gets assigned; it depends on module load + order, the hub configuration, the order in which devices are plugged in, + and the phase of the moon (i.e. it can be random). With this option you + can give the driver a hint as to what video device node (/dev/videoX) it + should use with a specific camera. This is also handy if you have two + cameras of the same model. + + A camera is specified by its type (the number from the camera model, + like PCA645, PCVC750VC, etc) and optionally the serial number (visible + in /proc/bus/usb/devices). A hint consists of a string with the following + format: + + [type[.serialnumber]:]node + + The square brackets mean that both the type and the serialnumber are + optional, but a serialnumber cannot be specified without a type (which + would be rather pointless). The serialnumber is separated from the type + by a '.'; the node number by a ':'. + + This somewhat cryptic syntax is best explained by a few examples: + + dev_hint=3,5 The first detected cam gets assigned + /dev/video3, the second /dev/video5. Any + other cameras will get the first free + available slot (see below). + + dev_hint=645:1,680:2 The PCA645 camera will get /dev/video1, + and a PCVC680 /dev/video2. + + dev_hint=645.0123:3,645.4567:0 The PCA645 camera with serialnumber + 0123 goes to /dev/video3, the same + camera model with the 4567 serial + gets /dev/video0. + + dev_hint=750:1,4,5,6 The PCVC750 camera will get /dev/video1, the + next 3 Philips cams will use /dev/video4 + through /dev/video6. + + Some points worth knowing: + - Serialnumbers are case sensitive and must be written full, including + leading zeroes (it's treated as a string). + - If a device node is already occupied, registration will fail and + the webcam is not available. + - You can have up to 64 video devices; be sure to make enough device + nodes in /dev if you want to spread the numbers (this does not apply + to devfs). After /dev/video9 comes /dev/video10 (not /dev/videoA). + - If a camera does not match any dev_hint, it will simply get assigned + the first available device node, just as it used to be. + +trace + In order to better detect problems, it is now possible to turn on a + 'trace' of some of the calls the module makes; it logs all items in your + kernel log at debug level. + + The trace variable is a bitmask; each bit represents a certain feature. + If you want to trace something, look up the bit value(s) in the table + below, add the values together and supply that to the trace variable. + + Value Value Description Default + (dec) (hex) + 1 0x1 Module initialization; this will log messages On + while loading and unloading the module + + 2 0x2 probe() and disconnect() traces On + + 4 0x4 Trace open() and close() calls Off + + 8 0x8 read(), mmap() and associated ioctl() calls Off + + 16 0x10 Memory allocation of buffers, etc. Off + + 32 0x20 Showing underflow, overflow and Dumping frame On + messages + + 64 0x40 Show viewport and image sizes Off + + 128 0x80 PWCX debugging Off + + For example, to trace the open() & read() fuctions, sum 8 + 4 = 12, + so you would supply trace=12 during insmod or modprobe. If + you want to turn the initialization and probing tracing off, set trace=0. + The default value for trace is 35 (0x23). + + + +Example: + + # modprobe pwc size=cif fps=15 power_save=1 + +The fbufs, mbufs and trace parameters are global and apply to all connected +cameras. Each camera has its own set of buffers. + +size and fps only specify defaults when you open() the device; this is to +accommodate some tools that don't set the size. You can change these +settings after open() with the Video4Linux ioctl() calls. The default of +defaults is QCIF size at 10 fps. + +The compression parameter is semiglobal; it sets the initial compression +preference for all camera's, but this parameter can be set per camera with +the VIDIOCPWCSCQUAL ioctl() call. + +All parameters are optional. + diff --git a/linux/drivers/media/video/pwc/pwc-ctrl.c b/linux/drivers/media/video/pwc/pwc-ctrl.c new file mode 100644 index 000000000..4ba549bfa --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-ctrl.c @@ -0,0 +1,1541 @@ +/* Driver for Philips webcam + Functions that send various control messages to the webcam, including + video modes. + (C) 1999-2003 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + Changes + 2001/08/03 Alvarado Added methods for changing white balance and + red/green gains + */ + +/* Control functions for the cam; brightness, contrast, video mode, etc. */ + +#ifdef __KERNEL__ +#include +#endif +#include + +#include "pwc.h" +#include "pwc-ioctl.h" +#include "pwc-uncompress.h" +#include "pwc-kiara.h" +#include "pwc-timon.h" + +/* Request types: video */ +#define SET_LUM_CTL 0x01 +#define GET_LUM_CTL 0x02 +#define SET_CHROM_CTL 0x03 +#define GET_CHROM_CTL 0x04 +#define SET_STATUS_CTL 0x05 +#define GET_STATUS_CTL 0x06 +#define SET_EP_STREAM_CTL 0x07 +#define GET_EP_STREAM_CTL 0x08 +#define SET_MPT_CTL 0x0D +#define GET_MPT_CTL 0x0E + +/* Selectors for the Luminance controls [GS]ET_LUM_CTL */ +#define AGC_MODE_FORMATTER 0x2000 +#define PRESET_AGC_FORMATTER 0x2100 +#define SHUTTER_MODE_FORMATTER 0x2200 +#define PRESET_SHUTTER_FORMATTER 0x2300 +#define PRESET_CONTOUR_FORMATTER 0x2400 +#define AUTO_CONTOUR_FORMATTER 0x2500 +#define BACK_LIGHT_COMPENSATION_FORMATTER 0x2600 +#define CONTRAST_FORMATTER 0x2700 +#define DYNAMIC_NOISE_CONTROL_FORMATTER 0x2800 +#define FLICKERLESS_MODE_FORMATTER 0x2900 +#define AE_CONTROL_SPEED 0x2A00 +#define BRIGHTNESS_FORMATTER 0x2B00 +#define GAMMA_FORMATTER 0x2C00 + +/* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */ +#define WB_MODE_FORMATTER 0x1000 +#define AWB_CONTROL_SPEED_FORMATTER 0x1100 +#define AWB_CONTROL_DELAY_FORMATTER 0x1200 +#define PRESET_MANUAL_RED_GAIN_FORMATTER 0x1300 +#define PRESET_MANUAL_BLUE_GAIN_FORMATTER 0x1400 +#define COLOUR_MODE_FORMATTER 0x1500 +#define SATURATION_MODE_FORMATTER1 0x1600 +#define SATURATION_MODE_FORMATTER2 0x1700 + +/* Selectors for the Status controls [GS]ET_STATUS_CTL */ +#define SAVE_USER_DEFAULTS_FORMATTER 0x0200 +#define RESTORE_USER_DEFAULTS_FORMATTER 0x0300 +#define RESTORE_FACTORY_DEFAULTS_FORMATTER 0x0400 +#define READ_AGC_FORMATTER 0x0500 +#define READ_SHUTTER_FORMATTER 0x0600 +#define READ_RED_GAIN_FORMATTER 0x0700 +#define READ_BLUE_GAIN_FORMATTER 0x0800 +#define SENSOR_TYPE_FORMATTER1 0x0C00 +#define READ_RAW_Y_MEAN_FORMATTER 0x3100 +#define SET_POWER_SAVE_MODE_FORMATTER 0x3200 +#define MIRROR_IMAGE_FORMATTER 0x3300 +#define LED_FORMATTER 0x3400 +#define SENSOR_TYPE_FORMATTER2 0x3700 + +/* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */ +#define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100 + +/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */ +#define PT_RELATIVE_CONTROL_FORMATTER 0x01 +#define PT_RESET_CONTROL_FORMATTER 0x02 +#define PT_STATUS_FORMATTER 0x03 + +static const char *size2name[PSZ_MAX] = +{ + "subQCIF", + "QSIF", + "QCIF", + "SIF", + "CIF", + "VGA", +}; + +/********/ + +/* Entries for the Nala (645/646) camera; the Nala doesn't have compression + preferences, so you either get compressed or non-compressed streams. + + An alternate value of 0 means this mode is not available at all. + */ + +struct Nala_table_entry { + char alternate; /* USB alternate setting */ + int compressed; /* Compressed yes/no */ + + unsigned char mode[3]; /* precomputed mode table */ +}; + +static struct Nala_table_entry Nala_table[PSZ_MAX][8] = +{ +#include "pwc-nala.h" +}; + + +/****************************************************************************/ + + +#define SendControlMsg(request, value, buflen) \ + usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), \ + request, \ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \ + value, \ + pdev->vcinterface, \ + &buf, buflen, 500) + +#define RecvControlMsg(request, value, buflen) \ + usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), \ + request, \ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \ + value, \ + pdev->vcinterface, \ + &buf, buflen, 500) + + +#if PWC_DEBUG +void pwc_hexdump(void *p, int len) +{ + int i; + unsigned char *s; + char buf[100], *d; + + s = (unsigned char *)p; + d = buf; + *d = '\0'; + Debug("Doing hexdump @ %p, %d bytes.\n", p, len); + for (i = 0; i < len; i++) { + d += sprintf(d, "%02X ", *s++); + if ((i & 0xF) == 0xF) { + Debug("%s\n", buf); + d = buf; + *d = '\0'; + } + } + if ((i & 0xF) != 0) + Debug("%s\n", buf); +} +#endif + +static inline int send_video_command(struct usb_device *udev, int index, void *buf, int buflen) +{ + return usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + SET_EP_STREAM_CTL, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + VIDEO_OUTPUT_CONTROL_FORMATTER, + index, + buf, buflen, 1000); +} + + + +static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames) +{ + unsigned char buf[3]; + int ret, fps; + struct Nala_table_entry *pEntry; + int frames2frames[31] = + { /* closest match of framerate */ + 0, 0, 0, 0, 4, /* 0-4 */ + 5, 5, 7, 7, 10, /* 5-9 */ + 10, 10, 12, 12, 15, /* 10-14 */ + 15, 15, 15, 20, 20, /* 15-19 */ + 20, 20, 20, 24, 24, /* 20-24 */ + 24, 24, 24, 24, 24, /* 25-29 */ + 24 /* 30 */ + }; + int frames2table[31] = + { 0, 0, 0, 0, 0, /* 0-4 */ + 1, 1, 1, 2, 2, /* 5-9 */ + 3, 3, 4, 4, 4, /* 10-14 */ + 5, 5, 5, 5, 5, /* 15-19 */ + 6, 6, 6, 6, 7, /* 20-24 */ + 7, 7, 7, 7, 7, /* 25-29 */ + 7 /* 30 */ + }; + + if (size < 0 || size > PSZ_CIF || frames < 4 || frames > 25) + return -EINVAL; + frames = frames2frames[frames]; + fps = frames2table[frames]; + pEntry = &Nala_table[size][fps]; + if (pEntry->alternate == 0) + return -EINVAL; + + if (pEntry->compressed) + return -ENOENT; /* Not supported. */ + + memcpy(buf, pEntry->mode, 3); + ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 3); + if (ret < 0) { + Debug("Failed to send video command... %d\n", ret); + return ret; + } + if (pEntry->compressed && pdev->vpalette != VIDEO_PALETTE_RAW) + { + switch(pdev->type) { + case 645: + case 646: +/* pwc_dec1_init(pdev->type, pdev->release, buf, pdev->decompress_data); */ + break; + + case 675: + case 680: + case 690: + case 720: + case 730: + case 740: + case 750: +/* pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); */ + break; + } + } + + pdev->cmd_len = 3; + memcpy(pdev->cmd_buf, buf, 3); + + /* Set various parameters */ + pdev->vframes = frames; + pdev->vsize = size; + pdev->valternate = pEntry->alternate; + pdev->image = pwc_image_sizes[size]; + pdev->frame_size = (pdev->image.x * pdev->image.y * 3) / 2; + if (pEntry->compressed) { + if (pdev->release < 5) { /* 4 fold compression */ + pdev->vbandlength = 528; + pdev->frame_size /= 4; + } + else { + pdev->vbandlength = 704; + pdev->frame_size /= 3; + } + } + else + pdev->vbandlength = 0; + return 0; +} + + +static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) +{ + unsigned char buf[13]; + const struct Timon_table_entry *pChoose; + int ret, fps; + + if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) + return -EINVAL; + if (size == PSZ_VGA && frames > 15) + return -EINVAL; + fps = (frames / 5) - 1; + + /* Find a supported framerate with progressively higher compression ratios + if the preferred ratio is not available. + */ + pChoose = NULL; + while (compression <= 3) { + pChoose = &Timon_table[size][fps][compression]; + if (pChoose->alternate != 0) + break; + compression++; + } + if (pChoose == NULL || pChoose->alternate == 0) + return -ENOENT; /* Not supported. */ + + memcpy(buf, pChoose->mode, 13); + if (snapshot) + buf[0] |= 0x80; + ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 13); + if (ret < 0) + return ret; + +/* if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) + pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); */ + + pdev->cmd_len = 13; + memcpy(pdev->cmd_buf, buf, 13); + + /* Set various parameters */ + pdev->vframes = frames; + pdev->vsize = size; + pdev->vsnapshot = snapshot; + pdev->valternate = pChoose->alternate; + pdev->image = pwc_image_sizes[size]; + pdev->vbandlength = pChoose->bandlength; + if (pChoose->bandlength > 0) + pdev->frame_size = (pChoose->bandlength * pdev->image.y) / 4; + else + pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; + return 0; +} + + +static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) +{ + const struct Kiara_table_entry *pChoose = NULL; + int fps, ret; + unsigned char buf[12]; + struct Kiara_table_entry RawEntry = {6, 773, 1272, {0xAD, 0xF4, 0x10, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}}; + + if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) + return -EINVAL; + if (size == PSZ_VGA && frames > 15) + return -EINVAL; + fps = (frames / 5) - 1; + + /* special case: VGA @ 5 fps and snapshot is raw bayer mode */ + if (size == PSZ_VGA && frames == 5 && snapshot) + { + /* Only available in case the raw palette is selected or + we have the decompressor available. This mode is + only available in compressed form + */ + if (pdev->vpalette == VIDEO_PALETTE_RAW) + { + Info("Choosing VGA/5 BAYER mode (%d).\n", pdev->vpalette); + pChoose = &RawEntry; + } + else + { + Info("VGA/5 BAYER mode _must_ have a decompressor available, or use RAW palette.\n"); + } + } + else + { + /* Find a supported framerate with progressively higher compression ratios + if the preferred ratio is not available. + Skip this step when using RAW modes. + */ + while (compression <= 3) { + pChoose = &Kiara_table[size][fps][compression]; + if (pChoose->alternate != 0) + break; + compression++; + } + } + if (pChoose == NULL || pChoose->alternate == 0) + return -ENOENT; /* Not supported. */ + + Debug("Using alternate setting %d.\n", pChoose->alternate); + + /* usb_control_msg won't take staticly allocated arrays as argument?? */ + memcpy(buf, pChoose->mode, 12); + if (snapshot) + buf[0] |= 0x80; + + /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */ + ret = send_video_command(pdev->udev, 4 /* pdev->vendpoint */, buf, 12); + if (ret < 0) + return ret; + +/* if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) + pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); */ + + pdev->cmd_len = 12; + memcpy(pdev->cmd_buf, buf, 12); + /* All set and go */ + pdev->vframes = frames; + pdev->vsize = size; + pdev->vsnapshot = snapshot; + pdev->valternate = pChoose->alternate; + pdev->image = pwc_image_sizes[size]; + pdev->vbandlength = pChoose->bandlength; + if (pdev->vbandlength > 0) + pdev->frame_size = (pdev->vbandlength * pdev->image.y) / 4; + else + pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; + return 0; +} + + + +static void pwc_set_image_buffer_size(struct pwc_device *pdev) +{ + int i, factor = 0, filler = 0; + + /* for PALETTE_YUV420P */ + switch(pdev->vpalette) + { + case VIDEO_PALETTE_YUV420P: + factor = 6; + filler = 128; + break; + case VIDEO_PALETTE_RAW: + factor = 6; /* can be uncompressed YUV420P */ + filler = 0; + break; + } + + /* Set sizes in bytes */ + pdev->image.size = pdev->image.x * pdev->image.y * factor / 4; + pdev->view.size = pdev->view.x * pdev->view.y * factor / 4; + + /* Align offset, or you'll get some very weird results in + YUV420 mode... x must be multiple of 4 (to get the Y's in + place), and y even (or you'll mixup U & V). This is less of a + problem for YUV420P. + */ + pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC; + pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE; + + /* Fill buffers with gray or black */ + for (i = 0; i < MAX_IMAGES; i++) { + if (pdev->image_ptr[i] != NULL) + memset(pdev->image_ptr[i], filler, pdev->view.size); + } +} + + + +/** + @pdev: device structure + @width: viewport width + @height: viewport height + @frame: framerate, in fps + @compression: preferred compression ratio + @snapshot: snapshot mode or streaming + */ +int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot) +{ + int ret, size; + + Trace(TRACE_FLOW, "set_video_mode(%dx%d @ %d, palette %d).\n", width, height, frames, pdev->vpalette); + size = pwc_decode_size(pdev, width, height); + if (size < 0) { + Debug("Could not find suitable size.\n"); + return -ERANGE; + } + Debug("decode_size = %d.\n", size); + + ret = -EINVAL; + switch(pdev->type) { + case 645: + case 646: + ret = set_video_mode_Nala(pdev, size, frames); + break; + + case 675: + case 680: + case 690: + ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot); + break; + + case 720: + case 730: + case 740: + case 750: + ret = set_video_mode_Kiara(pdev, size, frames, compression, snapshot); + break; + } + if (ret < 0) { + if (ret == -ENOENT) + Info("Video mode %s@%d fps is only supported with the decompressor module (pwcx).\n", size2name[size], frames); + else { + Err("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); + } + return ret; + } + pdev->view.x = width; + pdev->view.y = height; + pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; + pwc_set_image_buffer_size(pdev); + Trace(TRACE_SIZE, "Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y); + return 0; +} + + +/* BRIGHTNESS */ + +int pwc_get_brightness(struct pwc_device *pdev) +{ + char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, BRIGHTNESS_FORMATTER, 1); + if (ret < 0) + return ret; + return buf << 9; +} + +int pwc_set_brightness(struct pwc_device *pdev, int value) +{ + char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + buf = (value >> 9) & 0x7f; + return SendControlMsg(SET_LUM_CTL, BRIGHTNESS_FORMATTER, 1); +} + +/* CONTRAST */ + +int pwc_get_contrast(struct pwc_device *pdev) +{ + char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, CONTRAST_FORMATTER, 1); + if (ret < 0) + return ret; + return buf << 10; +} + +int pwc_set_contrast(struct pwc_device *pdev, int value) +{ + char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + buf = (value >> 10) & 0x3f; + return SendControlMsg(SET_LUM_CTL, CONTRAST_FORMATTER, 1); +} + +/* GAMMA */ + +int pwc_get_gamma(struct pwc_device *pdev) +{ + char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, GAMMA_FORMATTER, 1); + if (ret < 0) + return ret; + return buf << 11; +} + +int pwc_set_gamma(struct pwc_device *pdev, int value) +{ + char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + buf = (value >> 11) & 0x1f; + return SendControlMsg(SET_LUM_CTL, GAMMA_FORMATTER, 1); +} + + +/* SATURATION */ + +int pwc_get_saturation(struct pwc_device *pdev) +{ + char buf; + int ret; + + if (pdev->type < 675) + return -1; + ret = RecvControlMsg(GET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1); + if (ret < 0) + return ret; + return 32768 + buf * 327; +} + +int pwc_set_saturation(struct pwc_device *pdev, int value) +{ + char buf; + + if (pdev->type < 675) + return -EINVAL; + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + /* saturation ranges from -100 to +100 */ + buf = (value - 32768) / 327; + return SendControlMsg(SET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1); +} + +/* AGC */ + +static inline int pwc_set_agc(struct pwc_device *pdev, int mode, int value) +{ + char buf; + int ret; + + if (mode) + buf = 0x0; /* auto */ + else + buf = 0xff; /* fixed */ + + ret = SendControlMsg(SET_LUM_CTL, AGC_MODE_FORMATTER, 1); + + if (!mode && ret >= 0) { + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + buf = (value >> 10) & 0x3F; + ret = SendControlMsg(SET_LUM_CTL, PRESET_AGC_FORMATTER, 1); + } + if (ret < 0) + return ret; + return 0; +} + +static inline int pwc_get_agc(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, AGC_MODE_FORMATTER, 1); + if (ret < 0) + return ret; + + if (buf != 0) { /* fixed */ + ret = RecvControlMsg(GET_LUM_CTL, PRESET_AGC_FORMATTER, 1); + if (ret < 0) + return ret; + if (buf > 0x3F) + buf = 0x3F; + *value = (buf << 10); + } + else { /* auto */ + ret = RecvControlMsg(GET_STATUS_CTL, READ_AGC_FORMATTER, 1); + if (ret < 0) + return ret; + /* Gah... this value ranges from 0x00 ... 0x9F */ + if (buf > 0x9F) + buf = 0x9F; + *value = -(48 + buf * 409); + } + + return 0; +} + +static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value) +{ + char buf[2]; + int speed, ret; + + + if (mode) + buf[0] = 0x0; /* auto */ + else + buf[0] = 0xff; /* fixed */ + + ret = SendControlMsg(SET_LUM_CTL, SHUTTER_MODE_FORMATTER, 1); + + if (!mode && ret >= 0) { + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + switch(pdev->type) { + case 675: + case 680: + case 690: + /* speed ranges from 0x0 to 0x290 (656) */ + speed = (value / 100); + buf[1] = speed >> 8; + buf[0] = speed & 0xff; + break; + case 720: + case 730: + case 740: + case 750: + /* speed seems to range from 0x0 to 0xff */ + buf[1] = 0; + buf[0] = value >> 8; + break; + } + + ret = SendControlMsg(SET_LUM_CTL, PRESET_SHUTTER_FORMATTER, 2); + } + return ret; +} + + +/* POWER */ + +int pwc_camera_power(struct pwc_device *pdev, int power) +{ + char buf; + + if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6)) + return 0; /* Not supported by Nala or Timon < release 6 */ + + if (power) + buf = 0x00; /* active */ + else + buf = 0xFF; /* power save */ + return SendControlMsg(SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER, 1); +} + + + +/* private calls */ + +static inline int pwc_restore_user(struct pwc_device *pdev) +{ + char buf; /* dummy */ + return SendControlMsg(SET_STATUS_CTL, RESTORE_USER_DEFAULTS_FORMATTER, 0); +} + +static inline int pwc_save_user(struct pwc_device *pdev) +{ + char buf; /* dummy */ + return SendControlMsg(SET_STATUS_CTL, SAVE_USER_DEFAULTS_FORMATTER, 0); +} + +static inline int pwc_restore_factory(struct pwc_device *pdev) +{ + char buf; /* dummy */ + return SendControlMsg(SET_STATUS_CTL, RESTORE_FACTORY_DEFAULTS_FORMATTER, 0); +} + + /* ************************************************* */ + /* Patch by Alvarado: (not in the original version */ + + /* + * the camera recognizes modes from 0 to 4: + * + * 00: indoor (incandescant lighting) + * 01: outdoor (sunlight) + * 02: fluorescent lighting + * 03: manual + * 04: auto + */ +static inline int pwc_set_awb(struct pwc_device *pdev, int mode) +{ + char buf; + int ret; + + if (mode < 0) + mode = 0; + + if (mode > 4) + mode = 4; + + buf = mode & 0x07; /* just the lowest three bits */ + + ret = SendControlMsg(SET_CHROM_CTL, WB_MODE_FORMATTER, 1); + + if (ret < 0) + return ret; + return 0; +} + +static inline int pwc_get_awb(struct pwc_device *pdev) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, WB_MODE_FORMATTER, 1); + + if (ret < 0) + return ret; + return buf; +} + +static inline int pwc_set_red_gain(struct pwc_device *pdev, int value) +{ + unsigned char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + /* only the msb is considered */ + buf = value >> 8; + return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1); +} + +static inline int pwc_get_red_gain(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 8; + return 0; +} + + +static inline int pwc_set_blue_gain(struct pwc_device *pdev, int value) +{ + unsigned char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + /* only the msb is considered */ + buf = value >> 8; + return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1); +} + +static inline int pwc_get_blue_gain(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 8; + return 0; +} + + +/* The following two functions are different, since they only read the + internal red/blue gains, which may be different from the manual + gains set or read above. + */ +static inline int pwc_read_red_gain(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_STATUS_CTL, READ_RED_GAIN_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 8; + return 0; +} + +static inline int pwc_read_blue_gain(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_STATUS_CTL, READ_BLUE_GAIN_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 8; + return 0; +} + + +static inline int pwc_set_wb_speed(struct pwc_device *pdev, int speed) +{ + unsigned char buf; + + /* useful range is 0x01..0x20 */ + buf = speed / 0x7f0; + return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1); +} + +static inline int pwc_get_wb_speed(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf * 0x7f0; + return 0; +} + + +static inline int pwc_set_wb_delay(struct pwc_device *pdev, int delay) +{ + unsigned char buf; + + /* useful range is 0x01..0x3F */ + buf = (delay >> 10); + return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1); +} + +static inline int pwc_get_wb_delay(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 10; + return 0; +} + + +int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) +{ + unsigned char buf[2]; + + if (pdev->type < 730) + return 0; + on_value /= 100; + off_value /= 100; + if (on_value < 0) + on_value = 0; + if (on_value > 0xff) + on_value = 0xff; + if (off_value < 0) + off_value = 0; + if (off_value > 0xff) + off_value = 0xff; + + buf[0] = on_value; + buf[1] = off_value; + + return SendControlMsg(SET_STATUS_CTL, LED_FORMATTER, 2); +} + +static int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) +{ + unsigned char buf[2]; + int ret; + + if (pdev->type < 730) { + *on_value = -1; + *off_value = -1; + return 0; + } + + ret = RecvControlMsg(GET_STATUS_CTL, LED_FORMATTER, 2); + if (ret < 0) + return ret; + *on_value = buf[0] * 100; + *off_value = buf[1] * 100; + return 0; +} + +static inline int pwc_set_contour(struct pwc_device *pdev, int contour) +{ + unsigned char buf; + int ret; + + if (contour < 0) + buf = 0xff; /* auto contour on */ + else + buf = 0x0; /* auto contour off */ + ret = SendControlMsg(SET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1); + if (ret < 0) + return ret; + + if (contour < 0) + return 0; + if (contour > 0xffff) + contour = 0xffff; + + buf = (contour >> 10); /* contour preset is [0..3f] */ + ret = SendControlMsg(SET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1); + if (ret < 0) + return ret; + return 0; +} + +static inline int pwc_get_contour(struct pwc_device *pdev, int *contour) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1); + if (ret < 0) + return ret; + + if (buf == 0) { + /* auto mode off, query current preset value */ + ret = RecvControlMsg(GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1); + if (ret < 0) + return ret; + *contour = buf << 10; + } + else + *contour = -1; + return 0; +} + + +static inline int pwc_set_backlight(struct pwc_device *pdev, int backlight) +{ + unsigned char buf; + + if (backlight) + buf = 0xff; + else + buf = 0x0; + return SendControlMsg(SET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); +} + +static inline int pwc_get_backlight(struct pwc_device *pdev, int *backlight) +{ + int ret; + unsigned char buf; + + ret = RecvControlMsg(GET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); + if (ret < 0) + return ret; + *backlight = buf; + return 0; +} + + +static inline int pwc_set_flicker(struct pwc_device *pdev, int flicker) +{ + unsigned char buf; + + if (flicker) + buf = 0xff; + else + buf = 0x0; + return SendControlMsg(SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); +} + +static inline int pwc_get_flicker(struct pwc_device *pdev, int *flicker) +{ + int ret; + unsigned char buf; + + ret = RecvControlMsg(GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); + if (ret < 0) + return ret; + *flicker = buf; + return 0; +} + + +static inline int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise) +{ + unsigned char buf; + + if (noise < 0) + noise = 0; + if (noise > 3) + noise = 3; + buf = noise; + return SendControlMsg(SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); +} + +static inline int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise) +{ + int ret; + unsigned char buf; + + ret = RecvControlMsg(GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); + if (ret < 0) + return ret; + *noise = buf; + return 0; +} + +static int pwc_mpt_reset(struct pwc_device *pdev, int flags) +{ + unsigned char buf; + + buf = flags & 0x03; // only lower two bits are currently used + return SendControlMsg(SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, 1); +} + +static inline int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) +{ + unsigned char buf[4]; + + /* set new relative angle; angles are expressed in degrees * 100, + but cam as .5 degree resolution, hence divide by 200. Also + the angle must be multiplied by 64 before it's send to + the cam (??) + */ + pan = 64 * pan / 100; + tilt = -64 * tilt / 100; /* positive tilt is down, which is not what the user would expect */ + buf[0] = pan & 0xFF; + buf[1] = (pan >> 8) & 0xFF; + buf[2] = tilt & 0xFF; + buf[3] = (tilt >> 8) & 0xFF; + return SendControlMsg(SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, 4); +} + +static inline int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status) +{ + int ret; + unsigned char buf[5]; + + ret = RecvControlMsg(GET_MPT_CTL, PT_STATUS_FORMATTER, 5); + if (ret < 0) + return ret; + status->status = buf[0] & 0x7; // 3 bits are used for reporting + status->time_pan = (buf[1] << 8) + buf[2]; + status->time_tilt = (buf[3] << 8) + buf[4]; + return 0; +} + + +int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) +{ + unsigned char buf; + int ret = -1, request; + + if (pdev->type < 675) + request = SENSOR_TYPE_FORMATTER1; + else if (pdev->type < 730) + return -1; /* The Vesta series doesn't have this call */ + else + request = SENSOR_TYPE_FORMATTER2; + + ret = RecvControlMsg(GET_STATUS_CTL, request, 1); + if (ret < 0) + return ret; + if (pdev->type < 675) + *sensor = buf | 0x100; + else + *sensor = buf; + return 0; +} + + + /* End of Add-Ons */ + /* ************************************************* */ + + +int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) +{ + int ret = 0; + + switch(cmd) { + case VIDIOCPWCRUSER: + { + if (pwc_restore_user(pdev)) + ret = -EINVAL; + break; + } + + case VIDIOCPWCSUSER: + { + if (pwc_save_user(pdev)) + ret = -EINVAL; + break; + } + + case VIDIOCPWCFACTORY: + { + if (pwc_restore_factory(pdev)) + ret = -EINVAL; + break; + } + + case VIDIOCPWCSCQUAL: + { + int *qual = arg; + + if (*qual < 0 || *qual > 3) + ret = -EINVAL; + else + ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, *qual, pdev->vsnapshot); + if (ret >= 0) + pdev->vcompression = *qual; + break; + } + + case VIDIOCPWCGCQUAL: + { + int *qual = arg; + *qual = pdev->vcompression; + break; + } + + case VIDIOCPWCPROBE: + { + struct pwc_probe *probe = arg; + strcpy(probe->name, pdev->vdev->name); + probe->type = pdev->type; + break; + } + + case VIDIOCPWCGSERIAL: + { + struct pwc_serial *serial = arg; + strcpy(serial->serial, pdev->serial); + break; + } + + case VIDIOCPWCSAGC: + { + int *agc = arg; + if (pwc_set_agc(pdev, *agc < 0 ? 1 : 0, *agc)) + ret = -EINVAL; + break; + } + + case VIDIOCPWCGAGC: + { + int *agc = arg; + + if (pwc_get_agc(pdev, agc)) + ret = -EINVAL; + break; + } + + case VIDIOCPWCSSHUTTER: + { + int *shutter_speed = arg; + ret = pwc_set_shutter_speed(pdev, *shutter_speed < 0 ? 1 : 0, *shutter_speed); + break; + } + + case VIDIOCPWCSAWB: + { + struct pwc_whitebalance *wb = arg; + + ret = pwc_set_awb(pdev, wb->mode); + if (ret >= 0 && wb->mode == PWC_WB_MANUAL) { + pwc_set_red_gain(pdev, wb->manual_red); + pwc_set_blue_gain(pdev, wb->manual_blue); + } + break; + } + + case VIDIOCPWCGAWB: + { + struct pwc_whitebalance *wb = arg; + + memset(wb, 0, sizeof(struct pwc_whitebalance)); + wb->mode = pwc_get_awb(pdev); + if (wb->mode < 0) + ret = -EINVAL; + else { + if (wb->mode == PWC_WB_MANUAL) { + ret = pwc_get_red_gain(pdev, &wb->manual_red); + if (ret < 0) + break; + ret = pwc_get_blue_gain(pdev, &wb->manual_blue); + if (ret < 0) + break; + } + if (wb->mode == PWC_WB_AUTO) { + ret = pwc_read_red_gain(pdev, &wb->read_red); + if (ret < 0) + break; + ret = pwc_read_blue_gain(pdev, &wb->read_blue); + if (ret < 0) + break; + } + } + break; + } + + case VIDIOCPWCSAWBSPEED: + { + struct pwc_wb_speed *wbs = arg; + + if (wbs->control_speed > 0) { + ret = pwc_set_wb_speed(pdev, wbs->control_speed); + } + if (wbs->control_delay > 0) { + ret = pwc_set_wb_delay(pdev, wbs->control_delay); + } + break; + } + + case VIDIOCPWCGAWBSPEED: + { + struct pwc_wb_speed *wbs = arg; + + ret = pwc_get_wb_speed(pdev, &wbs->control_speed); + if (ret < 0) + break; + ret = pwc_get_wb_delay(pdev, &wbs->control_delay); + if (ret < 0) + break; + break; + } + + case VIDIOCPWCSLED: + { + struct pwc_leds *leds = arg; + ret = pwc_set_leds(pdev, leds->led_on, leds->led_off); + break; + } + + + case VIDIOCPWCGLED: + { + struct pwc_leds *leds = arg; + ret = pwc_get_leds(pdev, &leds->led_on, &leds->led_off); + break; + } + + case VIDIOCPWCSCONTOUR: + { + int *contour = arg; + ret = pwc_set_contour(pdev, *contour); + break; + } + + case VIDIOCPWCGCONTOUR: + { + int *contour = arg; + ret = pwc_get_contour(pdev, contour); + break; + } + + case VIDIOCPWCSBACKLIGHT: + { + int *backlight = arg; + ret = pwc_set_backlight(pdev, *backlight); + break; + } + + case VIDIOCPWCGBACKLIGHT: + { + int *backlight = arg; + ret = pwc_get_backlight(pdev, backlight); + break; + } + + case VIDIOCPWCSFLICKER: + { + int *flicker = arg; + ret = pwc_set_flicker(pdev, *flicker); + break; + } + + case VIDIOCPWCGFLICKER: + { + int *flicker = arg; + ret = pwc_get_flicker(pdev, flicker); + break; + } + + case VIDIOCPWCSDYNNOISE: + { + int *dynnoise = arg; + ret = pwc_set_dynamic_noise(pdev, *dynnoise); + break; + } + + case VIDIOCPWCGDYNNOISE: + { + int *dynnoise = arg; + ret = pwc_get_dynamic_noise(pdev, dynnoise); + break; + } + + case VIDIOCPWCGREALSIZE: + { + struct pwc_imagesize *size = arg; + size->width = pdev->image.x; + size->height = pdev->image.y; + break; + } + + case VIDIOCPWCMPTRESET: + { + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + int *flags = arg; + + ret = pwc_mpt_reset(pdev, *flags); + if (ret >= 0) + { + pdev->pan_angle = 0; + pdev->tilt_angle = 0; + } + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTGRANGE: + { + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + struct pwc_mpt_range *range = arg; + *range = pdev->angle_range; + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTSANGLE: + { + int new_pan, new_tilt; + + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + struct pwc_mpt_angles *angles = arg; + /* The camera can only set relative angles, so + do some calculations when getting an absolute angle . + */ + if (angles->absolute) + { + new_pan = angles->pan; + new_tilt = angles->tilt; + } + else + { + new_pan = pdev->pan_angle + angles->pan; + new_tilt = pdev->tilt_angle + angles->tilt; + } + /* check absolute ranges */ + if (new_pan < pdev->angle_range.pan_min || + new_pan > pdev->angle_range.pan_max || + new_tilt < pdev->angle_range.tilt_min || + new_tilt > pdev->angle_range.tilt_max) + { + ret = -ERANGE; + } + else + { + /* go to relative range, check again */ + new_pan -= pdev->pan_angle; + new_tilt -= pdev->tilt_angle; + /* angles are specified in degrees * 100, thus the limit = 36000 */ + if (new_pan < -36000 || new_pan > 36000 || new_tilt < -36000 || new_tilt > 36000) + ret = -ERANGE; + } + if (ret == 0) /* no errors so far */ + { + ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt); + if (ret >= 0) + { + pdev->pan_angle += new_pan; + pdev->tilt_angle += new_tilt; + } + if (ret == -EPIPE) /* stall -> out of range */ + ret = -ERANGE; + } + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTGANGLE: + { + + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + struct pwc_mpt_angles *angles = arg; + + angles->absolute = 1; + angles->pan = pdev->pan_angle; + angles->tilt = pdev->tilt_angle; + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTSTATUS: + { + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + struct pwc_mpt_status *status = arg; + ret = pwc_mpt_get_status(pdev, status); + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCGVIDCMD: + { + struct pwc_video_command *cmd = arg; + + cmd->type = pdev->type; + cmd->release = pdev->release; + cmd->command_len = pdev->cmd_len; + memcpy(&cmd->command_buf, pdev->cmd_buf, pdev->cmd_len); + cmd->bandlength = pdev->vbandlength; + cmd->frame_size = pdev->frame_size; + break; + } + /* + case VIDIOCPWCGVIDTABLE: + { + struct pwc_table_init_buffer *table = arg; + table->len = pdev->cmd_len; + memcpy(&table->buffer, pdev->decompress_data, pdev->decompressor->table_size); + break; + } + */ + + default: + ret = -ENOIOCTLCMD; + break; + } + + if (ret > 0) + return 0; + return ret; +} + + + diff --git a/linux/drivers/media/video/pwc/pwc-if.c b/linux/drivers/media/video/pwc/pwc-if.c new file mode 100644 index 000000000..41418294a --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-if.c @@ -0,0 +1,2205 @@ +/* Linux driver for Philips webcam + USB and Video4Linux interface part. + (C) 1999-2004 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +/* + This code forms the interface between the USB layers and the Philips + specific stuff. Some adanved stuff of the driver falls under an + NDA, signed between me and Philips B.V., Eindhoven, the Netherlands, and + is thus not distributed in source form. The binary pwcx.o module + contains the code that falls under the NDA. + + In case you're wondering: 'pwc' stands for "Philips WebCam", but + I really didn't want to type 'philips_web_cam' every time (I'm lazy as + any Linux kernel hacker, but I don't like uncomprehensible abbreviations + without explanation). + + Oh yes, convention: to disctinguish between all the various pointers to + device-structures, I use these names for the pointer variables: + udev: struct usb_device * + vdev: struct video_device * + pdev: struct pwc_devive * +*/ + +/* Contributors: + - Alvarado: adding whitebalance code + - Alistar Moire: QuickCam 3000 Pro device/product ID + - Tony Hoyle: Creative Labs Webcam 5 device/product ID + - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged + - Jk Fang: Sotec Afina Eye ID + - Xavier Roche: QuickCam Pro 4000 ID + - Jens Knudsen: QuickCam Zoom ID + - J. Debert: QuickCam for Notebooks ID +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pwc.h" +#include "pwc-ioctl.h" +#include "pwc-kiara.h" +#include "pwc-timon.h" +#include "pwc-uncompress.h" + +/* Function prototypes and driver templates */ + +/* hotplug device table support */ +static struct usb_device_id pwc_device_table [] = { + { USB_DEVICE(0x0471, 0x0302) }, /* Philips models */ + { USB_DEVICE(0x0471, 0x0303) }, + { USB_DEVICE(0x0471, 0x0304) }, + { USB_DEVICE(0x0471, 0x0307) }, + { USB_DEVICE(0x0471, 0x0308) }, + { USB_DEVICE(0x0471, 0x030C) }, + { USB_DEVICE(0x0471, 0x0310) }, + { USB_DEVICE(0x0471, 0x0311) }, + { USB_DEVICE(0x0471, 0x0312) }, + { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */ + { USB_DEVICE(0x069A, 0x0001) }, /* Askey */ + { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */ + { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */ + { USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */ + { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */ + { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */ + { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */ + { USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */ + { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */ + { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */ + { USB_DEVICE(0x055D, 0x9000) }, /* Samsung */ + { USB_DEVICE(0x055D, 0x9001) }, + { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */ + { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */ + { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */ + { USB_DEVICE(0x06BE, 0x8116) }, /* new Afina Eye */ + { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */ + { USB_DEVICE(0x0d81, 0x1900) }, + { } +}; +MODULE_DEVICE_TABLE(usb, pwc_device_table); + +static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id); +static void usb_pwc_disconnect(struct usb_interface *intf); + +static struct usb_driver pwc_driver = { + .name = "Philips webcam", /* name */ + .id_table = pwc_device_table, + .probe = usb_pwc_probe, /* probe() */ + .disconnect = usb_pwc_disconnect, /* disconnect() */ +}; + +#define MAX_DEV_HINTS 20 +#define MAX_ISOC_ERRORS 20 + +static int default_size = PSZ_QCIF; +static int default_fps = 10; +static int default_fbufs = 3; /* Default number of frame buffers */ +static int default_mbufs = 2; /* Default number of mmap() buffers */ + int pwc_trace = TRACE_MODULE | TRACE_FLOW | TRACE_PWCX; +static int power_save = 0; +static int led_on = 100, led_off = 0; /* defaults to LED that is on while in use */ +static int pwc_preferred_compression = 2; /* 0..3 = uncompressed..high */ +static struct { + int type; + char serial_number[30]; + int device_node; + struct pwc_device *pdev; +} device_hint[MAX_DEV_HINTS]; + +/***/ + +static int pwc_video_open(struct inode *inode, struct file *file); +static int pwc_video_close(struct inode *inode, struct file *file); +static ssize_t pwc_video_read(struct file *file, char __user * buf, + size_t count, loff_t *ppos); +static unsigned int pwc_video_poll(struct file *file, poll_table *wait); +static int pwc_video_ioctl(struct inode *inode, struct file *file, + unsigned int ioctlnr, unsigned long arg); +static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma); + +static struct file_operations pwc_fops = { + .owner = THIS_MODULE, + .open = pwc_video_open, + .release = pwc_video_close, + .read = pwc_video_read, + .poll = pwc_video_poll, + .mmap = pwc_video_mmap, + .ioctl = pwc_video_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; +static struct video_device pwc_template = { + .owner = THIS_MODULE, + .name = "Philips Webcam", /* Filled in later */ + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_PWC, + .release = video_device_release, + .fops = &pwc_fops, + .minor = -1, +}; + +/***************************************************************************/ + +/* Okay, this is some magic that I worked out and the reasoning behind it... + + The biggest problem with any USB device is of course: "what to do + when the user unplugs the device while it is in use by an application?" + We have several options: + 1) Curse them with the 7 plagues when they do (requires divine intervention) + 2) Tell them not to (won't work: they'll do it anyway) + 3) Oops the kernel (this will have a negative effect on a user's uptime) + 4) Do something sensible. + + Of course, we go for option 4. + + It happens that this device will be linked to two times, once from + usb_device and once from the video_device in their respective 'private' + pointers. This is done when the device is probed() and all initialization + succeeded. The pwc_device struct links back to both structures. + + When a device is unplugged while in use it will be removed from the + list of known USB devices; I also de-register it as a V4L device, but + unfortunately I can't free the memory since the struct is still in use + by the file descriptor. This free-ing is then deferend until the first + opportunity. Crude, but it works. + + A small 'advantage' is that if a user unplugs the cam and plugs it back + in, it should get assigned the same video device minor, but unfortunately + it's non-trivial to re-link the cam back to the video device... (that + would surely be magic! :)) +*/ + +/***************************************************************************/ +/* Private functions */ + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the area. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long kva, ret; + + kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ + ret = __pa(kva); + return ret; +} + +static void * rvmalloc(unsigned long size) +{ + void * mem; + unsigned long adr; + + size=PAGE_ALIGN(size); + mem=vmalloc_32(size); + if (mem) + { + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr=(unsigned long) mem; + while (size > 0) + { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + } + return mem; +} + +static void rvfree(void * mem, unsigned long size) +{ + unsigned long adr; + + if (mem) + { + adr=(unsigned long) mem; + while ((long) size > 0) + { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + vfree(mem); + } +} + + + + +static int pwc_allocate_buffers(struct pwc_device *pdev) +{ + int i; + void *kbuf; + + Trace(TRACE_MEMORY, ">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev); + + if (pdev == NULL) + return -ENXIO; + +#ifdef PWC_MAGIC + if (pdev->magic != PWC_MAGIC) { + Err("allocate_buffers(): magic failed.\n"); + return -ENXIO; + } +#endif + /* Allocate Isochronous pipe buffers */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + if (pdev->sbuf[i].data == NULL) { + kbuf = kmalloc(ISO_BUFFER_SIZE, GFP_KERNEL); + if (kbuf == NULL) { + Err("Failed to allocate iso buffer %d.\n", i); + return -ENOMEM; + } + Trace(TRACE_MEMORY, "Allocated iso buffer at %p.\n", kbuf); + pdev->sbuf[i].data = kbuf; + memset(kbuf, 0, ISO_BUFFER_SIZE); + } + } + + /* Allocate frame buffer structure */ + if (pdev->fbuf == NULL) { + kbuf = kmalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL); + if (kbuf == NULL) { + Err("Failed to allocate frame buffer structure.\n"); + return -ENOMEM; + } + Trace(TRACE_MEMORY, "Allocated frame buffer structure at %p.\n", kbuf); + pdev->fbuf = kbuf; + memset(kbuf, 0, default_fbufs * sizeof(struct pwc_frame_buf)); + } + /* create frame buffers, and make circular ring */ + for (i = 0; i < default_fbufs; i++) { + if (pdev->fbuf[i].data == NULL) { + kbuf = vmalloc(PWC_FRAME_SIZE); /* need vmalloc since frame buffer > 128K */ + if (kbuf == NULL) { + Err("Failed to allocate frame buffer %d.\n", i); + return -ENOMEM; + } + Trace(TRACE_MEMORY, "Allocated frame buffer %d at %p.\n", i, kbuf); + pdev->fbuf[i].data = kbuf; + memset(kbuf, 128, PWC_FRAME_SIZE); + } + } + + /* Allocate decompressor table space */ + kbuf = NULL; + switch (pdev->type) + { + case 675: + case 680: + case 690: + case 720: + case 730: + case 740: + case 750: +#if 0 + Trace(TRACE_MEMORY,"private_data(%zu)\n",sizeof(struct pwc_dec23_private)); + kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); /* Timon & Kiara */ + break; + case 645: + case 646: + /* TODO & FIXME */ + kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); + break; +#endif + ; + } + pdev->decompress_data = kbuf; + + /* Allocate image buffer; double buffer for mmap() */ + kbuf = rvmalloc(default_mbufs * pdev->len_per_image); + if (kbuf == NULL) { + Err("Failed to allocate image buffer(s). needed (%d)\n",default_mbufs * pdev->len_per_image); + return -ENOMEM; + } + Trace(TRACE_MEMORY, "Allocated image buffer at %p.\n", kbuf); + pdev->image_data = kbuf; + for (i = 0; i < default_mbufs; i++) + pdev->image_ptr[i] = kbuf + i * pdev->len_per_image; + for (; i < MAX_IMAGES; i++) + pdev->image_ptr[i] = NULL; + + kbuf = NULL; + + Trace(TRACE_MEMORY, "<< pwc_allocate_buffers()\n"); + return 0; +} + +static void pwc_free_buffers(struct pwc_device *pdev) +{ + int i; + + Trace(TRACE_MEMORY, "Entering free_buffers(%p).\n", pdev); + + if (pdev == NULL) + return; +#ifdef PWC_MAGIC + if (pdev->magic != PWC_MAGIC) { + Err("free_buffers(): magic failed.\n"); + return; + } +#endif + + /* Release Iso-pipe buffers */ + for (i = 0; i < MAX_ISO_BUFS; i++) + if (pdev->sbuf[i].data != NULL) { + Trace(TRACE_MEMORY, "Freeing ISO buffer at %p.\n", pdev->sbuf[i].data); + kfree(pdev->sbuf[i].data); + pdev->sbuf[i].data = NULL; + } + + /* The same for frame buffers */ + if (pdev->fbuf != NULL) { + for (i = 0; i < default_fbufs; i++) { + if (pdev->fbuf[i].data != NULL) { + Trace(TRACE_MEMORY, "Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data); + vfree(pdev->fbuf[i].data); + pdev->fbuf[i].data = NULL; + } + } + kfree(pdev->fbuf); + pdev->fbuf = NULL; + } + + /* Intermediate decompression buffer & tables */ + if (pdev->decompress_data != NULL) { + Trace(TRACE_MEMORY, "Freeing decompression buffer at %p.\n", pdev->decompress_data); + kfree(pdev->decompress_data); + pdev->decompress_data = NULL; + } + pdev->decompressor = NULL; + + /* Release image buffers */ + if (pdev->image_data != NULL) { + Trace(TRACE_MEMORY, "Freeing image buffer at %p.\n", pdev->image_data); + rvfree(pdev->image_data, default_mbufs * pdev->len_per_image); + } + pdev->image_data = NULL; + + Trace(TRACE_MEMORY, "Leaving free_buffers().\n"); +} + +/* The frame & image buffer mess. + + Yes, this is a mess. Well, it used to be simple, but alas... In this + module, 3 buffers schemes are used to get the data from the USB bus to + the user program. The first scheme involves the ISO buffers (called thus + since they transport ISO data from the USB controller), and not really + interesting. Suffices to say the data from this buffer is quickly + gathered in an interrupt handler (pwc_isoc_handler) and placed into the + frame buffer. + + The frame buffer is the second scheme, and is the central element here. + It collects the data from a single frame from the camera (hence, the + name). Frames are delimited by the USB camera with a short USB packet, + so that's easy to detect. The frame buffers form a list that is filled + by the camera+USB controller and drained by the user process through + either read() or mmap(). + + The image buffer is the third scheme, in which frames are decompressed + and converted into planar format. For mmap() there is more than + one image buffer available. + + The frame buffers provide the image buffering. In case the user process + is a bit slow, this introduces lag and some undesired side-effects. + The problem arises when the frame buffer is full. I used to drop the last + frame, which makes the data in the queue stale very quickly. But dropping + the frame at the head of the queue proved to be a litte bit more difficult. + I tried a circular linked scheme, but this introduced more problems than + it solved. + + Because filling and draining are completely asynchronous processes, this + requires some fiddling with pointers and mutexes. + + Eventually, I came up with a system with 2 lists: an 'empty' frame list + and a 'full' frame list: + * Initially, all frame buffers but one are on the 'empty' list; the one + remaining buffer is our initial fill frame. + * If a frame is needed for filling, we try to take it from the 'empty' + list, unless that list is empty, in which case we take the buffer at + the head of the 'full' list. + * When our fill buffer has been filled, it is appended to the 'full' + list. + * If a frame is needed by read() or mmap(), it is taken from the head of + the 'full' list, handled, and then appended to the 'empty' list. If no + buffer is present on the 'full' list, we wait. + The advantage is that the buffer that is currently being decompressed/ + converted, is on neither list, and thus not in our way (any other scheme + I tried had the problem of old data lingering in the queue). + + Whatever strategy you choose, it always remains a tradeoff: with more + frame buffers the chances of a missed frame are reduced. On the other + hand, on slower machines it introduces lag because the queue will + always be full. + */ + +/** + \brief Find next frame buffer to fill. Take from empty or full list, whichever comes first. + */ +static inline int pwc_next_fill_frame(struct pwc_device *pdev) +{ + int ret; + unsigned long flags; + + ret = 0; + spin_lock_irqsave(&pdev->ptrlock, flags); + if (pdev->fill_frame != NULL) { + /* append to 'full' list */ + if (pdev->full_frames == NULL) { + pdev->full_frames = pdev->fill_frame; + pdev->full_frames_tail = pdev->full_frames; + } + else { + pdev->full_frames_tail->next = pdev->fill_frame; + pdev->full_frames_tail = pdev->fill_frame; + } + } + if (pdev->empty_frames != NULL) { + /* We have empty frames available. That's easy */ + pdev->fill_frame = pdev->empty_frames; + pdev->empty_frames = pdev->empty_frames->next; + } + else { + /* Hmm. Take it from the full list */ +#if PWC_DEBUG + /* sanity check */ + if (pdev->full_frames == NULL) { + Err("Neither empty or full frames available!\n"); + spin_unlock_irqrestore(&pdev->ptrlock, flags); + return -EINVAL; + } +#endif + pdev->fill_frame = pdev->full_frames; + pdev->full_frames = pdev->full_frames->next; + ret = 1; + } + pdev->fill_frame->next = NULL; +#if PWC_DEBUG + Trace(TRACE_SEQUENCE, "Assigning sequence number %d.\n", pdev->sequence); + pdev->fill_frame->sequence = pdev->sequence++; +#endif + spin_unlock_irqrestore(&pdev->ptrlock, flags); + return ret; +} + + +/** + \brief Reset all buffers, pointers and lists, except for the image_used[] buffer. + + If the image_used[] buffer is cleared too, mmap()/VIDIOCSYNC will run into trouble. + */ +static void pwc_reset_buffers(struct pwc_device *pdev) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&pdev->ptrlock, flags); + pdev->full_frames = NULL; + pdev->full_frames_tail = NULL; + for (i = 0; i < default_fbufs; i++) { + pdev->fbuf[i].filled = 0; + if (i > 0) + pdev->fbuf[i].next = &pdev->fbuf[i - 1]; + else + pdev->fbuf->next = NULL; + } + pdev->empty_frames = &pdev->fbuf[default_fbufs - 1]; + pdev->empty_frames_tail = pdev->fbuf; + pdev->read_frame = NULL; + pdev->fill_frame = pdev->empty_frames; + pdev->empty_frames = pdev->empty_frames->next; + + pdev->image_read_pos = 0; + pdev->fill_image = 0; + spin_unlock_irqrestore(&pdev->ptrlock, flags); +} + + +/** + \brief Do all the handling for getting one frame: get pointer, decompress, advance pointers. + */ +static int pwc_handle_frame(struct pwc_device *pdev) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&pdev->ptrlock, flags); + /* First grab our read_frame; this is removed from all lists, so + we can release the lock after this without problems */ + if (pdev->read_frame != NULL) { + /* This can't theoretically happen */ + Err("Huh? Read frame still in use?\n"); + } + else { + if (pdev->full_frames == NULL) { + Err("Woops. No frames ready.\n"); + } + else { + pdev->read_frame = pdev->full_frames; + pdev->full_frames = pdev->full_frames->next; + pdev->read_frame->next = NULL; + } + + if (pdev->read_frame != NULL) { +#if PWC_DEBUG + Trace(TRACE_SEQUENCE, "Decompressing frame %d\n", pdev->read_frame->sequence); +#endif + /* Decompression is a lenghty process, so it's outside of the lock. + This gives the isoc_handler the opportunity to fill more frames + in the mean time. + */ + spin_unlock_irqrestore(&pdev->ptrlock, flags); + ret = pwc_decompress(pdev); + spin_lock_irqsave(&pdev->ptrlock, flags); + + /* We're done with read_buffer, tack it to the end of the empty buffer list */ + if (pdev->empty_frames == NULL) { + pdev->empty_frames = pdev->read_frame; + pdev->empty_frames_tail = pdev->empty_frames; + } + else { + pdev->empty_frames_tail->next = pdev->read_frame; + pdev->empty_frames_tail = pdev->read_frame; + } + pdev->read_frame = NULL; + } + } + spin_unlock_irqrestore(&pdev->ptrlock, flags); + return ret; +} + +/** + \brief Advance pointers of image buffer (after each user request) +*/ +static inline void pwc_next_image(struct pwc_device *pdev) +{ + pdev->image_used[pdev->fill_image] = 0; + pdev->fill_image = (pdev->fill_image + 1) % default_mbufs; +} + + +/* This gets called for the Isochronous pipe (video). This is done in + * interrupt time, so it has to be fast, not crash, and not stall. Neat. + */ +static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs) +{ + struct pwc_device *pdev; + int i, fst, flen; + int awake; + struct pwc_frame_buf *fbuf; + unsigned char *fillptr = NULL, *iso_buf = NULL; + + awake = 0; + pdev = (struct pwc_device *)urb->context; + if (pdev == NULL) { + Err("isoc_handler() called with NULL device?!\n"); + return; + } +#ifdef PWC_MAGIC + if (pdev->magic != PWC_MAGIC) { + Err("isoc_handler() called with bad magic!\n"); + return; + } +#endif + if (urb->status == -ENOENT || urb->status == -ECONNRESET) { + Trace(TRACE_OPEN, "pwc_isoc_handler(): URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a"); + return; + } + if (urb->status != -EINPROGRESS && urb->status != 0) { + const char *errmsg; + + errmsg = "Unknown"; + switch(urb->status) { + case -ENOSR: errmsg = "Buffer error (overrun)"; break; + case -EPIPE: errmsg = "Stalled (device not responding)"; break; + case -EOVERFLOW: errmsg = "Babble (bad cable?)"; break; + case -EPROTO: errmsg = "Bit-stuff error (bad cable?)"; break; + case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break; + case -ETIMEDOUT: errmsg = "NAK (device does not respond)"; break; + } + Trace(TRACE_FLOW, "pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg); + /* Give up after a number of contiguous errors on the USB bus. + Appearantly something is wrong so we simulate an unplug event. + */ + if (++pdev->visoc_errors > MAX_ISOC_ERRORS) + { + Info("Too many ISOC errors, bailing out.\n"); + pdev->error_status = EIO; + awake = 1; + wake_up_interruptible(&pdev->frameq); + } + goto handler_end; // ugly, but practical + } + + fbuf = pdev->fill_frame; + if (fbuf == NULL) { + Err("pwc_isoc_handler without valid fill frame.\n"); + awake = 1; + goto handler_end; + } + else { + fillptr = fbuf->data + fbuf->filled; + } + + /* Reset ISOC error counter. We did get here, after all. */ + pdev->visoc_errors = 0; + + /* vsync: 0 = don't copy data + 1 = sync-hunt + 2 = synched + */ + /* Compact data */ + for (i = 0; i < urb->number_of_packets; i++) { + fst = urb->iso_frame_desc[i].status; + flen = urb->iso_frame_desc[i].actual_length; + iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + if (fst == 0) { + if (flen > 0) { /* if valid data... */ + if (pdev->vsync > 0) { /* ...and we are not sync-hunting... */ + pdev->vsync = 2; + + /* ...copy data to frame buffer, if possible */ + if (flen + fbuf->filled > pdev->frame_total_size) { + Trace(TRACE_FLOW, "Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, pdev->frame_total_size); + pdev->vsync = 0; /* Hmm, let's wait for an EOF (end-of-frame) */ + pdev->vframes_error++; + } + else { + memmove(fillptr, iso_buf, flen); + fillptr += flen; + } + } + fbuf->filled += flen; + } /* ..flen > 0 */ + + if (flen < pdev->vlast_packet_size) { + /* Shorter packet... We probably have the end of an image-frame; + wake up read() process and let select()/poll() do something. + Decompression is done in user time over there. + */ + if (pdev->vsync == 2) { + /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus + frames on the USB wire after an exposure change. This conditition is + however detected in the cam and a bit is set in the header. + */ + if (pdev->type == 730) { + unsigned char *ptr = (unsigned char *)fbuf->data; + + if (ptr[1] == 1 && ptr[0] & 0x10) { +#if PWC_DEBUG + Debug("Hyundai CMOS sensor bug. Dropping frame %d.\n", fbuf->sequence); +#endif + pdev->drop_frames += 2; + pdev->vframes_error++; + } + if ((ptr[0] ^ pdev->vmirror) & 0x01) { + if (ptr[0] & 0x01) + Info("Snapshot button pressed.\n"); + else + Info("Snapshot button released.\n"); + } + if ((ptr[0] ^ pdev->vmirror) & 0x02) { + if (ptr[0] & 0x02) + Info("Image is mirrored.\n"); + else + Info("Image is normal.\n"); + } + pdev->vmirror = ptr[0] & 0x03; + /* Sometimes the trailer of the 730 is still sent as a 4 byte packet + after a short frame; this condition is filtered out specifically. A 4 byte + frame doesn't make sense anyway. + So we get either this sequence: + drop_bit set -> 4 byte frame -> short frame -> good frame + Or this one: + drop_bit set -> short frame -> good frame + So we drop either 3 or 2 frames in all! + */ + if (fbuf->filled == 4) + pdev->drop_frames++; + } + + /* In case we were instructed to drop the frame, do so silently. + The buffer pointers are not updated either (but the counters are reset below). + */ + if (pdev->drop_frames > 0) + pdev->drop_frames--; + else { + /* Check for underflow first */ + if (fbuf->filled < pdev->frame_total_size) { + Trace(TRACE_FLOW, "Frame buffer underflow (%d bytes); discarded.\n", fbuf->filled); + pdev->vframes_error++; + } + else { + /* Send only once per EOF */ + awake = 1; /* delay wake_ups */ + + /* Find our next frame to fill. This will always succeed, since we + * nick a frame from either empty or full list, but if we had to + * take it from the full list, it means a frame got dropped. + */ + if (pwc_next_fill_frame(pdev)) { + pdev->vframes_dumped++; + if ((pdev->vframe_count > FRAME_LOWMARK) && (pwc_trace & TRACE_FLOW)) { + if (pdev->vframes_dumped < 20) + Trace(TRACE_FLOW, "Dumping frame %d.\n", pdev->vframe_count); + if (pdev->vframes_dumped == 20) + Trace(TRACE_FLOW, "Dumping frame %d (last message).\n", pdev->vframe_count); + } + } + fbuf = pdev->fill_frame; + } + } /* !drop_frames */ + pdev->vframe_count++; + } + fbuf->filled = 0; + fillptr = fbuf->data; + pdev->vsync = 1; + } /* .. flen < last_packet_size */ + pdev->vlast_packet_size = flen; + } /* ..status == 0 */ +#if PWC_DEBUG + /* This is normally not interesting to the user, unless you are really debugging something */ + else { + static int iso_error = 0; + iso_error++; + if (iso_error < 20) + Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst); + } +#endif + } + +handler_end: + if (awake) + wake_up_interruptible(&pdev->frameq); + + urb->dev = pdev->udev; + i = usb_submit_urb(urb, GFP_ATOMIC); + if (i != 0) + Err("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i); +} + + +static int pwc_isoc_init(struct pwc_device *pdev) +{ + struct usb_device *udev; + struct urb *urb; + int i, j, ret; + + struct usb_interface *intf; + struct usb_host_interface *idesc = NULL; + + if (pdev == NULL) + return -EFAULT; + if (pdev->iso_init) + return 0; + pdev->vsync = 0; + udev = pdev->udev; + + /* Get the current alternate interface, adjust packet size */ + if (!udev->actconfig) + return -EFAULT; + + intf = usb_ifnum_to_if(udev, 0); + if (intf) + idesc = usb_altnum_to_altsetting(intf, pdev->valternate); + + if (!idesc) + return -EFAULT; + + /* Search video endpoint */ + pdev->vmax_packet_size = -1; + for (i = 0; i < idesc->desc.bNumEndpoints; i++) + if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) { + pdev->vmax_packet_size = le16_to_cpu(idesc->endpoint[i].desc.wMaxPacketSize); + break; + } + + if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) { + Err("Failed to find packet size for video endpoint in current alternate setting.\n"); + return -ENFILE; /* Odd error, that should be noticeable */ + } + + /* Set alternate interface */ + ret = 0; + Trace(TRACE_OPEN, "Setting alternate interface %d\n", pdev->valternate); + ret = usb_set_interface(pdev->udev, 0, pdev->valternate); + if (ret < 0) + return ret; + + for (i = 0; i < MAX_ISO_BUFS; i++) { + urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); + if (urb == NULL) { + Err("Failed to allocate urb %d\n", i); + ret = -ENOMEM; + break; + } + pdev->sbuf[i].urb = urb; + Trace(TRACE_MEMORY, "Allocated URB at 0x%p\n", urb); + } + if (ret) { + /* De-allocate in reverse order */ + while (i >= 0) { + if (pdev->sbuf[i].urb != NULL) + usb_free_urb(pdev->sbuf[i].urb); + pdev->sbuf[i].urb = NULL; + i--; + } + return ret; + } + + /* init URB structure */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + urb = pdev->sbuf[i].urb; + + urb->interval = 1; // devik + urb->dev = udev; + urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = pdev->sbuf[i].data; + urb->transfer_buffer_length = ISO_BUFFER_SIZE; + urb->complete = pwc_isoc_handler; + urb->context = pdev; + urb->start_frame = 0; + urb->number_of_packets = ISO_FRAMES_PER_DESC; + for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { + urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE; + urb->iso_frame_desc[j].length = pdev->vmax_packet_size; + } + } + + /* link */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + ret = usb_submit_urb(pdev->sbuf[i].urb, GFP_KERNEL); + if (ret) + Err("isoc_init() submit_urb %d failed with error %d\n", i, ret); + else + Trace(TRACE_MEMORY, "URB 0x%p submitted.\n", pdev->sbuf[i].urb); + } + + /* All is done... */ + pdev->iso_init = 1; + Trace(TRACE_OPEN, "<< pwc_isoc_init()\n"); + return 0; +} + +static void pwc_isoc_cleanup(struct pwc_device *pdev) +{ + int i; + + Trace(TRACE_OPEN, ">> pwc_isoc_cleanup()\n"); + if (pdev == NULL) + return; + + /* Unlinking ISOC buffers one by one */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + struct urb *urb; + + urb = pdev->sbuf[i].urb; + if (urb != 0) { + if (pdev->iso_init) { + Trace(TRACE_MEMORY, "Unlinking URB %p\n", urb); + usb_kill_urb(urb); + } + Trace(TRACE_MEMORY, "Freeing URB\n"); + usb_free_urb(urb); + pdev->sbuf[i].urb = NULL; + } + } + + /* Stop camera, but only if we are sure the camera is still there (unplug + is signalled by EPIPE) + */ + if (pdev->error_status && pdev->error_status != EPIPE) { + Trace(TRACE_OPEN, "Setting alternate interface 0.\n"); + usb_set_interface(pdev->udev, 0, 0); + } + + pdev->iso_init = 0; + Trace(TRACE_OPEN, "<< pwc_isoc_cleanup()\n"); +} + +int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot) +{ + int ret, start; + + /* Stop isoc stuff */ + pwc_isoc_cleanup(pdev); + /* Reset parameters */ + pwc_reset_buffers(pdev); + /* Try to set video mode... */ + start = ret = pwc_set_video_mode(pdev, width, height, new_fps, new_compression, new_snapshot); + if (ret) { + Trace(TRACE_FLOW, "pwc_set_video_mode attempt 1 failed.\n"); + /* That failed... restore old mode (we know that worked) */ + start = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); + if (start) { + Trace(TRACE_FLOW, "pwc_set_video_mode attempt 2 failed.\n"); + } + } + if (start == 0) + { + if (pwc_isoc_init(pdev) < 0) + { + Info("Failed to restart ISOC transfers in pwc_try_video_mode.\n"); + ret = -EAGAIN; /* let's try again, who knows if it works a second time */ + } + } + pdev->drop_frames++; /* try to avoid garbage during switch */ + return ret; /* Return original error code */ +} + + +/***************************************************************************/ +/* Video4Linux functions */ + +static int pwc_video_open(struct inode *inode, struct file *file) +{ + int i; + struct video_device *vdev = video_devdata(file); + struct pwc_device *pdev; + + Trace(TRACE_OPEN, ">> video_open called(vdev = 0x%p).\n", vdev); + + pdev = (struct pwc_device *)vdev->priv; + if (pdev == NULL) + BUG(); + if (pdev->vopen) + return -EBUSY; + + down(&pdev->modlock); + if (!pdev->usb_init) { + Trace(TRACE_OPEN, "Doing first time initialization.\n"); + pdev->usb_init = 1; + + if (pwc_trace & TRACE_OPEN) + { + /* Query sensor type */ + const char *sensor_type = NULL; + int ret; + + ret = pwc_get_cmos_sensor(pdev, &i); + if (ret >= 0) + { + switch(i) { + case 0x00: sensor_type = "Hyundai CMOS sensor"; break; + case 0x20: sensor_type = "Sony CCD sensor + TDA8787"; break; + case 0x2E: sensor_type = "Sony CCD sensor + Exas 98L59"; break; + case 0x2F: sensor_type = "Sony CCD sensor + ADI 9804"; break; + case 0x30: sensor_type = "Sharp CCD sensor + TDA8787"; break; + case 0x3E: sensor_type = "Sharp CCD sensor + Exas 98L59"; break; + case 0x3F: sensor_type = "Sharp CCD sensor + ADI 9804"; break; + case 0x40: sensor_type = "UPA 1021 sensor"; break; + case 0x100: sensor_type = "VGA sensor"; break; + case 0x101: sensor_type = "PAL MR sensor"; break; + default: sensor_type = "unknown type of sensor"; break; + } + } + if (sensor_type != NULL) + Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev->name, sensor_type, i); + } + } + + /* Turn on camera */ + if (power_save) { + i = pwc_camera_power(pdev, 1); + if (i < 0) + Info("Failed to restore power to the camera! (%d)\n", i); + } + /* Set LED on/off time */ + if (pwc_set_leds(pdev, led_on, led_off) < 0) + Info("Failed to set LED on/off time.\n"); + + pwc_construct(pdev); /* set min/max sizes correct */ + + /* So far, so good. Allocate memory. */ + i = pwc_allocate_buffers(pdev); + if (i < 0) { + Trace(TRACE_OPEN, "Failed to allocate buffer memory.\n"); + up(&pdev->modlock); + return i; + } + + /* Reset buffers & parameters */ + pwc_reset_buffers(pdev); + for (i = 0; i < default_mbufs; i++) + pdev->image_used[i] = 0; + pdev->vframe_count = 0; + pdev->vframes_dumped = 0; + pdev->vframes_error = 0; + pdev->visoc_errors = 0; + pdev->error_status = 0; +#if PWC_DEBUG + pdev->sequence = 0; +#endif + pwc_construct(pdev); /* set min/max sizes correct */ + + /* Set some defaults */ + pdev->vsnapshot = 0; + + /* Start iso pipe for video; first try the last used video size + (or the default one); if that fails try QCIF/10 or QSIF/10; + it that fails too, give up. + */ + i = pwc_set_video_mode(pdev, pwc_image_sizes[pdev->vsize].x, pwc_image_sizes[pdev->vsize].y, pdev->vframes, pdev->vcompression, 0); + if (i) { + Trace(TRACE_OPEN, "First attempt at set_video_mode failed.\n"); + if (pdev->type == 730 || pdev->type == 740 || pdev->type == 750) + i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QSIF].x, pwc_image_sizes[PSZ_QSIF].y, 10, pdev->vcompression, 0); + else + i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QCIF].x, pwc_image_sizes[PSZ_QCIF].y, 10, pdev->vcompression, 0); + } + if (i) { + Trace(TRACE_OPEN, "Second attempt at set_video_mode failed.\n"); + up(&pdev->modlock); + return i; + } + + i = pwc_isoc_init(pdev); + if (i) { + Trace(TRACE_OPEN, "Failed to init ISOC stuff = %d.\n", i); + up(&pdev->modlock); + return i; + } + + pdev->vopen++; + file->private_data = vdev; + up(&pdev->modlock); + Trace(TRACE_OPEN, "<< video_open() returns 0.\n"); + return 0; +} + +/* Note that all cleanup is done in the reverse order as in _open */ +static int pwc_video_close(struct inode *inode, struct file *file) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + int i; + + Trace(TRACE_OPEN, ">> video_close called(vdev = 0x%p).\n", vdev); + + pdev = (struct pwc_device *)vdev->priv; + if (pdev->vopen == 0) + Info("video_close() called on closed device?\n"); + + /* Dump statistics, but only if a reasonable amount of frames were + processed (to prevent endless log-entries in case of snap-shot + programs) + */ + if (pdev->vframe_count > 20) + Info("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error); + + switch (pdev->type) + { + case 675: + case 680: + case 690: + case 720: + case 730: + case 740: + case 750: +/* pwc_dec23_exit(); *//* Timon & Kiara */ + break; + case 645: + case 646: +/* pwc_dec1_exit(); */ + break; + } + + pwc_isoc_cleanup(pdev); + pwc_free_buffers(pdev); + + /* Turn off LEDS and power down camera, but only when not unplugged */ + if (pdev->error_status != EPIPE) { + /* Turn LEDs off */ + if (pwc_set_leds(pdev, 0, 0) < 0) + Info("Failed to set LED on/off time.\n"); + if (power_save) { + i = pwc_camera_power(pdev, 0); + if (i < 0) + Err("Failed to power down camera (%d)\n", i); + } + } + pdev->vopen = 0; + Trace(TRACE_OPEN, "<< video_close()\n"); + return 0; +} + +/* + * FIXME: what about two parallel reads ???? + * ANSWER: Not supported. You can't open the device more than once, + despite what the V4L1 interface says. First, I don't see + the need, second there's no mechanism of alerting the + 2nd/3rd/... process of events like changing image size. + And I don't see the point of blocking that for the + 2nd/3rd/... process. + In multi-threaded environments reading parallel from any + device is tricky anyhow. + */ + +static ssize_t pwc_video_read(struct file *file, char __user * buf, + size_t count, loff_t *ppos) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + int noblock = file->f_flags & O_NONBLOCK; + DECLARE_WAITQUEUE(wait, current); + int bytes_to_read; + + Trace(TRACE_READ, "video_read(0x%p, %p, %zu) called.\n", vdev, buf, count); + if (vdev == NULL) + return -EFAULT; + pdev = vdev->priv; + if (pdev == NULL) + return -EFAULT; + if (pdev->error_status) + return -pdev->error_status; /* Something happened, report what. */ + + /* In case we're doing partial reads, we don't have to wait for a frame */ + if (pdev->image_read_pos == 0) { + /* Do wait queueing according to the (doc)book */ + add_wait_queue(&pdev->frameq, &wait); + while (pdev->full_frames == NULL) { + /* Check for unplugged/etc. here */ + if (pdev->error_status) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -pdev->error_status ; + } + if (noblock) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -EWOULDBLOCK; + } + if (signal_pending(current)) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -ERESTARTSYS; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + + /* Decompress and release frame */ + if (pwc_handle_frame(pdev)) + return -EFAULT; + } + + Trace(TRACE_READ, "Copying data to user space.\n"); + if (pdev->vpalette == VIDEO_PALETTE_RAW) + bytes_to_read = pdev->frame_size; + else + bytes_to_read = pdev->view.size; + + /* copy bytes to user space; we allow for partial reads */ + if (count + pdev->image_read_pos > bytes_to_read) + count = bytes_to_read - pdev->image_read_pos; + if (copy_to_user(buf, pdev->image_ptr[pdev->fill_image] + pdev->image_read_pos, count)) + return -EFAULT; + pdev->image_read_pos += count; + if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */ + pdev->image_read_pos = 0; + pwc_next_image(pdev); + } + return count; +} + +static unsigned int pwc_video_poll(struct file *file, poll_table *wait) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + + if (vdev == NULL) + return -EFAULT; + pdev = vdev->priv; + if (pdev == NULL) + return -EFAULT; + + poll_wait(file, &pdev->frameq, wait); + if (pdev->error_status) + return POLLERR; + if (pdev->full_frames != NULL) /* we have frames waiting */ + return (POLLIN | POLLRDNORM); + + return 0; +} + +static int pwc_video_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + DECLARE_WAITQUEUE(wait, current); + + if (vdev == NULL) + return -EFAULT; + pdev = vdev->priv; + if (pdev == NULL) + return -EFAULT; + + switch (cmd) { + /* Query cabapilities */ + case VIDIOCGCAP: + { + struct video_capability *caps = arg; + + strcpy(caps->name, vdev->name); + caps->type = VID_TYPE_CAPTURE; + caps->channels = 1; + caps->audios = 1; + caps->minwidth = pdev->view_min.x; + caps->minheight = pdev->view_min.y; + caps->maxwidth = pdev->view_max.x; + caps->maxheight = pdev->view_max.y; + break; + } + + /* Channel functions (simulate 1 channel) */ + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + + if (v->channel != 0) + return -EINVAL; + v->flags = 0; + v->tuners = 0; + v->type = VIDEO_TYPE_CAMERA; + strcpy(v->name, "Webcam"); + return 0; + } + + case VIDIOCSCHAN: + { + /* The spec says the argument is an integer, but + the bttv driver uses a video_channel arg, which + makes sense becasue it also has the norm flag. + */ + struct video_channel *v = arg; + if (v->channel != 0) + return -EINVAL; + return 0; + } + + + /* Picture functions; contrast etc. */ + case VIDIOCGPICT: + { + struct video_picture *p = arg; + int val; + + val = pwc_get_brightness(pdev); + if (val >= 0) + p->brightness = val; + else + p->brightness = 0xffff; + val = pwc_get_contrast(pdev); + if (val >= 0) + p->contrast = val; + else + p->contrast = 0xffff; + /* Gamma, Whiteness, what's the difference? :) */ + val = pwc_get_gamma(pdev); + if (val >= 0) + p->whiteness = val; + else + p->whiteness = 0xffff; + val = pwc_get_saturation(pdev); + if (val >= 0) + p->colour = val; + else + p->colour = 0xffff; + p->depth = 24; + p->palette = pdev->vpalette; + p->hue = 0xFFFF; /* N/A */ + break; + } + + case VIDIOCSPICT: + { + struct video_picture *p = arg; + /* + * FIXME: Suppose we are mid read + ANSWER: No problem: the firmware of the camera + can handle brightness/contrast/etc + changes at _any_ time, and the palette + is used exactly once in the uncompress + routine. + */ + pwc_set_brightness(pdev, p->brightness); + pwc_set_contrast(pdev, p->contrast); + pwc_set_gamma(pdev, p->whiteness); + pwc_set_saturation(pdev, p->colour); + if (p->palette && p->palette != pdev->vpalette) { + switch (p->palette) { + case VIDEO_PALETTE_YUV420P: + case VIDEO_PALETTE_RAW: + pdev->vpalette = p->palette; + return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); + break; + default: + return -EINVAL; + break; + } + } + break; + } + + /* Window/size parameters */ + case VIDIOCGWIN: + { + struct video_window *vw = arg; + + vw->x = 0; + vw->y = 0; + vw->width = pdev->view.x; + vw->height = pdev->view.y; + vw->chromakey = 0; + vw->flags = (pdev->vframes << PWC_FPS_SHIFT) | + (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0); + break; + } + + case VIDIOCSWIN: + { + struct video_window *vw = arg; + int fps, snapshot, ret; + + fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT; + snapshot = vw->flags & PWC_FPS_SNAPSHOT; + if (fps == 0) + fps = pdev->vframes; + if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot) + return 0; + ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot); + if (ret) + return ret; + break; + } + + /* We don't have overlay support (yet) */ + case VIDIOCGFBUF: + { + struct video_buffer *vb = arg; + + memset(vb,0,sizeof(*vb)); + break; + } + + /* mmap() functions */ + case VIDIOCGMBUF: + { + /* Tell the user program how much memory is needed for a mmap() */ + struct video_mbuf *vm = arg; + int i; + + memset(vm, 0, sizeof(*vm)); + vm->size = default_mbufs * pdev->len_per_image; + vm->frames = default_mbufs; /* double buffering should be enough for most applications */ + for (i = 0; i < default_mbufs; i++) + vm->offsets[i] = i * pdev->len_per_image; + break; + } + + case VIDIOCMCAPTURE: + { + /* Start capture into a given image buffer (called 'frame' in video_mmap structure) */ + struct video_mmap *vm = arg; + + Trace(TRACE_READ, "VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format); + if (vm->frame < 0 || vm->frame >= default_mbufs) + return -EINVAL; + + /* xawtv is nasty. It probes the available palettes + by setting a very small image size and trying + various palettes... The driver doesn't support + such small images, so I'm working around it. + */ + if (vm->format) + { + switch (vm->format) + { + case VIDEO_PALETTE_YUV420P: + case VIDEO_PALETTE_RAW: + break; + default: + return -EINVAL; + break; + } + } + + if ((vm->width != pdev->view.x || vm->height != pdev->view.y) && + (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) { + int ret; + + Trace(TRACE_OPEN, "VIDIOCMCAPTURE: changing size to please xawtv :-(.\n"); + ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot); + if (ret) + return ret; + } /* ... size mismatch */ + + /* FIXME: should we lock here? */ + if (pdev->image_used[vm->frame]) + return -EBUSY; /* buffer wasn't available. Bummer */ + pdev->image_used[vm->frame] = 1; + + /* Okay, we're done here. In the SYNC call we wait until a + frame comes available, then expand image into the given + buffer. + In contrast to the CPiA cam the Philips cams deliver a + constant stream, almost like a grabber card. Also, + we have separate buffers for the rawdata and the image, + meaning we can nearly always expand into the requested buffer. + */ + Trace(TRACE_READ, "VIDIOCMCAPTURE done.\n"); + break; + } + + case VIDIOCSYNC: + { + /* The doc says: "Whenever a buffer is used it should + call VIDIOCSYNC to free this frame up and continue." + + The only odd thing about this whole procedure is + that MCAPTURE flags the buffer as "in use", and + SYNC immediately unmarks it, while it isn't + after SYNC that you know that the buffer actually + got filled! So you better not start a CAPTURE in + the same frame immediately (use double buffering). + This is not a problem for this cam, since it has + extra intermediate buffers, but a hardware + grabber card will then overwrite the buffer + you're working on. + */ + int *mbuf = arg; + int ret; + + Trace(TRACE_READ, "VIDIOCSYNC called (%d).\n", *mbuf); + + /* bounds check */ + if (*mbuf < 0 || *mbuf >= default_mbufs) + return -EINVAL; + /* check if this buffer was requested anyway */ + if (pdev->image_used[*mbuf] == 0) + return -EINVAL; + + /* Add ourselves to the frame wait-queue. + + FIXME: needs auditing for safety. + QUESTION: In what respect? I think that using the + frameq is safe now. + */ + add_wait_queue(&pdev->frameq, &wait); + while (pdev->full_frames == NULL) { + if (pdev->error_status) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -pdev->error_status; + } + + if (signal_pending(current)) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -ERESTARTSYS; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + + /* The frame is ready. Expand in the image buffer + requested by the user. I don't care if you + mmap() 5 buffers and request data in this order: + buffer 4 2 3 0 1 2 3 0 4 3 1 . . . + Grabber hardware may not be so forgiving. + */ + Trace(TRACE_READ, "VIDIOCSYNC: frame ready.\n"); + pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */ + /* Decompress, etc */ + ret = pwc_handle_frame(pdev); + pdev->image_used[*mbuf] = 0; + if (ret) + return -EFAULT; + break; + } + + case VIDIOCGAUDIO: + { + struct video_audio *v = arg; + + strcpy(v->name, "Microphone"); + v->audio = -1; /* unknown audio minor */ + v->flags = 0; + v->mode = VIDEO_SOUND_MONO; + v->volume = 0; + v->bass = 0; + v->treble = 0; + v->balance = 0x8000; + v->step = 1; + break; + } + + case VIDIOCSAUDIO: + { + /* Dummy: nothing can be set */ + break; + } + + case VIDIOCGUNIT: + { + struct video_unit *vu = arg; + + vu->video = pdev->vdev->minor & 0x3F; + vu->audio = -1; /* not known yet */ + vu->vbi = -1; + vu->radio = -1; + vu->teletext = -1; + break; + } + default: + return pwc_ioctl(pdev, cmd, arg); + } /* ..switch */ + return 0; +} + +static int pwc_video_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl); +} + + +static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end-vma->vm_start; + unsigned long page, pos; + + Trace(TRACE_MEMORY, "mmap(0x%p, 0x%lx, %lu) called.\n", vdev, start, size); + pdev = vdev->priv; + + vma->vm_flags |= VM_IO; + + pos = (unsigned long)pdev->image_data; + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + +/***************************************************************************/ +/* USB functions */ + +/* This function gets called when a new device is plugged in or the usb core + * is loaded. + */ + +static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct pwc_device *pdev = NULL; + int vendor_id, product_id, type_id; + int i, hint; + int features = 0; + int video_nr = -1; /* default: use next available device */ + char serial_number[30], *name; + + /* Check if we can handle this device */ + Trace(TRACE_PROBE, "probe() called [%04X %04X], if %d\n", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + intf->altsetting->desc.bInterfaceNumber); + + /* the interfaces are probed one by one. We are only interested in the + video interface (0) now. + Interface 1 is the Audio Control, and interface 2 Audio itself. + */ + if (intf->altsetting->desc.bInterfaceNumber > 0) + return -ENODEV; + + vendor_id = le16_to_cpu(udev->descriptor.idVendor); + product_id = le16_to_cpu(udev->descriptor.idProduct); + + if (vendor_id == 0x0471) { + switch (product_id) { + case 0x0302: + Info("Philips PCA645VC USB webcam detected.\n"); + name = "Philips 645 webcam"; + type_id = 645; + break; + case 0x0303: + Info("Philips PCA646VC USB webcam detected.\n"); + name = "Philips 646 webcam"; + type_id = 646; + break; + case 0x0304: + Info("Askey VC010 type 2 USB webcam detected.\n"); + name = "Askey VC010 webcam"; + type_id = 646; + break; + case 0x0307: + Info("Philips PCVC675K (Vesta) USB webcam detected.\n"); + name = "Philips 675 webcam"; + type_id = 675; + break; + case 0x0308: + Info("Philips PCVC680K (Vesta Pro) USB webcam detected.\n"); + name = "Philips 680 webcam"; + type_id = 680; + break; + case 0x030C: + Info("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n"); + name = "Philips 690 webcam"; + type_id = 690; + break; + case 0x0310: + Info("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n"); + name = "Philips 730 webcam"; + type_id = 730; + break; + case 0x0311: + Info("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n"); + name = "Philips 740 webcam"; + type_id = 740; + break; + case 0x0312: + Info("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n"); + name = "Philips 750 webcam"; + type_id = 750; + break; + case 0x0313: + Info("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n"); + name = "Philips 720K/40 webcam"; + type_id = 720; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x069A) { + switch(product_id) { + case 0x0001: + Info("Askey VC010 type 1 USB webcam detected.\n"); + name = "Askey VC010 webcam"; + type_id = 645; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x046d) { + switch(product_id) { + case 0x08b0: + Info("Logitech QuickCam Pro 3000 USB webcam detected.\n"); + name = "Logitech QuickCam Pro 3000"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b1: + Info("Logitech QuickCam Notebook Pro USB webcam detected.\n"); + name = "Logitech QuickCam Notebook Pro"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b2: + Info("Logitech QuickCam 4000 Pro USB webcam detected.\n"); + name = "Logitech QuickCam Pro 4000"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b3: + Info("Logitech QuickCam Zoom USB webcam detected.\n"); + name = "Logitech QuickCam Zoom"; + type_id = 740; /* CCD sensor */ + break; + case 0x08B4: + Info("Logitech QuickCam Zoom (new model) USB webcam detected.\n"); + name = "Logitech QuickCam Zoom"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b5: + Info("Logitech QuickCam Orbit/Sphere USB webcam detected.\n"); + name = "Logitech QuickCam Orbit"; + type_id = 740; /* CCD sensor */ + features |= FEATURE_MOTOR_PANTILT; + break; + case 0x08b6: + case 0x08b7: + case 0x08b8: + Info("Logitech QuickCam detected (reserved ID).\n"); + name = "Logitech QuickCam (res.)"; + type_id = 730; /* Assuming CMOS */ + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x055d) { + /* I don't know the difference between the C10 and the C30; + I suppose the difference is the sensor, but both cameras + work equally well with a type_id of 675 + */ + switch(product_id) { + case 0x9000: + Info("Samsung MPC-C10 USB webcam detected.\n"); + name = "Samsung MPC-C10"; + type_id = 675; + break; + case 0x9001: + Info("Samsung MPC-C30 USB webcam detected.\n"); + name = "Samsung MPC-C30"; + type_id = 675; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x041e) { + switch(product_id) { + case 0x400c: + Info("Creative Labs Webcam 5 detected.\n"); + name = "Creative Labs Webcam 5"; + type_id = 730; + break; + case 0x4011: + Info("Creative Labs Webcam Pro Ex detected.\n"); + name = "Creative Labs Webcam Pro Ex"; + type_id = 740; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x04cc) { + switch(product_id) { + case 0x8116: + Info("Sotec Afina Eye USB webcam detected.\n"); + name = "Sotec Afina Eye"; + type_id = 730; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x06be) { + switch(product_id) { + case 0x8116: + /* This is essentially the same cam as the Sotec Afina Eye */ + Info("AME Co. Afina Eye USB webcam detected.\n"); + name = "AME Co. Afina Eye"; + type_id = 750; + break; + default: + return -ENODEV; + break; + } + + } + else if (vendor_id == 0x0d81) { + switch(product_id) { + case 0x1900: + Info("Visionite VCS-UC300 USB webcam detected.\n"); + name = "Visionite VCS-UC300"; + type_id = 740; /* CCD sensor */ + break; + case 0x1910: + Info("Visionite VCS-UM100 USB webcam detected.\n"); + name = "Visionite VCS-UM100"; + type_id = 730; /* CMOS sensor */ + break; + default: + return -ENODEV; + break; + } + } + else + return -ENODEV; /* Not any of the know types; but the list keeps growing. */ + + memset(serial_number, 0, 30); + usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29); + Trace(TRACE_PROBE, "Device serial number is %s\n", serial_number); + + if (udev->descriptor.bNumConfigurations > 1) + Info("Warning: more than 1 configuration available.\n"); + + /* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */ + pdev = kzalloc(sizeof(struct pwc_device), GFP_KERNEL); + if (pdev == NULL) { + Err("Oops, could not allocate memory for pwc_device.\n"); + return -ENOMEM; + } + pdev->type = type_id; + pdev->vsize = default_size; + pdev->vframes = default_fps; + strcpy(pdev->serial, serial_number); + pdev->features = features; + if (vendor_id == 0x046D && product_id == 0x08B5) + { + /* Logitech QuickCam Orbit + The ranges have been determined experimentally; they may differ from cam to cam. + Also, the exact ranges left-right and up-down are different for my cam + */ + pdev->angle_range.pan_min = -7000; + pdev->angle_range.pan_max = 7000; + pdev->angle_range.tilt_min = -3000; + pdev->angle_range.tilt_max = 2500; + } + + init_MUTEX(&pdev->modlock); + spin_lock_init(&pdev->ptrlock); + + pdev->udev = udev; + init_waitqueue_head(&pdev->frameq); + pdev->vcompression = pwc_preferred_compression; + + /* Allocate video_device structure */ + pdev->vdev = video_device_alloc(); + if (pdev->vdev == 0) + { + Err("Err, cannot allocate video_device struture. Failing probe."); + kfree(pdev); + return -ENOMEM; + } + memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template)); + strcpy(pdev->vdev->name, name); + pdev->vdev->owner = THIS_MODULE; + video_set_drvdata(pdev->vdev, pdev); + + pdev->release = le16_to_cpu(udev->descriptor.bcdDevice); + Trace(TRACE_PROBE, "Release: %04x\n", pdev->release); + + /* Now search device_hint[] table for a match, so we can hint a node number. */ + for (hint = 0; hint < MAX_DEV_HINTS; hint++) { + if (((device_hint[hint].type == -1) || (device_hint[hint].type == pdev->type)) && + (device_hint[hint].pdev == NULL)) { + /* so far, so good... try serial number */ + if ((device_hint[hint].serial_number[0] == '*') || !strcmp(device_hint[hint].serial_number, serial_number)) { + /* match! */ + video_nr = device_hint[hint].device_node; + Trace(TRACE_PROBE, "Found hint, will try to register as /dev/video%d\n", video_nr); + break; + } + } + } + + pdev->vdev->release = video_device_release; + i = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr); + if (i < 0) { + Err("Failed to register as video device (%d).\n", i); + video_device_release(pdev->vdev); /* Drip... drip... drip... */ + kfree(pdev); /* Oops, no memory leaks please */ + return -EIO; + } + else { + Info("Registered as /dev/video%d.\n", pdev->vdev->minor & 0x3F); + } + + /* occupy slot */ + if (hint < MAX_DEV_HINTS) + device_hint[hint].pdev = pdev; + + Trace(TRACE_PROBE, "probe() function returning struct at 0x%p.\n", pdev); + usb_set_intfdata (intf, pdev); + return 0; +} + +/* The user janked out the cable... */ +static void usb_pwc_disconnect(struct usb_interface *intf) +{ + struct pwc_device *pdev; + int hint; + + lock_kernel(); + pdev = usb_get_intfdata (intf); + usb_set_intfdata (intf, NULL); + if (pdev == NULL) { + Err("pwc_disconnect() Called without private pointer.\n"); + goto disconnect_out; + } + if (pdev->udev == NULL) { + Err("pwc_disconnect() already called for %p\n", pdev); + goto disconnect_out; + } + if (pdev->udev != interface_to_usbdev(intf)) { + Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n"); + goto disconnect_out; + } +#ifdef PWC_MAGIC + if (pdev->magic != PWC_MAGIC) { + Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n"); + goto disconnect_out; + } +#endif + + /* We got unplugged; this is signalled by an EPIPE error code */ + if (pdev->vopen) { + Info("Disconnected while webcam is in use!\n"); + pdev->error_status = EPIPE; + } + + /* Alert waiting processes */ + wake_up_interruptible(&pdev->frameq); + /* Wait until device is closed */ + while (pdev->vopen) + schedule(); + /* Device is now closed, so we can safely unregister it */ + Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n"); + video_unregister_device(pdev->vdev); + + /* Free memory (don't set pdev to 0 just yet) */ + kfree(pdev); + +disconnect_out: + /* search device_hint[] table if we occupy a slot, by any chance */ + for (hint = 0; hint < MAX_DEV_HINTS; hint++) + if (device_hint[hint].pdev == pdev) + device_hint[hint].pdev = NULL; + + unlock_kernel(); +} + + +/* *grunt* We have to do atoi ourselves :-( */ +static int pwc_atoi(const char *s) +{ + int k = 0; + + k = 0; + while (*s != '\0' && *s >= '0' && *s <= '9') { + k = 10 * k + (*s - '0'); + s++; + } + return k; +} + + +/* + * Initialization code & module stuff + */ + +static char size[10]; +static int fps = 0; +static int fbufs = 0; +static int mbufs = 0; +static int trace = -1; +static int compression = -1; +static int leds[2] = { -1, -1 }; +static char *dev_hint[MAX_DEV_HINTS] = { }; + +module_param_string(size, size, sizeof(size), 0); +MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif, vga"); +module_param(fps, int, 0000); +MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30"); +module_param(fbufs, int, 0000); +MODULE_PARM_DESC(fbufs, "Number of internal frame buffers to reserve"); +module_param(mbufs, int, 0000); +MODULE_PARM_DESC(mbufs, "Number of external (mmap()ed) image buffers"); +module_param(trace, int, 0000); +MODULE_PARM_DESC(trace, "For debugging purposes"); +module_param(power_save, bool, 0000); +MODULE_PARM_DESC(power_save, "Turn power save feature in camera on or off"); +module_param(compression, int, 0000); +MODULE_PARM_DESC(compression, "Preferred compression quality. Range 0 (uncompressed) to 3 (high compression)"); +module_param_array(leds, int, NULL, 0000); +MODULE_PARM_DESC(leds, "LED on,off time in milliseconds"); +module_param_array(dev_hint, charp, NULL, 0000); +MODULE_PARM_DESC(dev_hint, "Device node hints"); + +MODULE_DESCRIPTION("Philips & OEM USB webcam driver"); +MODULE_AUTHOR("Luc Saillard "); +MODULE_LICENSE("GPL"); + +static int __init usb_pwc_init(void) +{ + int i, sz; + char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" }; + + Info("Philips webcam module version " PWC_VERSION " loaded.\n"); + Info("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n"); + Info("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n"); + Info("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n"); + + if (fps) { + if (fps < 4 || fps > 30) { + Err("Framerate out of bounds (4-30).\n"); + return -EINVAL; + } + default_fps = fps; + Info("Default framerate set to %d.\n", default_fps); + } + + if (size[0]) { + /* string; try matching with array */ + for (sz = 0; sz < PSZ_MAX; sz++) { + if (!strcmp(sizenames[sz], size)) { /* Found! */ + default_size = sz; + break; + } + } + if (sz == PSZ_MAX) { + Err("Size not recognized; try size=[sqcif | qsif | qcif | sif | cif | vga].\n"); + return -EINVAL; + } + Info("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y); + } + if (mbufs) { + if (mbufs < 1 || mbufs > MAX_IMAGES) { + Err("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES); + return -EINVAL; + } + default_mbufs = mbufs; + Info("Number of image buffers set to %d.\n", default_mbufs); + } + if (fbufs) { + if (fbufs < 2 || fbufs > MAX_FRAMES) { + Err("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES); + return -EINVAL; + } + default_fbufs = fbufs; + Info("Number of frame buffers set to %d.\n", default_fbufs); + } + if (trace >= 0) { + Info("Trace options: 0x%04x\n", trace); + pwc_trace = trace; + } + if (compression >= 0) { + if (compression > 3) { + Err("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n"); + return -EINVAL; + } + pwc_preferred_compression = compression; + Info("Preferred compression set to %d.\n", pwc_preferred_compression); + } + if (power_save) + Info("Enabling power save on open/close.\n"); + if (leds[0] >= 0) + led_on = leds[0]; + if (leds[1] >= 0) + led_off = leds[1]; + + /* Big device node whoopla. Basically, it allows you to assign a + device node (/dev/videoX) to a camera, based on its type + & serial number. The format is [type[.serialnumber]:]node. + + Any camera that isn't matched by these rules gets the next + available free device node. + */ + for (i = 0; i < MAX_DEV_HINTS; i++) { + char *s, *colon, *dot; + + /* This loop also initializes the array */ + device_hint[i].pdev = NULL; + s = dev_hint[i]; + if (s != NULL && *s != '\0') { + device_hint[i].type = -1; /* wildcard */ + strcpy(device_hint[i].serial_number, "*"); + + /* parse string: chop at ':' & '/' */ + colon = dot = s; + while (*colon != '\0' && *colon != ':') + colon++; + while (*dot != '\0' && *dot != '.') + dot++; + /* Few sanity checks */ + if (*dot != '\0' && dot > colon) { + Err("Malformed camera hint: the colon must be after the dot.\n"); + return -EINVAL; + } + + if (*colon == '\0') { + /* No colon */ + if (*dot != '\0') { + Err("Malformed camera hint: no colon + device node given.\n"); + return -EINVAL; + } + else { + /* No type or serial number specified, just a number. */ + device_hint[i].device_node = pwc_atoi(s); + } + } + else { + /* There's a colon, so we have at least a type and a device node */ + device_hint[i].type = pwc_atoi(s); + device_hint[i].device_node = pwc_atoi(colon + 1); + if (*dot != '\0') { + /* There's a serial number as well */ + int k; + + dot++; + k = 0; + while (*dot != ':' && k < 29) { + device_hint[i].serial_number[k++] = *dot; + dot++; + } + device_hint[i].serial_number[k] = '\0'; + } + } +#if PWC_DEBUG + Debug("device_hint[%d]:\n", i); + Debug(" type : %d\n", device_hint[i].type); + Debug(" serial# : %s\n", device_hint[i].serial_number); + Debug(" node : %d\n", device_hint[i].device_node); +#endif + } + else + device_hint[i].type = 0; /* not filled */ + } /* ..for MAX_DEV_HINTS */ + + Trace(TRACE_PROBE, "Registering driver at address 0x%p.\n", &pwc_driver); + return usb_register(&pwc_driver); +} + +static void __exit usb_pwc_exit(void) +{ + Trace(TRACE_MODULE, "Deregistering driver.\n"); + usb_deregister(&pwc_driver); + Info("Philips webcam module removed.\n"); +} + +module_init(usb_pwc_init); +module_exit(usb_pwc_exit); + diff --git a/linux/drivers/media/video/pwc/pwc-ioctl.h b/linux/drivers/media/video/pwc/pwc-ioctl.h new file mode 100644 index 000000000..784bc7252 --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-ioctl.h @@ -0,0 +1,292 @@ +#ifndef PWC_IOCTL_H +#define PWC_IOCTL_H + +/* (C) 2001-2004 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This is pwc-ioctl.h belonging to PWC 8.12.1 + It contains structures and defines to communicate from user space + directly to the driver. + */ + +/* + Changes + 2001/08/03 Alvarado Added ioctl constants to access methods for + changing white balance and red/blue gains + 2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE + 2003/12/13 Nemosft Unv. Some modifications to make interfacing to + PWCX easier + */ + +/* These are private ioctl() commands, specific for the Philips webcams. + They contain functions not found in other webcams, and settings not + specified in the Video4Linux API. + + The #define names are built up like follows: + VIDIOC VIDeo IOCtl prefix + PWC Philps WebCam + G optional: Get + S optional: Set + ... the function + */ + + + /* Enumeration of image sizes */ +#define PSZ_SQCIF 0x00 +#define PSZ_QSIF 0x01 +#define PSZ_QCIF 0x02 +#define PSZ_SIF 0x03 +#define PSZ_CIF 0x04 +#define PSZ_VGA 0x05 +#define PSZ_MAX 6 + + +/* The frame rate is encoded in the video_window.flags parameter using + the upper 16 bits, since some flags are defined nowadays. The following + defines provide a mask and shift to filter out this value. + + In 'Snapshot' mode the camera freezes its automatic exposure and colour + balance controls. + */ +#define PWC_FPS_SHIFT 16 +#define PWC_FPS_MASK 0x00FF0000 +#define PWC_FPS_FRMASK 0x003F0000 +#define PWC_FPS_SNAPSHOT 0x00400000 + + +/* structure for transferring x & y coordinates */ +struct pwc_coord +{ + int x, y; /* guess what */ + int size; /* size, or offset */ +}; + + +/* Used with VIDIOCPWCPROBE */ +struct pwc_probe +{ + char name[32]; + int type; +}; + +struct pwc_serial +{ + char serial[30]; /* String with serial number. Contains terminating 0 */ +}; + +/* pwc_whitebalance.mode values */ +#define PWC_WB_INDOOR 0 +#define PWC_WB_OUTDOOR 1 +#define PWC_WB_FL 2 +#define PWC_WB_MANUAL 3 +#define PWC_WB_AUTO 4 + +/* Used with VIDIOCPWC[SG]AWB (Auto White Balance). + Set mode to one of the PWC_WB_* values above. + *red and *blue are the respective gains of these colour components inside + the camera; range 0..65535 + When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read; + otherwise undefined. + 'read_red' and 'read_blue' are read-only. +*/ +struct pwc_whitebalance +{ + int mode; + int manual_red, manual_blue; /* R/W */ + int read_red, read_blue; /* R/O */ +}; + +/* + 'control_speed' and 'control_delay' are used in automatic whitebalance mode, + and tell the camera how fast it should react to changes in lighting, and + with how much delay. Valid values are 0..65535. +*/ +struct pwc_wb_speed +{ + int control_speed; + int control_delay; + +}; + +/* Used with VIDIOCPWC[SG]LED */ +struct pwc_leds +{ + int led_on; /* Led on-time; range = 0..25000 */ + int led_off; /* Led off-time; range = 0..25000 */ +}; + +/* Image size (used with GREALSIZE) */ +struct pwc_imagesize +{ + int width; + int height; +}; + +/* Defines and structures for Motorized Pan & Tilt */ +#define PWC_MPT_PAN 0x01 +#define PWC_MPT_TILT 0x02 +#define PWC_MPT_TIMEOUT 0x04 /* for status */ + +/* Set angles; when absolute != 0, the angle is absolute and the + driver calculates the relative offset for you. This can only + be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns + absolute angles. + */ +struct pwc_mpt_angles +{ + int absolute; /* write-only */ + int pan; /* degrees * 100 */ + int tilt; /* degress * 100 */ +}; + +/* Range of angles of the camera, both horizontally and vertically. + */ +struct pwc_mpt_range +{ + int pan_min, pan_max; /* degrees * 100 */ + int tilt_min, tilt_max; +}; + +struct pwc_mpt_status +{ + int status; + int time_pan; + int time_tilt; +}; + + +/* This is used for out-of-kernel decompression. With it, you can get + all the necessary information to initialize and use the decompressor + routines in standalone applications. + */ +struct pwc_video_command +{ + int type; /* camera type (645, 675, 730, etc.) */ + int release; /* release number */ + + int size; /* one of PSZ_* */ + int alternate; + int command_len; /* length of USB video command */ + unsigned char command_buf[13]; /* Actual USB video command */ + int bandlength; /* >0 = compressed */ + int frame_size; /* Size of one (un)compressed frame */ +}; + +/* Flags for PWCX subroutines. Not all modules honour all flags. */ +#define PWCX_FLAG_PLANAR 0x0001 +#define PWCX_FLAG_BAYER 0x0008 + + +/* IOCTL definitions */ + + /* Restore user settings */ +#define VIDIOCPWCRUSER _IO('v', 192) + /* Save user settings */ +#define VIDIOCPWCSUSER _IO('v', 193) + /* Restore factory settings */ +#define VIDIOCPWCFACTORY _IO('v', 194) + + /* You can manipulate the compression factor. A compression preference of 0 + means use uncompressed modes when available; 1 is low compression, 2 is + medium and 3 is high compression preferred. Of course, the higher the + compression, the lower the bandwidth used but more chance of artefacts + in the image. The driver automatically chooses a higher compression when + the preferred mode is not available. + */ + /* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */ +#define VIDIOCPWCSCQUAL _IOW('v', 195, int) + /* Get preferred compression quality */ +#define VIDIOCPWCGCQUAL _IOR('v', 195, int) + + +/* Retrieve serial number of camera */ +#define VIDIOCPWCGSERIAL _IOR('v', 198, struct pwc_serial) + + /* This is a probe function; since so many devices are supported, it + becomes difficult to include all the names in programs that want to + check for the enhanced Philips stuff. So in stead, try this PROBE; + it returns a structure with the original name, and the corresponding + Philips type. + To use, fill the structure with zeroes, call PROBE and if that succeeds, + compare the name with that returned from VIDIOCGCAP; they should be the + same. If so, you can be assured it is a Philips (OEM) cam and the type + is valid. + */ +#define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe) + + /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */ +#define VIDIOCPWCSAGC _IOW('v', 200, int) + /* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCGAGC _IOR('v', 200, int) + /* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCSSHUTTER _IOW('v', 201, int) + + /* Color compensation (Auto White Balance) */ +#define VIDIOCPWCSAWB _IOW('v', 202, struct pwc_whitebalance) +#define VIDIOCPWCGAWB _IOR('v', 202, struct pwc_whitebalance) + + /* Auto WB speed */ +#define VIDIOCPWCSAWBSPEED _IOW('v', 203, struct pwc_wb_speed) +#define VIDIOCPWCGAWBSPEED _IOR('v', 203, struct pwc_wb_speed) + + /* LEDs on/off/blink; int range 0..65535 */ +#define VIDIOCPWCSLED _IOW('v', 205, struct pwc_leds) +#define VIDIOCPWCGLED _IOR('v', 205, struct pwc_leds) + + /* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */ +#define VIDIOCPWCSCONTOUR _IOW('v', 206, int) +#define VIDIOCPWCGCONTOUR _IOR('v', 206, int) + + /* Backlight compensation; 0 = off, otherwise on */ +#define VIDIOCPWCSBACKLIGHT _IOW('v', 207, int) +#define VIDIOCPWCGBACKLIGHT _IOR('v', 207, int) + + /* Flickerless mode; = 0 off, otherwise on */ +#define VIDIOCPWCSFLICKER _IOW('v', 208, int) +#define VIDIOCPWCGFLICKER _IOR('v', 208, int) + + /* Dynamic noise reduction; 0 off, 3 = high noise reduction */ +#define VIDIOCPWCSDYNNOISE _IOW('v', 209, int) +#define VIDIOCPWCGDYNNOISE _IOR('v', 209, int) + + /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */ +#define VIDIOCPWCGREALSIZE _IOR('v', 210, struct pwc_imagesize) + + /* Motorized pan & tilt functions */ +#define VIDIOCPWCMPTRESET _IOW('v', 211, int) +#define VIDIOCPWCMPTGRANGE _IOR('v', 211, struct pwc_mpt_range) +#define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status) + + /* Get the USB set-video command; needed for initializing libpwcx */ +#define VIDIOCPWCGVIDCMD _IOR('v', 215, struct pwc_video_command) +struct pwc_table_init_buffer { + int len; + char *buffer; + +}; +#define VIDIOCPWCGVIDTABLE _IOR('v', 216, struct pwc_table_init_buffer) + +#endif diff --git a/linux/drivers/media/video/pwc/pwc-kiara.c b/linux/drivers/media/video/pwc/pwc-kiara.c new file mode 100644 index 000000000..4c96037f7 --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-kiara.c @@ -0,0 +1,318 @@ +/* Linux driver for Philips webcam + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +/* This tables contains entries for the 730/740/750 (Kiara) camera, with + 4 different qualities (no compression, low, medium, high). + It lists the bandwidth requirements for said mode by its alternate interface + number. An alternate of 0 means that the mode is unavailable. + + There are 6 * 4 * 4 entries: + 6 different resolutions subqcif, qsif, qcif, sif, cif, vga + 6 framerates: 5, 10, 15, 20, 25, 30 + 4 compression modi: none, low, medium, high + + When an uncompressed mode is not available, the next available compressed mode + will be chosen (unless the decompressor is absent). Sometimes there are only + 1 or 2 compressed modes available; in that case entries are duplicated. +*/ + + +#include "pwc-kiara.h" +#include "pwc-uncompress.h" + +const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4] = +{ + /* SQCIF */ + { + /* 5 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 10 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 15 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, + /* QSIF */ + { + /* 5 fps */ + { + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + }, + /* 10 fps */ + { + {2, 291, 0, {0x1C, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0x01, 0x80}}, + {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, + {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, + {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, + }, + /* 15 fps */ + { + {3, 437, 0, {0x1B, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x01, 0x80}}, + {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}}, + {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}}, + {1, 192, 420, {0x13, 0xF4, 0x30, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x18, 0xC0, 0x00, 0x80}}, + }, + /* 20 fps */ + { + {4, 589, 0, {0x1A, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4D, 0x02, 0x80}}, + {3, 448, 730, {0x12, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xC0, 0x01, 0x80}}, + {2, 292, 476, {0x12, 0xF4, 0x30, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0x01, 0x80}}, + {1, 192, 312, {0x12, 0xF4, 0x50, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0x00, 0x80}}, + }, + /* 25 fps */ + { + {5, 703, 0, {0x19, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x02, 0x80}}, + {3, 447, 610, {0x11, 0xF4, 0x30, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x28, 0xBF, 0x01, 0x80}}, + {2, 292, 398, {0x11, 0xF4, 0x50, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x28, 0x24, 0x01, 0x80}}, + {1, 193, 262, {0x11, 0xF4, 0x50, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x28, 0xC1, 0x00, 0x80}}, + }, + /* 30 fps */ + { + {8, 874, 0, {0x18, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x6A, 0x03, 0x80}}, + {5, 704, 730, {0x10, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x28, 0xC0, 0x02, 0x80}}, + {3, 448, 492, {0x10, 0xF4, 0x30, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x28, 0xC0, 0x01, 0x80}}, + {2, 292, 320, {0x10, 0xF4, 0x50, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x28, 0x24, 0x01, 0x80}}, + }, + }, + /* QCIF */ + { + /* 5 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 10 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 15 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, + /* SIF */ + { + /* 5 fps */ + { + {4, 582, 0, {0x0D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x02, 0x80}}, + {3, 387, 1276, {0x05, 0xF4, 0x30, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x01, 0x80}}, + {2, 291, 960, {0x05, 0xF4, 0x30, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0x01, 0x80}}, + {1, 191, 630, {0x05, 0xF4, 0x50, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x18, 0xBF, 0x00, 0x80}}, + }, + /* 10 fps */ + { + {0, }, + {6, 775, 1278, {0x04, 0xF4, 0x30, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x03, 0x80}}, + {3, 447, 736, {0x04, 0xF4, 0x30, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x28, 0xBF, 0x01, 0x80}}, + {2, 292, 480, {0x04, 0xF4, 0x70, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x28, 0x24, 0x01, 0x80}}, + }, + /* 15 fps */ + { + {0, }, + {9, 955, 1050, {0x03, 0xF4, 0x30, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x03, 0x80}}, + {4, 592, 650, {0x03, 0xF4, 0x30, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x50, 0x02, 0x80}}, + {3, 448, 492, {0x03, 0xF4, 0x50, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x38, 0xC0, 0x01, 0x80}}, + }, + /* 20 fps */ + { + {0, }, + {9, 958, 782, {0x02, 0xF4, 0x30, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x03, 0x80}}, + {5, 703, 574, {0x02, 0xF4, 0x50, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x02, 0x80}}, + {3, 446, 364, {0x02, 0xF4, 0x90, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x38, 0xBE, 0x01, 0x80}}, + }, + /* 25 fps */ + { + {0, }, + {9, 958, 654, {0x01, 0xF4, 0x30, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x03, 0x80}}, + {6, 776, 530, {0x01, 0xF4, 0x50, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x03, 0x80}}, + {4, 592, 404, {0x01, 0xF4, 0x70, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x48, 0x50, 0x02, 0x80}}, + }, + /* 30 fps */ + { + {0, }, + {9, 957, 526, {0x00, 0xF4, 0x50, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x03, 0x80}}, + {6, 775, 426, {0x00, 0xF4, 0x70, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x03, 0x80}}, + {4, 590, 324, {0x00, 0x7A, 0x88, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x50, 0x4E, 0x02, 0x80}}, + }, + }, + /* CIF */ + { + /* 5 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 10 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 15 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, + /* VGA */ + { + /* 5 fps */ + { + {0, }, + {6, 773, 1272, {0x25, 0xF4, 0x30, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}}, + {4, 592, 976, {0x25, 0xF4, 0x50, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x02, 0x80}}, + {3, 448, 738, {0x25, 0xF4, 0x90, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x01, 0x80}}, + }, + /* 10 fps */ + { + {0, }, + {9, 956, 788, {0x24, 0xF4, 0x70, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x03, 0x80}}, + {6, 776, 640, {0x24, 0xF4, 0xB0, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x03, 0x80}}, + {4, 592, 488, {0x24, 0x7A, 0xE8, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x02, 0x80}}, + }, + /* 15 fps */ + { + {0, }, + {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}}, + {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}}, + {8, 895, 492, {0x23, 0x7A, 0xE8, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x03, 0x80}}, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, +}; + diff --git a/linux/drivers/media/video/pwc/pwc-kiara.h b/linux/drivers/media/video/pwc/pwc-kiara.h new file mode 100644 index 000000000..12929abbb --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-kiara.h @@ -0,0 +1,45 @@ +/* Linux driver for Philips webcam + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Entries for the Kiara (730/740/750) camera */ + +#ifndef PWC_KIARA_H +#define PWC_KIARA_H + +#include "pwc-ioctl.h" + +struct Kiara_table_entry +{ + char alternate; /* USB alternate interface */ + unsigned short packetsize; /* Normal packet size */ + unsigned short bandlength; /* Bandlength when decompressing */ + unsigned char mode[12]; /* precomputed mode settings for cam */ +}; + +const extern struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4]; +const extern unsigned int KiaraRomTable[8][2][16][8]; + +#endif + + diff --git a/linux/drivers/media/video/pwc/pwc-misc.c b/linux/drivers/media/video/pwc/pwc-misc.c new file mode 100644 index 000000000..58fe79747 --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-misc.c @@ -0,0 +1,140 @@ +/* Linux driver for Philips webcam + Various miscellaneous functions and tables. + (C) 1999-2003 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include + +#include "pwc.h" + +struct pwc_coord pwc_image_sizes[PSZ_MAX] = +{ + { 128, 96, 0 }, + { 160, 120, 0 }, + { 176, 144, 0 }, + { 320, 240, 0 }, + { 352, 288, 0 }, + { 640, 480, 0 }, +}; + +/* x,y -> PSZ_ */ +int pwc_decode_size(struct pwc_device *pdev, int width, int height) +{ + int i, find; + + /* Make sure we don't go beyond our max size. + NB: we have different limits for RAW and normal modes. In case + you don't have the decompressor loaded or use RAW mode, + the maximum viewable size is smaller. + */ + if (pdev->vpalette == VIDEO_PALETTE_RAW) + { + if (width > pdev->abs_max.x || height > pdev->abs_max.y) + { + Debug("VIDEO_PALETTE_RAW: going beyond abs_max.\n"); + return -1; + } + } + else + { + if (width > pdev->view_max.x || height > pdev->view_max.y) + { + Debug("VIDEO_PALETTE_ not RAW: going beyond view_max.\n"); + return -1; + } + } + + /* Find the largest size supported by the camera that fits into the + requested size. + */ + find = -1; + for (i = 0; i < PSZ_MAX; i++) { + if (pdev->image_mask & (1 << i)) { + if (pwc_image_sizes[i].x <= width && pwc_image_sizes[i].y <= height) + find = i; + } + } + return find; +} + +/* initialize variables depending on type and decompressor*/ +void pwc_construct(struct pwc_device *pdev) +{ + switch(pdev->type) { + case 645: + case 646: + pdev->view_min.x = 128; + pdev->view_min.y = 96; + pdev->view_max.x = 352; + pdev->view_max.y = 288; + pdev->abs_max.x = 352; + pdev->abs_max.y = 288; + pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QCIF | 1 << PSZ_CIF; + pdev->vcinterface = 2; + pdev->vendpoint = 4; + pdev->frame_header_size = 0; + pdev->frame_trailer_size = 0; + break; + case 675: + case 680: + case 690: + pdev->view_min.x = 128; + pdev->view_min.y = 96; + /* Anthill bug #38: PWC always reports max size, even without PWCX */ + pdev->view_max.x = 640; + pdev->view_max.y = 480; + pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA; + pdev->abs_max.x = 640; + pdev->abs_max.y = 480; + pdev->vcinterface = 3; + pdev->vendpoint = 4; + pdev->frame_header_size = 0; + pdev->frame_trailer_size = 0; + break; + case 720: + case 730: + case 740: + case 750: + pdev->view_min.x = 160; + pdev->view_min.y = 120; + pdev->view_max.x = 640; + pdev->view_max.y = 480; + pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA; + pdev->abs_max.x = 640; + pdev->abs_max.y = 480; + pdev->vcinterface = 3; + pdev->vendpoint = 5; + pdev->frame_header_size = TOUCAM_HEADER_SIZE; + pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE; + break; + } + Debug("type = %d\n",pdev->type); + pdev->vpalette = VIDEO_PALETTE_YUV420P; /* default */ + pdev->view_min.size = pdev->view_min.x * pdev->view_min.y; + pdev->view_max.size = pdev->view_max.x * pdev->view_max.y; + /* length of image, in YUV format; always allocate enough memory. */ + pdev->len_per_image = (pdev->abs_max.x * pdev->abs_max.y * 3) / 2; +} + + diff --git a/linux/drivers/media/video/pwc/pwc-nala.h b/linux/drivers/media/video/pwc/pwc-nala.h new file mode 100644 index 000000000..168c73ef7 --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-nala.h @@ -0,0 +1,66 @@ + /* SQCIF */ + { + {0, 0, {0x04, 0x01, 0x03}}, + {8, 0, {0x05, 0x01, 0x03}}, + {7, 0, {0x08, 0x01, 0x03}}, + {7, 0, {0x0A, 0x01, 0x03}}, + {6, 0, {0x0C, 0x01, 0x03}}, + {5, 0, {0x0F, 0x01, 0x03}}, + {4, 0, {0x14, 0x01, 0x03}}, + {3, 0, {0x18, 0x01, 0x03}}, + }, + /* QSIF */ + { + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + }, + /* QCIF */ + { + {0, 0, {0x04, 0x01, 0x02}}, + {8, 0, {0x05, 0x01, 0x02}}, + {7, 0, {0x08, 0x01, 0x02}}, + {6, 0, {0x0A, 0x01, 0x02}}, + {5, 0, {0x0C, 0x01, 0x02}}, + {4, 0, {0x0F, 0x01, 0x02}}, + {1, 0, {0x14, 0x01, 0x02}}, + {1, 0, {0x18, 0x01, 0x02}}, + }, + /* SIF */ + { + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + }, + /* CIF */ + { + {4, 0, {0x04, 0x01, 0x01}}, + {7, 1, {0x05, 0x03, 0x01}}, + {6, 1, {0x08, 0x03, 0x01}}, + {4, 1, {0x0A, 0x03, 0x01}}, + {3, 1, {0x0C, 0x03, 0x01}}, + {2, 1, {0x0F, 0x03, 0x01}}, + {0}, + {0}, + }, + /* VGA */ + { + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + }, diff --git a/linux/drivers/media/video/pwc/pwc-timon.c b/linux/drivers/media/video/pwc/pwc-timon.c new file mode 100644 index 000000000..175250d08 --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-timon.c @@ -0,0 +1,316 @@ +/* Linux driver for Philips webcam + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +/* This tables contains entries for the 675/680/690 (Timon) camera, with + 4 different qualities (no compression, low, medium, high). + It lists the bandwidth requirements for said mode by its alternate interface + number. An alternate of 0 means that the mode is unavailable. + + There are 6 * 4 * 4 entries: + 6 different resolutions subqcif, qsif, qcif, sif, cif, vga + 6 framerates: 5, 10, 15, 20, 25, 30 + 4 compression modi: none, low, medium, high + + When an uncompressed mode is not available, the next available compressed mode + will be chosen (unless the decompressor is absent). Sometimes there are only + 1 or 2 compressed modes available; in that case entries are duplicated. +*/ + +#include "pwc-timon.h" + +const struct Timon_table_entry Timon_table[PSZ_MAX][6][4] = +{ + /* SQCIF */ + { + /* 5 fps */ + { + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + }, + /* 10 fps */ + { + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + }, + /* 15 fps */ + { + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + }, + /* 20 fps */ + { + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + }, + /* 25 fps */ + { + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + }, + /* 30 fps */ + { + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + }, + }, + /* QSIF */ + { + /* 5 fps */ + { + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + }, + /* 10 fps */ + { + {2, 291, 0, {0x2C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0xA1, 0xC0, 0x02}}, + {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + }, + /* 15 fps */ + { + {3, 437, 0, {0x2B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x6D, 0xC0, 0x02}}, + {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {1, 191, 420, {0x2B, 0xF4, 0x0D, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + }, + /* 20 fps */ + { + {4, 588, 0, {0x2A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4C, 0x52, 0xC0, 0x02}}, + {3, 447, 730, {0x2A, 0xF4, 0x05, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 476, {0x2A, 0xF4, 0x0D, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, + {1, 192, 312, {0x2A, 0xF4, 0x1D, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}}, + }, + /* 25 fps */ + { + {5, 703, 0, {0x29, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x42, 0xC0, 0x02}}, + {3, 447, 610, {0x29, 0xF4, 0x05, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 398, {0x29, 0xF4, 0x0D, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, + {1, 192, 262, {0x29, 0xF4, 0x25, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}}, + }, + /* 30 fps */ + { + {8, 873, 0, {0x28, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x69, 0x37, 0xC0, 0x02}}, + {5, 704, 774, {0x28, 0xF4, 0x05, 0x18, 0x21, 0x17, 0x59, 0x0F, 0x18, 0xC0, 0x42, 0xC0, 0x02}}, + {3, 448, 492, {0x28, 0xF4, 0x05, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x18, 0xC0, 0x69, 0xC0, 0x02}}, + {2, 291, 320, {0x28, 0xF4, 0x1D, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}}, + }, + }, + /* QCIF */ + { + /* 5 fps */ + { + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + }, + /* 10 fps */ + { + {3, 385, 0, {0x0C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x81, 0x79, 0xC0, 0x02}}, + {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {1, 194, 532, {0x0C, 0xF4, 0x05, 0x10, 0x9A, 0x0F, 0xBE, 0x1B, 0x08, 0xC2, 0xF0, 0xC0, 0x02}}, + }, + /* 15 fps */ + { + {4, 577, 0, {0x0B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x41, 0x52, 0xC0, 0x02}}, + {3, 447, 818, {0x0B, 0xF4, 0x05, 0x19, 0x89, 0x18, 0xAD, 0x0F, 0x10, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 534, {0x0B, 0xF4, 0x05, 0x10, 0xA3, 0x0F, 0xC7, 0x19, 0x10, 0x24, 0xA1, 0xC0, 0x02}}, + {1, 195, 356, {0x0B, 0xF4, 0x15, 0x0B, 0x11, 0x0A, 0x35, 0x1E, 0x10, 0xC3, 0xF0, 0xC0, 0x02}}, + }, + /* 20 fps */ + { + {6, 776, 0, {0x0A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x08, 0x3F, 0xC0, 0x02}}, + {4, 591, 804, {0x0A, 0xF4, 0x05, 0x19, 0x1E, 0x18, 0x42, 0x0F, 0x18, 0x4F, 0x4E, 0xC0, 0x02}}, + {3, 447, 608, {0x0A, 0xF4, 0x05, 0x12, 0xFD, 0x12, 0x21, 0x15, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 291, 396, {0x0A, 0xF4, 0x15, 0x0C, 0x5E, 0x0B, 0x82, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}}, + }, + /* 25 fps */ + { + {9, 928, 0, {0x09, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA0, 0x33, 0xC0, 0x02}}, + {5, 703, 800, {0x09, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x10, 0x18, 0xBF, 0x42, 0xC0, 0x02}}, + {3, 447, 508, {0x09, 0xF4, 0x0D, 0x0F, 0xD2, 0x0E, 0xF6, 0x1B, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 332, {0x09, 0xF4, 0x1D, 0x0A, 0x5A, 0x09, 0x7E, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, + }, + /* 30 fps */ + { + {0, }, + {9, 956, 876, {0x08, 0xF4, 0x05, 0x1B, 0x58, 0x1A, 0x7C, 0x0E, 0x20, 0xBC, 0x33, 0x10, 0x02}}, + {4, 592, 542, {0x08, 0xF4, 0x05, 0x10, 0xE4, 0x10, 0x08, 0x17, 0x20, 0x50, 0x4E, 0x10, 0x02}}, + {2, 291, 266, {0x08, 0xF4, 0x25, 0x08, 0x48, 0x07, 0x6C, 0x1E, 0x20, 0x23, 0xA1, 0x10, 0x02}}, + }, + }, + /* SIF */ + { + /* 5 fps */ + { + {4, 582, 0, {0x35, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x52, 0x60, 0x02}}, + {3, 387, 1276, {0x35, 0xF4, 0x05, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x79, 0x60, 0x02}}, + {2, 291, 960, {0x35, 0xF4, 0x0D, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0xA1, 0x60, 0x02}}, + {1, 191, 630, {0x35, 0xF4, 0x1D, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x08, 0xBF, 0xF4, 0x60, 0x02}}, + }, + /* 10 fps */ + { + {0, }, + {6, 775, 1278, {0x34, 0xF4, 0x05, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x3F, 0x10, 0x02}}, + {3, 447, 736, {0x34, 0xF4, 0x15, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x18, 0xBF, 0x69, 0x10, 0x02}}, + {2, 291, 480, {0x34, 0xF4, 0x2D, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x18, 0x23, 0xA1, 0x10, 0x02}}, + }, + /* 15 fps */ + { + {0, }, + {9, 955, 1050, {0x33, 0xF4, 0x05, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x33, 0x10, 0x02}}, + {4, 591, 650, {0x33, 0xF4, 0x15, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x4F, 0x4E, 0x10, 0x02}}, + {3, 448, 492, {0x33, 0xF4, 0x25, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x28, 0xC0, 0x69, 0x10, 0x02}}, + }, + /* 20 fps */ + { + {0, }, + {9, 958, 782, {0x32, 0xF4, 0x0D, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x33, 0xD0, 0x02}}, + {5, 703, 574, {0x32, 0xF4, 0x1D, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x42, 0xD0, 0x02}}, + {3, 446, 364, {0x32, 0xF4, 0x3D, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x30, 0xBE, 0x69, 0xD0, 0x02}}, + }, + /* 25 fps */ + { + {0, }, + {9, 958, 654, {0x31, 0xF4, 0x15, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x33, 0x90, 0x02}}, + {6, 776, 530, {0x31, 0xF4, 0x25, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x3F, 0x90, 0x02}}, + {4, 592, 404, {0x31, 0xF4, 0x35, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x38, 0x50, 0x4E, 0x90, 0x02}}, + }, + /* 30 fps */ + { + {0, }, + {9, 957, 526, {0x30, 0xF4, 0x25, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x33, 0x60, 0x02}}, + {6, 775, 426, {0x30, 0xF4, 0x35, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x3F, 0x60, 0x02}}, + {4, 590, 324, {0x30, 0x7A, 0x4B, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x40, 0x4E, 0x52, 0x60, 0x02}}, + }, + }, + /* CIF */ + { + /* 5 fps */ + { + {6, 771, 0, {0x15, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x3F, 0x80, 0x02}}, + {4, 465, 1278, {0x15, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x03, 0x18, 0xD1, 0x65, 0x80, 0x02}}, + {2, 291, 800, {0x15, 0xF4, 0x15, 0x18, 0xF4, 0x17, 0x3C, 0x05, 0x18, 0x23, 0xA1, 0x80, 0x02}}, + {1, 193, 528, {0x15, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x18, 0xC1, 0xF4, 0x80, 0x02}}, + }, + /* 10 fps */ + { + {0, }, + {9, 932, 1278, {0x14, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x04, 0x30, 0xA4, 0x33, 0x10, 0x02}}, + {4, 591, 812, {0x14, 0xF4, 0x15, 0x19, 0x56, 0x17, 0x9E, 0x06, 0x28, 0x4F, 0x4E, 0x10, 0x02}}, + {2, 291, 400, {0x14, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x28, 0x23, 0xA1, 0x10, 0x02}}, + }, + /* 15 fps */ + { + {0, }, + {9, 956, 876, {0x13, 0xF4, 0x0D, 0x1B, 0x58, 0x19, 0xA0, 0x05, 0x38, 0xBC, 0x33, 0x60, 0x02}}, + {5, 703, 644, {0x13, 0xF4, 0x1D, 0x14, 0x1C, 0x12, 0x64, 0x08, 0x38, 0xBF, 0x42, 0x60, 0x02}}, + {3, 448, 410, {0x13, 0xF4, 0x3D, 0x0C, 0xC4, 0x0B, 0x0C, 0x0E, 0x38, 0xC0, 0x69, 0x60, 0x02}}, + }, + /* 20 fps */ + { + {0, }, + {9, 956, 650, {0x12, 0xF4, 0x1D, 0x14, 0x4A, 0x12, 0x92, 0x09, 0x48, 0xBC, 0x33, 0x10, 0x03}}, + {6, 776, 528, {0x12, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x40, 0x08, 0x3F, 0x10, 0x03}}, + {4, 591, 402, {0x12, 0xF4, 0x3D, 0x0C, 0x8F, 0x0A, 0xD7, 0x0E, 0x40, 0x4F, 0x4E, 0x10, 0x03}}, + }, + /* 25 fps */ + { + {0, }, + {9, 956, 544, {0x11, 0xF4, 0x25, 0x10, 0xF4, 0x0F, 0x3C, 0x0A, 0x48, 0xBC, 0x33, 0xC0, 0x02}}, + {7, 840, 478, {0x11, 0xF4, 0x2D, 0x0E, 0xEB, 0x0D, 0x33, 0x0B, 0x48, 0x48, 0x3B, 0xC0, 0x02}}, + {5, 703, 400, {0x11, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x48, 0xBF, 0x42, 0xC0, 0x02}}, + }, + /* 30 fps */ + { + {0, }, + {9, 956, 438, {0x10, 0xF4, 0x35, 0x0D, 0xAC, 0x0B, 0xF4, 0x0D, 0x50, 0xBC, 0x33, 0x10, 0x02}}, + {7, 838, 384, {0x10, 0xF4, 0x45, 0x0B, 0xFD, 0x0A, 0x45, 0x0F, 0x50, 0x46, 0x3B, 0x10, 0x02}}, + {6, 773, 354, {0x10, 0x7A, 0x4B, 0x0B, 0x0C, 0x09, 0x80, 0x10, 0x50, 0x05, 0x3F, 0x10, 0x02}}, + }, + }, + /* VGA */ + { + /* 5 fps */ + { + {0, }, + {6, 773, 1272, {0x1D, 0xF4, 0x15, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x3F, 0x10, 0x02}}, + {4, 592, 976, {0x1D, 0xF4, 0x25, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x4E, 0x10, 0x02}}, + {3, 448, 738, {0x1D, 0xF4, 0x3D, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x69, 0x10, 0x02}}, + }, + /* 10 fps */ + { + {0, }, + {9, 956, 788, {0x1C, 0xF4, 0x35, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x33, 0x10, 0x02}}, + {6, 776, 640, {0x1C, 0x7A, 0x53, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x3F, 0x10, 0x02}}, + {4, 592, 488, {0x1C, 0x7A, 0x6B, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x4E, 0x10, 0x02}}, + }, + /* 15 fps */ + { + {0, }, + {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}}, + {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}}, + {8, 895, 492, {0x1B, 0x7A, 0x6B, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x37, 0x80, 0x02}}, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, +}; + diff --git a/linux/drivers/media/video/pwc/pwc-timon.h b/linux/drivers/media/video/pwc/pwc-timon.h new file mode 100644 index 000000000..a86b3782a --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-timon.h @@ -0,0 +1,61 @@ +/* Linux driver for Philips webcam + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + + +/* This tables contains entries for the 675/680/690 (Timon) camera, with + 4 different qualities (no compression, low, medium, high). + It lists the bandwidth requirements for said mode by its alternate interface + number. An alternate of 0 means that the mode is unavailable. + + There are 6 * 4 * 4 entries: + 6 different resolutions subqcif, qsif, qcif, sif, cif, vga + 6 framerates: 5, 10, 15, 20, 25, 30 + 4 compression modi: none, low, medium, high + + When an uncompressed mode is not available, the next available compressed mode + will be chosen (unless the decompressor is absent). Sometimes there are only + 1 or 2 compressed modes available; in that case entries are duplicated. +*/ + +#ifndef PWC_TIMON_H +#define PWC_TIMON_H + +#include "pwc-ioctl.h" + +struct Timon_table_entry +{ + char alternate; /* USB alternate interface */ + unsigned short packetsize; /* Normal packet size */ + unsigned short bandlength; /* Bandlength when decompressing */ + unsigned char mode[13]; /* precomputed mode settings for cam */ +}; + +const extern struct Timon_table_entry Timon_table[PSZ_MAX][6][4]; +const extern unsigned int TimonRomTable [16][2][16][8]; + + +#endif + + diff --git a/linux/drivers/media/video/pwc/pwc-uncompress.c b/linux/drivers/media/video/pwc/pwc-uncompress.c new file mode 100644 index 000000000..b37a89a16 --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-uncompress.c @@ -0,0 +1,146 @@ +/* Linux driver for Philips webcam + Decompression frontend. + (C) 1999-2003 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include + +#include "pwc.h" +#include "pwc-uncompress.h" + +int pwc_decompress(struct pwc_device *pdev) +{ + struct pwc_frame_buf *fbuf; + int n, line, col, stride; + void *yuv, *image; + u16 *src; + u16 *dsty, *dstu, *dstv; + + if (pdev == NULL) + return -EFAULT; +#if defined(__KERNEL__) && defined(PWC_MAGIC) + if (pdev->magic != PWC_MAGIC) { + Err("pwc_decompress(): magic failed.\n"); + return -EFAULT; + } +#endif + + fbuf = pdev->read_frame; + if (fbuf == NULL) + return -EFAULT; + image = pdev->image_ptr[pdev->fill_image]; + if (!image) + return -EFAULT; + + yuv = fbuf->data + pdev->frame_header_size; /* Skip header */ + + /* Raw format; that's easy... */ + if (pdev->vpalette == VIDEO_PALETTE_RAW) + { + memcpy(image, yuv, pdev->frame_size); + return 0; + } + + if (pdev->vbandlength == 0) { + /* Uncompressed mode. We copy the data into the output buffer, + using the viewport size (which may be larger than the image + size). Unfortunately we have to do a bit of byte stuffing + to get the desired output format/size. + */ + /* + * We do some byte shuffling here to go from the + * native format to YUV420P. + */ + src = (u16 *)yuv; + n = pdev->view.x * pdev->view.y; + + /* offset in Y plane */ + stride = pdev->view.x * pdev->offset.y + pdev->offset.x; + dsty = (u16 *)(image + stride); + + /* offsets in U/V planes */ + stride = pdev->view.x * pdev->offset.y / 4 + pdev->offset.x / 2; + dstu = (u16 *)(image + n + stride); + dstv = (u16 *)(image + n + n / 4 + stride); + + /* increment after each line */ + stride = (pdev->view.x - pdev->image.x) / 2; /* u16 is 2 bytes */ + + for (line = 0; line < pdev->image.y; line++) { + for (col = 0; col < pdev->image.x; col += 4) { + *dsty++ = *src++; + *dsty++ = *src++; + if (line & 1) + *dstv++ = *src++; + else + *dstu++ = *src++; + } + dsty += stride; + if (line & 1) + dstv += (stride >> 1); + else + dstu += (stride >> 1); + } + } + else { + /* Compressed; the decompressor routines will write the data + in planar format immediately. + */ + int flags; + + flags = PWCX_FLAG_PLANAR; + if (pdev->vsize == PSZ_VGA && pdev->vframes == 5 && pdev->vsnapshot) + { + printk(KERN_ERR "pwc: Mode Bayer is not supported for now\n"); + flags |= PWCX_FLAG_BAYER; + return -ENXIO; /* No such device or address: missing decompressor */ + } + +#if 0 + switch (pdev->type) + { + case 675: + case 680: + case 690: + case 720: + case 730: + case 740: + case 750: + pwc_dec23_decompress(&pdev->image, &pdev->view, + &pdev->offset, yuv, image, flags, + pdev->decompress_data, pdev->vbandlength); + break; + case 645: + case 646: + /* TODO & FIXME */ + return -ENXIO; /* Missing decompressor */ + break; + } +#endif + } + return 0; +} + + diff --git a/linux/drivers/media/video/pwc/pwc-uncompress.h b/linux/drivers/media/video/pwc/pwc-uncompress.h new file mode 100644 index 000000000..f75e1b6cb --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-uncompress.h @@ -0,0 +1,41 @@ +/* (C) 1999-2003 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This file is the bridge between the kernel module and the plugin; it + describes the structures and datatypes used in both modules. Any + significant change should be reflected by increasing the + pwc_decompressor_version major number. + */ +#ifndef PWC_UNCOMPRESS_H +#define PWC_UNCOMPRESS_H + +#include + +#include "pwc-ioctl.h" + +/* from pwc-dec.h */ +#define PWCX_FLAG_PLANAR 0x0001 +/* */ + +#endif diff --git a/linux/drivers/media/video/pwc/pwc.h b/linux/drivers/media/video/pwc/pwc.h new file mode 100644 index 000000000..1b0ee0ced --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc.h @@ -0,0 +1,272 @@ +/* (C) 1999-2003 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef PWC_H +#define PWC_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pwc-uncompress.h" +#include "pwc-ioctl.h" + +/* Defines and structures for the Philips webcam */ +/* Used for checking memory corruption/pointer validation */ +#define PWC_MAGIC 0x89DC10ABUL +#undef PWC_MAGIC + +/* Turn some debugging options on/off */ +#define PWC_DEBUG 0 + +/* Trace certain actions in the driver */ +#define TRACE_MODULE 0x0001 +#define TRACE_PROBE 0x0002 +#define TRACE_OPEN 0x0004 +#define TRACE_READ 0x0008 +#define TRACE_MEMORY 0x0010 +#define TRACE_FLOW 0x0020 +#define TRACE_SIZE 0x0040 +#define TRACE_PWCX 0x0080 +#define TRACE_SEQUENCE 0x1000 + +#define Trace(R, A...) if (pwc_trace & R) printk(KERN_DEBUG PWC_NAME " " A) +#define Debug(A...) printk(KERN_DEBUG PWC_NAME " " A) +#define Info(A...) printk(KERN_INFO PWC_NAME " " A) +#define Err(A...) printk(KERN_ERR PWC_NAME " " A) + + +/* Defines for ToUCam cameras */ +#define TOUCAM_HEADER_SIZE 8 +#define TOUCAM_TRAILER_SIZE 4 + +#define FEATURE_MOTOR_PANTILT 0x0001 + +/* Version block */ +#define PWC_MAJOR 9 +#define PWC_MINOR 0 +#define PWC_VERSION "9.0.2-unofficial" +#define PWC_NAME "pwc" + +/* Turn certain features on/off */ +#define PWC_INT_PIPE 0 + +/* Ignore errors in the first N frames, to allow for startup delays */ +#define FRAME_LOWMARK 5 + +/* Size and number of buffers for the ISO pipe. */ +#define MAX_ISO_BUFS 2 +#define ISO_FRAMES_PER_DESC 10 +#define ISO_MAX_FRAME_SIZE 960 +#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) + +/* Frame buffers: contains compressed or uncompressed video data. */ +#define MAX_FRAMES 5 +/* Maximum size after decompression is 640x480 YUV data, 1.5 * 640 * 480 */ +#define PWC_FRAME_SIZE (460800 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE) + +/* Absolute maximum number of buffers available for mmap() */ +#define MAX_IMAGES 10 + +/* The following structures were based on cpia.h. Why reinvent the wheel? :-) */ +struct pwc_iso_buf +{ + void *data; + int length; + int read; + struct urb *urb; +}; + +/* intermediate buffers with raw data from the USB cam */ +struct pwc_frame_buf +{ + void *data; + volatile int filled; /* number of bytes filled */ + struct pwc_frame_buf *next; /* list */ +#if PWC_DEBUG + int sequence; /* Sequence number */ +#endif +}; + +struct pwc_device +{ + struct video_device *vdev; +#ifdef PWC_MAGIC + int magic; +#endif + /* Pointer to our usb_device */ + struct usb_device *udev; + + int type; /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */ + int release; /* release number */ + int features; /* feature bits */ + char serial[30]; /* serial number (string) */ + int error_status; /* set when something goes wrong with the cam (unplugged, USB errors) */ + int usb_init; /* set when the cam has been initialized over USB */ + + /*** Video data ***/ + int vopen; /* flag */ + int vendpoint; /* video isoc endpoint */ + int vcinterface; /* video control interface */ + int valternate; /* alternate interface needed */ + int vframes, vsize; /* frames-per-second & size (see PSZ_*) */ + int vpalette; /* palette: 420P, RAW or RGBBAYER */ + int vframe_count; /* received frames */ + int vframes_dumped; /* counter for dumped frames */ + int vframes_error; /* frames received in error */ + int vmax_packet_size; /* USB maxpacket size */ + int vlast_packet_size; /* for frame synchronisation */ + int visoc_errors; /* number of contiguous ISOC errors */ + int vcompression; /* desired compression factor */ + int vbandlength; /* compressed band length; 0 is uncompressed */ + char vsnapshot; /* snapshot mode */ + char vsync; /* used by isoc handler */ + char vmirror; /* for ToUCaM series */ + + int cmd_len; + unsigned char cmd_buf[13]; + + /* The image acquisition requires 3 to 4 steps: + 1. data is gathered in short packets from the USB controller + 2. data is synchronized and packed into a frame buffer + 3a. in case data is compressed, decompress it directly into image buffer + 3b. in case data is uncompressed, copy into image buffer with viewport + 4. data is transferred to the user process + + Note that MAX_ISO_BUFS != MAX_FRAMES != MAX_IMAGES.... + We have in effect a back-to-back-double-buffer system. + */ + /* 1: isoc */ + struct pwc_iso_buf sbuf[MAX_ISO_BUFS]; + char iso_init; + + /* 2: frame */ + struct pwc_frame_buf *fbuf; /* all frames */ + struct pwc_frame_buf *empty_frames, *empty_frames_tail; /* all empty frames */ + struct pwc_frame_buf *full_frames, *full_frames_tail; /* all filled frames */ + struct pwc_frame_buf *fill_frame; /* frame currently being filled */ + struct pwc_frame_buf *read_frame; /* frame currently read by user process */ + int frame_header_size, frame_trailer_size; + int frame_size; + int frame_total_size; /* including header & trailer */ + int drop_frames; +#if PWC_DEBUG + int sequence; /* Debugging aid */ +#endif + + /* 3: decompression */ + struct pwc_decompressor *decompressor; /* function block with decompression routines */ + void *decompress_data; /* private data for decompression engine */ + + /* 4: image */ + /* We have an 'image' and a 'view', where 'image' is the fixed-size image + as delivered by the camera, and 'view' is the size requested by the + program. The camera image is centered in this viewport, laced with + a gray or black border. view_min <= image <= view <= view_max; + */ + int image_mask; /* bitmask of supported sizes */ + struct pwc_coord view_min, view_max; /* minimum and maximum viewable sizes */ + struct pwc_coord abs_max; /* maximum supported size with compression */ + struct pwc_coord image, view; /* image and viewport size */ + struct pwc_coord offset; /* offset within the viewport */ + + void *image_data; /* total buffer, which is subdivided into ... */ + void *image_ptr[MAX_IMAGES]; /* ...several images... */ + int fill_image; /* ...which are rotated. */ + int len_per_image; /* length per image */ + int image_read_pos; /* In case we read data in pieces, keep track of were we are in the imagebuffer */ + int image_used[MAX_IMAGES]; /* For MCAPTURE and SYNC */ + + struct semaphore modlock; /* to prevent races in video_open(), etc */ + spinlock_t ptrlock; /* for manipulating the buffer pointers */ + + /*** motorized pan/tilt feature */ + struct pwc_mpt_range angle_range; + int pan_angle; /* in degrees * 100 */ + int tilt_angle; /* absolute angle; 0,0 is home position */ + + /*** Misc. data ***/ + wait_queue_head_t frameq; /* When waiting for a frame to finish... */ +#if PWC_INT_PIPE + void *usb_int_handler; /* for the interrupt endpoint */ +#endif +}; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Global variable */ +extern int pwc_trace; + +/** functions in pwc-if.c */ +int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot); + +/** Functions in pwc-misc.c */ +/* sizes in pixels */ +extern struct pwc_coord pwc_image_sizes[PSZ_MAX]; + +int pwc_decode_size(struct pwc_device *pdev, int width, int height); +void pwc_construct(struct pwc_device *pdev); + +/** Functions in pwc-ctrl.c */ +/* Request a certain video mode. Returns < 0 if not possible */ +extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot); + +/* Various controls; should be obvious. Value 0..65535, or < 0 on error */ +extern int pwc_get_brightness(struct pwc_device *pdev); +extern int pwc_set_brightness(struct pwc_device *pdev, int value); +extern int pwc_get_contrast(struct pwc_device *pdev); +extern int pwc_set_contrast(struct pwc_device *pdev, int value); +extern int pwc_get_gamma(struct pwc_device *pdev); +extern int pwc_set_gamma(struct pwc_device *pdev, int value); +extern int pwc_get_saturation(struct pwc_device *pdev); +extern int pwc_set_saturation(struct pwc_device *pdev, int value); +extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value); +extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor); + +/* Power down or up the camera; not supported by all models */ +extern int pwc_camera_power(struct pwc_device *pdev, int power); + +/* Private ioctl()s; see pwc-ioctl.h */ +extern int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg); + + +/** pwc-uncompress.c */ +/* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */ +extern int pwc_decompress(struct pwc_device *pdev); + +#ifdef __cplusplus +} +#endif + + +#endif -- cgit v1.2.3 From dfcaa3ea83ab15bc80c18f7cc0dcec2e04dd2dcc Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Apr 2006 16:20:19 -0300 Subject: Removed uneeded stuff from pwc Makefile From: Mauro Carvalho Chehab Also, allow pwc compilation in-tree. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/pwc/Makefile | 17 ----------------- linux/drivers/media/video/pwc/pwc.h | 1 + 2 files changed, 1 insertion(+), 17 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pwc/Makefile b/linux/drivers/media/video/pwc/Makefile index 8326684f4..33d60126c 100644 --- a/linux/drivers/media/video/pwc/Makefile +++ b/linux/drivers/media/video/pwc/Makefile @@ -1,20 +1,3 @@ -ifneq ($(KERNELRELEASE),) - pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o pwc-timon.o pwc-kiara.o obj-$(CONFIG_USB_PWC) += pwc.o - -else - -KDIR := /lib/modules/$(shell uname -r)/build -PWD := $(shell pwd) - -default: - $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules - -endif - -clean: - rm -f *.[oas] .*.flags *.ko .*.cmd .*.d .*.tmp *.mod.c - rm -rf .tmp_versions - diff --git a/linux/drivers/media/video/pwc/pwc.h b/linux/drivers/media/video/pwc/pwc.h index 1b0ee0ced..74d003695 100644 --- a/linux/drivers/media/video/pwc/pwc.h +++ b/linux/drivers/media/video/pwc/pwc.h @@ -29,6 +29,7 @@ #include #include #include +#include "compat.h" #include #include #include -- cgit v1.2.3 From 94d6b250a4e096977c6e3c22b3a9a8a10b436829 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Apr 2006 16:23:36 -0300 Subject: Added sn9c102 module in tree From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/sn9c102/Kconfig | 11 + linux/drivers/media/video/sn9c102/Makefile | 7 + linux/drivers/media/video/sn9c102/sn9c102.h | 219 ++ linux/drivers/media/video/sn9c102/sn9c102_core.c | 2919 ++++++++++++++++++++ .../drivers/media/video/sn9c102/sn9c102_hv7131d.c | 271 ++ linux/drivers/media/video/sn9c102/sn9c102_mi0343.c | 363 +++ linux/drivers/media/video/sn9c102/sn9c102_ov7630.c | 401 +++ .../drivers/media/video/sn9c102/sn9c102_pas106b.c | 307 ++ .../media/video/sn9c102/sn9c102_pas202bca.c | 238 ++ .../media/video/sn9c102/sn9c102_pas202bcb.c | 293 ++ linux/drivers/media/video/sn9c102/sn9c102_sensor.h | 390 +++ .../media/video/sn9c102/sn9c102_tas5110c1b.c | 159 ++ .../media/video/sn9c102/sn9c102_tas5130d1b.c | 169 ++ 13 files changed, 5747 insertions(+) create mode 100644 linux/drivers/media/video/sn9c102/Kconfig create mode 100644 linux/drivers/media/video/sn9c102/Makefile create mode 100644 linux/drivers/media/video/sn9c102/sn9c102.h create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_core.c create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_hv7131d.c create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_mi0343.c create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_ov7630.c create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_pas106b.c create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_pas202bca.c create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_pas202bcb.c create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_sensor.h create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/sn9c102/Kconfig b/linux/drivers/media/video/sn9c102/Kconfig new file mode 100644 index 000000000..cf552e6b8 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/Kconfig @@ -0,0 +1,11 @@ +config USB_SN9C102 + tristate "USB SN9C10x PC Camera Controller support" + depends on USB && VIDEO_V4L1 + ---help--- + Say Y here if you want support for cameras based on SONiX SN9C101, + SN9C102 or SN9C103 PC Camera Controllers. + + See for more info. + + To compile this driver as a module, choose M here: the + module will be called sn9c102. diff --git a/linux/drivers/media/video/sn9c102/Makefile b/linux/drivers/media/video/sn9c102/Makefile new file mode 100644 index 000000000..536ad3098 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/Makefile @@ -0,0 +1,7 @@ +sn9c102-objs := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o \ + sn9c102_ov7630.o sn9c102_pas106b.o sn9c102_pas202bca.o \ + sn9c102_pas202bcb.o sn9c102_tas5110c1b.o \ + sn9c102_tas5130d1b.o + +obj-$(CONFIG_USB_SN9C102) += sn9c102.o + diff --git a/linux/drivers/media/video/sn9c102/sn9c102.h b/linux/drivers/media/video/sn9c102/sn9c102.h new file mode 100644 index 000000000..3f644c63f --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102.h @@ -0,0 +1,219 @@ +/*************************************************************************** + * V4L2 driver for SN9C10x PC Camera Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _SN9C102_H_ +#define _SN9C102_H_ + +#include +#include +#include "compat.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sn9c102_sensor.h" + +/*****************************************************************************/ + +#define SN9C102_DEBUG +#define SN9C102_DEBUG_LEVEL 2 +#define SN9C102_MAX_DEVICES 64 +#define SN9C102_PRESERVE_IMGSCALE 0 +#define SN9C102_FORCE_MUNMAP 0 +#define SN9C102_MAX_FRAMES 32 +#define SN9C102_URBS 2 +#define SN9C102_ISO_PACKETS 7 +#define SN9C102_ALTERNATE_SETTING 8 +#define SN9C102_URB_TIMEOUT msecs_to_jiffies(2 * SN9C102_ISO_PACKETS) +#define SN9C102_CTRL_TIMEOUT 300 +#define SN9C102_FRAME_TIMEOUT 2 + +/*****************************************************************************/ + +enum sn9c102_bridge { + BRIDGE_SN9C101 = 0x01, + BRIDGE_SN9C102 = 0x02, + BRIDGE_SN9C103 = 0x04, +}; + +SN9C102_ID_TABLE +SN9C102_SENSOR_TABLE + +enum sn9c102_frame_state { + F_UNUSED, + F_QUEUED, + F_GRABBING, + F_DONE, + F_ERROR, +}; + +struct sn9c102_frame_t { + void* bufmem; + struct v4l2_buffer buf; + enum sn9c102_frame_state state; + struct list_head frame; + unsigned long vma_use_count; +}; + +enum sn9c102_dev_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +enum sn9c102_io_method { + IO_NONE, + IO_READ, + IO_MMAP, +}; + +enum sn9c102_stream_state { + STREAM_OFF, + STREAM_INTERRUPT, + STREAM_ON, +}; + +typedef char sn9c103_sof_header_t[18]; +typedef char sn9c102_sof_header_t[12]; +typedef char sn9c102_eof_header_t[4]; + +struct sn9c102_sysfs_attr { + u8 reg, i2c_reg; + sn9c103_sof_header_t frame_header; +}; + +struct sn9c102_module_param { + u8 force_munmap; + u16 frame_timeout; +}; + +static DEFINE_MUTEX(sn9c102_sysfs_lock); +static DECLARE_RWSEM(sn9c102_disconnect); + +struct sn9c102_device { + struct video_device* v4ldev; + + enum sn9c102_bridge bridge; + struct sn9c102_sensor sensor; + + struct usb_device* usbdev; + struct urb* urb[SN9C102_URBS]; + void* transfer_buffer[SN9C102_URBS]; + u8* control_buffer; + + struct sn9c102_frame_t *frame_current, frame[SN9C102_MAX_FRAMES]; + struct list_head inqueue, outqueue; + u32 frame_count, nbuffers, nreadbuffers; + + enum sn9c102_io_method io; + enum sn9c102_stream_state stream; + + struct v4l2_jpegcompression compression; + + struct sn9c102_sysfs_attr sysfs; + sn9c103_sof_header_t sof_header; + u16 reg[63]; + + struct sn9c102_module_param module_param; + + enum sn9c102_dev_state state; + u8 users; + + struct mutex dev_mutex, fileop_mutex; + spinlock_t queue_lock; + wait_queue_head_t open, wait_frame, wait_stream; +}; + +/*****************************************************************************/ + +struct sn9c102_device* +sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id) +{ + if (usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id)) + return cam; + + return NULL; +} + + +void +sn9c102_attach_sensor(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor) +{ + memcpy(&cam->sensor, sensor, sizeof(struct sn9c102_sensor)); +} + +/*****************************************************************************/ + +#undef DBG +#undef KDBG +#ifdef SN9C102_DEBUG +# define DBG(level, fmt, args...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1) \ + dev_err(&cam->usbdev->dev, fmt "\n", ## args); \ + else if ((level) == 2) \ + dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ + else if ((level) >= 3) \ + dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args); \ + } \ +} while (0) +# define V4LDBG(level, name, cmd) \ +do { \ + if (debug >= (level)) \ + v4l_print_ioctl(name, cmd); \ +} while (0) +# define KDBG(level, fmt, args...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1 || (level) == 2) \ + pr_info("sn9c102: " fmt "\n", ## args); \ + else if ((level) == 3) \ + pr_debug("sn9c102: [%s:%d] " fmt "\n", __FUNCTION__, \ + __LINE__ , ## args); \ + } \ +} while (0) +#else +# define DBG(level, fmt, args...) do {;} while(0) +# define V4LDBG(level, name, cmd) do {;} while(0) +# define KDBG(level, fmt, args...) do {;} while(0) +#endif + +#undef PDBG +#define PDBG(fmt, args...) \ +dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args) + +#undef PDBGG +#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ + +#endif /* _SN9C102_H_ */ diff --git a/linux/drivers/media/video/sn9c102/sn9c102_core.c b/linux/drivers/media/video/sn9c102/sn9c102_core.c new file mode 100644 index 000000000..ea4394dc9 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_core.c @@ -0,0 +1,2919 @@ +/*************************************************************************** + * V4L2 driver for SN9C10x PC Camera Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sn9c102.h" + +/*****************************************************************************/ + +#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10x PC Camera Controllers" +#define SN9C102_MODULE_AUTHOR "(C) 2004-2006 Luca Risolia" +#define SN9C102_AUTHOR_EMAIL "" +#define SN9C102_MODULE_LICENSE "GPL" +#define SN9C102_MODULE_VERSION "1:1.27" +#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 27) + +/*****************************************************************************/ + +MODULE_DEVICE_TABLE(usb, sn9c102_id_table); + +MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL); +MODULE_DESCRIPTION(SN9C102_MODULE_NAME); +MODULE_VERSION(SN9C102_MODULE_VERSION); +MODULE_LICENSE(SN9C102_MODULE_LICENSE); + +static short video_nr[] = {[0 ... SN9C102_MAX_DEVICES-1] = -1}; +module_param_array(video_nr, short, NULL, 0444); +MODULE_PARM_DESC(video_nr, + "\n<-1|n[,...]> Specify V4L2 minor mode number." + "\n -1 = use next available (default)" + "\n n = use minor number n (integer >= 0)" + "\nYou can specify up to "__MODULE_STRING(SN9C102_MAX_DEVICES) + " cameras this way." + "\nFor example:" + "\nvideo_nr=-1,2,-1 would assign minor number 2 to" + "\nthe second camera and use auto for the first" + "\none and for every other camera." + "\n"); + +static short force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] = + SN9C102_FORCE_MUNMAP}; +module_param_array(force_munmap, bool, NULL, 0444); +MODULE_PARM_DESC(force_munmap, + "\n<0|1[,...]> Force the application to unmap previously" + "\nmapped buffer memory before calling any VIDIOC_S_CROP or" + "\nVIDIOC_S_FMT ioctl's. Not all the applications support" + "\nthis feature. This parameter is specific for each" + "\ndetected camera." + "\n 0 = do not force memory unmapping" + "\n 1 = force memory unmapping (save memory)" + "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." + "\n"); + +static unsigned int frame_timeout[] = {[0 ... SN9C102_MAX_DEVICES-1] = + SN9C102_FRAME_TIMEOUT}; +module_param_array(frame_timeout, uint, NULL, 0644); +MODULE_PARM_DESC(frame_timeout, + "\n Timeout for a video frame in seconds." + "\nThis parameter is specific for each detected camera." + "\nDefault value is "__MODULE_STRING(SN9C102_FRAME_TIMEOUT)"." + "\n"); + +#ifdef SN9C102_DEBUG +static unsigned short debug = SN9C102_DEBUG_LEVEL; +module_param(debug, ushort, 0644); +MODULE_PARM_DESC(debug, + "\n Debugging information level, from 0 to 3:" + "\n0 = none (use carefully)" + "\n1 = critical errors" + "\n2 = significant informations" + "\n3 = more verbose messages" + "\nLevel 3 is useful for testing only, when only " + "one device is used." + "\nDefault value is "__MODULE_STRING(SN9C102_DEBUG_LEVEL)"." + "\n"); +#endif + +/*****************************************************************************/ + +static sn9c102_sof_header_t sn9c102_sof_header[] = { + {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x00}, + {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01}, +}; + +static sn9c103_sof_header_t sn9c103_sof_header[] = { + {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x20}, +}; + +static sn9c102_eof_header_t sn9c102_eof_header[] = { + {0x00, 0x00, 0x00, 0x00}, + {0x40, 0x00, 0x00, 0x00}, + {0x80, 0x00, 0x00, 0x00}, + {0xc0, 0x00, 0x00, 0x00}, +}; + +/*****************************************************************************/ + +static u32 +sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, + enum sn9c102_io_method io) +{ + struct v4l2_pix_format* p = &(cam->sensor.pix_format); + struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); + const size_t imagesize = cam->module_param.force_munmap || + io == IO_READ ? + (p->width * p->height * p->priv) / 8 : + (r->width * r->height * p->priv) / 8; + void* buff = NULL; + u32 i; + + if (count > SN9C102_MAX_FRAMES) + count = SN9C102_MAX_FRAMES; + + cam->nbuffers = count; + while (cam->nbuffers > 0) { + if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize)))) + break; + cam->nbuffers--; + } + + for (i = 0; i < cam->nbuffers; i++) { + cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); + cam->frame[i].buf.index = i; + cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); + cam->frame[i].buf.length = imagesize; + cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->frame[i].buf.sequence = 0; + cam->frame[i].buf.field = V4L2_FIELD_NONE; + cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; + cam->frame[i].buf.flags = 0; + } + + return cam->nbuffers; +} + + +static void sn9c102_release_buffers(struct sn9c102_device* cam) +{ + if (cam->nbuffers) { + vfree(cam->frame[0].bufmem); + cam->nbuffers = 0; + } + cam->frame_current = NULL; +} + + +static void sn9c102_empty_framequeues(struct sn9c102_device* cam) +{ + u32 i; + + INIT_LIST_HEAD(&cam->inqueue); + INIT_LIST_HEAD(&cam->outqueue); + + for (i = 0; i < SN9C102_MAX_FRAMES; i++) { + cam->frame[i].state = F_UNUSED; + cam->frame[i].buf.bytesused = 0; + } +} + + +static void sn9c102_requeue_outqueue(struct sn9c102_device* cam) +{ + struct sn9c102_frame_t *i; + + list_for_each_entry(i, &cam->outqueue, frame) { + i->state = F_QUEUED; + list_add(&i->frame, &cam->inqueue); + } + + INIT_LIST_HEAD(&cam->outqueue); +} + + +static void sn9c102_queue_unusedframes(struct sn9c102_device* cam) +{ + unsigned long lock_flags; + u32 i; + + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].state == F_UNUSED) { + cam->frame[i].state = F_QUEUED; + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_add_tail(&cam->frame[i].frame, &cam->inqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + } +} + +/*****************************************************************************/ + +int sn9c102_write_regs(struct sn9c102_device* cam, u8* buff, u16 index) +{ + struct usb_device* udev = cam->usbdev; + int i, res; + + if (index + sizeof(buff) >= ARRAY_SIZE(cam->reg)) + return -1; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + index, 0, buff, sizeof(buff), + SN9C102_CTRL_TIMEOUT*sizeof(buff)); + if (res < 0) { + DBG(3, "Failed to write registers (index 0x%02X, error %d)", + index, res); + return -1; + } + + for (i = 0; i < sizeof(buff); i++) + cam->reg[index+i] = buff[i]; + + return 0; +} + + +int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index) +{ + struct usb_device* udev = cam->usbdev; + u8* buff = cam->control_buffer; + int res; + + if (index >= ARRAY_SIZE(cam->reg)) + return -1; + + *buff = value; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + index, 0, buff, 1, SN9C102_CTRL_TIMEOUT); + if (res < 0) { + DBG(3, "Failed to write a register (value 0x%02X, index " + "0x%02X, error %d)", value, index, res); + return -1; + } + + cam->reg[index] = value; + + return 0; +} + + +/* NOTE: reading some registers always returns 0 */ +static int sn9c102_read_reg(struct sn9c102_device* cam, u16 index) +{ + struct usb_device* udev = cam->usbdev; + u8* buff = cam->control_buffer; + int res; + + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, + index, 0, buff, 1, SN9C102_CTRL_TIMEOUT); + if (res < 0) + DBG(3, "Failed to read a register (index 0x%02X, error %d)", + index, res); + + return (res >= 0) ? (int)(*buff) : -1; +} + + +int sn9c102_pread_reg(struct sn9c102_device* cam, u16 index) +{ + if (index >= ARRAY_SIZE(cam->reg)) + return -1; + + return cam->reg[index]; +} + + +static int +sn9c102_i2c_wait(struct sn9c102_device* cam, struct sn9c102_sensor* sensor) +{ + int i, r; + + for (i = 1; i <= 5; i++) { + r = sn9c102_read_reg(cam, 0x08); + if (r < 0) + return -EIO; + if (r & 0x04) + return 0; + if (sensor->frequency & SN9C102_I2C_400KHZ) + udelay(5*16); + else + udelay(16*16); + } + return -EBUSY; +} + + +static int +sn9c102_i2c_detect_read_error(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor) +{ + int r; + r = sn9c102_read_reg(cam, 0x08); + return (r < 0 || (r >= 0 && !(r & 0x08))) ? -EIO : 0; +} + + +static int +sn9c102_i2c_detect_write_error(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor) +{ + int r; + r = sn9c102_read_reg(cam, 0x08); + return (r < 0 || (r >= 0 && (r & 0x08))) ? -EIO : 0; +} + + +int +sn9c102_i2c_try_raw_read(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor, u8 data0, u8 data1, + u8 n, u8 buffer[]) +{ + struct usb_device* udev = cam->usbdev; + u8* data = cam->control_buffer; + int err = 0, res; + + /* Write cycle */ + data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | + ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | 0x10; + data[1] = data0; /* I2C slave id */ + data[2] = data1; /* address */ + data[7] = 0x10; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_wait(cam, sensor); + + /* Read cycle - n bytes */ + data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | + ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | + (n << 4) | 0x02; + data[1] = data0; + data[7] = 0x10; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_wait(cam, sensor); + + /* The first read byte will be placed in data[4] */ + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, + 0x0a, 0, data, 5, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_detect_read_error(cam, sensor); + + PDBGG("I2C read: address 0x%02X, first read byte: 0x%02X", data1, + data[4]); + + if (err) { + DBG(3, "I2C read failed for %s image sensor", sensor->name); + return -1; + } + + if (buffer) + memcpy(buffer, data, sizeof(buffer)); + + return (int)data[4]; +} + + +int +sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor, u8 n, u8 data0, + u8 data1, u8 data2, u8 data3, u8 data4, u8 data5) +{ + struct usb_device* udev = cam->usbdev; + u8* data = cam->control_buffer; + int err = 0, res; + + /* Write cycle. It usually is address + value */ + data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | + ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) + | ((n - 1) << 4); + data[1] = data0; + data[2] = data1; + data[3] = data2; + data[4] = data3; + data[5] = data4; + data[6] = data5; + data[7] = 0x14; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_wait(cam, sensor); + err += sn9c102_i2c_detect_write_error(cam, sensor); + + if (err) + DBG(3, "I2C write failed for %s image sensor", sensor->name); + + PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, " + "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X", + n, data0, data1, data2, data3, data4, data5); + + return err ? -1 : 0; +} + + +int +sn9c102_i2c_try_read(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor, u8 address) +{ + return sn9c102_i2c_try_raw_read(cam, sensor, sensor->i2c_slave_id, + address, 1, NULL); +} + + +int +sn9c102_i2c_try_write(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor, u8 address, u8 value) +{ + return sn9c102_i2c_try_raw_write(cam, sensor, 3, + sensor->i2c_slave_id, address, + value, 0, 0, 0); +} + + +int sn9c102_i2c_read(struct sn9c102_device* cam, u8 address) +{ + return sn9c102_i2c_try_read(cam, &cam->sensor, address); +} + + +int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value) +{ + return sn9c102_i2c_try_write(cam, &cam->sensor, address, value); +} + +/*****************************************************************************/ + +static void* +sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len) +{ + size_t soflen = 0, i; + u8 j, n = 0; + + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + soflen = sizeof(sn9c102_sof_header_t); + n = sizeof(sn9c102_sof_header) / soflen; + break; + case BRIDGE_SN9C103: + soflen = sizeof(sn9c103_sof_header_t); + n = sizeof(sn9c103_sof_header) / soflen; + } + + for (i = 0; (len >= soflen) && (i <= len - soflen); i++) + for (j = 0; j < n; j++) + /* The invariable part of the header is 6 bytes long */ + if ((cam->bridge != BRIDGE_SN9C103 && + !memcmp(mem + i, sn9c102_sof_header[j], 6)) || + (cam->bridge == BRIDGE_SN9C103 && + !memcmp(mem + i, sn9c103_sof_header[j], 6))) { + memcpy(cam->sof_header, mem + i, soflen); + /* Skip the header */ + return mem + i + soflen; + } + + return NULL; +} + + +static void* +sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len) +{ + size_t eoflen = sizeof(sn9c102_eof_header_t), i; + unsigned j, n = sizeof(sn9c102_eof_header) / eoflen; + + if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) + return NULL; /* EOF header does not exist in compressed data */ + + for (i = 0; (len >= eoflen) && (i <= len - eoflen); i++) + for (j = 0; j < n; j++) + if (!memcmp(mem + i, sn9c102_eof_header[j], eoflen)) + return mem + i; + + return NULL; +} + + +static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs) +{ + struct sn9c102_device* cam = urb->context; + struct sn9c102_frame_t** f; + size_t imagesize, soflen; + u8 i; + int err = 0; + + if (urb->status == -ENOENT) + return; + + f = &cam->frame_current; + + if (cam->stream == STREAM_INTERRUPT) { + cam->stream = STREAM_OFF; + if ((*f)) + (*f)->state = F_QUEUED; + DBG(3, "Stream interrupted"); + wake_up(&cam->wait_stream); + } + + if (cam->state & DEV_DISCONNECTED) + return; + + if (cam->state & DEV_MISCONFIGURED) { + wake_up_interruptible(&cam->wait_frame); + return; + } + + if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) + goto resubmit_urb; + + if (!(*f)) + (*f) = list_entry(cam->inqueue.next, struct sn9c102_frame_t, + frame); + + imagesize = (cam->sensor.pix_format.width * + cam->sensor.pix_format.height * + cam->sensor.pix_format.priv) / 8; + + soflen = (cam->bridge) == BRIDGE_SN9C103 ? + sizeof(sn9c103_sof_header_t) : + sizeof(sn9c102_sof_header_t); + + for (i = 0; i < urb->number_of_packets; i++) { + unsigned int img, len, status; + void *pos, *sof, *eof; + + len = urb->iso_frame_desc[i].actual_length; + status = urb->iso_frame_desc[i].status; + pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; + + if (status) { + DBG(3, "Error in isochronous frame"); + (*f)->state = F_ERROR; + continue; + } + + PDBGG("Isochrnous frame: length %u, #%u i", len, i); + +redo: + sof = sn9c102_find_sof_header(cam, pos, len); + if (likely(!sof)) { + eof = sn9c102_find_eof_header(cam, pos, len); + if ((*f)->state == F_GRABBING) { +end_of_frame: + img = len; + + if (eof) + img = (eof > pos) ? eof - pos - 1 : 0; + + if ((*f)->buf.bytesused+img > imagesize) { + u32 b; + b = (*f)->buf.bytesused + img - + imagesize; + img = imagesize - (*f)->buf.bytesused; + DBG(3, "Expected EOF not found: " + "video frame cut"); + if (eof) + DBG(3, "Exceeded limit: +%u " + "bytes", (unsigned)(b)); + } + + memcpy((*f)->bufmem + (*f)->buf.bytesused, pos, + img); + + if ((*f)->buf.bytesused == 0) + do_gettimeofday(&(*f)->buf.timestamp); + + (*f)->buf.bytesused += img; + + if ((*f)->buf.bytesused == imagesize || + (cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_SN9C10X && eof)) { + u32 b; + b = (*f)->buf.bytesused; + (*f)->state = F_DONE; + (*f)->buf.sequence= ++cam->frame_count; + spin_lock(&cam->queue_lock); + list_move_tail(&(*f)->frame, + &cam->outqueue); + if (!list_empty(&cam->inqueue)) + (*f) = list_entry( + cam->inqueue.next, + struct sn9c102_frame_t, + frame ); + else + (*f) = NULL; + spin_unlock(&cam->queue_lock); + memcpy(cam->sysfs.frame_header, + cam->sof_header, soflen); + DBG(3, "Video frame captured: %lu " + "bytes", (unsigned long)(b)); + + if (!(*f)) + goto resubmit_urb; + + } else if (eof) { + (*f)->state = F_ERROR; + DBG(3, "Not expected EOF after %lu " + "bytes of image data", + (unsigned long) + ((*f)->buf.bytesused)); + } + + if (sof) /* (1) */ + goto start_of_frame; + + } else if (eof) { + DBG(3, "EOF without SOF"); + continue; + + } else { + PDBGG("Ignoring pointless isochronous frame"); + continue; + } + + } else if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) { +start_of_frame: + (*f)->state = F_GRABBING; + (*f)->buf.bytesused = 0; + len -= (sof - pos); + pos = sof; + DBG(3, "SOF detected: new video frame"); + if (len) + goto redo; + + } else if ((*f)->state == F_GRABBING) { + eof = sn9c102_find_eof_header(cam, pos, len); + if (eof && eof < sof) + goto end_of_frame; /* (1) */ + else { + if (cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_SN9C10X) { + eof = sof - soflen; + goto end_of_frame; + } else { + DBG(3, "SOF before expected EOF after " + "%lu bytes of image data", + (unsigned long) + ((*f)->buf.bytesused)); + goto start_of_frame; + } + } + } + } + +resubmit_urb: + urb->dev = cam->usbdev; + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0 && err != -EPERM) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "usb_submit_urb() failed"); + } + + wake_up_interruptible(&cam->wait_frame); +} + + +static int sn9c102_start_transfer(struct sn9c102_device* cam) +{ + struct usb_device *udev = cam->usbdev; + struct urb* urb; + const unsigned int sn9c102_wMaxPacketSize[] = {0, 128, 256, 384, 512, + 680, 800, 900, 1023}; + const unsigned int sn9c103_wMaxPacketSize[] = {0, 128, 256, 384, 512, + 680, 800, 900, 1003}; + const unsigned int psz = (cam->bridge == BRIDGE_SN9C103) ? + sn9c103_wMaxPacketSize[SN9C102_ALTERNATE_SETTING] : + sn9c102_wMaxPacketSize[SN9C102_ALTERNATE_SETTING]; + s8 i, j; + int err = 0; + + for (i = 0; i < SN9C102_URBS; i++) { + cam->transfer_buffer[i] = kzalloc(SN9C102_ISO_PACKETS * psz, + GFP_KERNEL); + if (!cam->transfer_buffer[i]) { + err = -ENOMEM; + DBG(1, "Not enough memory"); + goto free_buffers; + } + } + + for (i = 0; i < SN9C102_URBS; i++) { + urb = usb_alloc_urb(SN9C102_ISO_PACKETS, GFP_KERNEL); + cam->urb[i] = urb; + if (!urb) { + err = -ENOMEM; + DBG(1, "usb_alloc_urb() failed"); + goto free_urbs; + } + urb->dev = udev; + urb->context = cam; + urb->pipe = usb_rcvisocpipe(udev, 1); + urb->transfer_flags = URB_ISO_ASAP; + urb->number_of_packets = SN9C102_ISO_PACKETS; + urb->complete = sn9c102_urb_complete; + urb->transfer_buffer = cam->transfer_buffer[i]; + urb->transfer_buffer_length = psz * SN9C102_ISO_PACKETS; + urb->interval = 1; + for (j = 0; j < SN9C102_ISO_PACKETS; j++) { + urb->iso_frame_desc[j].offset = psz * j; + urb->iso_frame_desc[j].length = psz; + } + } + + /* Enable video */ + if (!(cam->reg[0x01] & 0x04)) { + err = sn9c102_write_reg(cam, cam->reg[0x01] | 0x04, 0x01); + if (err) { + err = -EIO; + DBG(1, "I/O hardware error"); + goto free_urbs; + } + } + + err = usb_set_interface(udev, 0, SN9C102_ALTERNATE_SETTING); + if (err) { + DBG(1, "usb_set_interface() failed"); + goto free_urbs; + } + + cam->frame_current = NULL; + + for (i = 0; i < SN9C102_URBS; i++) { + err = usb_submit_urb(cam->urb[i], GFP_KERNEL); + if (err) { + for (j = i-1; j >= 0; j--) + usb_kill_urb(cam->urb[j]); + DBG(1, "usb_submit_urb() failed, error %d", err); + goto free_urbs; + } + } + + return 0; + +free_urbs: + for (i = 0; (i < SN9C102_URBS) && cam->urb[i]; i++) + usb_free_urb(cam->urb[i]); + +free_buffers: + for (i = 0; (i < SN9C102_URBS) && cam->transfer_buffer[i]; i++) + kfree(cam->transfer_buffer[i]); + + return err; +} + + +static int sn9c102_stop_transfer(struct sn9c102_device* cam) +{ + struct usb_device *udev = cam->usbdev; + s8 i; + int err = 0; + + if (cam->state & DEV_DISCONNECTED) + return 0; + + for (i = SN9C102_URBS-1; i >= 0; i--) { + usb_kill_urb(cam->urb[i]); + usb_free_urb(cam->urb[i]); + kfree(cam->transfer_buffer[i]); + } + + err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ + if (err) + DBG(3, "usb_set_interface() failed"); + + return err; +} + + +static int sn9c102_stream_interrupt(struct sn9c102_device* cam) +{ + long timeout; + + cam->stream = STREAM_INTERRUPT; + timeout = wait_event_timeout(cam->wait_stream, + (cam->stream == STREAM_OFF) || + (cam->state & DEV_DISCONNECTED), + SN9C102_URB_TIMEOUT); + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + else if (cam->stream != STREAM_OFF) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "URB timeout reached. The camera is misconfigured. " + "To use it, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + return 0; +} + +/*****************************************************************************/ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static u8 sn9c102_strtou8(const char* buff, size_t len, ssize_t* count) +{ + char str[5]; + char* endp; + unsigned long val; + + if (len < 4) { + strncpy(str, buff, len); + str[len+1] = '\0'; + } else { + strncpy(str, buff, 4); + str[4] = '\0'; + } + + val = simple_strtoul(str, &endp, 0); + + *count = 0; + if (val <= 0xff) + *count = (ssize_t)(endp - str); + if ((*count) && (len == *count+1) && (buff[*count] == '\n')) + *count += 1; + + return (u8)val; +} + +/* + NOTE 1: being inside one of the following methods implies that the v4l + device exists for sure (see kobjects and reference counters) + NOTE 2: buffers are PAGE_SIZE long +*/ + +static ssize_t sn9c102_show_reg(struct class_device* cd, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + count = sprintf(buf, "%u\n", cam->sysfs.reg); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_reg(struct class_device* cd, const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u8 index; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + index = sn9c102_strtou8(buf, len, &count); + if (index > 0x1f || !count) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EINVAL; + } + + cam->sysfs.reg = index; + + DBG(2, "Moved SN9C10X register index to 0x%02X", cam->sysfs.reg); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t sn9c102_show_val(struct class_device* cd, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + int val; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + if ((val = sn9c102_read_reg(cam, cam->sysfs.reg)) < 0) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EIO; + } + + count = sprintf(buf, "%d\n", val); + + DBG(3, "Read bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_val(struct class_device* cd, const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u8 value; + ssize_t count; + int err; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + value = sn9c102_strtou8(buf, len, &count); + if (!count) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EINVAL; + } + + err = sn9c102_write_reg(cam, value, cam->sysfs.reg); + if (err) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EIO; + } + + DBG(2, "Written SN9C10X reg. 0x%02X, val. 0x%02X", + cam->sysfs.reg, value); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t sn9c102_show_i2c_reg(struct class_device* cd, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg); + + DBG(3, "Read bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_i2c_reg(struct class_device* cd, const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u8 index; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + index = sn9c102_strtou8(buf, len, &count); + if (!count) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EINVAL; + } + + cam->sysfs.i2c_reg = index; + + DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + int val; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + if (!(cam->sensor.sysfs_ops & SN9C102_I2C_READ)) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENOSYS; + } + + if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EIO; + } + + count = sprintf(buf, "%d\n", val); + + DBG(3, "Read bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u8 value; + ssize_t count; + int err; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + if (!(cam->sensor.sysfs_ops & SN9C102_I2C_WRITE)) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENOSYS; + } + + value = sn9c102_strtou8(buf, len, &count); + if (!count) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EINVAL; + } + + err = sn9c102_i2c_write(cam, cam->sysfs.i2c_reg, value); + if (err) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EIO; + } + + DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X", + cam->sysfs.i2c_reg, value); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_green(struct class_device* cd, const char* buf, size_t len) +{ + struct sn9c102_device* cam; + enum sn9c102_bridge bridge; + ssize_t res = 0; + u8 value; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + bridge = cam->bridge; + + mutex_unlock(&sn9c102_sysfs_lock); + + value = sn9c102_strtou8(buf, len, &count); + if (!count) + return -EINVAL; + + switch (bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + if (value > 0x0f) + return -EINVAL; + if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0) + res = sn9c102_store_val(cd, buf, len); + break; + case BRIDGE_SN9C103: + if (value > 0x7f) + return -EINVAL; + if ((res = sn9c102_store_reg(cd, "0x04", 4)) >= 0) + res = sn9c102_store_val(cd, buf, len); + break; + } + + return res; +} + + +static ssize_t +sn9c102_store_blue(struct class_device* cd, const char* buf, size_t len) +{ + ssize_t res = 0; + u8 value; + ssize_t count; + + value = sn9c102_strtou8(buf, len, &count); + if (!count || value > 0x7f) + return -EINVAL; + + if ((res = sn9c102_store_reg(cd, "0x06", 4)) >= 0) + res = sn9c102_store_val(cd, buf, len); + + return res; +} + + +static ssize_t +sn9c102_store_red(struct class_device* cd, const char* buf, size_t len) +{ + ssize_t res = 0; + u8 value; + ssize_t count; + + value = sn9c102_strtou8(buf, len, &count); + if (!count || value > 0x7f) + return -EINVAL; + + if ((res = sn9c102_store_reg(cd, "0x05", 4)) >= 0) + res = sn9c102_store_val(cd, buf, len); + + return res; +} + + +static ssize_t sn9c102_show_frame_header(struct class_device* cd, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) + return -ENODEV; + + count = sizeof(cam->sysfs.frame_header); + memcpy(buf, cam->sysfs.frame_header, count); + + DBG(3, "Frame header, read bytes: %zd", count); + + return count; +} + + +static CLASS_DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, + sn9c102_show_reg, sn9c102_store_reg); +static CLASS_DEVICE_ATTR(val, S_IRUGO | S_IWUSR, + sn9c102_show_val, sn9c102_store_val); +static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, + sn9c102_show_i2c_reg, sn9c102_store_i2c_reg); +static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, + sn9c102_show_i2c_val, sn9c102_store_i2c_val); +static CLASS_DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green); +static CLASS_DEVICE_ATTR(blue, S_IWUGO, NULL, sn9c102_store_blue); +static CLASS_DEVICE_ATTR(red, S_IWUGO, NULL, sn9c102_store_red); +static CLASS_DEVICE_ATTR(frame_header, S_IRUGO, + sn9c102_show_frame_header, NULL); + + +static void sn9c102_create_sysfs(struct sn9c102_device* cam) +{ + struct video_device *v4ldev = cam->v4ldev; + + video_device_create_file(v4ldev, &class_device_attr_reg); + video_device_create_file(v4ldev, &class_device_attr_val); + video_device_create_file(v4ldev, &class_device_attr_frame_header); + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) + video_device_create_file(v4ldev, &class_device_attr_green); + else if (cam->bridge == BRIDGE_SN9C103) { + video_device_create_file(v4ldev, &class_device_attr_blue); + video_device_create_file(v4ldev, &class_device_attr_red); + } + if (cam->sensor.sysfs_ops) { + video_device_create_file(v4ldev, &class_device_attr_i2c_reg); + video_device_create_file(v4ldev, &class_device_attr_i2c_val); + } +} +#endif /* CONFIG_VIDEO_ADV_DEBUG */ + +/*****************************************************************************/ + +static int +sn9c102_set_pix_format(struct sn9c102_device* cam, struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, 0x18); + else + err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, 0x18); + + return err ? -EIO : 0; +} + + +static int +sn9c102_set_compression(struct sn9c102_device* cam, + struct v4l2_jpegcompression* compression) +{ + int err = 0; + + if (compression->quality == 0) + err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01, 0x17); + else if (compression->quality == 1) + err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe, 0x17); + + return err ? -EIO : 0; +} + + +static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale) +{ + u8 r = 0; + int err = 0; + + if (scale == 1) + r = cam->reg[0x18] & 0xcf; + else if (scale == 2) { + r = cam->reg[0x18] & 0xcf; + r |= 0x10; + } else if (scale == 4) + r = cam->reg[0x18] | 0x20; + + err += sn9c102_write_reg(cam, r, 0x18); + if (err) + return -EIO; + + PDBGG("Scaling factor: %u", scale); + + return 0; +} + + +static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &cam->sensor; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left), + v_start = (u8)(rect->top - s->cropcap.bounds.top), + h_size = (u8)(rect->width / 16), + v_size = (u8)(rect->height / 16); + int err = 0; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + err += sn9c102_write_reg(cam, h_size, 0x15); + err += sn9c102_write_reg(cam, v_size, 0x16); + if (err) + return -EIO; + + PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size " + "%u %u %u %u", h_start, v_start, h_size, v_size); + + return 0; +} + + +static int sn9c102_init(struct sn9c102_device* cam) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + struct v4l2_queryctrl *qctrl; + struct v4l2_rect* rect; + u8 i = 0; + int err = 0; + + if (!(cam->state & DEV_INITIALIZED)) { + init_waitqueue_head(&cam->open); + qctrl = s->qctrl; + rect = &(s->cropcap.defrect); + } else { /* use current values */ + qctrl = s->_qctrl; + rect = &(s->_rect); + } + + err += sn9c102_set_scale(cam, rect->width / s->pix_format.width); + err += sn9c102_set_crop(cam, rect); + if (err) + return err; + + if (s->init) { + err = s->init(cam); + if (err) { + DBG(3, "Sensor initialization failed"); + return err; + } + } + + if (!(cam->state & DEV_INITIALIZED)) + cam->compression.quality = cam->reg[0x17] & 0x01 ? 0 : 1; + else + err += sn9c102_set_compression(cam, &cam->compression); + err += sn9c102_set_pix_format(cam, &s->pix_format); + if (s->set_pix_format) + err += s->set_pix_format(cam, &s->pix_format); + if (err) + return err; + + if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) + DBG(3, "Compressed video format is active, quality %d", + cam->compression.quality); + else + DBG(3, "Uncompressed video format is active"); + + if (s->set_crop) + if ((err = s->set_crop(cam, rect))) { + DBG(3, "set_crop() failed"); + return err; + } + + if (s->set_ctrl) { + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (s->qctrl[i].id != 0 && + !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { + ctrl.id = s->qctrl[i].id; + ctrl.value = qctrl[i].default_value; + err = s->set_ctrl(cam, &ctrl); + if (err) { + DBG(3, "Set %s control failed", + s->qctrl[i].name); + return err; + } + DBG(3, "Image sensor supports '%s' control", + s->qctrl[i].name); + } + } + + if (!(cam->state & DEV_INITIALIZED)) { + mutex_init(&cam->fileop_mutex); + spin_lock_init(&cam->queue_lock); + init_waitqueue_head(&cam->wait_frame); + init_waitqueue_head(&cam->wait_stream); + cam->nreadbuffers = 2; + memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); + memcpy(&(s->_rect), &(s->cropcap.defrect), + sizeof(struct v4l2_rect)); + cam->state |= DEV_INITIALIZED; + } + + DBG(2, "Initialization succeeded"); + return 0; +} + + +static void sn9c102_release_resources(struct sn9c102_device* cam) +{ + mutex_lock(&sn9c102_sysfs_lock); + + DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor); + video_set_drvdata(cam->v4ldev, NULL); + video_unregister_device(cam->v4ldev); + + usb_put_dev(cam->usbdev); + + mutex_unlock(&sn9c102_sysfs_lock); + + kfree(cam->control_buffer); +} + +/*****************************************************************************/ + +static int sn9c102_open(struct inode* inode, struct file* filp) +{ + struct sn9c102_device* cam; + int err = 0; + + /* + This is the only safe way to prevent race conditions with + disconnect + */ + if (!down_read_trylock(&sn9c102_disconnect)) + return -ERESTARTSYS; + + cam = video_get_drvdata(video_devdata(filp)); + + if (mutex_lock_interruptible(&cam->dev_mutex)) { + up_read(&sn9c102_disconnect); + return -ERESTARTSYS; + } + + if (cam->users) { + DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor); + if ((filp->f_flags & O_NONBLOCK) || + (filp->f_flags & O_NDELAY)) { + err = -EWOULDBLOCK; + goto out; + } + mutex_unlock(&cam->dev_mutex); + err = wait_event_interruptible_exclusive(cam->open, + cam->state & DEV_DISCONNECTED + || !cam->users); + if (err) { + up_read(&sn9c102_disconnect); + return err; + } + if (cam->state & DEV_DISCONNECTED) { + up_read(&sn9c102_disconnect); + return -ENODEV; + } + mutex_lock(&cam->dev_mutex); + } + + + if (cam->state & DEV_MISCONFIGURED) { + err = sn9c102_init(cam); + if (err) { + DBG(1, "Initialization failed again. " + "I will retry on next open()."); + goto out; + } + cam->state &= ~DEV_MISCONFIGURED; + } + + if ((err = sn9c102_start_transfer(cam))) + goto out; + + filp->private_data = cam; + cam->users++; + cam->io = IO_NONE; + cam->stream = STREAM_OFF; + cam->nbuffers = 0; + cam->frame_count = 0; + sn9c102_empty_framequeues(cam); + + DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor); + +out: + mutex_unlock(&cam->dev_mutex); + up_read(&sn9c102_disconnect); + return err; +} + + +static int sn9c102_release(struct inode* inode, struct file* filp) +{ + struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + + mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */ + + sn9c102_stop_transfer(cam); + + sn9c102_release_buffers(cam); + + if (cam->state & DEV_DISCONNECTED) { + sn9c102_release_resources(cam); + mutex_unlock(&cam->dev_mutex); + kfree(cam); + return 0; + } + + cam->users--; + wake_up_interruptible_nr(&cam->open, 1); + + DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor); + + mutex_unlock(&cam->dev_mutex); + + return 0; +} + + +static ssize_t +sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) +{ + struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + struct sn9c102_frame_t* f, * i; + unsigned long lock_flags; + long timeout; + int err = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + if (cam->io == IO_MMAP) { + DBG(3, "Close and open the device again to choose " + "the read method"); + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + if (cam->io == IO_NONE) { + if (!sn9c102_request_buffers(cam,cam->nreadbuffers, IO_READ)) { + DBG(1, "read() failed, not enough memory"); + mutex_unlock(&cam->fileop_mutex); + return -ENOMEM; + } + cam->io = IO_READ; + cam->stream = STREAM_ON; + } + + if (list_empty(&cam->inqueue)) { + if (!list_empty(&cam->outqueue)) + sn9c102_empty_framequeues(cam); + sn9c102_queue_unusedframes(cam); + } + + if (!count) { + mutex_unlock(&cam->fileop_mutex); + return 0; + } + + if (list_empty(&cam->outqueue)) { + if (filp->f_flags & O_NONBLOCK) { + mutex_unlock(&cam->fileop_mutex); + return -EAGAIN; + } + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) { + mutex_unlock(&cam->fileop_mutex); + return timeout; + } + if (cam->state & DEV_DISCONNECTED) { + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + if (!timeout || (cam->state & DEV_MISCONFIGURED)) { + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + } + + f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame); + + if (count > f->buf.bytesused) + count = f->buf.bytesused; + + if (copy_to_user(buf, f->bufmem, count)) { + err = -EFAULT; + goto exit; + } + *f_pos += count; + +exit: + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_for_each_entry(i, &cam->outqueue, frame) + i->state = F_UNUSED; + INIT_LIST_HEAD(&cam->outqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + sn9c102_queue_unusedframes(cam); + + PDBGG("Frame #%lu, bytes read: %zu", + (unsigned long)f->buf.index, count); + + mutex_unlock(&cam->fileop_mutex); + + return count; +} + + +static unsigned int sn9c102_poll(struct file *filp, poll_table *wait) +{ + struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + struct sn9c102_frame_t* f; + unsigned long lock_flags; + unsigned int mask = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return POLLERR; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + goto error; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + goto error; + } + + if (cam->io == IO_NONE) { + if (!sn9c102_request_buffers(cam, cam->nreadbuffers, + IO_READ)) { + DBG(1, "poll() failed, not enough memory"); + goto error; + } + cam->io = IO_READ; + cam->stream = STREAM_ON; + } + + if (cam->io == IO_READ) { + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_for_each_entry(f, &cam->outqueue, frame) + f->state = F_UNUSED; + INIT_LIST_HEAD(&cam->outqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + sn9c102_queue_unusedframes(cam); + } + + poll_wait(filp, &cam->wait_frame, wait); + + if (!list_empty(&cam->outqueue)) + mask |= POLLIN | POLLRDNORM; + + mutex_unlock(&cam->fileop_mutex); + + return mask; + +error: + mutex_unlock(&cam->fileop_mutex); + return POLLERR; +} + + +static void sn9c102_vm_open(struct vm_area_struct* vma) +{ + struct sn9c102_frame_t* f = vma->vm_private_data; + f->vma_use_count++; +} + + +static void sn9c102_vm_close(struct vm_area_struct* vma) +{ + /* NOTE: buffers are not freed here */ + struct sn9c102_frame_t* f = vma->vm_private_data; + f->vma_use_count--; +} + + +static struct vm_operations_struct sn9c102_vm_ops = { + .open = sn9c102_vm_open, + .close = sn9c102_vm_close, +}; + + +static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) +{ + struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + unsigned long size = vma->vm_end - vma->vm_start, + start = vma->vm_start; + void *pos; + u32 i; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || + size != PAGE_ALIGN(cam->frame[0].buf.length)) { + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + for (i = 0; i < cam->nbuffers; i++) { + if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff) + break; + } + if (i == cam->nbuffers) { + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_RESERVED; + + pos = cam->frame[i].bufmem; + while (size > 0) { /* size is page-aligned */ + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { + mutex_unlock(&cam->fileop_mutex); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + size -= PAGE_SIZE; + } + + vma->vm_ops = &sn9c102_vm_ops; + vma->vm_private_data = &cam->frame[i]; + + sn9c102_vm_open(vma); + + mutex_unlock(&cam->fileop_mutex); + + return 0; +} + +/*****************************************************************************/ + +static int +sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_capability cap = { + .driver = "sn9c102", + .version = SN9C102_MODULE_VERSION_CODE, + .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING, + }; + + strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); + if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) + strlcpy(cap.bus_info, cam->usbdev->dev.bus_id, + sizeof(cap.bus_info)); + + if (copy_to_user(arg, &cap, sizeof(cap))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_enuminput(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_input i; + + if (copy_from_user(&i, arg, sizeof(i))) + return -EFAULT; + + if (i.index) + return -EINVAL; + + memset(&i, 0, sizeof(i)); + strcpy(i.name, "Camera"); + i.type = V4L2_INPUT_TYPE_CAMERA; + + if (copy_to_user(arg, &i, sizeof(i))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_g_input(struct sn9c102_device* cam, void __user * arg) +{ + int index = 0; + + if (copy_to_user(arg, &index, sizeof(index))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_input(struct sn9c102_device* cam, void __user * arg) +{ + int index; + + if (copy_from_user(&index, arg, sizeof(index))) + return -EFAULT; + + if (index != 0) + return -EINVAL; + + return 0; +} + + +static int +sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_queryctrl qc; + u8 i; + + if (copy_from_user(&qc, arg, sizeof(qc))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (qc.id && qc.id == s->qctrl[i].id) { + memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); + if (copy_to_user(arg, &qc, sizeof(qc))) + return -EFAULT; + return 0; + } + + return -EINVAL; +} + + +static int +sn9c102_vidioc_g_ctrl(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + int err = 0; + u8 i; + + if (!s->get_ctrl && !s->set_ctrl) + return -EINVAL; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + + if (!s->get_ctrl) { + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (ctrl.id && ctrl.id == s->qctrl[i].id) { + ctrl.value = s->_qctrl[i].default_value; + goto exit; + } + return -EINVAL; + } else + err = s->get_ctrl(cam, &ctrl); + +exit: + if (copy_to_user(arg, &ctrl, sizeof(ctrl))) + return -EFAULT; + + return err; +} + + +static int +sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + u8 i; + int err = 0; + + if (!s->set_ctrl) + return -EINVAL; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (ctrl.id == s->qctrl[i].id) { + if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) + return -EINVAL; + if (ctrl.value < s->qctrl[i].minimum || + ctrl.value > s->qctrl[i].maximum) + return -ERANGE; + ctrl.value -= ctrl.value % s->qctrl[i].step; + break; + } + + if ((err = s->set_ctrl(cam, &ctrl))) + return err; + + s->_qctrl[i].default_value = ctrl.value; + + PDBGG("VIDIOC_S_CTRL: id %lu, value %lu", + (unsigned long)ctrl.id, (unsigned long)ctrl.value); + + return 0; +} + + +static int +sn9c102_vidioc_cropcap(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_cropcap* cc = &(cam->sensor.cropcap); + + cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cc->pixelaspect.numerator = 1; + cc->pixelaspect.denominator = 1; + + if (copy_to_user(arg, cc, sizeof(*cc))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_crop crop = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + }; + + memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect)); + + if (copy_to_user(arg, &crop, sizeof(crop))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_crop crop; + struct v4l2_rect* rect; + struct v4l2_rect* bounds = &(s->cropcap.bounds); + struct v4l2_pix_format* pix_format = &(s->pix_format); + u8 scale; + const enum sn9c102_stream_state stream = cam->stream; + const u32 nbuffers = cam->nbuffers; + u32 i; + int err = 0; + + if (copy_from_user(&crop, arg, sizeof(crop))) + return -EFAULT; + + rect = &(crop.c); + + if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (cam->module_param.force_munmap) + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_S_CROP failed. " + "Unmap the buffers first."); + return -EINVAL; + } + + /* Preserve R,G or B origin */ + rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L; + rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L; + + if (rect->width < 16) + rect->width = 16; + if (rect->height < 16) + rect->height = 16; + if (rect->width > bounds->width) + rect->width = bounds->width; + if (rect->height > bounds->height) + rect->height = bounds->height; + if (rect->left < bounds->left) + rect->left = bounds->left; + if (rect->top < bounds->top) + rect->top = bounds->top; + if (rect->left + rect->width > bounds->left + bounds->width) + rect->left = bounds->left+bounds->width - rect->width; + if (rect->top + rect->height > bounds->top + bounds->height) + rect->top = bounds->top+bounds->height - rect->height; + + rect->width &= ~15L; + rect->height &= ~15L; + + if (SN9C102_PRESERVE_IMGSCALE) { + /* Calculate the actual scaling factor */ + u32 a, b; + a = rect->width * rect->height; + b = pix_format->width * pix_format->height; + scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; + } else + scale = 1; + + if (cam->stream == STREAM_ON) + if ((err = sn9c102_stream_interrupt(cam))) + return err; + + if (copy_to_user(arg, &crop, sizeof(crop))) { + cam->stream = stream; + return -EFAULT; + } + + if (cam->module_param.force_munmap || cam->io == IO_READ) + sn9c102_release_buffers(cam); + + err = sn9c102_set_crop(cam, rect); + if (s->set_crop) + err += s->set_crop(cam, rect); + err += sn9c102_set_scale(cam, scale); + + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + s->pix_format.width = rect->width/scale; + s->pix_format.height = rect->height/scale; + memcpy(&(s->_rect), rect, sizeof(*rect)); + + if ((cam->module_param.force_munmap || cam->io == IO_READ) && + nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -ENOMEM; + } + + if (cam->io == IO_READ) + sn9c102_empty_framequeues(cam); + else if (cam->module_param.force_munmap) + sn9c102_requeue_outqueue(cam); + + cam->stream = stream; + + return 0; +} + + +static int +sn9c102_vidioc_enum_fmt(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_fmtdesc fmtd; + + if (copy_from_user(&fmtd, arg, sizeof(fmtd))) + return -EFAULT; + + if (fmtd.index == 0) { + strcpy(fmtd.description, "bayer rgb"); + fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8; + } else if (fmtd.index == 1) { + strcpy(fmtd.description, "compressed"); + fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X; + fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; + } else + return -EINVAL; + + fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + memset(&fmtd.reserved, 0, sizeof(fmtd.reserved)); + + if (copy_to_user(arg, &fmtd, sizeof(fmtd))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_format format; + struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format); + + if (copy_from_user(&format, arg, sizeof(format))) + return -EFAULT; + + if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_SN9C10X) + ? 0 : (pfmt->width * pfmt->priv) / 8; + pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); + pfmt->field = V4L2_FIELD_NONE; + memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); + + if (copy_to_user(arg, &format, sizeof(format))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd, + void __user * arg) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_format format; + struct v4l2_pix_format* pix; + struct v4l2_pix_format* pfmt = &(s->pix_format); + struct v4l2_rect* bounds = &(s->cropcap.bounds); + struct v4l2_rect rect; + u8 scale; + const enum sn9c102_stream_state stream = cam->stream; + const u32 nbuffers = cam->nbuffers; + u32 i; + int err = 0; + + if (copy_from_user(&format, arg, sizeof(format))) + return -EFAULT; + + pix = &(format.fmt.pix); + + if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memcpy(&rect, &(s->_rect), sizeof(rect)); + + { /* calculate the actual scaling factor */ + u32 a, b; + a = rect.width * rect.height; + b = pix->width * pix->height; + scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; + } + + rect.width = scale * pix->width; + rect.height = scale * pix->height; + + if (rect.width < 16) + rect.width = 16; + if (rect.height < 16) + rect.height = 16; + if (rect.width > bounds->left + bounds->width - rect.left) + rect.width = bounds->left + bounds->width - rect.left; + if (rect.height > bounds->top + bounds->height - rect.top) + rect.height = bounds->top + bounds->height - rect.top; + + rect.width &= ~15L; + rect.height &= ~15L; + + { /* adjust the scaling factor */ + u32 a, b; + a = rect.width * rect.height; + b = pix->width * pix->height; + scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; + } + + pix->width = rect.width / scale; + pix->height = rect.height / scale; + + if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X && + pix->pixelformat != V4L2_PIX_FMT_SBGGR8) + pix->pixelformat = pfmt->pixelformat; + pix->priv = pfmt->priv; /* bpp */ + pix->colorspace = pfmt->colorspace; + pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + ? 0 : (pix->width * pix->priv) / 8; + pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); + pix->field = V4L2_FIELD_NONE; + + if (cmd == VIDIOC_TRY_FMT) { + if (copy_to_user(arg, &format, sizeof(format))) + return -EFAULT; + return 0; + } + + if (cam->module_param.force_munmap) + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_S_FMT failed. Unmap the " + "buffers first."); + return -EINVAL; + } + + if (cam->stream == STREAM_ON) + if ((err = sn9c102_stream_interrupt(cam))) + return err; + + if (copy_to_user(arg, &format, sizeof(format))) { + cam->stream = stream; + return -EFAULT; + } + + if (cam->module_param.force_munmap || cam->io == IO_READ) + sn9c102_release_buffers(cam); + + err += sn9c102_set_pix_format(cam, pix); + err += sn9c102_set_crop(cam, &rect); + if (s->set_pix_format) + err += s->set_pix_format(cam, pix); + if (s->set_crop) + err += s->set_crop(cam, &rect); + err += sn9c102_set_scale(cam, scale); + + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + memcpy(pfmt, pix, sizeof(*pix)); + memcpy(&(s->_rect), &rect, sizeof(rect)); + + if ((cam->module_param.force_munmap || cam->io == IO_READ) && + nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -ENOMEM; + } + + if (cam->io == IO_READ) + sn9c102_empty_framequeues(cam); + else if (cam->module_param.force_munmap) + sn9c102_requeue_outqueue(cam); + + cam->stream = stream; + + return 0; +} + + +static int +sn9c102_vidioc_g_jpegcomp(struct sn9c102_device* cam, void __user * arg) +{ + if (copy_to_user(arg, &cam->compression, + sizeof(cam->compression))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_jpegcomp(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_jpegcompression jc; + const enum sn9c102_stream_state stream = cam->stream; + int err = 0; + + if (copy_from_user(&jc, arg, sizeof(jc))) + return -EFAULT; + + if (jc.quality != 0 && jc.quality != 1) + return -EINVAL; + + if (cam->stream == STREAM_ON) + if ((err = sn9c102_stream_interrupt(cam))) + return err; + + err += sn9c102_set_compression(cam, &jc); + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware " + "problems. To use the camera, close and open " + "/dev/video%d again.", cam->v4ldev->minor); + return -EIO; + } + + cam->compression.quality = jc.quality; + + cam->stream = stream; + + return 0; +} + + +static int +sn9c102_vidioc_reqbufs(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_requestbuffers rb; + u32 i; + int err; + + if (copy_from_user(&rb, arg, sizeof(rb))) + return -EFAULT; + + if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + rb.memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (cam->io == IO_READ) { + DBG(3, "Close and open the device again to choose the mmap " + "I/O method"); + return -EINVAL; + } + + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_REQBUFS failed. Previous buffers are " + "still mapped."); + return -EINVAL; + } + + if (cam->stream == STREAM_ON) + if ((err = sn9c102_stream_interrupt(cam))) + return err; + + sn9c102_empty_framequeues(cam); + + sn9c102_release_buffers(cam); + if (rb.count) + rb.count = sn9c102_request_buffers(cam, rb.count, IO_MMAP); + + if (copy_to_user(arg, &rb, sizeof(rb))) { + sn9c102_release_buffers(cam); + cam->io = IO_NONE; + return -EFAULT; + } + + cam->io = rb.count ? IO_MMAP : IO_NONE; + + return 0; +} + + +static int +sn9c102_vidioc_querybuf(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_buffer b; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + b.index >= cam->nbuffers || cam->io != IO_MMAP) + return -EINVAL; + + memcpy(&b, &cam->frame[b.index].buf, sizeof(b)); + + if (cam->frame[b.index].vma_use_count) + b.flags |= V4L2_BUF_FLAG_MAPPED; + + if (cam->frame[b.index].state == F_DONE) + b.flags |= V4L2_BUF_FLAG_DONE; + else if (cam->frame[b.index].state != F_UNUSED) + b.flags |= V4L2_BUF_FLAG_QUEUED; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_qbuf(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_buffer b; + unsigned long lock_flags; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + b.index >= cam->nbuffers || cam->io != IO_MMAP) + return -EINVAL; + + if (cam->frame[b.index].state != F_UNUSED) + return -EINVAL; + + cam->frame[b.index].state = F_QUEUED; + + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_add_tail(&cam->frame[b.index].frame, &cam->inqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + PDBGG("Frame #%lu queued", (unsigned long)b.index); + + return 0; +} + + +static int +sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp, + void __user * arg) +{ + struct v4l2_buffer b; + struct sn9c102_frame_t *f; + unsigned long lock_flags; + long timeout; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + if (list_empty(&cam->outqueue)) { + if (cam->stream == STREAM_OFF) + return -EINVAL; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) + return timeout; + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + if (!timeout || (cam->state & DEV_MISCONFIGURED)) + return -EIO; + } + + spin_lock_irqsave(&cam->queue_lock, lock_flags); + f = list_entry(cam->outqueue.next, struct sn9c102_frame_t, frame); + list_del(cam->outqueue.next); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + f->state = F_UNUSED; + + memcpy(&b, &f->buf, sizeof(b)); + if (f->vma_use_count) + b.flags |= V4L2_BUF_FLAG_MAPPED; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index); + + return 0; +} + + +static int +sn9c102_vidioc_streamon(struct sn9c102_device* cam, void __user * arg) +{ + int type; + + if (copy_from_user(&type, arg, sizeof(type))) + return -EFAULT; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + if (list_empty(&cam->inqueue)) + return -EINVAL; + + cam->stream = STREAM_ON; + + DBG(3, "Stream on"); + + return 0; +} + + +static int +sn9c102_vidioc_streamoff(struct sn9c102_device* cam, void __user * arg) +{ + int type, err; + + if (copy_from_user(&type, arg, sizeof(type))) + return -EFAULT; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + if (cam->stream == STREAM_ON) + if ((err = sn9c102_stream_interrupt(cam))) + return err; + + sn9c102_empty_framequeues(cam); + + DBG(3, "Stream off"); + + return 0; +} + + +static int +sn9c102_vidioc_g_parm(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_streamparm sp; + + if (copy_from_user(&sp, arg, sizeof(sp))) + return -EFAULT; + + if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + sp.parm.capture.extendedmode = 0; + sp.parm.capture.readbuffers = cam->nreadbuffers; + + if (copy_to_user(arg, &sp, sizeof(sp))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_parm(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_streamparm sp; + + if (copy_from_user(&sp, arg, sizeof(sp))) + return -EFAULT; + + if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + sp.parm.capture.extendedmode = 0; + + if (sp.parm.capture.readbuffers == 0) + sp.parm.capture.readbuffers = cam->nreadbuffers; + + if (sp.parm.capture.readbuffers > SN9C102_MAX_FRAMES) + sp.parm.capture.readbuffers = SN9C102_MAX_FRAMES; + + if (copy_to_user(arg, &sp, sizeof(sp))) + return -EFAULT; + + cam->nreadbuffers = sp.parm.capture.readbuffers; + + return 0; +} + + +static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, + unsigned int cmd, void __user * arg) +{ + struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + + switch (cmd) { + + case VIDIOC_QUERYCAP: + return sn9c102_vidioc_querycap(cam, arg); + + case VIDIOC_ENUMINPUT: + return sn9c102_vidioc_enuminput(cam, arg); + + case VIDIOC_G_INPUT: + return sn9c102_vidioc_g_input(cam, arg); + + case VIDIOC_S_INPUT: + return sn9c102_vidioc_s_input(cam, arg); + + case VIDIOC_QUERYCTRL: + return sn9c102_vidioc_query_ctrl(cam, arg); + + case VIDIOC_G_CTRL: + return sn9c102_vidioc_g_ctrl(cam, arg); + + case VIDIOC_S_CTRL_OLD: + case VIDIOC_S_CTRL: + return sn9c102_vidioc_s_ctrl(cam, arg); + + case VIDIOC_CROPCAP_OLD: + case VIDIOC_CROPCAP: + return sn9c102_vidioc_cropcap(cam, arg); + + case VIDIOC_G_CROP: + return sn9c102_vidioc_g_crop(cam, arg); + + case VIDIOC_S_CROP: + return sn9c102_vidioc_s_crop(cam, arg); + + case VIDIOC_ENUM_FMT: + return sn9c102_vidioc_enum_fmt(cam, arg); + + case VIDIOC_G_FMT: + return sn9c102_vidioc_g_fmt(cam, arg); + + case VIDIOC_TRY_FMT: + case VIDIOC_S_FMT: + return sn9c102_vidioc_try_s_fmt(cam, cmd, arg); + + case VIDIOC_G_JPEGCOMP: + return sn9c102_vidioc_g_jpegcomp(cam, arg); + + case VIDIOC_S_JPEGCOMP: + return sn9c102_vidioc_s_jpegcomp(cam, arg); + + case VIDIOC_REQBUFS: + return sn9c102_vidioc_reqbufs(cam, arg); + + case VIDIOC_QUERYBUF: + return sn9c102_vidioc_querybuf(cam, arg); + + case VIDIOC_QBUF: + return sn9c102_vidioc_qbuf(cam, arg); + + case VIDIOC_DQBUF: + return sn9c102_vidioc_dqbuf(cam, filp, arg); + + case VIDIOC_STREAMON: + return sn9c102_vidioc_streamon(cam, arg); + + case VIDIOC_STREAMOFF: + return sn9c102_vidioc_streamoff(cam, arg); + + case VIDIOC_G_PARM: + return sn9c102_vidioc_g_parm(cam, arg); + + case VIDIOC_S_PARM_OLD: + case VIDIOC_S_PARM: + return sn9c102_vidioc_s_parm(cam, arg); + + case VIDIOC_G_STD: + case VIDIOC_S_STD: + case VIDIOC_QUERYSTD: + case VIDIOC_ENUMSTD: + case VIDIOC_QUERYMENU: + return -EINVAL; + + default: + return -EINVAL; + + } +} + + +static int sn9c102_ioctl(struct inode* inode, struct file* filp, + unsigned int cmd, unsigned long arg) +{ + struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + int err = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + V4LDBG(3, "sn9c102", cmd); + + err = sn9c102_ioctl_v4l2(inode, filp, cmd, (void __user *)arg); + + mutex_unlock(&cam->fileop_mutex); + + return err; +} + +/*****************************************************************************/ + +static struct file_operations sn9c102_fops = { + .owner = THIS_MODULE, + .open = sn9c102_open, + .release = sn9c102_release, + .ioctl = sn9c102_ioctl, + .read = sn9c102_read, + .poll = sn9c102_poll, + .mmap = sn9c102_mmap, + .llseek = no_llseek, +}; + +/*****************************************************************************/ + +/* It exists a single interface only. We do not need to validate anything. */ +static int +sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct sn9c102_device* cam; + static unsigned int dev_nr = 0; + unsigned int i; + int err = 0, r; + + if (!(cam = kzalloc(sizeof(struct sn9c102_device), GFP_KERNEL))) + return -ENOMEM; + + cam->usbdev = udev; + + if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) { + DBG(1, "kmalloc() failed"); + err = -ENOMEM; + goto fail; + } + + if (!(cam->v4ldev = video_device_alloc())) { + DBG(1, "video_device_alloc() failed"); + err = -ENOMEM; + goto fail; + } + + mutex_init(&cam->dev_mutex); + + r = sn9c102_read_reg(cam, 0x00); + if (r < 0 || r != 0x10) { + DBG(1, "Sorry, this is not a SN9C10x based camera " + "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct); + err = -ENODEV; + goto fail; + } + + cam->bridge = (id->idProduct & 0xffc0) == 0x6080 ? + BRIDGE_SN9C103 : BRIDGE_SN9C102; + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + DBG(2, "SN9C10[12] PC Camera Controller detected " + "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct); + break; + case BRIDGE_SN9C103: + DBG(2, "SN9C103 PC Camera Controller detected " + "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct); + break; + } + + for (i = 0; sn9c102_sensor_table[i]; i++) { + err = sn9c102_sensor_table[i](cam); + if (!err) + break; + } + + if (!err) { + DBG(2, "%s image sensor detected", cam->sensor.name); + DBG(3, "Support for %s maintained by %s", + cam->sensor.name, cam->sensor.maintainer); + } else { + DBG(1, "No supported image sensor detected"); + err = -ENODEV; + goto fail; + } + + if (sn9c102_init(cam)) { + DBG(1, "Initialization failed. I will retry on open()."); + cam->state |= DEV_MISCONFIGURED; + } + + strcpy(cam->v4ldev->name, "SN9C10x PC Camera"); + cam->v4ldev->owner = THIS_MODULE; + cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; + cam->v4ldev->hardware = 0; + cam->v4ldev->fops = &sn9c102_fops; + cam->v4ldev->minor = video_nr[dev_nr]; + cam->v4ldev->release = video_device_release; + video_set_drvdata(cam->v4ldev, cam); + + mutex_lock(&cam->dev_mutex); + + err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, + video_nr[dev_nr]); + if (err) { + DBG(1, "V4L2 device registration failed"); + if (err == -ENFILE && video_nr[dev_nr] == -1) + DBG(1, "Free /dev/videoX node not found"); + video_nr[dev_nr] = -1; + dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; + mutex_unlock(&cam->dev_mutex); + goto fail; + } + + DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor); + + cam->module_param.force_munmap = force_munmap[dev_nr]; + cam->module_param.frame_timeout = frame_timeout[dev_nr]; + + dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + sn9c102_create_sysfs(cam); + DBG(2, "Optional device control through 'sysfs' interface ready"); +#endif + + usb_set_intfdata(intf, cam); + + mutex_unlock(&cam->dev_mutex); + + return 0; + +fail: + if (cam) { + kfree(cam->control_buffer); + if (cam->v4ldev) + video_device_release(cam->v4ldev); + kfree(cam); + } + return err; +} + + +static void sn9c102_usb_disconnect(struct usb_interface* intf) +{ + struct sn9c102_device* cam = usb_get_intfdata(intf); + + if (!cam) + return; + + down_write(&sn9c102_disconnect); + + mutex_lock(&cam->dev_mutex); + + DBG(2, "Disconnecting %s...", cam->v4ldev->name); + + wake_up_interruptible_all(&cam->open); + + if (cam->users) { + DBG(2, "Device /dev/video%d is open! Deregistration and " + "memory deallocation are deferred on close.", + cam->v4ldev->minor); + cam->state |= DEV_MISCONFIGURED; + sn9c102_stop_transfer(cam); + cam->state |= DEV_DISCONNECTED; + wake_up_interruptible(&cam->wait_frame); + wake_up(&cam->wait_stream); + usb_get_dev(cam->usbdev); + } else { + cam->state |= DEV_DISCONNECTED; + sn9c102_release_resources(cam); + } + + mutex_unlock(&cam->dev_mutex); + + if (!cam->users) + kfree(cam); + + up_write(&sn9c102_disconnect); +} + + +static struct usb_driver sn9c102_usb_driver = { + .name = "sn9c102", + .id_table = sn9c102_id_table, + .probe = sn9c102_usb_probe, + .disconnect = sn9c102_usb_disconnect, +}; + +/*****************************************************************************/ + +static int __init sn9c102_module_init(void) +{ + int err = 0; + + KDBG(2, SN9C102_MODULE_NAME " v" SN9C102_MODULE_VERSION); + KDBG(3, SN9C102_MODULE_AUTHOR); + + if ((err = usb_register(&sn9c102_usb_driver))) + KDBG(1, "usb_register() failed"); + + return err; +} + + +static void __exit sn9c102_module_exit(void) +{ + usb_deregister(&sn9c102_usb_driver); +} + + +module_init(sn9c102_module_init); +module_exit(sn9c102_module_exit); diff --git a/linux/drivers/media/video/sn9c102/sn9c102_hv7131d.c b/linux/drivers/media/video/sn9c102/sn9c102_hv7131d.c new file mode 100644 index 000000000..c4117bf64 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_hv7131d.c @@ -0,0 +1,271 @@ +/*************************************************************************** + * Plug-in for HV7131D image sensor connected to the SN9C10x PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor hv7131d; + + +static int hv7131d_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x60, 0x17); + err += sn9c102_write_reg(cam, 0x0e, 0x18); + err += sn9c102_write_reg(cam, 0xf2, 0x19); + + err += sn9c102_i2c_write(cam, 0x01, 0x04); + err += sn9c102_i2c_write(cam, 0x02, 0x00); + err += sn9c102_i2c_write(cam, 0x28, 0x00); + + return err; +} + + +static int hv7131d_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + { + int r1 = sn9c102_i2c_read(cam, 0x26), + r2 = sn9c102_i2c_read(cam, 0x27); + if (r1 < 0 || r2 < 0) + return -EIO; + ctrl->value = (r1 << 8) | (r2 & 0xff); + } + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0) + return -EIO; + ctrl->value = 0x3f - (ctrl->value & 0x3f); + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0) + return -EIO; + ctrl->value = 0x3f - (ctrl->value & 0x3f); + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0) + return -EIO; + ctrl->value = 0x3f - (ctrl->value & 0x3f); + return 0; + case SN9C102_V4L2_CID_RESET_LEVEL: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0) + return -EIO; + ctrl->value &= 0x3f; + return 0; + case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x34)) < 0) + return -EIO; + ctrl->value &= 0x07; + return 0; + default: + return -EINVAL; + } +} + + +static int hv7131d_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x26, ctrl->value >> 8); + err += sn9c102_i2c_write(cam, 0x27, ctrl->value & 0xff); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x31, 0x3f - ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x33, 0x3f - ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x32, 0x3f - ctrl->value); + break; + case SN9C102_V4L2_CID_RESET_LEVEL: + err += sn9c102_i2c_write(cam, 0x30, ctrl->value); + break; + case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE: + err += sn9c102_i2c_write(cam, 0x34, ctrl->value); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int hv7131d_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &hv7131d; + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 2, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int hv7131d_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x42, 0x19); + else + err += sn9c102_write_reg(cam, 0xf2, 0x19); + + return err; +} + + +static struct sn9c102_sensor hv7131d = { + .name = "HV7131D", + .maintainer = "Luca Risolia ", + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x11, + .init = &hv7131d_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x0250, + .maximum = 0xffff, + .step = 0x0001, + .default_value = 0x0250, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x1e, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_RESET_LEVEL, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "reset level", + .minimum = 0x19, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x30, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "pixel bias voltage", + .minimum = 0x00, + .maximum = 0x07, + .step = 0x01, + .default_value = 0x02, + .flags = 0, + }, + }, + .get_ctrl = &hv7131d_get_ctrl, + .set_ctrl = &hv7131d_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &hv7131d_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &hv7131d_set_pix_format +}; + + +int sn9c102_probe_hv7131d(struct sn9c102_device* cam) +{ + int r0 = 0, r1 = 0, err = 0; + + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x00, 0x01); + err += sn9c102_write_reg(cam, 0x28, 0x17); + if (err) + return -EIO; + + r0 = sn9c102_i2c_try_read(cam, &hv7131d, 0x00); + r1 = sn9c102_i2c_try_read(cam, &hv7131d, 0x01); + if (r0 < 0 || r1 < 0) + return -EIO; + + if (r0 != 0x00 && r1 != 0x04) + return -ENODEV; + + sn9c102_attach_sensor(cam, &hv7131d); + + return 0; +} diff --git a/linux/drivers/media/video/sn9c102/sn9c102_mi0343.c b/linux/drivers/media/video/sn9c102/sn9c102_mi0343.c new file mode 100644 index 000000000..4169ea4a2 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_mi0343.c @@ -0,0 +1,363 @@ +/*************************************************************************** + * Plug-in for MI-0343 image sensor connected to the SN9C10x PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor mi0343; +static u8 mi0343_i2c_data[5+1]; + + +static int mi0343_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x0a, 0x14); + err += sn9c102_write_reg(cam, 0x40, 0x01); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x07, 0x18); + err += sn9c102_write_reg(cam, 0xa0, 0x19); + + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, + 0x0d, 0x00, 0x01, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, + 0x0d, 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, + 0x03, 0x01, 0xe1, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, + 0x04, 0x02, 0x81, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, + 0x05, 0x00, 0x17, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, + 0x06, 0x00, 0x11, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, + 0x62, 0x04, 0x9a, 0, 0); + + return err; +} + + +static int mi0343_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, + 0x09, 2+1, mi0343_i2c_data) < 0) + return -EIO; + ctrl->value = mi0343_i2c_data[2]; + return 0; + case V4L2_CID_GAIN: + if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, + 0x35, 2+1, mi0343_i2c_data) < 0) + return -EIO; + break; + case V4L2_CID_HFLIP: + if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, + 0x20, 2+1, mi0343_i2c_data) < 0) + return -EIO; + ctrl->value = mi0343_i2c_data[3] & 0x20 ? 1 : 0; + return 0; + case V4L2_CID_VFLIP: + if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, + 0x20, 2+1, mi0343_i2c_data) < 0) + return -EIO; + ctrl->value = mi0343_i2c_data[3] & 0x80 ? 1 : 0; + return 0; + case V4L2_CID_RED_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, + 0x2d, 2+1, mi0343_i2c_data) < 0) + return -EIO; + break; + case V4L2_CID_BLUE_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, + 0x2c, 2+1, mi0343_i2c_data) < 0) + return -EIO; + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, + 0x2e, 2+1, mi0343_i2c_data) < 0) + return -EIO; + break; + default: + return -EINVAL; + } + + switch (ctrl->id) { + case V4L2_CID_GAIN: + case V4L2_CID_RED_BALANCE: + case V4L2_CID_BLUE_BALANCE: + case SN9C102_V4L2_CID_GREEN_BALANCE: + ctrl->value = mi0343_i2c_data[3] | (mi0343_i2c_data[2] << 8); + if (ctrl->value >= 0x10 && ctrl->value <= 0x3f) + ctrl->value -= 0x10; + else if (ctrl->value >= 0x60 && ctrl->value <= 0x7f) + ctrl->value -= 0x60; + else if (ctrl->value >= 0xe0 && ctrl->value <= 0xff) + ctrl->value -= 0xe0; + } + + return 0; +} + + +static int mi0343_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + u16 reg = 0; + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + case V4L2_CID_RED_BALANCE: + case V4L2_CID_BLUE_BALANCE: + case SN9C102_V4L2_CID_GREEN_BALANCE: + if (ctrl->value <= (0x3f-0x10)) + reg = 0x10 + ctrl->value; + else if (ctrl->value <= ((0x3f-0x10) + (0x7f-0x60))) + reg = 0x60 + (ctrl->value - (0x3f-0x10)); + else + reg = 0xe0 + (ctrl->value - (0x3f-0x10) - (0x7f-0x60)); + break; + } + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x09, ctrl->value, 0x00, + 0, 0); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x35, reg >> 8, reg & 0xff, + 0, 0); + break; + case V4L2_CID_HFLIP: + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x20, ctrl->value ? 0x40:0x00, + ctrl->value ? 0x20:0x00, + 0, 0); + break; + case V4L2_CID_VFLIP: + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x20, ctrl->value ? 0x80:0x00, + ctrl->value ? 0x80:0x00, + 0, 0); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x2d, reg >> 8, reg & 0xff, + 0, 0); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x2c, reg >> 8, reg & 0xff, + 0, 0); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x2b, reg >> 8, reg & 0xff, + 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x2e, reg >> 8, reg & 0xff, + 0, 0); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int mi0343_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &mi0343; + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int mi0343_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) { + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x0a, 0x00, 0x03, 0, 0); + err += sn9c102_write_reg(cam, 0x20, 0x19); + } else { + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x0a, 0x00, 0x05, 0, 0); + err += sn9c102_write_reg(cam, 0xa0, 0x19); + } + + return err; +} + + +static struct sn9c102_sensor mi0343 = { + .name = "MI-0343", + .maintainer = "Luca Risolia ", + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x5d, + .init = &mi0343_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x06, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),/*0x6d*/ + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0), + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0), + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = ((0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0)), + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .get_ctrl = &mi0343_get_ctrl, + .set_ctrl = &mi0343_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &mi0343_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &mi0343_set_pix_format +}; + + +int sn9c102_probe_mi0343(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x00, 0x01); + err += sn9c102_write_reg(cam, 0x28, 0x17); + if (err) + return -EIO; + + if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, 0x00, + 2, mi0343_i2c_data) < 0) + return -EIO; + + if (mi0343_i2c_data[4] != 0x32 && mi0343_i2c_data[3] != 0xe3) + return -ENODEV; + + sn9c102_attach_sensor(cam, &mi0343); + + return 0; +} diff --git a/linux/drivers/media/video/sn9c102/sn9c102_ov7630.c b/linux/drivers/media/video/sn9c102/sn9c102_ov7630.c new file mode 100644 index 000000000..3da042021 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_ov7630.c @@ -0,0 +1,401 @@ +/*************************************************************************** + * Plug-in for OV7630 image sensor connected to the SN9C10x PC Camera * + * Controllers * + * * + * Copyright (C) 2005-2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor ov7630; + + +static int ov7630_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x60, 0x17); + err += sn9c102_write_reg(cam, 0x0f, 0x18); + err += sn9c102_write_reg(cam, 0x50, 0x19); + + err += sn9c102_i2c_write(cam, 0x12, 0x80); + err += sn9c102_i2c_write(cam, 0x11, 0x01); + err += sn9c102_i2c_write(cam, 0x15, 0x34); + err += sn9c102_i2c_write(cam, 0x16, 0x03); + err += sn9c102_i2c_write(cam, 0x17, 0x1c); + err += sn9c102_i2c_write(cam, 0x18, 0xbd); + err += sn9c102_i2c_write(cam, 0x19, 0x06); + err += sn9c102_i2c_write(cam, 0x1a, 0xf6); + err += sn9c102_i2c_write(cam, 0x1b, 0x04); + err += sn9c102_i2c_write(cam, 0x20, 0xf6); + err += sn9c102_i2c_write(cam, 0x23, 0xee); + err += sn9c102_i2c_write(cam, 0x26, 0xa0); + err += sn9c102_i2c_write(cam, 0x27, 0x9a); + err += sn9c102_i2c_write(cam, 0x28, 0xa0); + err += sn9c102_i2c_write(cam, 0x29, 0x30); + err += sn9c102_i2c_write(cam, 0x2a, 0xa0); + err += sn9c102_i2c_write(cam, 0x2b, 0x1f); + err += sn9c102_i2c_write(cam, 0x2f, 0x3d); + err += sn9c102_i2c_write(cam, 0x30, 0x24); + err += sn9c102_i2c_write(cam, 0x32, 0x86); + err += sn9c102_i2c_write(cam, 0x60, 0xa9); + err += sn9c102_i2c_write(cam, 0x61, 0x42); + err += sn9c102_i2c_write(cam, 0x65, 0x00); + err += sn9c102_i2c_write(cam, 0x69, 0x38); + err += sn9c102_i2c_write(cam, 0x6f, 0x88); + err += sn9c102_i2c_write(cam, 0x70, 0x0b); + err += sn9c102_i2c_write(cam, 0x71, 0x00); + err += sn9c102_i2c_write(cam, 0x74, 0x21); + err += sn9c102_i2c_write(cam, 0x7d, 0xf7); + + return err; +} + + +static int ov7630_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x10, ctrl->value >> 2); + err += sn9c102_i2c_write(cam, 0x76, ctrl->value & 0x03); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x02, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x01, ctrl->value); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x00, ctrl->value); + break; + case V4L2_CID_CONTRAST: + err += ctrl->value ? sn9c102_i2c_write(cam, 0x05, + (ctrl->value-1) | 0x20) + : sn9c102_i2c_write(cam, 0x05, 0x00); + break; + case V4L2_CID_BRIGHTNESS: + err += sn9c102_i2c_write(cam, 0x06, ctrl->value); + break; + case V4L2_CID_SATURATION: + err += sn9c102_i2c_write(cam, 0x03, ctrl->value << 4); + break; + case V4L2_CID_HUE: + err += ctrl->value ? sn9c102_i2c_write(cam, 0x04, + (ctrl->value-1) | 0x20) + : sn9c102_i2c_write(cam, 0x04, 0x00); + break; + case V4L2_CID_DO_WHITE_BALANCE: + err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); + break; + case V4L2_CID_WHITENESS: + err += sn9c102_i2c_write(cam, 0x0d, ctrl->value); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + err += sn9c102_i2c_write(cam, 0x12, (ctrl->value << 2) | 0x78); + break; + case V4L2_CID_AUTOGAIN: + err += sn9c102_i2c_write(cam, 0x13, ctrl->value); + break; + case V4L2_CID_VFLIP: + err += sn9c102_i2c_write(cam, 0x75, 0x0e | (ctrl->value << 7)); + break; + case V4L2_CID_BLACK_LEVEL: + err += sn9c102_i2c_write(cam, 0x25, ctrl->value); + break; + case SN9C102_V4L2_CID_BRIGHT_LEVEL: + err += sn9c102_i2c_write(cam, 0x24, ctrl->value); + break; + case SN9C102_V4L2_CID_GAMMA: + err += sn9c102_i2c_write(cam, 0x14, (ctrl->value << 2) | 0x80); + break; + case SN9C102_V4L2_CID_BAND_FILTER: + err += sn9c102_i2c_write(cam, 0x2d, ctrl->value << 2); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int ov7630_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &ov7630; + int err = 0; + u8 v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int ov7630_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x20, 0x19); + else + err += sn9c102_write_reg(cam, 0x50, 0x19); + + return err; +} + + +static struct sn9c102_sensor ov7630 = { + .name = "OV7630", + .maintainer = "Luca Risolia ", + .sysfs_ops = SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x21, + .init = &ov7630_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x14, + .flags = 0, + }, + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "hue", + .minimum = 0x00, + .maximum = 0x1f+1, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "saturation", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x08, + .flags = 0, + }, + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "contrast", + .minimum = 0x00, + .maximum = 0x1f+1, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x000, + .maximum = 0x3ff, + .step = 0x001, + .default_value = 0x83<<2, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x3a, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x77, + .flags = 0, + }, + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "brightness", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0xa0, + .flags = 0, + }, + { + .id = V4L2_CID_DO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "white balance background: blue", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + { + .id = V4L2_CID_WHITENESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "white balance background: red", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto white balance", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain & exposure mode", + .minimum = 0x00, + .maximum = 0x03, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + { + .id = V4L2_CID_BLACK_LEVEL, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "black pixel ratio", + .minimum = 0x01, + .maximum = 0x9a, + .step = 0x01, + .default_value = 0x8a, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_BRIGHT_LEVEL, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "bright pixel ratio", + .minimum = 0x01, + .maximum = 0x9a, + .step = 0x01, + .default_value = 0x10, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_BAND_FILTER, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "band filter", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "rgb gamma", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .set_ctrl = &ov7630_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &ov7630_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &ov7630_set_pix_format +}; + + +int sn9c102_probe_ov7630(struct sn9c102_device* cam) +{ + const struct usb_device_id ov7630_id_table[] = { + { USB_DEVICE(0x0c45, 0x602c), }, + { USB_DEVICE(0x0c45, 0x602d), }, + { USB_DEVICE(0x0c45, 0x608f), }, + { USB_DEVICE(0x0c45, 0x60b0), }, + { } + }; + int err = 0; + + if (!sn9c102_match_id(cam, ov7630_id_table)) + return -ENODEV; + + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x00, 0x01); + err += sn9c102_write_reg(cam, 0x28, 0x17); + if (err) + return -EIO; + + err += sn9c102_i2c_try_write(cam, &ov7630, 0x0b, 0); + if (err) + return -ENODEV; + + sn9c102_attach_sensor(cam, &ov7630); + + return 0; +} diff --git a/linux/drivers/media/video/sn9c102/sn9c102_pas106b.c b/linux/drivers/media/video/sn9c102/sn9c102_pas106b.c new file mode 100644 index 000000000..991594423 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_pas106b.c @@ -0,0 +1,307 @@ +/*************************************************************************** + * Plug-in for PAS106B image sensor connected to the SN9C10x PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor pas106b; + + +static int pas106b_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x20, 0x19); + err += sn9c102_write_reg(cam, 0x09, 0x18); + + err += sn9c102_i2c_write(cam, 0x02, 0x0c); + err += sn9c102_i2c_write(cam, 0x05, 0x5a); + err += sn9c102_i2c_write(cam, 0x06, 0x88); + err += sn9c102_i2c_write(cam, 0x07, 0x80); + err += sn9c102_i2c_write(cam, 0x10, 0x06); + err += sn9c102_i2c_write(cam, 0x11, 0x06); + err += sn9c102_i2c_write(cam, 0x12, 0x00); + err += sn9c102_i2c_write(cam, 0x14, 0x02); + err += sn9c102_i2c_write(cam, 0x13, 0x01); + + msleep(400); + + return err; +} + + +static int pas106b_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + { + int r1 = sn9c102_i2c_read(cam, 0x03), + r2 = sn9c102_i2c_read(cam, 0x04); + if (r1 < 0 || r2 < 0) + return -EIO; + ctrl->value = (r1 << 4) | (r2 & 0x0f); + } + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0e)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case V4L2_CID_CONTRAST: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0f)) < 0) + return -EIO; + ctrl->value &= 0x07; + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0a)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x1f) << 1; + return 0; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0) + return -EIO; + ctrl->value &= 0xf8; + return 0; + default: + return -EINVAL; + } +} + + +static int pas106b_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x03, ctrl->value >> 4); + err += sn9c102_i2c_write(cam, 0x04, ctrl->value & 0x0f); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x09, ctrl->value); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x0e, ctrl->value); + break; + case V4L2_CID_CONTRAST: + err += sn9c102_i2c_write(cam, 0x0f, ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x0a, ctrl->value >> 1); + err += sn9c102_i2c_write(cam, 0x0b, ctrl->value >> 1); + break; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + err += sn9c102_i2c_write(cam, 0x08, ctrl->value << 3); + break; + default: + return -EINVAL; + } + err += sn9c102_i2c_write(cam, 0x13, 0x01); + + return err ? -EIO : 0; +} + + +static int pas106b_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &pas106b; + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int pas106b_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x2c, 0x17); + else + err += sn9c102_write_reg(cam, 0x20, 0x17); + + return err; +} + + +static struct sn9c102_sensor pas106b = { + .name = "PAS106B", + .maintainer = "Luca Risolia ", + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x40, + .init = &pas106b_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x125, + .maximum = 0xfff, + .step = 0x001, + .default_value = 0x140, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x0d, + .flags = 0, + }, + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "contrast", + .minimum = 0x00, + .maximum = 0x07, + .step = 0x01, + .default_value = 0x00, /* 0x00~0x03 have same effect */ + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x04, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x06, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x3e, + .step = 0x02, + .default_value = 0x02, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "DAC magnitude", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + }, + .get_ctrl = &pas106b_get_ctrl, + .set_ctrl = &pas106b_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + }, + .set_crop = &pas106b_set_crop, + .pix_format = { + .width = 352, + .height = 288, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, /* we use this field as 'bits per pixel' */ + }, + .set_pix_format = &pas106b_set_pix_format +}; + + +int sn9c102_probe_pas106b(struct sn9c102_device* cam) +{ + int r0 = 0, r1 = 0, err = 0; + unsigned int pid = 0; + + /* + Minimal initialization to enable the I2C communication + NOTE: do NOT change the values! + */ + err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */ + err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */ + err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */ + if (err) + return -EIO; + + r0 = sn9c102_i2c_try_read(cam, &pas106b, 0x00); + r1 = sn9c102_i2c_try_read(cam, &pas106b, 0x01); + + if (r0 < 0 || r1 < 0) + return -EIO; + + pid = (r0 << 11) | ((r1 & 0xf0) >> 4); + if (pid != 0x007) + return -ENODEV; + + sn9c102_attach_sensor(cam, &pas106b); + + return 0; +} diff --git a/linux/drivers/media/video/sn9c102/sn9c102_pas202bca.c b/linux/drivers/media/video/sn9c102/sn9c102_pas202bca.c new file mode 100644 index 000000000..c8f1ae215 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_pas202bca.c @@ -0,0 +1,238 @@ +/*************************************************************************** + * Plug-in for PAS202BCA image sensor connected to the SN9C10x PC Camera * + * Controllers * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor pas202bca; + + +static int pas202bca_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x30, 0x19); + err += sn9c102_write_reg(cam, 0x09, 0x18); + + err += sn9c102_i2c_write(cam, 0x02, 0x14); + err += sn9c102_i2c_write(cam, 0x03, 0x40); + err += sn9c102_i2c_write(cam, 0x0d, 0x2c); + err += sn9c102_i2c_write(cam, 0x0e, 0x01); + err += sn9c102_i2c_write(cam, 0x0f, 0xa9); + err += sn9c102_i2c_write(cam, 0x10, 0x08); + err += sn9c102_i2c_write(cam, 0x13, 0x63); + err += sn9c102_i2c_write(cam, 0x15, 0x70); + err += sn9c102_i2c_write(cam, 0x11, 0x01); + + msleep(400); + + return err; +} + + +static int pas202bca_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x24, 0x17); + else + err += sn9c102_write_reg(cam, 0x20, 0x17); + + return err; +} + + +static int pas202bca_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6); + err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x09, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x07, ctrl->value); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x10, ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x08, ctrl->value); + break; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); + break; + default: + return -EINVAL; + } + err += sn9c102_i2c_write(cam, 0x11, 0x01); + + return err ? -EIO : 0; +} + + +static int pas202bca_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &pas202bca; + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 3, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static struct sn9c102_sensor pas202bca = { + .name = "PAS202BCA", + .maintainer = "Luca Risolia ", + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x40, + .init = &pas202bca_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x01e5, + .maximum = 0x3fff, + .step = 0x0001, + .default_value = 0x01e5, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x0c, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x05, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "DAC magnitude", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x04, + .flags = 0, + }, + }, + .set_ctrl = &pas202bca_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &pas202bca_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &pas202bca_set_pix_format +}; + + +int sn9c102_probe_pas202bca(struct sn9c102_device* cam) +{ + const struct usb_device_id pas202bca_id_table[] = { + { USB_DEVICE(0x0c45, 0x60af), }, + { } + }; + int err = 0; + + if (!sn9c102_match_id(cam,pas202bca_id_table)) + return -ENODEV; + + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x40, 0x01); + err += sn9c102_write_reg(cam, 0x28, 0x17); + if (err) + return -EIO; + + if (sn9c102_i2c_try_write(cam, &pas202bca, 0x10, 0)) /* try to write */ + return -ENODEV; + + sn9c102_attach_sensor(cam, &pas202bca); + + return 0; +} diff --git a/linux/drivers/media/video/sn9c102/sn9c102_pas202bcb.c b/linux/drivers/media/video/sn9c102/sn9c102_pas202bcb.c new file mode 100644 index 000000000..e3c1178e3 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_pas202bcb.c @@ -0,0 +1,293 @@ +/*************************************************************************** + * Plug-in for PAS202BCB image sensor connected to the SN9C10x PC Camera * + * Controllers * + * * + * Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio * + * * + * http://cadu.homelinux.com:8080/ * + * * + * DAC Magnitude, exposure and green gain controls added by * + * Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor pas202bcb; + + +static int pas202bcb_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x30, 0x19); + err += sn9c102_write_reg(cam, 0x09, 0x18); + + err += sn9c102_i2c_write(cam, 0x02, 0x14); + err += sn9c102_i2c_write(cam, 0x03, 0x40); + err += sn9c102_i2c_write(cam, 0x0d, 0x2c); + err += sn9c102_i2c_write(cam, 0x0e, 0x01); + err += sn9c102_i2c_write(cam, 0x0f, 0xa9); + err += sn9c102_i2c_write(cam, 0x10, 0x08); + err += sn9c102_i2c_write(cam, 0x13, 0x63); + err += sn9c102_i2c_write(cam, 0x15, 0x70); + err += sn9c102_i2c_write(cam, 0x11, 0x01); + + msleep(400); + + return err; +} + + +static int pas202bcb_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + { + int r1 = sn9c102_i2c_read(cam, 0x04), + r2 = sn9c102_i2c_read(cam, 0x05); + if (r1 < 0 || r2 < 0) + return -EIO; + ctrl->value = (r1 << 6) | (r2 & 0x3f); + } + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x07)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) + return -EIO; + return 0; + default: + return -EINVAL; + } +} + + +static int pas202bcb_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x24, 0x17); + else + err += sn9c102_write_reg(cam, 0x20, 0x17); + + return err; +} + + +static int pas202bcb_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6); + err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x09, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x07, ctrl->value); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x10, ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x08, ctrl->value); + break; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); + break; + default: + return -EINVAL; + } + err += sn9c102_i2c_write(cam, 0x11, 0x01); + + return err ? -EIO : 0; +} + + +static int pas202bcb_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &pas202bcb; + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static struct sn9c102_sensor pas202bcb = { + .name = "PAS202BCB", + .maintainer = "Carlos Eduardo Medaglia Dyonisio " + "", + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x40, + .init = &pas202bcb_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x01e5, + .maximum = 0x3fff, + .step = 0x0001, + .default_value = 0x01e5, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x0c, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x05, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "DAC magnitude", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x04, + .flags = 0, + }, + }, + .get_ctrl = &pas202bcb_get_ctrl, + .set_ctrl = &pas202bcb_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &pas202bcb_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &pas202bcb_set_pix_format +}; + + +int sn9c102_probe_pas202bcb(struct sn9c102_device* cam) +{ + int r0 = 0, r1 = 0, err = 0; + unsigned int pid = 0; + + /* + * Minimal initialization to enable the I2C communication + * NOTE: do NOT change the values! + */ + err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */ + err += sn9c102_write_reg(cam, 0x40, 0x01); /* sensor power on */ + err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */ + if (err) + return -EIO; + + r0 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x00); + r1 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x01); + + if (r0 < 0 || r1 < 0) + return -EIO; + + pid = (r0 << 4) | ((r1 & 0xf0) >> 4); + if (pid != 0x017) + return -ENODEV; + + sn9c102_attach_sensor(cam, &pas202bcb); + + return 0; +} diff --git a/linux/drivers/media/video/sn9c102/sn9c102_sensor.h b/linux/drivers/media/video/sn9c102/sn9c102_sensor.h new file mode 100644 index 000000000..88cd05d06 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_sensor.h @@ -0,0 +1,390 @@ +/*************************************************************************** + * API for image sensors connected to the SN9C10x PC Camera Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _SN9C102_SENSOR_H_ +#define _SN9C102_SENSOR_H_ + +#include +#include "compat.h" +#include +#include +#include +#include +#include + +struct sn9c102_device; +struct sn9c102_sensor; + +/*****************************************************************************/ + +/* + OVERVIEW. + This is a small interface that allows you to add support for any CCD/CMOS + image sensors connected to the SN9C10X bridges. The entire API is documented + below. In the most general case, to support a sensor there are three steps + you have to follow: + 1) define the main "sn9c102_sensor" structure by setting the basic fields; + 2) write a probing function to be called by the core module when the USB + camera is recognized, then add both the USB ids and the name of that + function to the two corresponding tables SENSOR_TABLE and ID_TABLE (see + below); + 3) implement the methods that you want/need (and fill the rest of the main + structure accordingly). + "sn9c102_pas106b.c" is an example of all this stuff. Remember that you do + NOT need to touch the source code of the core module for the things to work + properly, unless you find bugs or flaws in it. Finally, do not forget to + read the V4L2 API for completeness. +*/ + +/*****************************************************************************/ + +/* + Probing functions: on success, you must attach the sensor to the camera + by calling sn9c102_attach_sensor() provided below. + To enable the I2C communication, you might need to perform a really basic + initialization of the SN9C10X chip by using the write function declared + ahead. + Functions must return 0 on success, the appropriate error otherwise. +*/ +extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam); +extern int sn9c102_probe_mi0343(struct sn9c102_device* cam); +extern int sn9c102_probe_ov7630(struct sn9c102_device* cam); +extern int sn9c102_probe_pas106b(struct sn9c102_device* cam); +extern int sn9c102_probe_pas202bca(struct sn9c102_device* cam); +extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); + +/* + Add the above entries to this table. Be sure to add the entry in the right + place, since, on failure, the next probing routine is called according to + the order of the list below, from top to bottom. +*/ +#define SN9C102_SENSOR_TABLE \ +static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = { \ + &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ \ + &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ \ + &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ \ + &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ \ + &sn9c102_probe_pas202bca, /* detection mostly based on USB pid/vid */ \ + &sn9c102_probe_ov7630, /* detection mostly based on USB pid/vid */ \ + &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ \ + &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ \ + NULL, \ +}; + +/* Device identification */ +extern struct sn9c102_device* +sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id); + +/* Attach a probed sensor to the camera. */ +extern void +sn9c102_attach_sensor(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor); + +/* + Each SN9C10x camera has proper PID/VID identifiers. + SN9C103 supports multiple interfaces, but we only handle the video class + interface. +*/ +#define SN9C102_USB_DEVICE(vend, prod, intclass) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_CLASS, \ + .idVendor = (vend), \ + .idProduct = (prod), \ + .bInterfaceClass = (intclass) + +#define SN9C102_ID_TABLE \ +static const struct usb_device_id sn9c102_id_table[] = { \ + { USB_DEVICE(0x0c45, 0x6001), }, /* TAS5110C1B */ \ + { USB_DEVICE(0x0c45, 0x6005), }, /* TAS5110C1B */ \ + { USB_DEVICE(0x0c45, 0x6007), }, \ + { USB_DEVICE(0x0c45, 0x6009), }, /* PAS106B */ \ + { USB_DEVICE(0x0c45, 0x600d), }, /* PAS106B */ \ + { USB_DEVICE(0x0c45, 0x6024), }, \ + { USB_DEVICE(0x0c45, 0x6025), }, /* TAS5130D1B and TAS5110C1B */ \ + { USB_DEVICE(0x0c45, 0x6028), }, /* PAS202BCB */ \ + { USB_DEVICE(0x0c45, 0x6029), }, /* PAS106B */ \ + { USB_DEVICE(0x0c45, 0x602a), }, /* HV7131D */ \ + { USB_DEVICE(0x0c45, 0x602b), }, /* MI-0343 */ \ + { USB_DEVICE(0x0c45, 0x602c), }, /* OV7630 */ \ + { USB_DEVICE(0x0c45, 0x602d), }, \ + { USB_DEVICE(0x0c45, 0x602e), }, /* OV7630 */ \ + { USB_DEVICE(0x0c45, 0x6030), }, /* MI03x */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x6080, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x6082, 0xff), }, /* MI0343 & MI0360 */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x6083, 0xff), }, /* HV7131[D|E1] */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x6088, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x608a, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x608b, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x608c, 0xff), }, /* HV7131/R */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x608e, 0xff), }, /* CIS-VF10 */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x608f, 0xff), }, /* OV7630 */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x60a0, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60a2, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60a3, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60a8, 0xff), }, /* PAS106B */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x60aa, 0xff), }, /* TAS5130D1B */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x60ab, 0xff), }, /* TAS5110C1B */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x60ac, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60ae, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60af, 0xff), }, /* PAS202BCB */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x60b0, 0xff), }, /* OV7630 (?) */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x60b2, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60b3, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60b8, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60ba, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60bb, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60bc, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60be, 0xff), }, \ + { } \ +}; + +/*****************************************************************************/ + +/* + Read/write routines: they always return -1 on error, 0 or the read value + otherwise. NOTE that a real read operation is not supported by the SN9C10X + chip for some of its registers. To work around this problem, a pseudo-read + call is provided instead: it returns the last successfully written value + on the register (0 if it has never been written), the usual -1 on error. +*/ + +/* The "try" I2C I/O versions are used when probing the sensor */ +extern int sn9c102_i2c_try_write(struct sn9c102_device*,struct sn9c102_sensor*, + u8 address, u8 value); +extern int sn9c102_i2c_try_read(struct sn9c102_device*,struct sn9c102_sensor*, + u8 address); + +/* + These must be used if and only if the sensor doesn't implement the standard + I2C protocol. There are a number of good reasons why you must use the + single-byte versions of these functions: do not abuse. The first function + writes n bytes, from data0 to datan, to registers 0x09 - 0x09+n of SN9C10X + chip. The second one programs the registers 0x09 and 0x10 with data0 and + data1, and places the n bytes read from the sensor register table in the + buffer pointed by 'buffer'. Both the functions return -1 on error; the write + version returns 0 on success, while the read version returns the first read + byte. +*/ +extern int sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor, u8 n, + u8 data0, u8 data1, u8 data2, u8 data3, + u8 data4, u8 data5); +extern int sn9c102_i2c_try_raw_read(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor, u8 data0, + u8 data1, u8 n, u8 buffer[]); + +/* To be used after the sensor struct has been attached to the camera struct */ +extern int sn9c102_i2c_write(struct sn9c102_device*, u8 address, u8 value); +extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address); + +/* I/O on registers in the bridge. Could be used by the sensor methods too */ +extern int sn9c102_write_regs(struct sn9c102_device*, u8* buff, u16 index); +extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index); +extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index); + +/* + NOTE: there are no exported debugging functions. To uniform the output you + must use the dev_info()/dev_warn()/dev_err() macros defined in device.h, + already included here, the argument being the struct device '&usbdev->dev' + of the sensor structure. Do NOT use these macros before the sensor is + attached or the kernel will crash! However, you should not need to notify + the user about common errors or other messages, since this is done by the + master module. +*/ + +/*****************************************************************************/ + +enum sn9c102_i2c_sysfs_ops { + SN9C102_I2C_READ = 0x01, + SN9C102_I2C_WRITE = 0x02, +}; + +enum sn9c102_i2c_frequency { /* sensors may support both the frequencies */ + SN9C102_I2C_100KHZ = 0x01, + SN9C102_I2C_400KHZ = 0x02, +}; + +enum sn9c102_i2c_interface { + SN9C102_I2C_2WIRES, + SN9C102_I2C_3WIRES, +}; + +#define SN9C102_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10 + +struct sn9c102_sensor { + char name[32], /* sensor name */ + maintainer[64]; /* name of the mantainer */ + + /* Supported operations through the 'sysfs' interface */ + enum sn9c102_i2c_sysfs_ops sysfs_ops; + + /* + These sensor capabilities must be provided if the SN9C10X controller + needs to communicate through the sensor serial interface by using + at least one of the i2c functions available. + */ + enum sn9c102_i2c_frequency frequency; + enum sn9c102_i2c_interface interface; + + /* + This identifier must be provided if the image sensor implements + the standard I2C protocol. + */ + u8 i2c_slave_id; /* reg. 0x09 */ + + /* + NOTE: Where not noted,most of the functions below are not mandatory. + Set to null if you do not implement them. If implemented, + they must return 0 on success, the proper error otherwise. + */ + + int (*init)(struct sn9c102_device* cam); + /* + This function will be called after the sensor has been attached. + It should be used to initialize the sensor only, but may also + configure part of the SN9C10X chip if necessary. You don't need to + setup picture settings like brightness, contrast, etc.. here, if + the corrisponding controls are implemented (see below), since + they are adjusted in the core driver by calling the set_ctrl() + method after init(), where the arguments are the default values + specified in the v4l2_queryctrl list of supported controls; + Same suggestions apply for other settings, _if_ the corresponding + methods are present; if not, the initialization must configure the + sensor according to the default configuration structures below. + */ + + struct v4l2_queryctrl qctrl[SN9C102_MAX_CTRLS]; + /* + Optional list of default controls, defined as indicated in the + V4L2 API. Menu type controls are not handled by this interface. + */ + + int (*get_ctrl)(struct sn9c102_device* cam, struct v4l2_control* ctrl); + int (*set_ctrl)(struct sn9c102_device* cam, + const struct v4l2_control* ctrl); + /* + You must implement at least the set_ctrl method if you have defined + the list above. The returned value must follow the V4L2 + specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER + are not supported by this driver, so do not implement them. Also, + you don't have to check whether the passed values are out of bounds, + given that this is done by the core module. + */ + + struct v4l2_cropcap cropcap; + /* + Think the image sensor as a grid of R,G,B monochromatic pixels + disposed according to a particular Bayer pattern, which describes + the complete array of pixels, from (0,0) to (xmax, ymax). We will + use this coordinate system from now on. It is assumed the sensor + chip can be programmed to capture/transmit a subsection of that + array of pixels: we will call this subsection "active window". + It is not always true that the largest achievable active window can + cover the whole array of pixels. The V4L2 API defines another + area called "source rectangle", which, in turn, is a subrectangle of + the active window. The SN9C10X chip is always programmed to read the + source rectangle. + The bounds of both the active window and the source rectangle are + specified in the cropcap substructures 'bounds' and 'defrect'. + By default, the source rectangle should cover the largest possible + area. Again, it is not always true that the largest source rectangle + can cover the entire active window, although it is a rare case for + the hardware we have. The bounds of the source rectangle _must_ be + multiple of 16 and must use the same coordinate system as indicated + before; their centers shall align initially. + If necessary, the sensor chip must be initialized during init() to + set the bounds of the active sensor window; however, by default, it + usually covers the largest achievable area (maxwidth x maxheight) + of pixels, so no particular initialization is needed, if you have + defined the correct default bounds in the structures. + See the V4L2 API for further details. + NOTE: once you have defined the bounds of the active window + (struct cropcap.bounds) you must not change them.anymore. + Only 'bounds' and 'defrect' fields are mandatory, other fields + will be ignored. + */ + + int (*set_crop)(struct sn9c102_device* cam, + const struct v4l2_rect* rect); + /* + To be called on VIDIOC_C_SETCROP. The core module always calls a + default routine which configures the appropriate SN9C10X regs (also + scaling), but you may need to override/adjust specific stuff. + 'rect' contains width and height values that are multiple of 16: in + case you override the default function, you always have to program + the chip to match those values; on error return the corresponding + error code without rolling back. + NOTE: in case, you must program the SN9C10X chip to get rid of + blank pixels or blank lines at the _start_ of each line or + frame after each HSYNC or VSYNC, so that the image starts with + real RGB data (see regs 0x12, 0x13) (having set H_SIZE and, + V_SIZE you don't have to care about blank pixels or blank + lines at the end of each line or frame). + */ + + struct v4l2_pix_format pix_format; + /* + What you have to define here are: 1) initial 'width' and 'height' of + the target rectangle 2) the initial 'pixelformat', which can be + either V4L2_PIX_FMT_SN9C10X (for compressed video) or + V4L2_PIX_FMT_SBGGR8 3) 'priv', which we'll be used to indicate the + number of bits per pixel for uncompressed video, 8 or 9 (despite the + current value of 'pixelformat'). + NOTE 1: both 'width' and 'height' _must_ be either 1/1 or 1/2 or 1/4 + of cropcap.defrect.width and cropcap.defrect.height. I + suggest 1/1. + NOTE 2: The initial compression quality is defined by the first bit + of reg 0x17 during the initialization of the image sensor. + NOTE 3: as said above, you have to program the SN9C10X chip to get + rid of any blank pixels, so that the output of the sensor + matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR). + */ + + int (*set_pix_format)(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix); + /* + To be called on VIDIOC_S_FMT, when switching from the SBGGR8 to + SN9C10X pixel format or viceversa. On error return the corresponding + error code without rolling back. + */ + + /* + Do NOT write to the data below, it's READ ONLY. It is used by the + core module to store successfully updated values of the above + settings, for rollbacks..etc..in case of errors during atomic I/O + */ + struct v4l2_queryctrl _qctrl[SN9C102_MAX_CTRLS]; + struct v4l2_rect _rect; +}; + +/*****************************************************************************/ + +/* Private ioctl's for control settings supported by some image sensors */ +#define SN9C102_V4L2_CID_DAC_MAGNITUDE V4L2_CID_PRIVATE_BASE +#define SN9C102_V4L2_CID_GREEN_BALANCE V4L2_CID_PRIVATE_BASE + 1 +#define SN9C102_V4L2_CID_RESET_LEVEL V4L2_CID_PRIVATE_BASE + 2 +#define SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE V4L2_CID_PRIVATE_BASE + 3 +#define SN9C102_V4L2_CID_GAMMA V4L2_CID_PRIVATE_BASE + 4 +#define SN9C102_V4L2_CID_BAND_FILTER V4L2_CID_PRIVATE_BASE + 5 +#define SN9C102_V4L2_CID_BRIGHT_LEVEL V4L2_CID_PRIVATE_BASE + 6 + +#endif /* _SN9C102_SENSOR_H_ */ diff --git a/linux/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c b/linux/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c new file mode 100644 index 000000000..294eb02fb --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c @@ -0,0 +1,159 @@ +/*************************************************************************** + * Plug-in for TAS5110C1B image sensor connected to the SN9C10x PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor tas5110c1b; + + +static int tas5110c1b_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x44, 0x01); + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x0a, 0x14); + err += sn9c102_write_reg(cam, 0x60, 0x17); + err += sn9c102_write_reg(cam, 0x06, 0x18); + err += sn9c102_write_reg(cam, 0xfb, 0x19); + + err += sn9c102_i2c_write(cam, 0xc0, 0x80); + + return err; +} + + +static int tas5110c1b_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int tas5110c1b_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &tas5110c1b; + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + /* Don't change ! */ + err += sn9c102_write_reg(cam, 0x14, 0x1a); + err += sn9c102_write_reg(cam, 0x0a, 0x1b); + err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19); + + return err; +} + + +static int tas5110c1b_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x2b, 0x19); + else + err += sn9c102_write_reg(cam, 0xfb, 0x19); + + return err; +} + + +static struct sn9c102_sensor tas5110c1b = { + .name = "TAS5110C1B", + .maintainer = "Luca Risolia ", + .sysfs_ops = SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_3WIRES, + .init = &tas5110c1b_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xf6, + .step = 0x01, + .default_value = 0x40, + .flags = 0, + }, + }, + .set_ctrl = &tas5110c1b_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + }, + .set_crop = &tas5110c1b_set_crop, + .pix_format = { + .width = 352, + .height = 288, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &tas5110c1b_set_pix_format +}; + + +int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam) +{ + const struct usb_device_id tas5110c1b_id_table[] = { + { USB_DEVICE(0x0c45, 0x6001), }, + { USB_DEVICE(0x0c45, 0x6005), }, + { USB_DEVICE(0x0c45, 0x60ab), }, + { } + }; + + /* Sensor detection is based on USB pid/vid */ + if (!sn9c102_match_id(cam, tas5110c1b_id_table)) + return -ENODEV; + + sn9c102_attach_sensor(cam, &tas5110c1b); + + return 0; +} diff --git a/linux/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c b/linux/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c new file mode 100644 index 000000000..9ecb09032 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c @@ -0,0 +1,169 @@ +/*************************************************************************** + * Plug-in for TAS5130D1B image sensor connected to the SN9C10x PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor tas5130d1b; + + +static int tas5130d1b_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x04, 0x01); + err += sn9c102_write_reg(cam, 0x01, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x60, 0x17); + err += sn9c102_write_reg(cam, 0x07, 0x18); + + return err; +} + + +static int tas5130d1b_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value); + break; + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x40, 0x47 - ctrl->value); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int tas5130d1b_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &tas5130d1b; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12; + int err = 0; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + /* Do NOT change! */ + err += sn9c102_write_reg(cam, 0x1f, 0x1a); + err += sn9c102_write_reg(cam, 0x1a, 0x1b); + err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19); + + return err; +} + + +static int tas5130d1b_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x63, 0x19); + else + err += sn9c102_write_reg(cam, 0xf3, 0x19); + + return err; +} + + +static struct sn9c102_sensor tas5130d1b = { + .name = "TAS5130D1B", + .maintainer = "Luca Risolia ", + .sysfs_ops = SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_3WIRES, + .init = &tas5130d1b_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xf6, + .step = 0x02, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x47, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .set_ctrl = &tas5130d1b_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &tas5130d1b_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &tas5130d1b_set_pix_format +}; + + +int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam) +{ + const struct usb_device_id tas5130d1b_id_table[] = { + { USB_DEVICE(0x0c45, 0x6025), }, + { USB_DEVICE(0x0c45, 0x60aa), }, + { } + }; + + /* Sensor detection is based on USB pid/vid */ + if (!sn9c102_match_id(cam, tas5130d1b_id_table)) + return -ENODEV; + + sn9c102_attach_sensor(cam, &tas5130d1b); + + return 0; +} -- cgit v1.2.3 From 9c3c5a718fce00294e192f6d722db93fd60e0006 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Apr 2006 16:27:54 -0300 Subject: Added module zc0301 in V4L tree From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/zc0301/Kconfig | 11 + linux/drivers/media/video/zc0301/Makefile | 3 + linux/drivers/media/video/zc0301/zc0301.h | 193 ++ linux/drivers/media/video/zc0301/zc0301_core.c | 2055 ++++++++++++++++++++ .../drivers/media/video/zc0301/zc0301_pas202bcb.c | 361 ++++ linux/drivers/media/video/zc0301/zc0301_sensor.h | 104 + 6 files changed, 2727 insertions(+) create mode 100644 linux/drivers/media/video/zc0301/Kconfig create mode 100644 linux/drivers/media/video/zc0301/Makefile create mode 100644 linux/drivers/media/video/zc0301/zc0301.h create mode 100644 linux/drivers/media/video/zc0301/zc0301_core.c create mode 100644 linux/drivers/media/video/zc0301/zc0301_pas202bcb.c create mode 100644 linux/drivers/media/video/zc0301/zc0301_sensor.h (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/zc0301/Kconfig b/linux/drivers/media/video/zc0301/Kconfig new file mode 100644 index 000000000..115833e4f --- /dev/null +++ b/linux/drivers/media/video/zc0301/Kconfig @@ -0,0 +1,11 @@ +config USB_ZC0301 + tristate "USB ZC0301 Image Processor and Control Chip support" + depends on USB && VIDEO_V4L1 + ---help--- + Say Y here if you want support for cameras based on the ZC0301 + Image Processor and Control Chip. + + See for more info. + + To compile this driver as a module, choose M here: the + module will be called zc0301. diff --git a/linux/drivers/media/video/zc0301/Makefile b/linux/drivers/media/video/zc0301/Makefile new file mode 100644 index 000000000..d749199d8 --- /dev/null +++ b/linux/drivers/media/video/zc0301/Makefile @@ -0,0 +1,3 @@ +zc0301-objs := zc0301_core.o zc0301_pas202bcb.o + +obj-$(CONFIG_USB_ZC0301) += zc0301.o diff --git a/linux/drivers/media/video/zc0301/zc0301.h b/linux/drivers/media/video/zc0301/zc0301.h new file mode 100644 index 000000000..6959c980a --- /dev/null +++ b/linux/drivers/media/video/zc0301/zc0301.h @@ -0,0 +1,193 @@ +/*************************************************************************** + * V4L2 driver for ZC0301 Image Processor and Control Chip * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _ZC0301_H_ +#define _ZC0301_H_ + +#include +#include +#include "compat.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zc0301_sensor.h" + +/*****************************************************************************/ + +#define ZC0301_DEBUG +#define ZC0301_DEBUG_LEVEL 2 +#define ZC0301_MAX_DEVICES 64 +#define ZC0301_FORCE_MUNMAP 0 +#define ZC0301_MAX_FRAMES 32 +#define ZC0301_COMPRESSION_QUALITY 0 +#define ZC0301_URBS 2 +#define ZC0301_ISO_PACKETS 7 +#define ZC0301_ALTERNATE_SETTING 7 +#define ZC0301_URB_TIMEOUT msecs_to_jiffies(2 * ZC0301_ISO_PACKETS) +#define ZC0301_CTRL_TIMEOUT 100 +#define ZC0301_FRAME_TIMEOUT 2 + +/*****************************************************************************/ + +ZC0301_ID_TABLE +ZC0301_SENSOR_TABLE + +enum zc0301_frame_state { + F_UNUSED, + F_QUEUED, + F_GRABBING, + F_DONE, + F_ERROR, +}; + +struct zc0301_frame_t { + void* bufmem; + struct v4l2_buffer buf; + enum zc0301_frame_state state; + struct list_head frame; + unsigned long vma_use_count; +}; + +enum zc0301_dev_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +enum zc0301_io_method { + IO_NONE, + IO_READ, + IO_MMAP, +}; + +enum zc0301_stream_state { + STREAM_OFF, + STREAM_INTERRUPT, + STREAM_ON, +}; + +struct zc0301_module_param { + u8 force_munmap; + u16 frame_timeout; +}; + +static DECLARE_RWSEM(zc0301_disconnect); + +struct zc0301_device { + struct video_device* v4ldev; + + struct zc0301_sensor sensor; + + struct usb_device* usbdev; + struct urb* urb[ZC0301_URBS]; + void* transfer_buffer[ZC0301_URBS]; + u8* control_buffer; + + struct zc0301_frame_t *frame_current, frame[ZC0301_MAX_FRAMES]; + struct list_head inqueue, outqueue; + u32 frame_count, nbuffers, nreadbuffers; + + enum zc0301_io_method io; + enum zc0301_stream_state stream; + + struct v4l2_jpegcompression compression; + + struct zc0301_module_param module_param; + + enum zc0301_dev_state state; + u8 users; + + struct mutex dev_mutex, fileop_mutex; + spinlock_t queue_lock; + wait_queue_head_t open, wait_frame, wait_stream; +}; + +/*****************************************************************************/ + +struct zc0301_device* +zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id) +{ + return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL; +} + +void +zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor) +{ + memcpy(&cam->sensor, sensor, sizeof(struct zc0301_sensor)); +} + +/*****************************************************************************/ + +#undef DBG +#undef KDBG +#ifdef ZC0301_DEBUG +# define DBG(level, fmt, args...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1) \ + dev_err(&cam->usbdev->dev, fmt "\n", ## args); \ + else if ((level) == 2) \ + dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ + else if ((level) >= 3) \ + dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args); \ + } \ +} while (0) +# define KDBG(level, fmt, args...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1 || (level) == 2) \ + pr_info("zc0301: " fmt "\n", ## args); \ + else if ((level) == 3) \ + pr_debug("zc0301: [%s:%d] " fmt "\n", __FUNCTION__, \ + __LINE__ , ## args); \ + } \ +} while (0) +# define V4LDBG(level, name, cmd) \ +do { \ + if (debug >= (level)) \ + v4l_print_ioctl(name, cmd); \ +} while (0) +#else +# define DBG(level, fmt, args...) do {;} while(0) +# define KDBG(level, fmt, args...) do {;} while(0) +# define V4LDBG(level, name, cmd) do {;} while(0) +#endif + +#undef PDBG +#define PDBG(fmt, args...) \ +dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args) + +#undef PDBGG +#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ + +#endif /* _ZC0301_H_ */ diff --git a/linux/drivers/media/video/zc0301/zc0301_core.c b/linux/drivers/media/video/zc0301/zc0301_core.c new file mode 100644 index 000000000..0fad39754 --- /dev/null +++ b/linux/drivers/media/video/zc0301/zc0301_core.c @@ -0,0 +1,2055 @@ +/*************************************************************************** + * Video4Linux2 driver for ZC0301 Image Processor and Control Chip * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * Informations about the chip internals needed to enable the I2C protocol * + * have been taken from the documentation of the ZC030x Video4Linux1 * + * driver written by Andrew Birkett * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zc0301.h" + +/*****************************************************************************/ + +#define ZC0301_MODULE_NAME "V4L2 driver for ZC0301 " \ + "Image Processor and Control Chip" +#define ZC0301_MODULE_AUTHOR "(C) 2006 Luca Risolia" +#define ZC0301_AUTHOR_EMAIL "" +#define ZC0301_MODULE_LICENSE "GPL" +#define ZC0301_MODULE_VERSION "1:1.03" +#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 3) + +/*****************************************************************************/ + +MODULE_DEVICE_TABLE(usb, zc0301_id_table); + +MODULE_AUTHOR(ZC0301_MODULE_AUTHOR " " ZC0301_AUTHOR_EMAIL); +MODULE_DESCRIPTION(ZC0301_MODULE_NAME); +MODULE_VERSION(ZC0301_MODULE_VERSION); +MODULE_LICENSE(ZC0301_MODULE_LICENSE); + +static short video_nr[] = {[0 ... ZC0301_MAX_DEVICES-1] = -1}; +module_param_array(video_nr, short, NULL, 0444); +MODULE_PARM_DESC(video_nr, + "\n<-1|n[,...]> Specify V4L2 minor mode number." + "\n -1 = use next available (default)" + "\n n = use minor number n (integer >= 0)" + "\nYou can specify up to " + __MODULE_STRING(ZC0301_MAX_DEVICES) " cameras this way." + "\nFor example:" + "\nvideo_nr=-1,2,-1 would assign minor number 2 to" + "\nthe second registered camera and use auto for the first" + "\none and for every other camera." + "\n"); + +static short force_munmap[] = {[0 ... ZC0301_MAX_DEVICES-1] = + ZC0301_FORCE_MUNMAP}; +module_param_array(force_munmap, bool, NULL, 0444); +MODULE_PARM_DESC(force_munmap, + "\n<0|1[,...]> Force the application to unmap previously" + "\nmapped buffer memory before calling any VIDIOC_S_CROP or" + "\nVIDIOC_S_FMT ioctl's. Not all the applications support" + "\nthis feature. This parameter is specific for each" + "\ndetected camera." + "\n 0 = do not force memory unmapping" + "\n 1 = force memory unmapping (save memory)" + "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." + "\n"); + +static unsigned int frame_timeout[] = {[0 ... ZC0301_MAX_DEVICES-1] = + ZC0301_FRAME_TIMEOUT}; +module_param_array(frame_timeout, uint, NULL, 0644); +MODULE_PARM_DESC(frame_timeout, + "\n Timeout for a video frame in seconds." + "\nThis parameter is specific for each detected camera." + "\nDefault value is "__MODULE_STRING(ZC0301_FRAME_TIMEOUT)"." + "\n"); + +#ifdef ZC0301_DEBUG +static unsigned short debug = ZC0301_DEBUG_LEVEL; +module_param(debug, ushort, 0644); +MODULE_PARM_DESC(debug, + "\n Debugging information level, from 0 to 3:" + "\n0 = none (use carefully)" + "\n1 = critical errors" + "\n2 = significant informations" + "\n3 = more verbose messages" + "\nLevel 3 is useful for testing only, when only " + "one device is used." + "\nDefault value is "__MODULE_STRING(ZC0301_DEBUG_LEVEL)"." + "\n"); +#endif + +/*****************************************************************************/ + +static u32 +zc0301_request_buffers(struct zc0301_device* cam, u32 count, + enum zc0301_io_method io) +{ + struct v4l2_pix_format* p = &(cam->sensor.pix_format); + struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); + const size_t imagesize = cam->module_param.force_munmap || + io == IO_READ ? + (p->width * p->height * p->priv) / 8 : + (r->width * r->height * p->priv) / 8; + void* buff = NULL; + u32 i; + + if (count > ZC0301_MAX_FRAMES) + count = ZC0301_MAX_FRAMES; + + cam->nbuffers = count; + while (cam->nbuffers > 0) { + if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize)))) + break; + cam->nbuffers--; + } + + for (i = 0; i < cam->nbuffers; i++) { + cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); + cam->frame[i].buf.index = i; + cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); + cam->frame[i].buf.length = imagesize; + cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->frame[i].buf.sequence = 0; + cam->frame[i].buf.field = V4L2_FIELD_NONE; + cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; + cam->frame[i].buf.flags = 0; + } + + return cam->nbuffers; +} + + +static void zc0301_release_buffers(struct zc0301_device* cam) +{ + if (cam->nbuffers) { + vfree(cam->frame[0].bufmem); + cam->nbuffers = 0; + } + cam->frame_current = NULL; +} + + +static void zc0301_empty_framequeues(struct zc0301_device* cam) +{ + u32 i; + + INIT_LIST_HEAD(&cam->inqueue); + INIT_LIST_HEAD(&cam->outqueue); + + for (i = 0; i < ZC0301_MAX_FRAMES; i++) { + cam->frame[i].state = F_UNUSED; + cam->frame[i].buf.bytesused = 0; + } +} + + +static void zc0301_requeue_outqueue(struct zc0301_device* cam) +{ + struct zc0301_frame_t *i; + + list_for_each_entry(i, &cam->outqueue, frame) { + i->state = F_QUEUED; + list_add(&i->frame, &cam->inqueue); + } + + INIT_LIST_HEAD(&cam->outqueue); +} + + +static void zc0301_queue_unusedframes(struct zc0301_device* cam) +{ + unsigned long lock_flags; + u32 i; + + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].state == F_UNUSED) { + cam->frame[i].state = F_QUEUED; + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_add_tail(&cam->frame[i].frame, &cam->inqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + } +} + +/*****************************************************************************/ + +int zc0301_write_reg(struct zc0301_device* cam, u16 index, u16 value) +{ + struct usb_device* udev = cam->usbdev; + int res; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0xa0, 0x40, + value, index, NULL, 0, ZC0301_CTRL_TIMEOUT); + if (res < 0) { + DBG(3, "Failed to write a register (index 0x%04X, " + "value 0x%02X, error %d)",index, value, res); + return -1; + } + + return 0; +} + + +int zc0301_read_reg(struct zc0301_device* cam, u16 index) +{ + struct usb_device* udev = cam->usbdev; + u8* buff = cam->control_buffer; + int res; + + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0xa1, 0xc0, + 0x0001, index, buff, 1, ZC0301_CTRL_TIMEOUT); + if (res < 0) + DBG(3, "Failed to read a register (index 0x%04X, error %d)", + index, res); + + PDBGG("Read: index 0x%04X, value: 0x%04X", index, (int)(*buff)); + + return (res >= 0) ? (int)(*buff) : -1; +} + + +int zc0301_i2c_read(struct zc0301_device* cam, u16 address, u8 length) +{ + int err = 0, res, r0, r1; + + err += zc0301_write_reg(cam, 0x0092, address); + err += zc0301_write_reg(cam, 0x0090, 0x02); + + msleep(1); + + res = zc0301_read_reg(cam, 0x0091); + if (res < 0) + err += res; + r0 = zc0301_read_reg(cam, 0x0095); + if (r0 < 0) + err += r0; + r1 = zc0301_read_reg(cam, 0x0096); + if (r1 < 0) + err += r1; + + res = (length <= 1) ? r0 : r0 | (r1 << 8); + + if (err) + DBG(3, "I2C read failed at address 0x%04X, value: 0x%04X", + address, res); + + + PDBGG("I2C read: address 0x%04X, value: 0x%04X", address, res); + + return err ? -1 : res; +} + + +int zc0301_i2c_write(struct zc0301_device* cam, u16 address, u16 value) +{ + int err = 0, res; + + err += zc0301_write_reg(cam, 0x0092, address); + err += zc0301_write_reg(cam, 0x0093, value & 0xff); + err += zc0301_write_reg(cam, 0x0094, value >> 8); + err += zc0301_write_reg(cam, 0x0090, 0x01); + + msleep(1); + + res = zc0301_read_reg(cam, 0x0091); + if (res < 0) + err += res; + + if (err) + DBG(3, "I2C write failed at address 0x%04X, value: 0x%04X", + address, value); + + PDBGG("I2C write: address 0x%04X, value: 0x%04X", address, value); + + return err ? -1 : 0; +} + +/*****************************************************************************/ + +static void zc0301_urb_complete(struct urb *urb, struct pt_regs* regs) +{ + struct zc0301_device* cam = urb->context; + struct zc0301_frame_t** f; + size_t imagesize; + u8 i; + int err = 0; + + if (urb->status == -ENOENT) + return; + + f = &cam->frame_current; + + if (cam->stream == STREAM_INTERRUPT) { + cam->stream = STREAM_OFF; + if ((*f)) + (*f)->state = F_QUEUED; + DBG(3, "Stream interrupted"); + wake_up(&cam->wait_stream); + } + + if (cam->state & DEV_DISCONNECTED) + return; + + if (cam->state & DEV_MISCONFIGURED) { + wake_up_interruptible(&cam->wait_frame); + return; + } + + if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) + goto resubmit_urb; + + if (!(*f)) + (*f) = list_entry(cam->inqueue.next, struct zc0301_frame_t, + frame); + + imagesize = (cam->sensor.pix_format.width * + cam->sensor.pix_format.height * + cam->sensor.pix_format.priv) / 8; + + for (i = 0; i < urb->number_of_packets; i++) { + unsigned int len, status; + void *pos; + u16* soi; + u8 sof; + + len = urb->iso_frame_desc[i].actual_length; + status = urb->iso_frame_desc[i].status; + pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; + + if (status) { + DBG(3, "Error in isochronous frame"); + (*f)->state = F_ERROR; + continue; + } + + sof = (*(soi = pos) == 0xd8ff); + + PDBGG("Isochrnous frame: length %u, #%u i,", len, i); + + if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) +start_of_frame: + if (sof) { + (*f)->state = F_GRABBING; + (*f)->buf.bytesused = 0; + do_gettimeofday(&(*f)->buf.timestamp); + DBG(3, "SOF detected: new video frame"); + } + + if ((*f)->state == F_GRABBING) { + if (sof && (*f)->buf.bytesused) + goto end_of_frame; + + if ((*f)->buf.bytesused + len > imagesize) { + DBG(3, "Video frame size exceeded"); + (*f)->state = F_ERROR; + continue; + } + + memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, len); + (*f)->buf.bytesused += len; + + if ((*f)->buf.bytesused == imagesize) { + u32 b; +end_of_frame: + b = (*f)->buf.bytesused; + (*f)->state = F_DONE; + (*f)->buf.sequence= ++cam->frame_count; + spin_lock(&cam->queue_lock); + list_move_tail(&(*f)->frame, &cam->outqueue); + if (!list_empty(&cam->inqueue)) + (*f) = list_entry(cam->inqueue.next, + struct zc0301_frame_t, + frame); + else + (*f) = NULL; + spin_unlock(&cam->queue_lock); + DBG(3, "Video frame captured: : %lu bytes", + (unsigned long)(b)); + + if (!(*f)) + goto resubmit_urb; + + if (sof) + goto start_of_frame; + } + } + } + +resubmit_urb: + urb->dev = cam->usbdev; + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0 && err != -EPERM) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "usb_submit_urb() failed"); + } + + wake_up_interruptible(&cam->wait_frame); +} + + +static int zc0301_start_transfer(struct zc0301_device* cam) +{ + struct usb_device *udev = cam->usbdev; + struct urb* urb; + const unsigned int wMaxPacketSize[] = {0, 128, 192, 256, 384, + 512, 768, 1023}; + const unsigned int psz = wMaxPacketSize[ZC0301_ALTERNATE_SETTING]; + s8 i, j; + int err = 0; + + for (i = 0; i < ZC0301_URBS; i++) { + cam->transfer_buffer[i] = kzalloc(ZC0301_ISO_PACKETS * psz, + GFP_KERNEL); + if (!cam->transfer_buffer[i]) { + err = -ENOMEM; + DBG(1, "Not enough memory"); + goto free_buffers; + } + } + + for (i = 0; i < ZC0301_URBS; i++) { + urb = usb_alloc_urb(ZC0301_ISO_PACKETS, GFP_KERNEL); + cam->urb[i] = urb; + if (!urb) { + err = -ENOMEM; + DBG(1, "usb_alloc_urb() failed"); + goto free_urbs; + } + urb->dev = udev; + urb->context = cam; + urb->pipe = usb_rcvisocpipe(udev, 1); + urb->transfer_flags = URB_ISO_ASAP; + urb->number_of_packets = ZC0301_ISO_PACKETS; + urb->complete = zc0301_urb_complete; + urb->transfer_buffer = cam->transfer_buffer[i]; + urb->transfer_buffer_length = psz * ZC0301_ISO_PACKETS; + urb->interval = 1; + for (j = 0; j < ZC0301_ISO_PACKETS; j++) { + urb->iso_frame_desc[j].offset = psz * j; + urb->iso_frame_desc[j].length = psz; + } + } + + err = usb_set_interface(udev, 0, ZC0301_ALTERNATE_SETTING); + if (err) { + DBG(1, "usb_set_interface() failed"); + goto free_urbs; + } + + cam->frame_current = NULL; + + for (i = 0; i < ZC0301_URBS; i++) { + err = usb_submit_urb(cam->urb[i], GFP_KERNEL); + if (err) { + for (j = i-1; j >= 0; j--) + usb_kill_urb(cam->urb[j]); + DBG(1, "usb_submit_urb() failed, error %d", err); + goto free_urbs; + } + } + + return 0; + +free_urbs: + for (i = 0; (i < ZC0301_URBS) && cam->urb[i]; i++) + usb_free_urb(cam->urb[i]); + +free_buffers: + for (i = 0; (i < ZC0301_URBS) && cam->transfer_buffer[i]; i++) + kfree(cam->transfer_buffer[i]); + + return err; +} + + +static int zc0301_stop_transfer(struct zc0301_device* cam) +{ + struct usb_device *udev = cam->usbdev; + s8 i; + int err = 0; + + if (cam->state & DEV_DISCONNECTED) + return 0; + + for (i = ZC0301_URBS-1; i >= 0; i--) { + usb_kill_urb(cam->urb[i]); + usb_free_urb(cam->urb[i]); + kfree(cam->transfer_buffer[i]); + } + + err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ + if (err) + DBG(3, "usb_set_interface() failed"); + + return err; +} + + +static int zc0301_stream_interrupt(struct zc0301_device* cam) +{ + long timeout; + + cam->stream = STREAM_INTERRUPT; + timeout = wait_event_timeout(cam->wait_stream, + (cam->stream == STREAM_OFF) || + (cam->state & DEV_DISCONNECTED), + ZC0301_URB_TIMEOUT); + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + else if (cam->stream != STREAM_OFF) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "URB timeout reached. The camera is misconfigured. To " + "use it, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + return 0; +} + +/*****************************************************************************/ + +static int +zc0301_set_compression(struct zc0301_device* cam, + struct v4l2_jpegcompression* compression) +{ + int r, err = 0; + + if ((r = zc0301_read_reg(cam, 0x0008)) < 0) + err += r; + err += zc0301_write_reg(cam, 0x0008, r | 0x11 | compression->quality); + + return err ? -EIO : 0; +} + + +static int zc0301_init(struct zc0301_device* cam) +{ + struct zc0301_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + struct v4l2_queryctrl *qctrl; + struct v4l2_rect* rect; + u8 i = 0; + int err = 0; + + if (!(cam->state & DEV_INITIALIZED)) { + init_waitqueue_head(&cam->open); + qctrl = s->qctrl; + rect = &(s->cropcap.defrect); + cam->compression.quality = ZC0301_COMPRESSION_QUALITY; + } else { /* use current values */ + qctrl = s->_qctrl; + rect = &(s->_rect); + } + + if (s->init) { + err = s->init(cam); + if (err) { + DBG(3, "Sensor initialization failed"); + return err; + } + } + + if ((err = zc0301_set_compression(cam, &cam->compression))) { + DBG(3, "set_compression() failed"); + return err; + } + + if (s->set_crop) + if ((err = s->set_crop(cam, rect))) { + DBG(3, "set_crop() failed"); + return err; + } + + if (s->set_ctrl) { + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (s->qctrl[i].id != 0 && + !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { + ctrl.id = s->qctrl[i].id; + ctrl.value = qctrl[i].default_value; + err = s->set_ctrl(cam, &ctrl); + if (err) { + DBG(3, "Set %s control failed", + s->qctrl[i].name); + return err; + } + DBG(3, "Image sensor supports '%s' control", + s->qctrl[i].name); + } + } + + if (!(cam->state & DEV_INITIALIZED)) { + mutex_init(&cam->fileop_mutex); + spin_lock_init(&cam->queue_lock); + init_waitqueue_head(&cam->wait_frame); + init_waitqueue_head(&cam->wait_stream); + cam->nreadbuffers = 2; + memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); + memcpy(&(s->_rect), &(s->cropcap.defrect), + sizeof(struct v4l2_rect)); + cam->state |= DEV_INITIALIZED; + } + + DBG(2, "Initialization succeeded"); + return 0; +} + + +static void zc0301_release_resources(struct zc0301_device* cam) +{ + DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor); + video_set_drvdata(cam->v4ldev, NULL); + video_unregister_device(cam->v4ldev); + kfree(cam->control_buffer); +} + +/*****************************************************************************/ + +static int zc0301_open(struct inode* inode, struct file* filp) +{ + struct zc0301_device* cam; + int err = 0; + + /* + This is the only safe way to prevent race conditions with + disconnect + */ + if (!down_read_trylock(&zc0301_disconnect)) + return -ERESTARTSYS; + + cam = video_get_drvdata(video_devdata(filp)); + + if (mutex_lock_interruptible(&cam->dev_mutex)) { + up_read(&zc0301_disconnect); + return -ERESTARTSYS; + } + + if (cam->users) { + DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor); + if ((filp->f_flags & O_NONBLOCK) || + (filp->f_flags & O_NDELAY)) { + err = -EWOULDBLOCK; + goto out; + } + mutex_unlock(&cam->dev_mutex); + err = wait_event_interruptible_exclusive(cam->open, + cam->state & DEV_DISCONNECTED + || !cam->users); + if (err) { + up_read(&zc0301_disconnect); + return err; + } + if (cam->state & DEV_DISCONNECTED) { + up_read(&zc0301_disconnect); + return -ENODEV; + } + mutex_lock(&cam->dev_mutex); + } + + + if (cam->state & DEV_MISCONFIGURED) { + err = zc0301_init(cam); + if (err) { + DBG(1, "Initialization failed again. " + "I will retry on next open()."); + goto out; + } + cam->state &= ~DEV_MISCONFIGURED; + } + + if ((err = zc0301_start_transfer(cam))) + goto out; + + filp->private_data = cam; + cam->users++; + cam->io = IO_NONE; + cam->stream = STREAM_OFF; + cam->nbuffers = 0; + cam->frame_count = 0; + zc0301_empty_framequeues(cam); + + DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor); + +out: + mutex_unlock(&cam->dev_mutex); + up_read(&zc0301_disconnect); + return err; +} + + +static int zc0301_release(struct inode* inode, struct file* filp) +{ + struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + + mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */ + + zc0301_stop_transfer(cam); + + zc0301_release_buffers(cam); + + if (cam->state & DEV_DISCONNECTED) { + zc0301_release_resources(cam); + usb_put_dev(cam->usbdev); + mutex_unlock(&cam->dev_mutex); + kfree(cam); + return 0; + } + + cam->users--; + wake_up_interruptible_nr(&cam->open, 1); + + DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor); + + mutex_unlock(&cam->dev_mutex); + + return 0; +} + + +static ssize_t +zc0301_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) +{ + struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + struct zc0301_frame_t* f, * i; + unsigned long lock_flags; + long timeout; + int err = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + if (cam->io == IO_MMAP) { + DBG(3, "Close and open the device again to choose the read " + "method"); + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + if (cam->io == IO_NONE) { + if (!zc0301_request_buffers(cam, cam->nreadbuffers, IO_READ)) { + DBG(1, "read() failed, not enough memory"); + mutex_unlock(&cam->fileop_mutex); + return -ENOMEM; + } + cam->io = IO_READ; + cam->stream = STREAM_ON; + } + + if (list_empty(&cam->inqueue)) { + if (!list_empty(&cam->outqueue)) + zc0301_empty_framequeues(cam); + zc0301_queue_unusedframes(cam); + } + + if (!count) { + mutex_unlock(&cam->fileop_mutex); + return 0; + } + + if (list_empty(&cam->outqueue)) { + if (filp->f_flags & O_NONBLOCK) { + mutex_unlock(&cam->fileop_mutex); + return -EAGAIN; + } + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) { + mutex_unlock(&cam->fileop_mutex); + return timeout; + } + if (cam->state & DEV_DISCONNECTED) { + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + if (!timeout || (cam->state & DEV_MISCONFIGURED)) { + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + } + + f = list_entry(cam->outqueue.prev, struct zc0301_frame_t, frame); + + if (count > f->buf.bytesused) + count = f->buf.bytesused; + + if (copy_to_user(buf, f->bufmem, count)) { + err = -EFAULT; + goto exit; + } + *f_pos += count; + +exit: + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_for_each_entry(i, &cam->outqueue, frame) + i->state = F_UNUSED; + INIT_LIST_HEAD(&cam->outqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + zc0301_queue_unusedframes(cam); + + PDBGG("Frame #%lu, bytes read: %zu", + (unsigned long)f->buf.index, count); + + mutex_unlock(&cam->fileop_mutex); + + return err ? err : count; +} + + +static unsigned int zc0301_poll(struct file *filp, poll_table *wait) +{ + struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + struct zc0301_frame_t* f; + unsigned long lock_flags; + unsigned int mask = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return POLLERR; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + goto error; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + goto error; + } + + if (cam->io == IO_NONE) { + if (!zc0301_request_buffers(cam, cam->nreadbuffers, IO_READ)) { + DBG(1, "poll() failed, not enough memory"); + goto error; + } + cam->io = IO_READ; + cam->stream = STREAM_ON; + } + + if (cam->io == IO_READ) { + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_for_each_entry(f, &cam->outqueue, frame) + f->state = F_UNUSED; + INIT_LIST_HEAD(&cam->outqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + zc0301_queue_unusedframes(cam); + } + + poll_wait(filp, &cam->wait_frame, wait); + + if (!list_empty(&cam->outqueue)) + mask |= POLLIN | POLLRDNORM; + + mutex_unlock(&cam->fileop_mutex); + + return mask; + +error: + mutex_unlock(&cam->fileop_mutex); + return POLLERR; +} + + +static void zc0301_vm_open(struct vm_area_struct* vma) +{ + struct zc0301_frame_t* f = vma->vm_private_data; + f->vma_use_count++; +} + + +static void zc0301_vm_close(struct vm_area_struct* vma) +{ + /* NOTE: buffers are not freed here */ + struct zc0301_frame_t* f = vma->vm_private_data; + f->vma_use_count--; +} + + +static struct vm_operations_struct zc0301_vm_ops = { + .open = zc0301_vm_open, + .close = zc0301_vm_close, +}; + + +static int zc0301_mmap(struct file* filp, struct vm_area_struct *vma) +{ + struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + unsigned long size = vma->vm_end - vma->vm_start, + start = vma->vm_start; + void *pos; + u32 i; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || + size != PAGE_ALIGN(cam->frame[0].buf.length)) { + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + for (i = 0; i < cam->nbuffers; i++) { + if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff) + break; + } + if (i == cam->nbuffers) { + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_RESERVED; + + pos = cam->frame[i].bufmem; + while (size > 0) { /* size is page-aligned */ + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { + mutex_unlock(&cam->fileop_mutex); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + size -= PAGE_SIZE; + } + + vma->vm_ops = &zc0301_vm_ops; + vma->vm_private_data = &cam->frame[i]; + + zc0301_vm_open(vma); + + mutex_unlock(&cam->fileop_mutex); + + return 0; +} + +/*****************************************************************************/ + +static int +zc0301_vidioc_querycap(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_capability cap = { + .driver = "zc0301", + .version = ZC0301_MODULE_VERSION_CODE, + .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING, + }; + + strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); + if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) + strlcpy(cap.bus_info, cam->usbdev->dev.bus_id, + sizeof(cap.bus_info)); + + if (copy_to_user(arg, &cap, sizeof(cap))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_enuminput(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_input i; + + if (copy_from_user(&i, arg, sizeof(i))) + return -EFAULT; + + if (i.index) + return -EINVAL; + + memset(&i, 0, sizeof(i)); + strcpy(i.name, "Camera"); + i.type = V4L2_INPUT_TYPE_CAMERA; + + if (copy_to_user(arg, &i, sizeof(i))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_g_input(struct zc0301_device* cam, void __user * arg) +{ + int index = 0; + + if (copy_to_user(arg, &index, sizeof(index))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_s_input(struct zc0301_device* cam, void __user * arg) +{ + int index; + + if (copy_from_user(&index, arg, sizeof(index))) + return -EFAULT; + + if (index != 0) + return -EINVAL; + + return 0; +} + + +static int +zc0301_vidioc_query_ctrl(struct zc0301_device* cam, void __user * arg) +{ + struct zc0301_sensor* s = &cam->sensor; + struct v4l2_queryctrl qc; + u8 i; + + if (copy_from_user(&qc, arg, sizeof(qc))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (qc.id && qc.id == s->qctrl[i].id) { + memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); + if (copy_to_user(arg, &qc, sizeof(qc))) + return -EFAULT; + return 0; + } + + return -EINVAL; +} + + +static int +zc0301_vidioc_g_ctrl(struct zc0301_device* cam, void __user * arg) +{ + struct zc0301_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + int err = 0; + u8 i; + + if (!s->get_ctrl && !s->set_ctrl) + return -EINVAL; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + + if (!s->get_ctrl) { + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (ctrl.id == s->qctrl[i].id) { + ctrl.value = s->_qctrl[i].default_value; + goto exit; + } + return -EINVAL; + } else + err = s->get_ctrl(cam, &ctrl); + +exit: + if (copy_to_user(arg, &ctrl, sizeof(ctrl))) + return -EFAULT; + + return err; +} + + +static int +zc0301_vidioc_s_ctrl(struct zc0301_device* cam, void __user * arg) +{ + struct zc0301_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + u8 i; + int err = 0; + + if (!s->set_ctrl) + return -EINVAL; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (ctrl.id == s->qctrl[i].id) { + if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) + return -EINVAL; + if (ctrl.value < s->qctrl[i].minimum || + ctrl.value > s->qctrl[i].maximum) + return -ERANGE; + ctrl.value -= ctrl.value % s->qctrl[i].step; + break; + } + + if ((err = s->set_ctrl(cam, &ctrl))) + return err; + + s->_qctrl[i].default_value = ctrl.value; + + return 0; +} + + +static int +zc0301_vidioc_cropcap(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_cropcap* cc = &(cam->sensor.cropcap); + + cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cc->pixelaspect.numerator = 1; + cc->pixelaspect.denominator = 1; + + if (copy_to_user(arg, cc, sizeof(*cc))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_g_crop(struct zc0301_device* cam, void __user * arg) +{ + struct zc0301_sensor* s = &cam->sensor; + struct v4l2_crop crop = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + }; + + memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect)); + + if (copy_to_user(arg, &crop, sizeof(crop))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_s_crop(struct zc0301_device* cam, void __user * arg) +{ + struct zc0301_sensor* s = &cam->sensor; + struct v4l2_crop crop; + struct v4l2_rect* rect; + struct v4l2_rect* bounds = &(s->cropcap.bounds); + const enum zc0301_stream_state stream = cam->stream; + const u32 nbuffers = cam->nbuffers; + u32 i; + int err = 0; + + if (copy_from_user(&crop, arg, sizeof(crop))) + return -EFAULT; + + rect = &(crop.c); + + if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (cam->module_param.force_munmap) + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_S_CROP failed. " + "Unmap the buffers first."); + return -EINVAL; + } + + if (!s->set_crop) { + memcpy(rect, &(s->_rect), sizeof(*rect)); + if (copy_to_user(arg, &crop, sizeof(crop))) + return -EFAULT; + return 0; + } + + rect->left &= ~7L; + rect->top &= ~7L; + if (rect->width < 8) + rect->width = 8; + if (rect->height < 8) + rect->height = 8; + if (rect->width > bounds->width) + rect->width = bounds->width; + if (rect->height > bounds->height) + rect->height = bounds->height; + if (rect->left < bounds->left) + rect->left = bounds->left; + if (rect->top < bounds->top) + rect->top = bounds->top; + if (rect->left + rect->width > bounds->left + bounds->width) + rect->left = bounds->left+bounds->width - rect->width; + if (rect->top + rect->height > bounds->top + bounds->height) + rect->top = bounds->top+bounds->height - rect->height; + rect->width &= ~7L; + rect->height &= ~7L; + + if (cam->stream == STREAM_ON) + if ((err = zc0301_stream_interrupt(cam))) + return err; + + if (copy_to_user(arg, &crop, sizeof(crop))) { + cam->stream = stream; + return -EFAULT; + } + + if (cam->module_param.force_munmap || cam->io == IO_READ) + zc0301_release_buffers(cam); + + if (s->set_crop) + err += s->set_crop(cam, rect); + + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + s->pix_format.width = rect->width; + s->pix_format.height = rect->height; + memcpy(&(s->_rect), rect, sizeof(*rect)); + + if ((cam->module_param.force_munmap || cam->io == IO_READ) && + nbuffers != zc0301_request_buffers(cam, nbuffers, cam->io)) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -ENOMEM; + } + + if (cam->io == IO_READ) + zc0301_empty_framequeues(cam); + else if (cam->module_param.force_munmap) + zc0301_requeue_outqueue(cam); + + cam->stream = stream; + + return 0; +} + + +static int +zc0301_vidioc_enum_fmt(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_fmtdesc fmtd; + + if (copy_from_user(&fmtd, arg, sizeof(fmtd))) + return -EFAULT; + + if (fmtd.index == 0) { + strcpy(fmtd.description, "JPEG"); + fmtd.pixelformat = V4L2_PIX_FMT_JPEG; + fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; + } else + return -EINVAL; + + fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + memset(&fmtd.reserved, 0, sizeof(fmtd.reserved)); + + if (copy_to_user(arg, &fmtd, sizeof(fmtd))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_g_fmt(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_format format; + struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format); + + if (copy_from_user(&format, arg, sizeof(format))) + return -EFAULT; + + if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + pfmt->bytesperline = 0; + pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); + pfmt->field = V4L2_FIELD_NONE; + memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); + + if (copy_to_user(arg, &format, sizeof(format))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_try_s_fmt(struct zc0301_device* cam, unsigned int cmd, + void __user * arg) +{ + struct zc0301_sensor* s = &cam->sensor; + struct v4l2_format format; + struct v4l2_pix_format* pix; + struct v4l2_pix_format* pfmt = &(s->pix_format); + struct v4l2_rect* bounds = &(s->cropcap.bounds); + struct v4l2_rect rect; + const enum zc0301_stream_state stream = cam->stream; + const u32 nbuffers = cam->nbuffers; + u32 i; + int err = 0; + + if (copy_from_user(&format, arg, sizeof(format))) + return -EFAULT; + + pix = &(format.fmt.pix); + + if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memcpy(&rect, &(s->_rect), sizeof(rect)); + + if (!s->set_crop) { + pix->width = rect.width; + pix->height = rect.height; + } else { + rect.width = pix->width; + rect.height = pix->height; + } + + if (rect.width < 8) + rect.width = 8; + if (rect.height < 8) + rect.height = 8; + if (rect.width > bounds->left + bounds->width - rect.left) + rect.width = bounds->left + bounds->width - rect.left; + if (rect.height > bounds->top + bounds->height - rect.top) + rect.height = bounds->top + bounds->height - rect.top; + rect.width &= ~7L; + rect.height &= ~7L; + + pix->width = rect.width; + pix->height = rect.height; + pix->pixelformat = pfmt->pixelformat; + pix->priv = pfmt->priv; + pix->colorspace = pfmt->colorspace; + pix->bytesperline = 0; + pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); + pix->field = V4L2_FIELD_NONE; + + if (cmd == VIDIOC_TRY_FMT) { + if (copy_to_user(arg, &format, sizeof(format))) + return -EFAULT; + return 0; + } + + if (cam->module_param.force_munmap) + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_S_FMT failed. " + "Unmap the buffers first."); + return -EINVAL; + } + + if (cam->stream == STREAM_ON) + if ((err = zc0301_stream_interrupt(cam))) + return err; + + if (copy_to_user(arg, &format, sizeof(format))) { + cam->stream = stream; + return -EFAULT; + } + + if (cam->module_param.force_munmap || cam->io == IO_READ) + zc0301_release_buffers(cam); + + if (s->set_crop) + err += s->set_crop(cam, &rect); + + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + memcpy(pfmt, pix, sizeof(*pix)); + memcpy(&(s->_rect), &rect, sizeof(rect)); + + if ((cam->module_param.force_munmap || cam->io == IO_READ) && + nbuffers != zc0301_request_buffers(cam, nbuffers, cam->io)) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -ENOMEM; + } + + if (cam->io == IO_READ) + zc0301_empty_framequeues(cam); + else if (cam->module_param.force_munmap) + zc0301_requeue_outqueue(cam); + + cam->stream = stream; + + return 0; +} + + +static int +zc0301_vidioc_g_jpegcomp(struct zc0301_device* cam, void __user * arg) +{ + if (copy_to_user(arg, &cam->compression, sizeof(cam->compression))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_s_jpegcomp(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_jpegcompression jc; + const enum zc0301_stream_state stream = cam->stream; + int err = 0; + + if (copy_from_user(&jc, arg, sizeof(jc))) + return -EFAULT; + + if (jc.quality != 0) + return -EINVAL; + + if (cam->stream == STREAM_ON) + if ((err = zc0301_stream_interrupt(cam))) + return err; + + err += zc0301_set_compression(cam, &jc); + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware " + "problems. To use the camera, close and open " + "/dev/video%d again.", cam->v4ldev->minor); + return -EIO; + } + + cam->compression.quality = jc.quality; + + cam->stream = stream; + + return 0; +} + + +static int +zc0301_vidioc_reqbufs(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_requestbuffers rb; + u32 i; + int err; + + if (copy_from_user(&rb, arg, sizeof(rb))) + return -EFAULT; + + if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + rb.memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (cam->io == IO_READ) { + DBG(3, "Close and open the device again to choose the mmap " + "I/O method"); + return -EINVAL; + } + + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_REQBUFS failed. " + "Previous buffers are still mapped."); + return -EINVAL; + } + + if (cam->stream == STREAM_ON) + if ((err = zc0301_stream_interrupt(cam))) + return err; + + zc0301_empty_framequeues(cam); + + zc0301_release_buffers(cam); + if (rb.count) + rb.count = zc0301_request_buffers(cam, rb.count, IO_MMAP); + + if (copy_to_user(arg, &rb, sizeof(rb))) { + zc0301_release_buffers(cam); + cam->io = IO_NONE; + return -EFAULT; + } + + cam->io = rb.count ? IO_MMAP : IO_NONE; + + return 0; +} + + +static int +zc0301_vidioc_querybuf(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_buffer b; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + b.index >= cam->nbuffers || cam->io != IO_MMAP) + return -EINVAL; + + memcpy(&b, &cam->frame[b.index].buf, sizeof(b)); + + if (cam->frame[b.index].vma_use_count) + b.flags |= V4L2_BUF_FLAG_MAPPED; + + if (cam->frame[b.index].state == F_DONE) + b.flags |= V4L2_BUF_FLAG_DONE; + else if (cam->frame[b.index].state != F_UNUSED) + b.flags |= V4L2_BUF_FLAG_QUEUED; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_qbuf(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_buffer b; + unsigned long lock_flags; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + b.index >= cam->nbuffers || cam->io != IO_MMAP) + return -EINVAL; + + if (cam->frame[b.index].state != F_UNUSED) + return -EINVAL; + + cam->frame[b.index].state = F_QUEUED; + + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_add_tail(&cam->frame[b.index].frame, &cam->inqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + PDBGG("Frame #%lu queued", (unsigned long)b.index); + + return 0; +} + + +static int +zc0301_vidioc_dqbuf(struct zc0301_device* cam, struct file* filp, + void __user * arg) +{ + struct v4l2_buffer b; + struct zc0301_frame_t *f; + unsigned long lock_flags; + long timeout; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP) + return -EINVAL; + + if (list_empty(&cam->outqueue)) { + if (cam->stream == STREAM_OFF) + return -EINVAL; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) + return timeout; + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + if (!timeout || (cam->state & DEV_MISCONFIGURED)) + return -EIO; + } + + spin_lock_irqsave(&cam->queue_lock, lock_flags); + f = list_entry(cam->outqueue.next, struct zc0301_frame_t, frame); + list_del(cam->outqueue.next); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + f->state = F_UNUSED; + + memcpy(&b, &f->buf, sizeof(b)); + if (f->vma_use_count) + b.flags |= V4L2_BUF_FLAG_MAPPED; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index); + + return 0; +} + + +static int +zc0301_vidioc_streamon(struct zc0301_device* cam, void __user * arg) +{ + int type; + + if (copy_from_user(&type, arg, sizeof(type))) + return -EFAULT; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + if (list_empty(&cam->inqueue)) + return -EINVAL; + + cam->stream = STREAM_ON; + + DBG(3, "Stream on"); + + return 0; +} + + +static int +zc0301_vidioc_streamoff(struct zc0301_device* cam, void __user * arg) +{ + int type, err; + + if (copy_from_user(&type, arg, sizeof(type))) + return -EFAULT; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + if (cam->stream == STREAM_ON) + if ((err = zc0301_stream_interrupt(cam))) + return err; + + zc0301_empty_framequeues(cam); + + DBG(3, "Stream off"); + + return 0; +} + + +static int +zc0301_vidioc_g_parm(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_streamparm sp; + + if (copy_from_user(&sp, arg, sizeof(sp))) + return -EFAULT; + + if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + sp.parm.capture.extendedmode = 0; + sp.parm.capture.readbuffers = cam->nreadbuffers; + + if (copy_to_user(arg, &sp, sizeof(sp))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_s_parm(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_streamparm sp; + + if (copy_from_user(&sp, arg, sizeof(sp))) + return -EFAULT; + + if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + sp.parm.capture.extendedmode = 0; + + if (sp.parm.capture.readbuffers == 0) + sp.parm.capture.readbuffers = cam->nreadbuffers; + + if (sp.parm.capture.readbuffers > ZC0301_MAX_FRAMES) + sp.parm.capture.readbuffers = ZC0301_MAX_FRAMES; + + if (copy_to_user(arg, &sp, sizeof(sp))) + return -EFAULT; + + cam->nreadbuffers = sp.parm.capture.readbuffers; + + return 0; +} + + +static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp, + unsigned int cmd, void __user * arg) +{ + struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + + switch (cmd) { + + case VIDIOC_QUERYCAP: + return zc0301_vidioc_querycap(cam, arg); + + case VIDIOC_ENUMINPUT: + return zc0301_vidioc_enuminput(cam, arg); + + case VIDIOC_G_INPUT: + return zc0301_vidioc_g_input(cam, arg); + + case VIDIOC_S_INPUT: + return zc0301_vidioc_s_input(cam, arg); + + case VIDIOC_QUERYCTRL: + return zc0301_vidioc_query_ctrl(cam, arg); + + case VIDIOC_G_CTRL: + return zc0301_vidioc_g_ctrl(cam, arg); + + case VIDIOC_S_CTRL_OLD: + case VIDIOC_S_CTRL: + return zc0301_vidioc_s_ctrl(cam, arg); + + case VIDIOC_CROPCAP_OLD: + case VIDIOC_CROPCAP: + return zc0301_vidioc_cropcap(cam, arg); + + case VIDIOC_G_CROP: + return zc0301_vidioc_g_crop(cam, arg); + + case VIDIOC_S_CROP: + return zc0301_vidioc_s_crop(cam, arg); + + case VIDIOC_ENUM_FMT: + return zc0301_vidioc_enum_fmt(cam, arg); + + case VIDIOC_G_FMT: + return zc0301_vidioc_g_fmt(cam, arg); + + case VIDIOC_TRY_FMT: + case VIDIOC_S_FMT: + return zc0301_vidioc_try_s_fmt(cam, cmd, arg); + + case VIDIOC_G_JPEGCOMP: + return zc0301_vidioc_g_jpegcomp(cam, arg); + + case VIDIOC_S_JPEGCOMP: + return zc0301_vidioc_s_jpegcomp(cam, arg); + + case VIDIOC_REQBUFS: + return zc0301_vidioc_reqbufs(cam, arg); + + case VIDIOC_QUERYBUF: + return zc0301_vidioc_querybuf(cam, arg); + + case VIDIOC_QBUF: + return zc0301_vidioc_qbuf(cam, arg); + + case VIDIOC_DQBUF: + return zc0301_vidioc_dqbuf(cam, filp, arg); + + case VIDIOC_STREAMON: + return zc0301_vidioc_streamon(cam, arg); + + case VIDIOC_STREAMOFF: + return zc0301_vidioc_streamoff(cam, arg); + + case VIDIOC_G_PARM: + return zc0301_vidioc_g_parm(cam, arg); + + case VIDIOC_S_PARM_OLD: + case VIDIOC_S_PARM: + return zc0301_vidioc_s_parm(cam, arg); + + case VIDIOC_G_STD: + case VIDIOC_S_STD: + case VIDIOC_QUERYSTD: + case VIDIOC_ENUMSTD: + case VIDIOC_QUERYMENU: + return -EINVAL; + + default: + return -EINVAL; + + } +} + + +static int zc0301_ioctl(struct inode* inode, struct file* filp, + unsigned int cmd, unsigned long arg) +{ + struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + int err = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + V4LDBG(3, "zc0301", cmd); + + err = zc0301_ioctl_v4l2(inode, filp, cmd, (void __user *)arg); + + mutex_unlock(&cam->fileop_mutex); + + return err; +} + + +static struct file_operations zc0301_fops = { + .owner = THIS_MODULE, + .open = zc0301_open, + .release = zc0301_release, + .ioctl = zc0301_ioctl, + .read = zc0301_read, + .poll = zc0301_poll, + .mmap = zc0301_mmap, + .llseek = no_llseek, +}; + +/*****************************************************************************/ + +static int +zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct zc0301_device* cam; + static unsigned int dev_nr = 0; + unsigned int i; + int err = 0; + + if (!(cam = kzalloc(sizeof(struct zc0301_device), GFP_KERNEL))) + return -ENOMEM; + + cam->usbdev = udev; + + if (!(cam->control_buffer = kzalloc(4, GFP_KERNEL))) { + DBG(1, "kmalloc() failed"); + err = -ENOMEM; + goto fail; + } + + if (!(cam->v4ldev = video_device_alloc())) { + DBG(1, "video_device_alloc() failed"); + err = -ENOMEM; + goto fail; + } + + mutex_init(&cam->dev_mutex); + + DBG(2, "ZC0301 Image Processor and Control Chip detected " + "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct); + + for (i = 0; zc0301_sensor_table[i]; i++) { + err = zc0301_sensor_table[i](cam); + if (!err) + break; + } + + if (!err) + DBG(2, "%s image sensor detected", cam->sensor.name); + else { + DBG(1, "No supported image sensor detected"); + err = -ENODEV; + goto fail; + } + + if (zc0301_init(cam)) { + DBG(1, "Initialization failed. I will retry on open()."); + cam->state |= DEV_MISCONFIGURED; + } + + strcpy(cam->v4ldev->name, "ZC0301 PC Camera"); + cam->v4ldev->owner = THIS_MODULE; + cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; + cam->v4ldev->hardware = 0; + cam->v4ldev->fops = &zc0301_fops; + cam->v4ldev->minor = video_nr[dev_nr]; + cam->v4ldev->release = video_device_release; + video_set_drvdata(cam->v4ldev, cam); + + mutex_lock(&cam->dev_mutex); + + err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, + video_nr[dev_nr]); + if (err) { + DBG(1, "V4L2 device registration failed"); + if (err == -ENFILE && video_nr[dev_nr] == -1) + DBG(1, "Free /dev/videoX node not found"); + video_nr[dev_nr] = -1; + dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0; + mutex_unlock(&cam->dev_mutex); + goto fail; + } + + DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor); + + cam->module_param.force_munmap = force_munmap[dev_nr]; + cam->module_param.frame_timeout = frame_timeout[dev_nr]; + + dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0; + + usb_set_intfdata(intf, cam); + + mutex_unlock(&cam->dev_mutex); + + return 0; + +fail: + if (cam) { + kfree(cam->control_buffer); + if (cam->v4ldev) + video_device_release(cam->v4ldev); + kfree(cam); + } + return err; +} + + +static void zc0301_usb_disconnect(struct usb_interface* intf) +{ + struct zc0301_device* cam = usb_get_intfdata(intf); + + if (!cam) + return; + + down_write(&zc0301_disconnect); + + mutex_lock(&cam->dev_mutex); + + DBG(2, "Disconnecting %s...", cam->v4ldev->name); + + wake_up_interruptible_all(&cam->open); + + if (cam->users) { + DBG(2, "Device /dev/video%d is open! Deregistration and " + "memory deallocation are deferred on close.", + cam->v4ldev->minor); + cam->state |= DEV_MISCONFIGURED; + zc0301_stop_transfer(cam); + cam->state |= DEV_DISCONNECTED; + wake_up_interruptible(&cam->wait_frame); + wake_up(&cam->wait_stream); + usb_get_dev(cam->usbdev); + } else { + cam->state |= DEV_DISCONNECTED; + zc0301_release_resources(cam); + } + + mutex_unlock(&cam->dev_mutex); + + if (!cam->users) + kfree(cam); + + up_write(&zc0301_disconnect); +} + + +static struct usb_driver zc0301_usb_driver = { + .name = "zc0301", + .id_table = zc0301_id_table, + .probe = zc0301_usb_probe, + .disconnect = zc0301_usb_disconnect, +}; + +/*****************************************************************************/ + +static int __init zc0301_module_init(void) +{ + int err = 0; + + KDBG(2, ZC0301_MODULE_NAME " v" ZC0301_MODULE_VERSION); + KDBG(3, ZC0301_MODULE_AUTHOR); + + if ((err = usb_register(&zc0301_usb_driver))) + KDBG(1, "usb_register() failed"); + + return err; +} + + +static void __exit zc0301_module_exit(void) +{ + usb_deregister(&zc0301_usb_driver); +} + + +module_init(zc0301_module_init); +module_exit(zc0301_module_exit); diff --git a/linux/drivers/media/video/zc0301/zc0301_pas202bcb.c b/linux/drivers/media/video/zc0301/zc0301_pas202bcb.c new file mode 100644 index 000000000..eaadf0252 --- /dev/null +++ b/linux/drivers/media/video/zc0301/zc0301_pas202bcb.c @@ -0,0 +1,361 @@ +/*************************************************************************** + * Plug-in for PAS202BCB image sensor connected to the ZC030! Image * + * Processor and Control Chip * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * Initialization values of the ZC0301 have been taken from the SPCA5XX * + * driver maintained by Michel Xhaard * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +/* + NOTE: Sensor controls are disabled for now, becouse changing them while + streaming sometimes results in out-of-sync video frames. We'll use + the default initialization, until we know how to stop and start video + in the chip. However, the image quality still looks good under various + light conditions. +*/ + +#include +#include "zc0301_sensor.h" + + +static struct zc0301_sensor pas202bcb; + + +static int pas202bcb_init(struct zc0301_device* cam) +{ + int err = 0; + + err += zc0301_write_reg(cam, 0x0002, 0x00); + err += zc0301_write_reg(cam, 0x0003, 0x02); + err += zc0301_write_reg(cam, 0x0004, 0x80); + err += zc0301_write_reg(cam, 0x0005, 0x01); + err += zc0301_write_reg(cam, 0x0006, 0xE0); + err += zc0301_write_reg(cam, 0x0098, 0x00); + err += zc0301_write_reg(cam, 0x009A, 0x03); + err += zc0301_write_reg(cam, 0x011A, 0x00); + err += zc0301_write_reg(cam, 0x011C, 0x03); + err += zc0301_write_reg(cam, 0x009B, 0x01); + err += zc0301_write_reg(cam, 0x009C, 0xE6); + err += zc0301_write_reg(cam, 0x009D, 0x02); + err += zc0301_write_reg(cam, 0x009E, 0x86); + + err += zc0301_i2c_write(cam, 0x02, 0x02); + err += zc0301_i2c_write(cam, 0x0A, 0x01); + err += zc0301_i2c_write(cam, 0x0B, 0x01); + err += zc0301_i2c_write(cam, 0x0D, 0x00); + err += zc0301_i2c_write(cam, 0x12, 0x05); + err += zc0301_i2c_write(cam, 0x13, 0x63); + err += zc0301_i2c_write(cam, 0x15, 0x70); + + err += zc0301_write_reg(cam, 0x0101, 0xB7); + err += zc0301_write_reg(cam, 0x0100, 0x0D); + err += zc0301_write_reg(cam, 0x0189, 0x06); + err += zc0301_write_reg(cam, 0x01AD, 0x00); + err += zc0301_write_reg(cam, 0x01C5, 0x03); + err += zc0301_write_reg(cam, 0x01CB, 0x13); + err += zc0301_write_reg(cam, 0x0250, 0x08); + err += zc0301_write_reg(cam, 0x0301, 0x08); + err += zc0301_write_reg(cam, 0x018D, 0x70); + err += zc0301_write_reg(cam, 0x0008, 0x03); + err += zc0301_write_reg(cam, 0x01C6, 0x04); + err += zc0301_write_reg(cam, 0x01CB, 0x07); + err += zc0301_write_reg(cam, 0x0120, 0x11); + err += zc0301_write_reg(cam, 0x0121, 0x37); + err += zc0301_write_reg(cam, 0x0122, 0x58); + err += zc0301_write_reg(cam, 0x0123, 0x79); + err += zc0301_write_reg(cam, 0x0124, 0x91); + err += zc0301_write_reg(cam, 0x0125, 0xA6); + err += zc0301_write_reg(cam, 0x0126, 0xB8); + err += zc0301_write_reg(cam, 0x0127, 0xC7); + err += zc0301_write_reg(cam, 0x0128, 0xD3); + err += zc0301_write_reg(cam, 0x0129, 0xDE); + err += zc0301_write_reg(cam, 0x012A, 0xE6); + err += zc0301_write_reg(cam, 0x012B, 0xED); + err += zc0301_write_reg(cam, 0x012C, 0xF3); + err += zc0301_write_reg(cam, 0x012D, 0xF8); + err += zc0301_write_reg(cam, 0x012E, 0xFB); + err += zc0301_write_reg(cam, 0x012F, 0xFF); + err += zc0301_write_reg(cam, 0x0130, 0x26); + err += zc0301_write_reg(cam, 0x0131, 0x23); + err += zc0301_write_reg(cam, 0x0132, 0x20); + err += zc0301_write_reg(cam, 0x0133, 0x1C); + err += zc0301_write_reg(cam, 0x0134, 0x16); + err += zc0301_write_reg(cam, 0x0135, 0x13); + err += zc0301_write_reg(cam, 0x0136, 0x10); + err += zc0301_write_reg(cam, 0x0137, 0x0D); + err += zc0301_write_reg(cam, 0x0138, 0x0B); + err += zc0301_write_reg(cam, 0x0139, 0x09); + err += zc0301_write_reg(cam, 0x013A, 0x07); + err += zc0301_write_reg(cam, 0x013B, 0x06); + err += zc0301_write_reg(cam, 0x013C, 0x05); + err += zc0301_write_reg(cam, 0x013D, 0x04); + err += zc0301_write_reg(cam, 0x013E, 0x03); + err += zc0301_write_reg(cam, 0x013F, 0x02); + err += zc0301_write_reg(cam, 0x010A, 0x4C); + err += zc0301_write_reg(cam, 0x010B, 0xF5); + err += zc0301_write_reg(cam, 0x010C, 0xFF); + err += zc0301_write_reg(cam, 0x010D, 0xF9); + err += zc0301_write_reg(cam, 0x010E, 0x51); + err += zc0301_write_reg(cam, 0x010F, 0xF5); + err += zc0301_write_reg(cam, 0x0110, 0xFB); + err += zc0301_write_reg(cam, 0x0111, 0xED); + err += zc0301_write_reg(cam, 0x0112, 0x5F); + err += zc0301_write_reg(cam, 0x0180, 0x00); + err += zc0301_write_reg(cam, 0x0019, 0x00); + err += zc0301_write_reg(cam, 0x0087, 0x20); + err += zc0301_write_reg(cam, 0x0088, 0x21); + + err += zc0301_i2c_write(cam, 0x20, 0x02); + err += zc0301_i2c_write(cam, 0x21, 0x1B); + err += zc0301_i2c_write(cam, 0x03, 0x44); + err += zc0301_i2c_write(cam, 0x0E, 0x01); + err += zc0301_i2c_write(cam, 0x0F, 0x00); + + err += zc0301_write_reg(cam, 0x01A9, 0x14); + err += zc0301_write_reg(cam, 0x01AA, 0x24); + err += zc0301_write_reg(cam, 0x0190, 0x00); + err += zc0301_write_reg(cam, 0x0191, 0x02); + err += zc0301_write_reg(cam, 0x0192, 0x1B); + err += zc0301_write_reg(cam, 0x0195, 0x00); + err += zc0301_write_reg(cam, 0x0196, 0x00); + err += zc0301_write_reg(cam, 0x0197, 0x4D); + err += zc0301_write_reg(cam, 0x018C, 0x10); + err += zc0301_write_reg(cam, 0x018F, 0x20); + err += zc0301_write_reg(cam, 0x001D, 0x44); + err += zc0301_write_reg(cam, 0x001E, 0x6F); + err += zc0301_write_reg(cam, 0x001F, 0xAD); + err += zc0301_write_reg(cam, 0x0020, 0xEB); + err += zc0301_write_reg(cam, 0x0087, 0x0F); + err += zc0301_write_reg(cam, 0x0088, 0x0E); + err += zc0301_write_reg(cam, 0x0180, 0x40); + err += zc0301_write_reg(cam, 0x0192, 0x1B); + err += zc0301_write_reg(cam, 0x0191, 0x02); + err += zc0301_write_reg(cam, 0x0190, 0x00); + err += zc0301_write_reg(cam, 0x0116, 0x1D); + err += zc0301_write_reg(cam, 0x0117, 0x40); + err += zc0301_write_reg(cam, 0x0118, 0x99); + err += zc0301_write_reg(cam, 0x0180, 0x42); + err += zc0301_write_reg(cam, 0x0116, 0x1D); + err += zc0301_write_reg(cam, 0x0117, 0x40); + err += zc0301_write_reg(cam, 0x0118, 0x99); + err += zc0301_write_reg(cam, 0x0007, 0x00); + + err += zc0301_i2c_write(cam, 0x11, 0x01); + + msleep(100); + + return err; +} + + +static int pas202bcb_get_ctrl(struct zc0301_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + { + int r1 = zc0301_i2c_read(cam, 0x04, 1), + r2 = zc0301_i2c_read(cam, 0x05, 1); + if (r1 < 0 || r2 < 0) + return -EIO; + ctrl->value = (r1 << 6) | (r2 & 0x3f); + } + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = zc0301_i2c_read(cam, 0x09, 1)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = zc0301_i2c_read(cam, 0x07, 1)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case V4L2_CID_GAIN: + if ((ctrl->value = zc0301_i2c_read(cam, 0x10, 1)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case ZC0301_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = zc0301_i2c_read(cam, 0x08, 1)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case ZC0301_V4L2_CID_DAC_MAGNITUDE: + if ((ctrl->value = zc0301_i2c_read(cam, 0x0c, 1)) < 0) + return -EIO; + return 0; + default: + return -EINVAL; + } +} + + +static int pas202bcb_set_ctrl(struct zc0301_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += zc0301_i2c_write(cam, 0x04, ctrl->value >> 6); + err += zc0301_i2c_write(cam, 0x05, ctrl->value & 0x3f); + break; + case V4L2_CID_RED_BALANCE: + err += zc0301_i2c_write(cam, 0x09, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += zc0301_i2c_write(cam, 0x07, ctrl->value); + break; + case V4L2_CID_GAIN: + err += zc0301_i2c_write(cam, 0x10, ctrl->value); + break; + case ZC0301_V4L2_CID_GREEN_BALANCE: + err += zc0301_i2c_write(cam, 0x08, ctrl->value); + break; + case ZC0301_V4L2_CID_DAC_MAGNITUDE: + err += zc0301_i2c_write(cam, 0x0c, ctrl->value); + break; + default: + return -EINVAL; + } + err += zc0301_i2c_write(cam, 0x11, 0x01); + + return err ? -EIO : 0; +} + + +static struct zc0301_sensor pas202bcb = { + .name = "PAS202BCB", + .init = &pas202bcb_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x01e5, + .maximum = 0x3fff, + .step = 0x0001, + .default_value = 0x01e5, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x0c, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = ZC0301_V4L2_CID_DAC_MAGNITUDE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "DAC magnitude", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x00, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x01, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x05, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = ZC0301_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x00, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + }, + .get_ctrl = &pas202bcb_get_ctrl, + .set_ctrl = &pas202bcb_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_JPEG, + .priv = 8, + }, +}; + + +int zc0301_probe_pas202bcb(struct zc0301_device* cam) +{ + int r0 = 0, r1 = 0, err = 0; + unsigned int pid = 0; + + err += zc0301_write_reg(cam, 0x0000, 0x01); + err += zc0301_write_reg(cam, 0x0010, 0x0e); + err += zc0301_write_reg(cam, 0x0001, 0x01); + err += zc0301_write_reg(cam, 0x0012, 0x03); + err += zc0301_write_reg(cam, 0x0012, 0x01); + err += zc0301_write_reg(cam, 0x008d, 0x08); + + msleep(10); + + r0 = zc0301_i2c_read(cam, 0x00, 1); + r1 = zc0301_i2c_read(cam, 0x01, 1); + + if (r0 < 0 || r1 < 0 || err) + return -EIO; + + pid = (r0 << 4) | ((r1 & 0xf0) >> 4); + if (pid != 0x017) + return -ENODEV; + + zc0301_attach_sensor(cam, &pas202bcb); + + return 0; +} diff --git a/linux/drivers/media/video/zc0301/zc0301_sensor.h b/linux/drivers/media/video/zc0301/zc0301_sensor.h new file mode 100644 index 000000000..7f11465ce --- /dev/null +++ b/linux/drivers/media/video/zc0301/zc0301_sensor.h @@ -0,0 +1,104 @@ +/*************************************************************************** + * API for image sensors connected to the ZC030! Image Processor and * + * Control Chip * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _ZC0301_SENSOR_H_ +#define _ZC0301_SENSOR_H_ + +#include +#include "compat.h" +#include +#include +#include +#include +#include + +struct zc0301_device; +struct zc0301_sensor; + +/*****************************************************************************/ + +extern int zc0301_probe_pas202bcb(struct zc0301_device* cam); + +#define ZC0301_SENSOR_TABLE \ +/* Weak detections must go at the end of the list */ \ +static int (*zc0301_sensor_table[])(struct zc0301_device*) = { \ + &zc0301_probe_pas202bcb, \ + NULL, \ +}; + +extern struct zc0301_device* +zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id); + +extern void +zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor); + +#define ZC0301_USB_DEVICE(vend, prod, intclass) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_CLASS, \ + .idVendor = (vend), \ + .idProduct = (prod), \ + .bInterfaceClass = (intclass) + +#define ZC0301_ID_TABLE \ +static const struct usb_device_id zc0301_id_table[] = { \ + { ZC0301_USB_DEVICE(0x041e, 0x4017, 0xff), }, \ + { ZC0301_USB_DEVICE(0x041e, 0x401c, 0xff), }, /* PAS106 */ \ + { ZC0301_USB_DEVICE(0x041e, 0x401e, 0xff), }, /* HV7131B */ \ + { ZC0301_USB_DEVICE(0x041e, 0x4034, 0xff), }, /* PAS106 */ \ + { ZC0301_USB_DEVICE(0x041e, 0x4035, 0xff), }, /* PAS106 */ \ + { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202BCB */ \ + { ZC0301_USB_DEVICE(0x0ac8, 0x0301, 0xff), }, \ + { ZC0301_USB_DEVICE(0x10fd, 0x8050, 0xff), }, /* TAS5130D */ \ + { } \ +}; + +/*****************************************************************************/ + +extern int zc0301_write_reg(struct zc0301_device*, u16 index, u16 value); +extern int zc0301_read_reg(struct zc0301_device*, u16 index); +extern int zc0301_i2c_write(struct zc0301_device*, u16 address, u16 value); +extern int zc0301_i2c_read(struct zc0301_device*, u16 address, u8 length); + +/*****************************************************************************/ + +#define ZC0301_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10 +#define ZC0301_V4L2_CID_DAC_MAGNITUDE V4L2_CID_PRIVATE_BASE +#define ZC0301_V4L2_CID_GREEN_BALANCE V4L2_CID_PRIVATE_BASE + 1 + +struct zc0301_sensor { + char name[32]; + + struct v4l2_queryctrl qctrl[ZC0301_MAX_CTRLS]; + struct v4l2_cropcap cropcap; + struct v4l2_pix_format pix_format; + + int (*init)(struct zc0301_device*); + int (*get_ctrl)(struct zc0301_device*, struct v4l2_control* ctrl); + int (*set_ctrl)(struct zc0301_device*, + const struct v4l2_control* ctrl); + int (*set_crop)(struct zc0301_device*, const struct v4l2_rect* rect); + + /* Private */ + struct v4l2_queryctrl _qctrl[ZC0301_MAX_CTRLS]; + struct v4l2_rect _rect; +}; + +#endif /* _ZC0301_SENSOR_H_ */ -- cgit v1.2.3 From 04f6a576c701b57b6e63d7d896d1910f2358ca9d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Apr 2006 18:48:04 -0300 Subject: Make new drivers compile with older kernels From: Trent Piepho Lots of drivers wouldn't compile with kernels < 2.6.16 because of the semaphore -> mutex rename. Also a small i2c bit for kernels < 2.6.13. Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/radio/miropcm20-rds-core.c | 7 +++++++ linux/drivers/media/radio/radio-aimslab.c | 4 ++++ linux/drivers/media/radio/radio-aztech.c | 4 ++++ linux/drivers/media/radio/radio-maestro.c | 8 +++++++- linux/drivers/media/radio/radio-maxiradio.c | 8 +++++++- linux/drivers/media/radio/radio-sf16fmi.c | 7 ++++++- linux/drivers/media/radio/radio-sf16fmr2.c | 4 ++++ linux/drivers/media/radio/radio-typhoon.c | 4 ++++ linux/drivers/media/radio/radio-zoltrix.c | 4 ++++ linux/drivers/media/video/bw-qcam.c | 2 ++ linux/drivers/media/video/bw-qcam.h | 4 ++++ linux/drivers/media/video/c-qcam.c | 6 ++++++ linux/drivers/media/video/cpia.c | 3 +++ linux/drivers/media/video/cpia.h | 12 +++++++++++- linux/drivers/media/video/dabusb.c | 3 +++ linux/drivers/media/video/dabusb.h | 4 ++++ linux/drivers/media/video/meye.h | 6 ++++++ linux/drivers/media/video/ov511.h | 5 +++++ linux/drivers/media/video/pms.c | 6 ++++++ linux/drivers/media/video/saa5246a.c | 9 +++++++++ linux/drivers/media/video/saa5249.c | 9 +++++++++ linux/drivers/media/video/se401.h | 6 ++++++ linux/drivers/media/video/stv680.c | 2 ++ linux/drivers/media/video/stv680.h | 4 ++++ linux/drivers/media/video/w9968cf.h | 5 +++++ linux/drivers/media/video/zoran.h | 4 ++++ linux/drivers/media/video/zoran_card.c | 2 ++ linux/drivers/media/video/zoran_driver.c | 3 +++ 28 files changed, 141 insertions(+), 4 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/radio/miropcm20-rds-core.c b/linux/drivers/media/radio/miropcm20-rds-core.c index dfb8252b2..3f5aaf5fa 100644 --- a/linux/drivers/media/radio/miropcm20-rds-core.c +++ b/linux/drivers/media/radio/miropcm20-rds-core.c @@ -18,7 +18,10 @@ #include #include #include +#include "compat.h" +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#endif #include #include "../../../sound/oss/aci.h" @@ -26,7 +29,11 @@ #define DEBUG 0 +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) static struct mutex aci_rds_mutex; +#else +static struct semaphore aci_rds_mutex; +#endif #define RDS_DATASHIFT 2 /* Bit 2 */ #define RDS_DATAMASK (1 << RDS_DATASHIFT) diff --git a/linux/drivers/media/radio/radio-aimslab.c b/linux/drivers/media/radio/radio-aimslab.c index 8e966733b..c167f19c0 100644 --- a/linux/drivers/media/radio/radio-aimslab.c +++ b/linux/drivers/media/radio/radio-aimslab.c @@ -44,7 +44,11 @@ static int io = CONFIG_RADIO_RTRACK_PORT; static int radio_nr = -1; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) static struct mutex lock; +#else +static struct semaphore lock; +#endif struct rt_device { diff --git a/linux/drivers/media/radio/radio-aztech.c b/linux/drivers/media/radio/radio-aztech.c index 3aa679a47..4a06891d8 100644 --- a/linux/drivers/media/radio/radio-aztech.c +++ b/linux/drivers/media/radio/radio-aztech.c @@ -43,7 +43,11 @@ static int io = CONFIG_RADIO_AZTECH_PORT; static int radio_nr = -1; static int radio_wait_time = 1000; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) static struct mutex lock; +#else +static struct semaphore lock; +#endif struct az_device { diff --git a/linux/drivers/media/radio/radio-maestro.c b/linux/drivers/media/radio/radio-maestro.c index d93357579..dfa1e6ded 100644 --- a/linux/drivers/media/radio/radio-maestro.c +++ b/linux/drivers/media/radio/radio-maestro.c @@ -23,10 +23,12 @@ #include #include #include -#include #include #include "compat.h" #include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) +#include +#endif #define DRIVER_VERSION "0.05" @@ -106,7 +108,11 @@ struct radio_device { muted, /* VIDEO_AUDIO_MUTE */ stereo, /* VIDEO_TUNER_STEREO_ON */ tuned; /* signal strength (0 or 0xffff) */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex lock; +#else + struct semaphore lock; +#endif }; static u32 radio_bits_get(struct radio_device *dev) diff --git a/linux/drivers/media/radio/radio-maxiradio.c b/linux/drivers/media/radio/radio-maxiradio.c index 2b83fa519..d9bfe3f32 100644 --- a/linux/drivers/media/radio/radio-maxiradio.c +++ b/linux/drivers/media/radio/radio-maxiradio.c @@ -37,10 +37,12 @@ #include #include #include +#include "compat.h" +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#endif #include -#include "compat.h" #include /* version 0.75 Sun Feb 4 22:51:27 EET 2001 */ @@ -103,7 +105,11 @@ static struct radio_device unsigned long freq; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex lock; +#else + struct semaphore lock; +#endif } radio_unit = {0, 0, 0, 0, }; diff --git a/linux/drivers/media/radio/radio-sf16fmi.c b/linux/drivers/media/radio/radio-sf16fmi.c index 90ecea017..62bb19d2c 100644 --- a/linux/drivers/media/radio/radio-sf16fmi.c +++ b/linux/drivers/media/radio/radio-sf16fmi.c @@ -25,8 +25,14 @@ #include #include /* outb, outb_p */ #include /* copy to/from user */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +static struct mutex lock; +#else +static struct semaphore lock; +#endif + struct fmi_device { int port; @@ -38,7 +44,6 @@ struct fmi_device static int io = -1; static int radio_nr = -1; static struct pnp_dev *dev = NULL; -static struct mutex lock; /* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */ /* It is only useful to give freq in intervall of 800 (=0.05Mhz), diff --git a/linux/drivers/media/radio/radio-sf16fmr2.c b/linux/drivers/media/radio/radio-sf16fmr2.c index f4d80c0e4..0bafd3267 100644 --- a/linux/drivers/media/radio/radio-sf16fmr2.c +++ b/linux/drivers/media/radio/radio-sf16fmr2.c @@ -20,9 +20,13 @@ #include /* copy to/from user */ #include "compat.h" #include /* kernel radio structs */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include static struct mutex lock; +#else +static struct semaphore lock; +#endif #undef DEBUG //#define DEBUG 1 diff --git a/linux/drivers/media/radio/radio-typhoon.c b/linux/drivers/media/radio/radio-typhoon.c index 541072e7c..02cc2c093 100644 --- a/linux/drivers/media/radio/radio-typhoon.c +++ b/linux/drivers/media/radio/radio-typhoon.c @@ -60,7 +60,11 @@ struct typhoon_device { int muted; unsigned long curfreq; unsigned long mutefreq; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex lock; +#else + struct semaphore lock; +#endif }; static void typhoon_setvol_generic(struct typhoon_device *dev, int vol); diff --git a/linux/drivers/media/radio/radio-zoltrix.c b/linux/drivers/media/radio/radio-zoltrix.c index d428514c7..e8363cf6b 100644 --- a/linux/drivers/media/radio/radio-zoltrix.c +++ b/linux/drivers/media/radio/radio-zoltrix.c @@ -49,7 +49,11 @@ struct zol_device { unsigned long curfreq; int muted; unsigned int stereo; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex lock; +#else + struct semaphore lock; +#endif }; static int zol_setvol(struct zol_device *dev, int vol) diff --git a/linux/drivers/media/video/bw-qcam.c b/linux/drivers/media/video/bw-qcam.c index 0a276d967..25ce06421 100644 --- a/linux/drivers/media/video/bw-qcam.c +++ b/linux/drivers/media/video/bw-qcam.c @@ -74,7 +74,9 @@ OTHER DEALINGS IN THE SOFTWARE. #include #include "compat.h" #include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#endif #include #include "bw-qcam.h" diff --git a/linux/drivers/media/video/bw-qcam.h b/linux/drivers/media/video/bw-qcam.h index 6701dafbc..f023c465f 100644 --- a/linux/drivers/media/video/bw-qcam.h +++ b/linux/drivers/media/video/bw-qcam.h @@ -55,7 +55,11 @@ struct qcam_device { struct video_device vdev; struct pardevice *pdev; struct parport *pport; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex lock; +#else + struct semaphore lock; +#endif int width, height; int bpp; int mode; diff --git a/linux/drivers/media/video/c-qcam.c b/linux/drivers/media/video/c-qcam.c index c1957a143..5cfbb30e5 100644 --- a/linux/drivers/media/video/c-qcam.c +++ b/linux/drivers/media/video/c-qcam.c @@ -35,7 +35,9 @@ #include #include "compat.h" #include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#endif #include @@ -49,7 +51,11 @@ struct qcam_device { int contrast, brightness, whitebal; int top, left; unsigned int bidirectional; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex lock; +#else + struct semaphore lock; +#endif }; /* cameras maximum */ diff --git a/linux/drivers/media/video/cpia.c b/linux/drivers/media/video/cpia.c index b8d491311..d955004b7 100644 --- a/linux/drivers/media/video/cpia.c +++ b/linux/drivers/media/video/cpia.c @@ -39,7 +39,10 @@ #include #include #include +#include "compat.h" +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#endif #ifdef CONFIG_KMOD #include diff --git a/linux/drivers/media/video/cpia.h b/linux/drivers/media/video/cpia.h index 120b66388..b8539f244 100644 --- a/linux/drivers/media/video/cpia.h +++ b/linux/drivers/media/video/cpia.h @@ -48,7 +48,9 @@ #include #include #include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#endif struct cpia_camera_ops { @@ -248,7 +250,11 @@ enum v4l_camstates { struct cam_data { struct list_head cam_data_list; - struct mutex busy_lock; /* guard against SMP multithreading */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) + struct mutex busy_lock; /* guard against SMP multithreading */ +#else + struct semaphore busy_lock; /* guard against SMP multithreading */ +#endif struct cpia_camera_ops *ops; /* lowlevel driver operations */ void *lowlevel_data; /* private data for lowlevel driver */ u8 *raw_image; /* buffer for raw image data */ @@ -263,7 +269,11 @@ struct cam_data { u8 mainsFreq; /* for flicker control */ /* proc interface */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex param_lock; /* params lock for this camera */ +#else + struct semaphore param_lock; /* params lock for this camera */ +#endif struct cam_params params; /* camera settings */ struct proc_dir_entry *proc_entry; /* /proc/cpia/videoX */ diff --git a/linux/drivers/media/video/dabusb.c b/linux/drivers/media/video/dabusb.c index b9ba95f5e..5896b6d4b 100644 --- a/linux/drivers/media/video/dabusb.c +++ b/linux/drivers/media/video/dabusb.c @@ -38,7 +38,10 @@ #include #include #include +#include "compat.h" +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#endif #include "dabusb.h" #include "dabfirmware.h" diff --git a/linux/drivers/media/video/dabusb.h b/linux/drivers/media/video/dabusb.h index 00eb34c86..761461910 100644 --- a/linux/drivers/media/video/dabusb.h +++ b/linux/drivers/media/video/dabusb.h @@ -18,7 +18,11 @@ typedef enum { _stopped=0, _started } driver_state_t; typedef struct { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex mutex; +#else + struct semaphore mutex; +#endif struct usb_device *usbdev; wait_queue_head_t wait; wait_queue_head_t remove_ok; diff --git a/linux/drivers/media/video/meye.h b/linux/drivers/media/video/meye.h index 0d09a0e38..afd1aace1 100644 --- a/linux/drivers/media/video/meye.h +++ b/linux/drivers/media/video/meye.h @@ -260,7 +260,9 @@ /* private API definitions */ #include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#endif /* Enable jpg software correction */ @@ -303,7 +305,11 @@ struct meye { /* list of buffers */ struct meye_grab_buffer grab_buffer[MEYE_MAX_BUFNBRS]; int vma_use_count[MEYE_MAX_BUFNBRS]; /* mmap count */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex lock; /* mutex for open/mmap... */ +#else + struct semaphore lock; /* mutex for open/mmap... */ +#endif struct kfifo *grabq; /* queue for buffers to be grabbed */ spinlock_t grabq_lock; /* lock protecting the queue */ struct kfifo *doneq; /* queue for grabbed buffers */ diff --git a/linux/drivers/media/video/ov511.h b/linux/drivers/media/video/ov511.h index 63da4e8d8..3f5078a85 100644 --- a/linux/drivers/media/video/ov511.h +++ b/linux/drivers/media/video/ov511.h @@ -6,7 +6,12 @@ #include #include #include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#else +/* Having a version check for each mutex defined is annoying */ +#define mutex semaphore +#endif #define OV511_DEBUG /* Turn on debug messages */ diff --git a/linux/drivers/media/video/pms.c b/linux/drivers/media/video/pms.c index 7df71e084..e5d16a5d0 100644 --- a/linux/drivers/media/video/pms.c +++ b/linux/drivers/media/video/pms.c @@ -31,7 +31,9 @@ #include #include "compat.h" #include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#endif #include @@ -47,7 +49,11 @@ struct pms_device struct video_picture picture; int height; int width; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex lock; +#else + struct semaphore lock; +#endif }; struct i2c_info diff --git a/linux/drivers/media/video/saa5246a.c b/linux/drivers/media/video/saa5246a.c index bfc970087..7aa9134a8 100644 --- a/linux/drivers/media/video/saa5246a.c +++ b/linux/drivers/media/video/saa5246a.c @@ -47,7 +47,9 @@ #include #include "compat.h" #include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#endif #include "saa5246a.h" @@ -60,13 +62,20 @@ struct saa5246a_device u8 pgbuf[NUM_DAUS][VTX_VIRTUALSIZE]; int is_searching[NUM_DAUS]; struct i2c_client *client; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex lock; +#else + struct semaphore lock; +#endif }; static struct video_device saa_template; /* Declared near bottom */ /* Addresses to scan */ static unsigned short normal_i2c[] = { I2C_ADDRESS, I2C_CLIENT_END }; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) +static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; +#endif I2C_CLIENT_INSMOD; static struct i2c_client client_template; diff --git a/linux/drivers/media/video/saa5249.c b/linux/drivers/media/video/saa5249.c index 42c2052e4..6fd6e1626 100644 --- a/linux/drivers/media/video/saa5249.c +++ b/linux/drivers/media/video/saa5249.c @@ -57,7 +57,9 @@ #include #include "compat.h" #include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#endif #include @@ -108,7 +110,11 @@ struct saa5249_device int disp_mode; int virtual_mode; struct i2c_client *client; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex lock; +#else + struct semaphore lock; +#endif }; @@ -135,6 +141,9 @@ static struct video_device saa_template; /* Declared near bottom */ /* Addresses to scan */ static unsigned short normal_i2c[] = {34>>1,I2C_CLIENT_END}; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) +static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; +#endif I2C_CLIENT_INSMOD; static struct i2c_client client_template; diff --git a/linux/drivers/media/video/se401.h b/linux/drivers/media/video/se401.h index a78aa0720..bf14af36d 100644 --- a/linux/drivers/media/video/se401.h +++ b/linux/drivers/media/video/se401.h @@ -6,7 +6,9 @@ #include "compat.h" #include #include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#endif #define se401_DEBUG /* Turn on debug messages */ @@ -191,7 +193,11 @@ struct usb_se401 { int maxframesize; int cframesize; /* current framesize */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex lock; +#else + struct semaphore lock; +#endif int user; /* user count for exclusive use */ int removed; /* device disconnected */ diff --git a/linux/drivers/media/video/stv680.c b/linux/drivers/media/video/stv680.c index b33ed6f73..f0d5bd79b 100644 --- a/linux/drivers/media/video/stv680.c +++ b/linux/drivers/media/video/stv680.c @@ -68,7 +68,9 @@ #include "compat.h" #include #include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#endif #include "stv680.h" diff --git a/linux/drivers/media/video/stv680.h b/linux/drivers/media/video/stv680.h index a08f1b08a..cbbcd4f25 100644 --- a/linux/drivers/media/video/stv680.h +++ b/linux/drivers/media/video/stv680.h @@ -118,7 +118,11 @@ struct usb_stv { int origGain; int origMode; /* original camera mode */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex lock; /* to lock the structure */ +#else + struct semaphore lock; /* to lock the structure */ +#endif int user; /* user count for exclusive use */ int removed; /* device disconnected */ int streaming; /* Are we streaming video? */ diff --git a/linux/drivers/media/video/w9968cf.h b/linux/drivers/media/video/w9968cf.h index a89013fa3..f32a2d266 100644 --- a/linux/drivers/media/video/w9968cf.h +++ b/linux/drivers/media/video/w9968cf.h @@ -33,7 +33,12 @@ #include #include #include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#else +/* Having a version check for each mutex defined is annoying */ +#define mutex semaphore +#endif #include diff --git a/linux/drivers/media/video/zoran.h b/linux/drivers/media/video/zoran.h index 0166f555a..0dc51a46c 100644 --- a/linux/drivers/media/video/zoran.h +++ b/linux/drivers/media/video/zoran.h @@ -395,7 +395,11 @@ struct zoran { struct videocodec *codec; /* video codec */ struct videocodec *vfe; /* video front end */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex resource_lock; /* prevent evil stuff */ +#else + struct semaphore resource_lock; /* prevent evil stuff */ +#endif u8 initialized; /* flag if zoran has been correctly initalized */ int user; /* number of current users */ diff --git a/linux/drivers/media/video/zoran_card.c b/linux/drivers/media/video/zoran_card.c index 54c8b2a2c..a55bd31ea 100644 --- a/linux/drivers/media/video/zoran_card.c +++ b/linux/drivers/media/video/zoran_card.c @@ -48,7 +48,9 @@ #include #include #include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#endif #include diff --git a/linux/drivers/media/video/zoran_driver.c b/linux/drivers/media/video/zoran_driver.c index b5a576a37..5ced8b516 100644 --- a/linux/drivers/media/video/zoran_driver.c +++ b/linux/drivers/media/video/zoran_driver.c @@ -81,7 +81,10 @@ #include #include +#include "compat.h" +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#endif #include "zoran.h" #include "zoran_device.h" #include "zoran_card.h" -- cgit v1.2.3 From e96f69c1735c817cc19e220cb84e0f339190cf1b Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Mon, 10 Apr 2006 09:27:08 -0300 Subject: Silence some dprintk's in cx88-mpeg From: Trent Piepho Changeset 395c7c4d8f0c changed several dprintks from level 1 to level 0 (always on). They generate four lines every time DMA starts, which don't containing any information with a use outside of debugging. This turns them back to level 1. Signed-off-by: Trent Piepho --- linux/drivers/media/video/cx88/cx88-mpeg.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-mpeg.c b/linux/drivers/media/video/cx88/cx88-mpeg.c index 487c6d16c..f24700f40 100644 --- a/linux/drivers/media/video/cx88/cx88-mpeg.c +++ b/linux/drivers/media/video/cx88/cx88-mpeg.c @@ -62,7 +62,7 @@ static int cx8802_start_dma(struct cx8802_dev *dev, { struct cx88_core *core = dev->core; - dprintk(0, "cx8802_start_dma w: %d, h: %d, f: %d\n", dev->width, dev->height, buf->vb.field); + dprintk(1, "cx8802_start_dma w: %d, h: %d, f: %d\n", dev->width, dev->height, buf->vb.field); /* setup fifo + format */ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], @@ -127,7 +127,7 @@ static int cx8802_start_dma(struct cx8802_dev *dev, q->count = 1; /* enable irqs */ - dprintk( 0, "setting the interrupt mask\n" ); + dprintk( 1, "setting the interrupt mask\n" ); cx_set(MO_PCI_INTMSK, core->pci_irqmask | 0x04); cx_set(MO_TS_INTMSK, 0x1f0011); #if 0 @@ -143,7 +143,7 @@ static int cx8802_start_dma(struct cx8802_dev *dev, static int cx8802_stop_dma(struct cx8802_dev *dev) { struct cx88_core *core = dev->core; - dprintk( 0, "cx8802_stop_dma\n" ); + dprintk( 1, "cx8802_stop_dma\n" ); /* stop dma */ cx_clear(MO_TS_DMACNTRL, 0x11); @@ -258,13 +258,13 @@ void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf) buf->risc.jmp[1] = cpu_to_le32(cx88q->stopper.dma); if (list_empty(&cx88q->active)) { - dprintk( 0, "queue is empty - first active\n" ); + dprintk( 1, "queue is empty - first active\n" ); list_add_tail(&buf->vb.queue,&cx88q->active); cx8802_start_dma(dev, cx88q, buf); buf->vb.state = STATE_ACTIVE; buf->count = cx88q->count++; mod_timer(&cx88q->timeout, jiffies+BUFFER_TIMEOUT); - dprintk(0,"[%p/%d] %s - first active\n", + dprintk(1,"[%p/%d] %s - first active\n", buf, buf->vb.i, __FUNCTION__); #if 0 udelay(100); @@ -304,7 +304,7 @@ static void do_cancel_buffers(struct cx8802_dev *dev, char *reason, int restart) } if (restart) { - dprintk(0, "restarting queue\n" ); + dprintk(1, "restarting queue\n" ); cx8802_restart_queue(dev,q); } spin_unlock_irqrestore(&dev->slock,flags); -- cgit v1.2.3 From c3bf1b32148a59bc0a8e58c126d642236c7febc1 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Mon, 10 Apr 2006 09:27:37 -0300 Subject: Add sysfs device links to dvb devices From: Andrew de Quincey Currently in /sys/class/dvb/dvbX.demuxY/ we have: dev uevent With the patch, we have (for a PCI DVB device): dev device -> ../../../devices/pci0000:00/0000:00:1e.0/0000:03:0d.0 uevent So userspace tools can (finally) work out which physical device a DVB adapter refers to. Previously you had to kinda look through dmesg and hope that it hadn't been dumped out of the buffer. This makes debugging a lot easier if the system has been up for a long time! This is done by adding an extra 'struct device *' parameter to dvb_register_adapter(). It will work with any kind of standard linux 'device'. Additionally, if someone has an embedded system which does things differently, they can simply supply 'NULL' and the behaviour will be as before - the link will simply not appear. Signed-off-by: Andrew de Quincey Ack'd-by: Manu Abraham Acked-by: Michael Krufky --- linux/drivers/media/dvb/b2c2/flexcop.c | 2 +- linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c | 2 +- linux/drivers/media/dvb/cinergyT2/cinergyT2.c | 2 +- linux/drivers/media/dvb/dvb-core/dvbdev.c | 5 +++-- linux/drivers/media/dvb/dvb-core/dvbdev.h | 4 +++- linux/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c | 2 +- linux/drivers/media/dvb/pluto2/pluto2.c | 2 +- linux/drivers/media/dvb/ttpci/av7110.c | 2 +- linux/drivers/media/dvb/ttpci/budget-core.c | 2 +- linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c | 2 +- linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c | 2 +- linux/drivers/media/video/cx88/cx88-dvb.c | 2 +- linux/drivers/media/video/saa7134/saa7134-dvb.c | 2 +- linux/drivers/media/video/video-buf-dvb.c | 5 +++-- 14 files changed, 20 insertions(+), 16 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/b2c2/flexcop.c b/linux/drivers/media/dvb/b2c2/flexcop.c index 56ba52470..5c276b379 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop.c +++ b/linux/drivers/media/dvb/b2c2/flexcop.c @@ -67,7 +67,7 @@ static int flexcop_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) static int flexcop_dvb_init(struct flexcop_device *fc) { int ret; - if ((ret = dvb_register_adapter(&fc->dvb_adapter,"FlexCop Digital TV device",fc->owner)) < 0) { + if ((ret = dvb_register_adapter(&fc->dvb_adapter,"FlexCop Digital TV device",fc->owner,fc->dev)) < 0) { err("error registering DVB adapter"); return ret; } diff --git a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c index 033d9d8fc..cd6e41a49 100644 --- a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -714,7 +714,7 @@ static int __init dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type) { int result; - if ((result = dvb_register_adapter(&card->dvb_adapter, card->card_name, THIS_MODULE)) < 0) { + if ((result = dvb_register_adapter(&card->dvb_adapter, card->card_name, THIS_MODULE, &card->bt->dev->dev)) < 0) { printk("dvb_bt8xx: dvb_register_adapter failed (errno = %d)\n", result); return result; } diff --git a/linux/drivers/media/dvb/cinergyT2/cinergyT2.c b/linux/drivers/media/dvb/cinergyT2/cinergyT2.c index 994d12194..8dbba1ca7 100644 --- a/linux/drivers/media/dvb/cinergyT2/cinergyT2.c +++ b/linux/drivers/media/dvb/cinergyT2/cinergyT2.c @@ -919,7 +919,7 @@ static int cinergyt2_probe (struct usb_interface *intf, return -ENOMEM; } - if ((err = dvb_register_adapter(&cinergyt2->adapter, DRIVER_NAME, THIS_MODULE)) < 0) { + if ((err = dvb_register_adapter(&cinergyt2->adapter, DRIVER_NAME, THIS_MODULE, &cinergyt2->udev->dev)) < 0) { kfree(cinergyt2); return err; } diff --git a/linux/drivers/media/dvb/dvb-core/dvbdev.c b/linux/drivers/media/dvb/dvb-core/dvbdev.c index 041ed7fd1..66d9ffdb4 100644 --- a/linux/drivers/media/dvb/dvb-core/dvbdev.c +++ b/linux/drivers/media/dvb/dvb-core/dvbdev.c @@ -247,7 +247,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, "dvb/adapter%d/%s%d", adap->num, dnames[type], id); class_device_create(dvb_class, NULL, MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)), - NULL, "dvb%d.%s%d", adap->num, dnames[type], id); + adap->device, "dvb%d.%s%d", adap->num, dnames[type], id); dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n", adap->num, dnames[type], id, nums2minor(adap->num, type, id), @@ -296,7 +296,7 @@ skip: } -int dvb_register_adapter(struct dvb_adapter *adap, const char *name, struct module *module) +int dvb_register_adapter(struct dvb_adapter *adap, const char *name, struct module *module, struct device *device) { int num; @@ -317,6 +317,7 @@ int dvb_register_adapter(struct dvb_adapter *adap, const char *name, struct modu adap->num = num; adap->name = name; adap->module = module; + adap->device = device; list_add_tail (&adap->list_head, &dvb_adapter_list); diff --git a/linux/drivers/media/dvb/dvb-core/dvbdev.h b/linux/drivers/media/dvb/dvb-core/dvbdev.h index 9f5f984cc..9ec9cecf5 100644 --- a/linux/drivers/media/dvb/dvb-core/dvbdev.h +++ b/linux/drivers/media/dvb/dvb-core/dvbdev.h @@ -52,6 +52,8 @@ struct dvb_adapter { u8 proposed_mac [6]; void* priv; + struct device *device; + struct module *module; }; @@ -77,7 +79,7 @@ struct dvb_device { }; -extern int dvb_register_adapter (struct dvb_adapter *adap, const char *name, struct module *module); +extern int dvb_register_adapter (struct dvb_adapter *adap, const char *name, struct module *module, struct device *device); extern int dvb_unregister_adapter (struct dvb_adapter *adap); extern int dvb_register_device (struct dvb_adapter *adap, diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c b/linux/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c index 6fa921002..500896d02 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c @@ -82,7 +82,7 @@ int dvb_usb_dvb_init(struct dvb_usb_device *d) int ret; if ((ret = dvb_register_adapter(&d->dvb_adap, d->desc->name, - d->owner)) < 0) { + d->owner, &d->udev->dev)) < 0) { deb_info("dvb_register_adapter failed: error %d", ret); goto err; } diff --git a/linux/drivers/media/dvb/pluto2/pluto2.c b/linux/drivers/media/dvb/pluto2/pluto2.c index 1c5316e20..c9afad038 100644 --- a/linux/drivers/media/dvb/pluto2/pluto2.c +++ b/linux/drivers/media/dvb/pluto2/pluto2.c @@ -647,7 +647,7 @@ static int __devinit pluto2_probe(struct pci_dev *pdev, goto err_pluto_hw_exit; /* dvb */ - ret = dvb_register_adapter(&pluto->dvb_adapter, DRIVER_NAME, THIS_MODULE); + ret = dvb_register_adapter(&pluto->dvb_adapter, DRIVER_NAME, THIS_MODULE, &pdev->dev); if (ret < 0) goto err_i2c_bit_del_bus; diff --git a/linux/drivers/media/dvb/ttpci/av7110.c b/linux/drivers/media/dvb/ttpci/av7110.c index 940b05439..6dedb175a 100644 --- a/linux/drivers/media/dvb/ttpci/av7110.c +++ b/linux/drivers/media/dvb/ttpci/av7110.c @@ -2413,7 +2413,7 @@ static int __devinit av7110_attach(struct saa7146_dev* dev, goto err_kfree_0; ret = dvb_register_adapter(&av7110->dvb_adapter, av7110->card_name, - THIS_MODULE); + THIS_MODULE, &dev->pci->dev); if (ret < 0) goto err_put_firmware_1; diff --git a/linux/drivers/media/dvb/ttpci/budget-core.c b/linux/drivers/media/dvb/ttpci/budget-core.c index d8af690fa..e4cf7775e 100644 --- a/linux/drivers/media/dvb/ttpci/budget-core.c +++ b/linux/drivers/media/dvb/ttpci/budget-core.c @@ -400,7 +400,7 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, budget->dev->name, budget->buffer_width, budget->buffer_height); printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size); - if ((ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name, owner)) < 0) { + if ((ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name, owner, &budget->dev->pci->dev)) < 0) { return ret; } diff --git a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index 9484f26f7..e9347c42d 100644 --- a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -1515,7 +1515,7 @@ static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *i mutex_unlock(&ttusb->semi2c); - if ((result = dvb_register_adapter(&ttusb->adapter, "Technotrend/Hauppauge Nova-USB", THIS_MODULE)) < 0) { + if ((result = dvb_register_adapter(&ttusb->adapter, "Technotrend/Hauppauge Nova-USB", THIS_MODULE, &udev->dev)) < 0) { ttusb_free_iso_urbs(ttusb); kfree(ttusb); return result; diff --git a/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c index 50741a8df..4aacf83c5 100644 --- a/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c +++ b/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c @@ -1443,7 +1443,7 @@ static int ttusb_dec_init_dvb(struct ttusb_dec *dec) dprintk("%s\n", __FUNCTION__); if ((result = dvb_register_adapter(&dec->adapter, - dec->model_name, THIS_MODULE)) < 0) { + dec->model_name, THIS_MODULE, &dec->udev->dev)) < 0) { printk("%s: dvb_register_adapter failed: error %d\n", __FUNCTION__, result); diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index 38564eaca..e1bdd9a61 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -724,7 +724,7 @@ static int dvb_register(struct cx8802_dev *dev) cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); /* register everything */ - return videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev); + return videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev, &dev->pci->dev); } /* ----------------------------------------------------------- */ diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index 9cbc8343d..fe7ca962a 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -1090,7 +1090,7 @@ static int dvb_init(struct saa7134_dev *dev) } /* register everything else */ - return videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev); + return videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev, &dev->pci->dev); } static int dvb_fini(struct saa7134_dev *dev) diff --git a/linux/drivers/media/video/video-buf-dvb.c b/linux/drivers/media/video/video-buf-dvb.c index 02e5ffaf3..1b9968a19 100644 --- a/linux/drivers/media/video/video-buf-dvb.c +++ b/linux/drivers/media/video/video-buf-dvb.c @@ -144,14 +144,15 @@ static int videobuf_dvb_stop_feed(struct dvb_demux_feed *feed) int videobuf_dvb_register(struct videobuf_dvb *dvb, struct module *module, - void *adapter_priv) + void *adapter_priv, + struct device *device) { int result; mutex_init(&dvb->lock); /* register adapter */ - result = dvb_register_adapter(&dvb->adapter, dvb->name, module); + result = dvb_register_adapter(&dvb->adapter, dvb->name, module, device); if (result < 0) { printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n", dvb->name, result); -- cgit v1.2.3 From cd337acd390bb0ac18af8d020561a28fd37c427f Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 10 Apr 2006 09:40:37 -0300 Subject: bug fix: Wrong tuner was used pcHDTV HD-3000 card From: Trent Piepho It looks like the HD3000 was prototyped with the 7610 tuner when the driver was developed, but the cards appear to have always shipped with the 7612 tuner and the driver was never adjusted for it. The definition needs to be corrected. - The HD-3000 was prototyped with a Thomson DTT7610, but production versions used a DTT7612 tuner. - This patch changes both dvb-pll settings and V4L tuner type. Signed-off-by: Trent Piepho Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/cx88-cards.c | 2 +- linux/drivers/media/video/cx88/cx88-dvb.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index 9d9c58028..c1bea5a20 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -566,7 +566,7 @@ struct cx88_board cx88_boards[] = { }, [CX88_BOARD_PCHDTV_HD3000] = { .name = "pcHDTV HD3000 HDTV", - .tuner_type = TUNER_THOMSON_DTT7610, + .tuner_type = TUNER_THOMSON_DTT761X, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index e1bdd9a61..6b194c632 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -374,7 +374,7 @@ static int or51132_set_ts_param(struct dvb_frontend* fe, static struct or51132_config pchdtv_hd3000 = { .demod_address = 0x15, .pll_address = 0x61, - .pll_desc = &dvb_pll_thomson_dtt7610, + .pll_desc = &dvb_pll_thomson_dtt761x, .set_ts_params = or51132_set_ts_param, }; #endif -- cgit v1.2.3 From cfeb6bbf927c9a3403a7ba956ef11743eddf60de Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 10 Apr 2006 09:40:40 -0300 Subject: lgdt330x: update supported cards list in comments From: Michael Krufky - pcHDTV HD5500 ATSC/QAM uses LG DT3303 Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/frontends/lgdt330x.c | 1 + 1 file changed, 1 insertion(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/lgdt330x.c b/linux/drivers/media/dvb/frontends/lgdt330x.c index ed2acee48..25ea3a838 100644 --- a/linux/drivers/media/dvb/frontends/lgdt330x.c +++ b/linux/drivers/media/dvb/frontends/lgdt330x.c @@ -29,6 +29,7 @@ * DViCO FusionHDTV 5 Lite * DViCO FusionHDTV 5 USB Gold * Air2PC/AirStar 2 ATSC 3rd generation (HD5000) + * pcHDTV HD5500 * * TODO: * signal strength always returns 0. -- cgit v1.2.3 From 5afa1d2f3acfdd9039865f615405fd9e621c8deb Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 11 Apr 2006 00:46:46 -0400 Subject: dvb-bt8xx: add support for DViCO FusionHDTV DVB-T Lite 2nd revision From: Michael Krufky This patch adds support for the new revision of the DViCO FusionHDTV DVB-T Lite, based on the zl10353 demod instead of mt352. Both mt352 and zl10353 revisions of this card have the same PCI subsystem ID. Signed-off-by: Michael Krufky Acked-by: Chris Pascoe Ack'd-by: Manu Abraham --- linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c | 10 ++++++++++ linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h | 1 + 2 files changed, 11 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c index cd6e41a49..0e01863ab 100644 --- a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -185,6 +185,11 @@ static struct mt352_config thomson_dtt7579_config = { .pll_set = thomson_dtt7579_pll_set, }; +static struct zl10353_config thomson_dtt7579_zl10353_config = { + .demod_address = 0x0f, + .pll_set = thomson_dtt7579_pll_set, +}; + static int cx24108_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { u32 freq = params->frequency; @@ -618,6 +623,11 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) switch(type) { case BTTV_BOARD_DVICO_DVBT_LITE: card->fe = mt352_attach(&thomson_dtt7579_config, card->i2c_adapter); + + if (card->fe == NULL) + card->fe = zl10353_attach(&thomson_dtt7579_zl10353_config, + card->i2c_adapter); + if (card->fe != NULL) { card->fe->ops->info.frequency_min = 174000000; card->fe->ops->info.frequency_max = 862000000; diff --git a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h index 9992d1071..626718774 100644 --- a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h +++ b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h @@ -40,6 +40,7 @@ #include "cx24110.h" #include "or51211.h" #include "lgdt330x.h" +#include "zl10353.h" struct dvb_bt8xx_card { #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) -- cgit v1.2.3 From e9ca7fc16788e8d4cb52c4ef94ba17caca9f1d35 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Apr 2006 10:18:57 -0300 Subject: Correct buffer size calculations in cx88-core.c From: Duncan Sands The computation in cx88_risc_buffer suffers from the mistake: a non-zero padding value can cause more page borders to be crossed, leading to big buffer over-runs. This patch changes the additive constant from 3 + 4 to 4 It also changees the constant in cx88_risc_databuffer from 3 + 4 to 2, because 2 dwords are the correct vaule. Signed-off-by: Duncan Sands Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/cx88/cx88-core.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-core.c b/linux/drivers/media/video/cx88/cx88-core.c index 9636dedcf..9587434ca 100644 --- a/linux/drivers/media/video/cx88/cx88-core.c +++ b/linux/drivers/media/video/cx88/cx88-core.c @@ -163,9 +163,11 @@ int cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, fields++; /* estimate risc mem: worst case is one write per page border + - one write per scan line + syncs + jump (all 2 dwords) */ - instructions = (bpl * lines * fields) / PAGE_SIZE + lines * fields; - instructions += 3 + 4; + one write per scan line + syncs + jump (all 2 dwords). Padding + can cause next bpl to start close to a page border. First DMA + region may be smaller than PAGE_SIZE */ + instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines); + instructions += 2; if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0) return rc; @@ -193,9 +195,11 @@ int cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc, int rc; /* estimate risc mem: worst case is one write per page border + - one write per scan line + syncs + jump (all 2 dwords) */ - instructions = (bpl * lines) / PAGE_SIZE + lines; - instructions += 3 + 4; + one write per scan line + syncs + jump (all 2 dwords). Here + there is no padding and no sync. First DMA region may be smaller + than PAGE_SIZE */ + instructions = 1 + (bpl * lines) / PAGE_SIZE + lines; + instructions += 1; if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0) return rc; -- cgit v1.2.3 From baab13454ff9e6843328d4b834c88940c5a37cf0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Apr 2006 10:19:25 -0300 Subject: Pvr350 tv out (saa7127) From: Jose Alberto Reguero Witout this patch tv out don't work properly with my pvr350 card. Thanks. Signed-off-by: Jose Alberto Reguero Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/saa7127.c | 1 + 1 file changed, 1 insertion(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/saa7127.c b/linux/drivers/media/video/saa7127.c index 379de09ab..70170d487 100644 --- a/linux/drivers/media/video/saa7127.c +++ b/linux/drivers/media/video/saa7127.c @@ -154,6 +154,7 @@ struct i2c_reg_value { static const struct i2c_reg_value saa7129_init_config_extra[] = { { SAA7127_REG_OUTPUT_PORT_CONTROL, 0x38 }, { SAA7127_REG_VTRIG, 0xfa }, + { 0, 0 } }; static const struct i2c_reg_value saa7127_init_config_common[] = { -- cgit v1.2.3 From 8f7cd43433d286300ba9384277102b80e6a70aab Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Apr 2006 10:20:34 -0300 Subject: Fix a tab error in cx14110.c, dprintk From: Uwe Bugla Signed-off-by: Uwe Bugla Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/frontends/cx24110.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/cx24110.c b/linux/drivers/media/dvb/frontends/cx24110.c index f3edf8b51..a8d0edd02 100644 --- a/linux/drivers/media/dvb/frontends/cx24110.c +++ b/linux/drivers/media/dvb/frontends/cx24110.c @@ -250,7 +250,7 @@ static int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate) static const u32 bands[]={5000000UL,15000000UL,90999000UL/2}; int i; -dprintk("cx24110 debug: entering %s(%d)\n",__FUNCTION__,srate); + dprintk("cx24110 debug: entering %s(%d)\n",__FUNCTION__,srate); if (srate>90999000UL/2) srate=90999000UL/2; if (srate<500000) -- cgit v1.2.3 From 9bf95407c2d498d0a55fa10b54379ae9871bfe1a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Apr 2006 10:21:37 -0300 Subject: Fix a type error in dvb_frontend.c From: Uwe Bugla Signed-off-by: Uwe Bugla Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 62063b00d..12e890e06 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -56,7 +56,7 @@ MODULE_PARM_DESC(dvb_force_auto_inversion, "0: normal (default), 1: INVERSION_AU module_param(dvb_override_tune_delay, int, 0644); MODULE_PARM_DESC(dvb_override_tune_delay, "0: normal (default), >0 => delay in milliseconds to wait for lock after a tune attempt"); module_param(dvb_powerdown_on_sleep, int, 0644); -MODULE_PARM_DESC(dvb_powerdown_on_sleep, "0: do not power down, 1: turn LNB volatage off on sleep (default)"); +MODULE_PARM_DESC(dvb_powerdown_on_sleep, "0: do not power down, 1: turn LNB voltage off on sleep (default)"); #define dprintk if (dvb_frontend_debug) printk -- cgit v1.2.3 From 790b1162d78755ecad42e57486dce29be2377781 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Apr 2006 10:26:57 -0300 Subject: Kill drivers/media/common/saa7146_vv_ksyms.c From: Adrian Bunk This patch moves the EXPORT_SYMBOL's from drivers/media/common/saa7146_vv_ksyms.c to the files with the actual functions. Signed-off-by: Adrian Bunk Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/common/Makefile | 2 +- linux/drivers/media/common/saa7146_fops.c | 4 ++++ linux/drivers/media/common/saa7146_hlp.c | 1 + linux/drivers/media/common/saa7146_video.c | 2 ++ linux/drivers/media/common/saa7146_vv_ksyms.c | 13 ------------- 5 files changed, 8 insertions(+), 14 deletions(-) delete mode 100644 linux/drivers/media/common/saa7146_vv_ksyms.c (limited to 'linux/drivers') diff --git a/linux/drivers/media/common/Makefile b/linux/drivers/media/common/Makefile index 61b89617a..8e7448230 100644 --- a/linux/drivers/media/common/Makefile +++ b/linux/drivers/media/common/Makefile @@ -1,5 +1,5 @@ saa7146-objs := saa7146_i2c.o saa7146_core.o -saa7146_vv-objs := saa7146_vv_ksyms.o saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o +saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o ir-common-objs := ir-functions.o ir-keymaps.o obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o diff --git a/linux/drivers/media/common/saa7146_fops.c b/linux/drivers/media/common/saa7146_fops.c index 2dde5a240..20016d2cd 100644 --- a/linux/drivers/media/common/saa7146_fops.c +++ b/linux/drivers/media/common/saa7146_fops.c @@ -502,6 +502,7 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) return 0; } +EXPORT_SYMBOL_GPL(saa7146_vv_init); int saa7146_vv_release(struct saa7146_dev* dev) { @@ -516,6 +517,7 @@ int saa7146_vv_release(struct saa7146_dev* dev) return 0; } +EXPORT_SYMBOL_GPL(saa7146_vv_release); int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev, char *name, int type) @@ -554,6 +556,7 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev, *vid = vfd; return 0; } +EXPORT_SYMBOL_GPL(saa7146_register_device); int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev) { @@ -572,6 +575,7 @@ int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev return 0; } +EXPORT_SYMBOL_GPL(saa7146_unregister_device); static int __init saa7146_vv_init_module(void) { diff --git a/linux/drivers/media/common/saa7146_hlp.c b/linux/drivers/media/common/saa7146_hlp.c index d6bc2e7c9..cc0c1b965 100644 --- a/linux/drivers/media/common/saa7146_hlp.c +++ b/linux/drivers/media/common/saa7146_hlp.c @@ -642,6 +642,7 @@ void saa7146_set_hps_source_and_sync(struct saa7146_dev *dev, int source, int sy vv->current_hps_source = source; vv->current_hps_sync = sync; } +EXPORT_SYMBOL_GPL(saa7146_set_hps_source_and_sync); int saa7146_enable_overlay(struct saa7146_fh *fh) { diff --git a/linux/drivers/media/common/saa7146_video.c b/linux/drivers/media/common/saa7146_video.c index e24edb892..480c09122 100644 --- a/linux/drivers/media/common/saa7146_video.c +++ b/linux/drivers/media/common/saa7146_video.c @@ -319,6 +319,7 @@ int saa7146_start_preview(struct saa7146_fh *fh) return 0; } +EXPORT_SYMBOL_GPL(saa7146_start_preview); int saa7146_stop_preview(struct saa7146_fh *fh) { @@ -353,6 +354,7 @@ int saa7146_stop_preview(struct saa7146_fh *fh) return 0; } +EXPORT_SYMBOL_GPL(saa7146_stop_preview); static int s_fmt(struct saa7146_fh *fh, struct v4l2_format *f) { diff --git a/linux/drivers/media/common/saa7146_vv_ksyms.c b/linux/drivers/media/common/saa7146_vv_ksyms.c deleted file mode 100644 index 08afde9f8..000000000 --- a/linux/drivers/media/common/saa7146_vv_ksyms.c +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include "compat.h" -#include - -EXPORT_SYMBOL_GPL(saa7146_start_preview); -EXPORT_SYMBOL_GPL(saa7146_stop_preview); - -EXPORT_SYMBOL_GPL(saa7146_set_hps_source_and_sync); -EXPORT_SYMBOL_GPL(saa7146_register_device); -EXPORT_SYMBOL_GPL(saa7146_unregister_device); - -EXPORT_SYMBOL_GPL(saa7146_vv_init); -EXPORT_SYMBOL_GPL(saa7146_vv_release); -- cgit v1.2.3 From c271ec73f1129f6e81861829f463dae7ce2315d5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Apr 2006 17:17:38 -0300 Subject: Adjust some compat changes to be no changes at all for -git kernel From: Trent Piepho Some of the compat changes produced cosmetic differences when applied to kernel. This removes those differences. Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/radio/radio-maestro.c | 4 ++-- linux/drivers/media/radio/radio-sf16fmi.c | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/radio/radio-maestro.c b/linux/drivers/media/radio/radio-maestro.c index dfa1e6ded..0a1ea1ce7 100644 --- a/linux/drivers/media/radio/radio-maestro.c +++ b/linux/drivers/media/radio/radio-maestro.c @@ -23,12 +23,12 @@ #include #include #include -#include #include "compat.h" -#include #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include #endif +#include +#include #define DRIVER_VERSION "0.05" diff --git a/linux/drivers/media/radio/radio-sf16fmi.c b/linux/drivers/media/radio/radio-sf16fmi.c index 62bb19d2c..928ac9b2d 100644 --- a/linux/drivers/media/radio/radio-sf16fmi.c +++ b/linux/drivers/media/radio/radio-sf16fmi.c @@ -27,10 +27,6 @@ #include /* copy to/from user */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include - -static struct mutex lock; -#else -static struct semaphore lock; #endif struct fmi_device @@ -44,6 +40,11 @@ struct fmi_device static int io = -1; static int radio_nr = -1; static struct pnp_dev *dev = NULL; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) +static struct mutex lock; +#else +static struct semaphore lock; +#endif /* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */ /* It is only useful to give freq in intervall of 800 (=0.05Mhz), -- cgit v1.2.3 From de23398fdb3acd84db447947bd6c2e347cdb7f6f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Apr 2006 17:28:37 -0300 Subject: added /* keep */ to all #if 0 to avoid being removed by gentree.pl From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/adv7170.c | 2 +- linux/drivers/media/video/arv.c | 12 ++++++------ linux/drivers/media/video/bw-qcam.c | 2 +- linux/drivers/media/video/c-qcam.c | 2 +- linux/drivers/media/video/cpia.c | 2 +- linux/drivers/media/video/meye.c | 2 +- linux/drivers/media/video/ov511.c | 22 +++++++++++----------- linux/drivers/media/video/planb.c | 6 +++--- linux/drivers/media/video/stradis.c | 8 ++++---- linux/drivers/media/video/vino.c | 8 ++++---- linux/drivers/media/video/w9966.c | 8 ++++---- linux/drivers/media/video/zoran_driver.c | 2 +- linux/drivers/media/video/zr36016.c | 2 +- 13 files changed, 39 insertions(+), 39 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/adv7170.c b/linux/drivers/media/video/adv7170.c index 48fe2344b..cd0b4562a 100644 --- a/linux/drivers/media/video/adv7170.c +++ b/linux/drivers/media/video/adv7170.c @@ -240,7 +240,7 @@ adv7170_command (struct i2c_client *client, switch (cmd) { case 0: -#if 0 +#if 0 /* keep */; /* This is just for testing!!! */ adv7170_write_block(client, init_common, sizeof(init_common)); diff --git a/linux/drivers/media/video/arv.c b/linux/drivers/media/video/arv.c index aec57f485..af3980d95 100644 --- a/linux/drivers/media/video/arv.c +++ b/linux/drivers/media/video/arv.c @@ -40,7 +40,7 @@ #include #include -#if 0 +#if 0 /* keep */; #define DEBUG(n, args...) printk(args) #define CHECK_LOST 1 #else @@ -576,11 +576,11 @@ static void ar_interrupt(int irq, void *dev, struct pt_regs *regs) * we have to start capture. */ disable_dma(); -#if 0 +#if 0 /* keep */; ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); /* needless? */ #endif memcpy(ar->frame[0], ar->line_buff, ar->line_bytes); -#if 0 +#if 0 /* keep */; ar_outl(0xa1861300, M32R_DMA0CR0_PORTL); #endif enable_dma(); @@ -606,7 +606,7 @@ static void ar_interrupt(int irq, void *dev, struct pt_regs *regs) ar_outl(arvcr1, ARVCR1); /* disable */ wake_up_interruptible(&ar->wait); } else { -#if 0 +#if 0 /* keep */; ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); ar_outl(0xa1861300, M32R_DMA0CR0_PORTL); #endif @@ -690,7 +690,7 @@ static int ar_initialize(struct video_device *dev) printk("."); iic(2,0x78,0x8e,0x0c,0x00); iic(2,0x78,0x8f,0x00,0x00); -#if 0 +#if 0 /* keep */; iic(2,0x78,0x90,0x00,0x00); /* AWB on=1 off=0 */ #endif iic(2,0x78,0x93,0x01,0x00); @@ -709,7 +709,7 @@ static int ar_initialize(struct video_device *dev) iic(2,0x78,0x9e,0x2e,0x00); iic(2,0x78,0xb8,0x78,0x00); iic(2,0x78,0xba,0x05,0x00); -#if 0 +#if 0 /* keep */; iic(2,0x78,0x83,0x8c,0x00); /* brightness */ #endif printk("."); diff --git a/linux/drivers/media/video/bw-qcam.c b/linux/drivers/media/video/bw-qcam.c index 25ce06421..62287e3d8 100644 --- a/linux/drivers/media/video/bw-qcam.c +++ b/linux/drivers/media/video/bw-qcam.c @@ -334,7 +334,7 @@ static int qc_detect(struct qcam_device *q) } -#if 0 +#if 0 /* keep */; /* Force camera detection during testing. Sometimes the camera won't be flashing these bits. Possibly unloading the module in the middle of a grab? Or some timeout condition? diff --git a/linux/drivers/media/video/c-qcam.c b/linux/drivers/media/video/c-qcam.c index 5cfbb30e5..4fa309398 100644 --- a/linux/drivers/media/video/c-qcam.c +++ b/linux/drivers/media/video/c-qcam.c @@ -626,7 +626,7 @@ static int qcam_do_ioctl(struct inode *inode, struct file *file, qcam->mode = QC_DECIMATION_1; } qcam->mode |= QC_MILLIONS; -#if 0 +#if 0 /* keep */; if(vw->width>=640 && vw->height>=480) { qcam->width = 640; diff --git a/linux/drivers/media/video/cpia.c b/linux/drivers/media/video/cpia.c index d955004b7..2e66820f1 100644 --- a/linux/drivers/media/video/cpia.c +++ b/linux/drivers/media/video/cpia.c @@ -2430,7 +2430,7 @@ static void set_flicker(struct cam_params *params, volatile u32 *command_flags, #define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \ params->version.firmwareRevision == (y)) /* define for compgain calculation */ -#if 0 +#if 0 /* keep */; #define COMPGAIN(base, curexp, newexp) \ (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5) #define EXP_FROM_COMP(basecomp, curcomp, curexp) \ diff --git a/linux/drivers/media/video/meye.c b/linux/drivers/media/video/meye.c index dcd90f56b..73749c740 100644 --- a/linux/drivers/media/video/meye.c +++ b/linux/drivers/media/video/meye.c @@ -750,7 +750,7 @@ static int mchip_compress_frame(u8 *buf, int bufsize) return len; } -#if 0 +#if 0 /* keep */; /* uncompress one image into a buffer */ static int mchip_uncompress_frame(u8 *img, int imgsize, u8 *buf, int bufsize) { diff --git a/linux/drivers/media/video/ov511.c b/linux/drivers/media/video/ov511.c index fdc8e3f13..1bf382115 100644 --- a/linux/drivers/media/video/ov511.c +++ b/linux/drivers/media/video/ov511.c @@ -638,7 +638,7 @@ ov511_i2c_write_internal(struct usb_ov511 *ov, rc = 0; break; } -#if 0 +#if 0 /* keep */; /* I2C abort */ reg_w(ov, R511_I2C_CTL, 0x10); #endif @@ -1105,7 +1105,7 @@ ov51x_clear_snapshot(struct usb_ov511 *ov) } } -#if 0 +#if 0 /* keep */; /* Checks the status of the snapshot button. Returns 1 if it was pressed since * it was last cleared, and zero in all other cases (including errors) */ static int @@ -1673,7 +1673,7 @@ sensor_set_hue(struct usb_ov511 *ov, unsigned short val) break; case SEN_OV7620: // Hue control is causing problems. I will enable it once it's fixed. -#if 0 +#if 0 /* keep */; rc = i2c_w(ov, 0x7a, (unsigned char)(val >> 8) + 0xb); if (rc < 0) goto out; @@ -1804,7 +1804,7 @@ sensor_get_picture(struct usb_ov511 *ov, struct video_picture *p) return 0; } -#if 0 +#if 0 /* keep */; // FIXME: Exposure range is only 0x00-0x7f in interlace mode /* Sets current exposure for sensor. This only has an effect if auto-exposure * is off */ @@ -2171,7 +2171,7 @@ mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height, case SEN_OV7610: i2c_w(ov, 0x14, qvga?0x24:0x04); // FIXME: Does this improve the image quality or frame rate? -#if 0 +#if 0 /* keep */; i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); i2c_w(ov, 0x24, 0x10); i2c_w(ov, 0x25, qvga?0x40:0x8a); @@ -2193,7 +2193,7 @@ mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height, // i2c_w(ov, 0x2b, 0x00); i2c_w(ov, 0x14, qvga?0xa4:0x84); // FIXME: Enable this once 7620AE uses 7620 initial settings -#if 0 +#if 0 /* keep */; i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); i2c_w(ov, 0x24, qvga?0x20:0x3a); i2c_w(ov, 0x25, qvga?0x30:0x60); @@ -3435,7 +3435,7 @@ sof: PDEBUG(4, "Starting capture on frame %d", frame->framenum); // Snapshot not reverse-engineered yet. -#if 0 +#if 0 /* keep */; /* Check to see if it's a snapshot frame */ /* FIXME?? Should the snapshot reset go here? Performance? */ if (in[8] & 0x02) { @@ -4193,7 +4193,7 @@ ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file, PDEBUG(4, "VIDIOCSWIN: %dx%d", vw->width, vw->height); -#if 0 +#if 0 /* keep */; if (vw->flags) return -EINVAL; if (vw->clipcount) @@ -4306,7 +4306,7 @@ ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file, rc = mode_init_regs(ov, vm->width, vm->height, vm->format, ov->sub_flag); -#if 0 +#if 0 /* keep */; if (rc < 0) { PDEBUG(1, "Got error while initializing regs "); return ret; @@ -5068,7 +5068,7 @@ ks0127_configure(struct usb_ov511 *ov) int rc; // FIXME: I don't know how to sync or reset it yet -#if 0 +#if 0 /* keep */; if (ov51x_init_ks_sensor(ov) < 0) { err("Failed to initialize the KS0127"); return -1; @@ -5155,7 +5155,7 @@ saa7111a_configure(struct usb_ov511 *ov) }; // FIXME: I don't know how to sync or reset it yet -#if 0 +#if 0 /* keep */; if (ov51x_init_saa_sensor(ov) < 0) { err("Failed to initialize the SAA7111A"); return -1; diff --git a/linux/drivers/media/video/planb.c b/linux/drivers/media/video/planb.c index c3026d701..3cfdb504d 100644 --- a/linux/drivers/media/video/planb.c +++ b/linux/drivers/media/video/planb.c @@ -55,13 +55,13 @@ #include "saa7196.h" /* Would you mind for some ugly debugging? */ -#if 0 +#if 0 /* keep */; #define DEBUG(x...) printk(KERN_DEBUG ## x) /* Debug driver */ #else #define DEBUG(x...) /* Don't debug driver */ #endif -#if 0 +#if 0 /* keep */; #define IDEBUG(x...) printk(KERN_DEBUG ## x) /* Debug interrupt part */ #else #define IDEBUG(x...) /* Don't debug interrupt part */ @@ -2042,7 +2042,7 @@ static int init_planb(struct planb *pb) pb->win.width=768; /* 640 */ pb->win.height=576; /* 480 */ pb->maxlines=576; -#if 0 +#if 0 /* keep */; btv->win.cropwidth=768; /* 640 */ btv->win.cropheight=576; /* 480 */ btv->win.cropx=0; diff --git a/linux/drivers/media/video/stradis.c b/linux/drivers/media/video/stradis.c index 9aad8d66d..8cfb32392 100644 --- a/linux/drivers/media/video/stradis.c +++ b/linux/drivers/media/video/stradis.c @@ -455,7 +455,7 @@ static irqreturn_t saa7146_irq(int irq, void *dev_id, struct pt_regs *regs) saa->vidinfo.frame_count = 0; saa->vidinfo.h_size = 704; saa->vidinfo.v_size = 480; -#if 0 +#if 0 /* keep */; if (saa->endmarkhead != saa->endmarktail) { saa->audhead = saa->endmark[saa->endmarkhead]; @@ -940,7 +940,7 @@ send_fpga_stuff: if (NewCard) set_genlock_offset(saa, 0); debiwrite(saa, debNormal, IBM_MP2_FRNT_ATTEN, 0, 2); -#if 0 +#if 0 /* keep */; /* enable genlock */ debiwrite(saa, debNormal, XILINX_CTL0, 0x8000, 2); #else @@ -1043,7 +1043,7 @@ static int initialize_ibmmpeg2(struct video_code *microcode) if (i != 0xa55a) { printk(KERN_INFO "stradis%d: %04x != 0xa55a\n", saa->nr, i); -#if 0 +#if 0 /* keep */; return -1; #endif } @@ -2048,7 +2048,7 @@ static int __devinit init_saa7146(struct pci_dev *pdev) dev_err(&pdev->dev, "%d: debi kmalloc failed\n", saa->nr); goto err; } -#if 0 +#if 0 /* keep */; saa->pagedebi = saa->dmadebi + 32768; /* top 4k is for mmu */ saawrite(virt_to_bus(saa->pagedebi) /*|0x800 */ , SAA7146_DEBI_PAGE); for (i = 0; i < 12; i++) /* setup mmu page table */ diff --git a/linux/drivers/media/video/vino.c b/linux/drivers/media/video/vino.c index a8c101494..a739919ba 100644 --- a/linux/drivers/media/video/vino.c +++ b/linux/drivers/media/video/vino.c @@ -914,7 +914,7 @@ static int vino_allocate_buffer(struct vino_framebuffer *fb, return ret; } -#if 0 +#if 0 /* keep */; /* user buffers not fully implemented yet */ static int vino_prepare_user_buffer(struct vino_framebuffer *fb, void *user, @@ -1043,7 +1043,7 @@ static inline int vino_fifo_has_id(struct vino_framebuffer_fifo *f, return 0; } -#if 0 +#if 0 /* keep */; /* returns true/false */ static inline int vino_fifo_full(struct vino_framebuffer_fifo *f) { @@ -1399,7 +1399,7 @@ out: return ret; } -#if 0 +#if 0 /* keep */; static int vino_queue_get_total(struct vino_framebuffer_queue *q, unsigned int *total) { @@ -2210,7 +2210,7 @@ out: spin_unlock_irqrestore(&vcs->capture_lock, flags); } -#if 0 +#if 0 /* keep */; static int vino_capture_failed(struct vino_channel_settings *vcs) { struct vino_framebuffer *fb; diff --git a/linux/drivers/media/video/w9966.c b/linux/drivers/media/video/w9966.c index 5aea29c69..fe368bfb7 100644 --- a/linux/drivers/media/video/w9966.c +++ b/linux/drivers/media/video/w9966.c @@ -158,7 +158,7 @@ static inline void w9966_pdev_release(struct w9966_dev *vdev); static int w9966_rReg(struct w9966_dev* cam, int reg); static int w9966_wReg(struct w9966_dev* cam, int reg, int data); -#if 0 +#if 0 /* keep */; static int w9966_rReg_i2c(struct w9966_dev* cam, int reg); #endif static int w9966_wReg_i2c(struct w9966_dev* cam, int reg, int data); @@ -174,7 +174,7 @@ static inline int w9966_i2c_setscl(struct w9966_dev* cam, int state); static inline int w9966_i2c_getsda(struct w9966_dev* cam); static inline int w9966_i2c_getscl(struct w9966_dev* cam); static int w9966_i2c_wbyte(struct w9966_dev* cam, int data); -#if 0 +#if 0 /* keep */; static int w9966_i2c_rbyte(struct w9966_dev* cam); #endif @@ -623,7 +623,7 @@ static int w9966_i2c_wbyte(struct w9966_dev* cam, int data) // Read a data byte with ack from the i2c-bus // Expects a claimed pdev. -1 on error -#if 0 +#if 0 /* keep */; static int w9966_i2c_rbyte(struct w9966_dev* cam) { unsigned char data = 0x00; @@ -647,7 +647,7 @@ static int w9966_i2c_rbyte(struct w9966_dev* cam) // Read a register from the i2c device. // Expects claimed pdev. -1 on error -#if 0 +#if 0 /* keep */; static int w9966_rReg_i2c(struct w9966_dev* cam, int reg) { int data; diff --git a/linux/drivers/media/video/zoran_driver.c b/linux/drivers/media/video/zoran_driver.c index 5ced8b516..2f20c87fa 100644 --- a/linux/drivers/media/video/zoran_driver.c +++ b/linux/drivers/media/video/zoran_driver.c @@ -1519,7 +1519,7 @@ setup_fbuffer (struct file *file, if (!bytesperline) bytesperline = width * ((fmt->depth + 7) & ~7) / 8; -#if 0 +#if 0 /* keep */; if (zr->overlay_active) { /* dzjee... stupid users... don't even bother to turn off * overlay before changing the memory location... diff --git a/linux/drivers/media/video/zr36016.c b/linux/drivers/media/video/zr36016.c index 7f958d803..2a99e8d2e 100644 --- a/linux/drivers/media/video/zr36016.c +++ b/linux/drivers/media/video/zr36016.c @@ -229,7 +229,7 @@ zr36016_basic_test (struct zr36016 *ptr) simple loop for pushing the init datasets - NO USE -- ========================================================================= */ -#if 0 +#if 0 /* keep */; static int zr36016_pushit (struct zr36016 *ptr, u16 startreg, u16 len, -- cgit v1.2.3 From 8a12d77dcb14376f2521bfc9ff2244a17c113dd3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Apr 2006 17:36:49 -0300 Subject: Since we are not using CVS, $Id makes no sense anymore From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/common/ir-functions.c | 1 - linux/drivers/media/common/ir-keymaps.c | 1 - linux/drivers/media/video/bt8xx/bttv-cards.c | 1 - linux/drivers/media/video/bt8xx/bttv-driver.c | 1 - linux/drivers/media/video/bt8xx/bttv-gpio.c | 1 - linux/drivers/media/video/bt8xx/bttv-i2c.c | 1 - linux/drivers/media/video/bt8xx/bttv-if.c | 1 - linux/drivers/media/video/bt8xx/bttv-input.c | 1 - linux/drivers/media/video/bt8xx/bttv-risc.c | 1 - linux/drivers/media/video/bt8xx/bttv-vbi.c | 1 - linux/drivers/media/video/bt8xx/bttv.h | 1 - linux/drivers/media/video/bt8xx/bttvp.h | 1 - linux/drivers/media/video/btcx-risc.c | 1 - linux/drivers/media/video/btcx-risc.h | 1 - linux/drivers/media/video/cpia2/cpia2.h | 1 - linux/drivers/media/video/cpia2/cpia2_core.c | 1 - linux/drivers/media/video/cpia2/cpia2_registers.h | 1 - linux/drivers/media/video/cpia2/cpia2_usb.c | 1 - linux/drivers/media/video/cpia2/cpia2_v4l.c | 1 - linux/drivers/media/video/cpia2/cpia2dev.h | 1 - linux/drivers/media/video/cpia2/cpia2patch.h | 1 - linux/drivers/media/video/cx88/cx88-alsa.c | 1 - linux/drivers/media/video/cx88/cx88-blackbird.c | 1 - linux/drivers/media/video/cx88/cx88-cards.c | 1 - linux/drivers/media/video/cx88/cx88-core.c | 1 - linux/drivers/media/video/cx88/cx88-dvb.c | 1 - linux/drivers/media/video/cx88/cx88-i2c.c | 1 - linux/drivers/media/video/cx88/cx88-input.c | 1 - linux/drivers/media/video/cx88/cx88-mpeg.c | 1 - linux/drivers/media/video/cx88/cx88-reg.h | 1 - linux/drivers/media/video/cx88/cx88-tvaudio.c | 1 - linux/drivers/media/video/cx88/cx88-vbi.c | 1 - linux/drivers/media/video/cx88/cx88-video.c | 1 - linux/drivers/media/video/cx88/cx88-vp3054-i2c.c | 1 - linux/drivers/media/video/cx88/cx88-vp3054-i2c.h | 1 - linux/drivers/media/video/cx88/cx88.h | 1 - linux/drivers/media/video/ir-kbd-i2c.c | 1 - linux/drivers/media/video/msp3400-driver.h | 1 - linux/drivers/media/video/mt20xx.c | 1 - linux/drivers/media/video/saa7134/saa7134-alsa.c | 1 - linux/drivers/media/video/saa7134/saa7134-cards.c | 1 - linux/drivers/media/video/saa7134/saa7134-core.c | 1 - linux/drivers/media/video/saa7134/saa7134-dvb.c | 1 - linux/drivers/media/video/saa7134/saa7134-empress.c | 1 - linux/drivers/media/video/saa7134/saa7134-i2c.c | 1 - linux/drivers/media/video/saa7134/saa7134-input.c | 1 - linux/drivers/media/video/saa7134/saa7134-oss.c | 1 - linux/drivers/media/video/saa7134/saa7134-reg.h | 1 - linux/drivers/media/video/saa7134/saa7134-ts.c | 1 - linux/drivers/media/video/saa7134/saa7134-tvaudio.c | 1 - linux/drivers/media/video/saa7134/saa7134-vbi.c | 1 - linux/drivers/media/video/saa7134/saa7134-video.c | 1 - linux/drivers/media/video/saa7134/saa7134.h | 1 - linux/drivers/media/video/tda8290.c | 1 - linux/drivers/media/video/tea5767.c | 1 - linux/drivers/media/video/tuner-core.c | 1 - linux/drivers/media/video/tuner-simple.c | 1 - linux/drivers/media/video/tuner-types.c | 1 - linux/drivers/media/video/tvmixer.c | 1 - linux/drivers/media/video/v4l1-compat.c | 1 - linux/drivers/media/video/video-buf-dvb.c | 1 - linux/drivers/media/video/video-buf.c | 1 - 62 files changed, 62 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/common/ir-functions.c b/linux/drivers/media/common/ir-functions.c index 3b988b012..fbf4abe39 100644 --- a/linux/drivers/media/common/ir-functions.c +++ b/linux/drivers/media/common/ir-functions.c @@ -1,5 +1,4 @@ /* - * $Id: ir-functions.c,v 1.1 2006/01/17 20:05:21 rmcc Exp $ * * some common structs and functions to handle infrared remotes via * input layer ... diff --git a/linux/drivers/media/common/ir-keymaps.c b/linux/drivers/media/common/ir-keymaps.c index 1cdd5b0df..f356ad9b4 100644 --- a/linux/drivers/media/common/ir-keymaps.c +++ b/linux/drivers/media/common/ir-keymaps.c @@ -1,6 +1,5 @@ /* - $Id: ir-keymaps.c,v 1.5 2006/01/18 20:41:55 nsh Exp $ Keytables for supported remote controls. This file is part of video4linux. diff --git a/linux/drivers/media/video/bt8xx/bttv-cards.c b/linux/drivers/media/video/bt8xx/bttv-cards.c index f3e3c624d..b11967fa5 100644 --- a/linux/drivers/media/video/bt8xx/bttv-cards.c +++ b/linux/drivers/media/video/bt8xx/bttv-cards.c @@ -1,5 +1,4 @@ /* - $Id: bttv-cards.c,v 1.111 2006/01/19 18:14:20 mkrufky Exp $ bttv-cards.c diff --git a/linux/drivers/media/video/bt8xx/bttv-driver.c b/linux/drivers/media/video/bt8xx/bttv-driver.c index 9586f91e3..7c819c5b9 100644 --- a/linux/drivers/media/video/bt8xx/bttv-driver.c +++ b/linux/drivers/media/video/bt8xx/bttv-driver.c @@ -1,5 +1,4 @@ /* - $Id: bttv-driver.c,v 1.87 2006/01/15 09:35:15 mchehab Exp $ bttv - Bt848 frame grabber driver diff --git a/linux/drivers/media/video/bt8xx/bttv-gpio.c b/linux/drivers/media/video/bt8xx/bttv-gpio.c index 8682662b3..b6eb7eea9 100644 --- a/linux/drivers/media/video/bt8xx/bttv-gpio.c +++ b/linux/drivers/media/video/bt8xx/bttv-gpio.c @@ -1,5 +1,4 @@ /* - $Id: bttv-gpio.c,v 1.12 2006/01/15 23:19:19 mchehab Exp $ bttv-gpio.c -- gpio sub drivers diff --git a/linux/drivers/media/video/bt8xx/bttv-i2c.c b/linux/drivers/media/video/bt8xx/bttv-i2c.c index 6576bd3b1..0558ee220 100644 --- a/linux/drivers/media/video/bt8xx/bttv-i2c.c +++ b/linux/drivers/media/video/bt8xx/bttv-i2c.c @@ -1,5 +1,4 @@ /* - $Id: bttv-i2c.c,v 1.42 2006/01/12 13:09:55 mchehab Exp $ bttv-i2c.c -- all the i2c code is here diff --git a/linux/drivers/media/video/bt8xx/bttv-if.c b/linux/drivers/media/video/bt8xx/bttv-if.c index d264e0e53..c286f5db7 100644 --- a/linux/drivers/media/video/bt8xx/bttv-if.c +++ b/linux/drivers/media/video/bt8xx/bttv-if.c @@ -1,5 +1,4 @@ /* - $Id: bttv-if.c,v 1.5 2005/10/16 12:13:58 mchehab Exp $ bttv-if.c -- old gpio interface to other kernel modules don't use in new code, will go away in 2.7 diff --git a/linux/drivers/media/video/bt8xx/bttv-input.c b/linux/drivers/media/video/bt8xx/bttv-input.c index 8a619ddb1..bab7cb123 100644 --- a/linux/drivers/media/video/bt8xx/bttv-input.c +++ b/linux/drivers/media/video/bt8xx/bttv-input.c @@ -1,5 +1,4 @@ /* - * $Id: bttv-input.c,v 1.7 2006/01/18 20:21:46 nsh Exp $ * * Copyright (c) 2003 Gerd Knorr * Copyright (c) 2003 Pavel Machek diff --git a/linux/drivers/media/video/bt8xx/bttv-risc.c b/linux/drivers/media/video/bt8xx/bttv-risc.c index 4401d7320..824e17d2a 100644 --- a/linux/drivers/media/video/bt8xx/bttv-risc.c +++ b/linux/drivers/media/video/bt8xx/bttv-risc.c @@ -1,5 +1,4 @@ /* - $Id: bttv-risc.c,v 1.12 2005/10/16 12:34:29 mchehab Exp $ bttv-risc.c -- interfaces to other kernel modules diff --git a/linux/drivers/media/video/bt8xx/bttv-vbi.c b/linux/drivers/media/video/bt8xx/bttv-vbi.c index 35a8f0d69..8c9f0f7cf 100644 --- a/linux/drivers/media/video/bt8xx/bttv-vbi.c +++ b/linux/drivers/media/video/bt8xx/bttv-vbi.c @@ -1,5 +1,4 @@ /* - $Id: bttv-vbi.c,v 1.10 2005/12/14 22:05:02 hverkuil Exp $ bttv - Bt848 frame grabber driver vbi interface diff --git a/linux/drivers/media/video/bt8xx/bttv.h b/linux/drivers/media/video/bt8xx/bttv.h index 563b4fb61..88b72b8a3 100644 --- a/linux/drivers/media/video/bt8xx/bttv.h +++ b/linux/drivers/media/video/bt8xx/bttv.h @@ -1,5 +1,4 @@ /* - * $Id: bttv.h,v 1.38 2006/01/15 23:19:19 mchehab Exp $ * * bttv - Bt848 frame grabber driver * diff --git a/linux/drivers/media/video/bt8xx/bttvp.h b/linux/drivers/media/video/bt8xx/bttvp.h index a6fa1511a..70158aa5f 100644 --- a/linux/drivers/media/video/bt8xx/bttvp.h +++ b/linux/drivers/media/video/bt8xx/bttvp.h @@ -1,5 +1,4 @@ /* - $Id: bttvp.h,v 1.32 2006/01/15 09:35:15 mchehab Exp $ bttv - Bt848 frame grabber driver diff --git a/linux/drivers/media/video/btcx-risc.c b/linux/drivers/media/video/btcx-risc.c index 073d5361f..a3bd492eb 100644 --- a/linux/drivers/media/video/btcx-risc.c +++ b/linux/drivers/media/video/btcx-risc.c @@ -1,5 +1,4 @@ /* - $Id: btcx-risc.c,v 1.8 2006/01/12 13:09:55 mchehab Exp $ btcx-risc.c diff --git a/linux/drivers/media/video/btcx-risc.h b/linux/drivers/media/video/btcx-risc.h index 41f60395a..503e6c6d7 100644 --- a/linux/drivers/media/video/btcx-risc.h +++ b/linux/drivers/media/video/btcx-risc.h @@ -1,5 +1,4 @@ /* - * $Id: btcx-risc.h,v 1.2 2004/09/15 16:15:24 kraxel Exp $ */ struct btcx_riscmem { unsigned int size; diff --git a/linux/drivers/media/video/cpia2/cpia2.h b/linux/drivers/media/video/cpia2/cpia2.h index 7480c1827..cfbafc02d 100644 --- a/linux/drivers/media/video/cpia2/cpia2.h +++ b/linux/drivers/media/video/cpia2/cpia2.h @@ -27,7 +27,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ****************************************************************************/ -/* $Id: cpia2.h,v 1.25 2005/10/05 21:19:28 sbertin Exp $ */ #ifndef __CPIA2_H__ #define __CPIA2_H__ diff --git a/linux/drivers/media/video/cpia2/cpia2_core.c b/linux/drivers/media/video/cpia2/cpia2_core.c index 756cb260c..fd771c7a2 100644 --- a/linux/drivers/media/video/cpia2/cpia2_core.c +++ b/linux/drivers/media/video/cpia2/cpia2_core.c @@ -28,7 +28,6 @@ * Alan Cox * ****************************************************************************/ -/* $Id:*/ #include "cpia2.h" diff --git a/linux/drivers/media/video/cpia2/cpia2_registers.h b/linux/drivers/media/video/cpia2/cpia2_registers.h index 98676b2ce..3bbec514a 100644 --- a/linux/drivers/media/video/cpia2/cpia2_registers.h +++ b/linux/drivers/media/video/cpia2/cpia2_registers.h @@ -22,7 +22,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ****************************************************************************/ -/* $Id: cpia2_registers.h,v 1.7 2005/08/29 23:39:57 sbertin Exp $ */ #ifndef CPIA2_REGISTER_HEADER #define CPIA2_REGISTER_HEADER diff --git a/linux/drivers/media/video/cpia2/cpia2_usb.c b/linux/drivers/media/video/cpia2/cpia2_usb.c index cd79542c1..f4da02941 100644 --- a/linux/drivers/media/video/cpia2/cpia2_usb.c +++ b/linux/drivers/media/video/cpia2/cpia2_usb.c @@ -27,7 +27,6 @@ * Stripped of 2.4 stuff ready for main kernel submit by * Alan Cox ****************************************************************************/ -/* $Id: cpia2_usb.c,v 1.20 2005/09/13 23:12:27 sbertin Exp $ */ #include #include diff --git a/linux/drivers/media/video/cpia2/cpia2_v4l.c b/linux/drivers/media/video/cpia2/cpia2_v4l.c index 0f289979f..481e178ef 100644 --- a/linux/drivers/media/video/cpia2/cpia2_v4l.c +++ b/linux/drivers/media/video/cpia2/cpia2_v4l.c @@ -28,7 +28,6 @@ * Stripped of 2.4 stuff ready for main kernel submit by * Alan Cox ****************************************************************************/ -/* $Id: cpia2_v4l.c,v 1.27 2005/12/12 15:03:58 sbertin Exp $ */ #include diff --git a/linux/drivers/media/video/cpia2/cpia2dev.h b/linux/drivers/media/video/cpia2/cpia2dev.h index 09822f3a4..d58097ce0 100644 --- a/linux/drivers/media/video/cpia2/cpia2dev.h +++ b/linux/drivers/media/video/cpia2/cpia2dev.h @@ -25,7 +25,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ****************************************************************************/ -/* $Id: cpia2dev.h,v 1.3 2005/08/29 23:39:57 sbertin Exp $ */ #ifndef CPIA2_DEV_HEADER #define CPIA2_DEV_HEADER diff --git a/linux/drivers/media/video/cpia2/cpia2patch.h b/linux/drivers/media/video/cpia2/cpia2patch.h index e74c753f5..7f085fbe7 100644 --- a/linux/drivers/media/video/cpia2/cpia2patch.h +++ b/linux/drivers/media/video/cpia2/cpia2patch.h @@ -24,7 +24,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ****************************************************************************/ -/* $Id: cpia2patch.h,v 1.4 2001/09/02 17:47:50 sbertin Exp $ */ #ifndef CPIA2_PATCH_HEADER #define CPIA2_PATCH_HEADER diff --git a/linux/drivers/media/video/cx88/cx88-alsa.c b/linux/drivers/media/video/cx88/cx88-alsa.c index df82e8251..69d3c3c8f 100644 --- a/linux/drivers/media/video/cx88/cx88-alsa.c +++ b/linux/drivers/media/video/cx88/cx88-alsa.c @@ -1,5 +1,4 @@ /* - * $Id: cx88-alsa.c,v 1.32 2006/01/15 17:33:02 mkrufky Exp $ * * Support for audio capture * PCI function #1 of the cx2388x. diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index 0ffef13d5..6fe32a92a 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -1,5 +1,4 @@ /* - * $Id: cx88-blackbird.c,v 1.41 2006/01/11 19:28:02 mchehab Exp $ * * Support for a cx23416 mpeg encoder via cx2388x host port. * "blackbird" reference design. diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index c1bea5a20..839e91a98 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -1,5 +1,4 @@ /* - * $Id: cx88-cards.c,v 1.122 2006/01/29 20:28:54 mkrufky Exp $ * * device driver for Conexant 2388x based TV cards * card-specific stuff. diff --git a/linux/drivers/media/video/cx88/cx88-core.c b/linux/drivers/media/video/cx88/cx88-core.c index 9587434ca..b657894e8 100644 --- a/linux/drivers/media/video/cx88/cx88-core.c +++ b/linux/drivers/media/video/cx88/cx88-core.c @@ -1,5 +1,4 @@ /* - * $Id: cx88-core.c,v 1.54 2006/01/15 09:52:23 mchehab Exp $ * * device driver for Conexant 2388x based TV cards * driver core diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index 6b194c632..6d9417687 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -1,5 +1,4 @@ /* - * $Id: cx88-dvb.c,v 1.83 2006/01/11 19:28:02 mchehab Exp $ * * device driver for Conexant 2388x based TV cards * MPEG Transport Stream (DVB) routines diff --git a/linux/drivers/media/video/cx88/cx88-i2c.c b/linux/drivers/media/video/cx88/cx88-i2c.c index fb39ce10a..1e9bd01de 100644 --- a/linux/drivers/media/video/cx88/cx88-i2c.c +++ b/linux/drivers/media/video/cx88/cx88-i2c.c @@ -1,5 +1,4 @@ /* - $Id: cx88-i2c.c,v 1.39 2006/01/07 20:43:23 mchehab Exp $ cx88-i2c.c -- all the i2c code is here diff --git a/linux/drivers/media/video/cx88/cx88-input.c b/linux/drivers/media/video/cx88/cx88-input.c index 11d2e625a..bda5a9994 100644 --- a/linux/drivers/media/video/cx88/cx88-input.c +++ b/linux/drivers/media/video/cx88/cx88-input.c @@ -1,5 +1,4 @@ /* - * $Id: cx88-input.c,v 1.32 2006/01/18 20:21:47 nsh Exp $ * * Device driver for GPIO attached remote control interfaces * on Conexant 2388x based TV/DVB cards. diff --git a/linux/drivers/media/video/cx88/cx88-mpeg.c b/linux/drivers/media/video/cx88/cx88-mpeg.c index f24700f40..5b80d555a 100644 --- a/linux/drivers/media/video/cx88/cx88-mpeg.c +++ b/linux/drivers/media/video/cx88/cx88-mpeg.c @@ -1,5 +1,4 @@ /* - * $Id: cx88-mpeg.c,v 1.42 2005/12/11 18:11:56 mchehab Exp $ * * Support for the mpeg transport stream transfers * PCI function #2 of the cx2388x. diff --git a/linux/drivers/media/video/cx88/cx88-reg.h b/linux/drivers/media/video/cx88/cx88-reg.h index 10eff3e6d..c8c2d9957 100644 --- a/linux/drivers/media/video/cx88/cx88-reg.h +++ b/linux/drivers/media/video/cx88/cx88-reg.h @@ -1,5 +1,4 @@ /* - $Id: cx88-reg.h,v 1.10 2005/10/16 12:13:58 mchehab Exp $ cx88x-hw.h - CX2388x register offsets diff --git a/linux/drivers/media/video/cx88/cx88-tvaudio.c b/linux/drivers/media/video/cx88/cx88-tvaudio.c index 8bd458f61..68c157427 100644 --- a/linux/drivers/media/video/cx88/cx88-tvaudio.c +++ b/linux/drivers/media/video/cx88/cx88-tvaudio.c @@ -1,5 +1,4 @@ /* - $Id: cx88-tvaudio.c,v 1.53 2006/01/15 09:01:54 mchehab Exp $ cx88x-audio.c - Conexant CX23880/23881 audio downstream driver driver diff --git a/linux/drivers/media/video/cx88/cx88-vbi.c b/linux/drivers/media/video/cx88/cx88-vbi.c index bb5ae86c8..749187d69 100644 --- a/linux/drivers/media/video/cx88/cx88-vbi.c +++ b/linux/drivers/media/video/cx88/cx88-vbi.c @@ -1,5 +1,4 @@ /* - * $Id: cx88-vbi.c,v 1.18 2005/07/15 21:44:14 mchehab Exp $ */ #include #include diff --git a/linux/drivers/media/video/cx88/cx88-video.c b/linux/drivers/media/video/cx88/cx88-video.c index 1bcd20d23..e17c91b4c 100644 --- a/linux/drivers/media/video/cx88/cx88-video.c +++ b/linux/drivers/media/video/cx88/cx88-video.c @@ -1,5 +1,4 @@ /* - * $Id: cx88-video.c,v 1.105 2006/01/11 19:28:02 mchehab Exp $ * * device driver for Conexant 2388x based TV cards * video4linux video interface diff --git a/linux/drivers/media/video/cx88/cx88-vp3054-i2c.c b/linux/drivers/media/video/cx88/cx88-vp3054-i2c.c index 6d5536600..504a02215 100644 --- a/linux/drivers/media/video/cx88/cx88-vp3054-i2c.c +++ b/linux/drivers/media/video/cx88/cx88-vp3054-i2c.c @@ -1,5 +1,4 @@ /* - $Id: cx88-vp3054-i2c.c,v 1.4 2006/01/12 13:28:34 mchehab Exp $ cx88-vp3054-i2c.c -- support for the secondary I2C bus of the DNTV Live! DVB-T Pro (VP-3054), wired as: diff --git a/linux/drivers/media/video/cx88/cx88-vp3054-i2c.h b/linux/drivers/media/video/cx88/cx88-vp3054-i2c.h index 8d9a5c654..b7a0a04d2 100644 --- a/linux/drivers/media/video/cx88/cx88-vp3054-i2c.h +++ b/linux/drivers/media/video/cx88/cx88-vp3054-i2c.h @@ -1,5 +1,4 @@ /* - $Id: cx88-vp3054-i2c.h,v 1.2 2005/12/23 13:37:10 mchehab Exp $ cx88-vp3054-i2c.h -- support for the secondary I2C bus of the DNTV Live! DVB-T Pro (VP-3054), wired as: diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h index 2f8f4fd12..aad9260e4 100644 --- a/linux/drivers/media/video/cx88/cx88.h +++ b/linux/drivers/media/video/cx88/cx88.h @@ -1,5 +1,4 @@ /* - * $Id: cx88.h,v 1.99 2006/01/29 20:28:54 mkrufky Exp $ * * v4l2 device driver for cx2388x based TV cards * diff --git a/linux/drivers/media/video/ir-kbd-i2c.c b/linux/drivers/media/video/ir-kbd-i2c.c index 15ce0ff08..049b2cf1c 100644 --- a/linux/drivers/media/video/ir-kbd-i2c.c +++ b/linux/drivers/media/video/ir-kbd-i2c.c @@ -1,5 +1,4 @@ /* - * $Id: ir-kbd-i2c.c,v 1.36 2006/01/17 21:54:41 rmcc Exp $ * * keyboard input driver for i2c IR remote controls * diff --git a/linux/drivers/media/video/msp3400-driver.h b/linux/drivers/media/video/msp3400-driver.h index 3e26d7b4d..eeaafc5b4 100644 --- a/linux/drivers/media/video/msp3400-driver.h +++ b/linux/drivers/media/video/msp3400-driver.h @@ -1,5 +1,4 @@ /* - * $Id: msp3400.h,v 1.11 2006/01/22 13:08:51 hverkuil Exp $ */ #ifndef MSP3400_DRIVER_H diff --git a/linux/drivers/media/video/mt20xx.c b/linux/drivers/media/video/mt20xx.c index 9a0b05575..140eed9a6 100644 --- a/linux/drivers/media/video/mt20xx.c +++ b/linux/drivers/media/video/mt20xx.c @@ -1,5 +1,4 @@ /* - * $Id: mt20xx.c,v 1.15 2006/01/15 17:04:52 hverkuil Exp $ * * i2c tv tuner chip device driver * controls microtune tuners, mt2032 + mt2050 at the moment. diff --git a/linux/drivers/media/video/saa7134/saa7134-alsa.c b/linux/drivers/media/video/saa7134/saa7134-alsa.c index 54fc877ba..fefcce8f6 100644 --- a/linux/drivers/media/video/saa7134/saa7134-alsa.c +++ b/linux/drivers/media/video/saa7134/saa7134-alsa.c @@ -1,7 +1,6 @@ /* * SAA713x ALSA support for V4L * - * $Id: saa7134-alsa.c,v 1.34 2005/12/26 15:14:45 mchehab Exp $ * * Caveats: * - Volume doesn't work (it's always at max) diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index 20d0327c4..3629aee32 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -1,5 +1,4 @@ /* - * $Id: saa7134-cards.c,v 1.124 2006/01/19 15:33:09 nsh Exp $ * * device driver for philips saa7134 based TV cards * card-specific stuff. diff --git a/linux/drivers/media/video/saa7134/saa7134-core.c b/linux/drivers/media/video/saa7134/saa7134-core.c index 1be908a46..e3433d894 100644 --- a/linux/drivers/media/video/saa7134/saa7134-core.c +++ b/linux/drivers/media/video/saa7134/saa7134-core.c @@ -1,5 +1,4 @@ /* - * $Id: saa7134-core.c,v 1.64 2006/01/15 09:52:23 mchehab Exp $ * * device driver for philips saa7134 based TV cards * driver core diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index fe7ca962a..985ef0422 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -1,5 +1,4 @@ /* - * $Id: saa7134-dvb.c,v 1.38 2006/01/19 15:33:09 nsh Exp $ * * (c) 2004 Gerd Knorr [SuSE Labs] * diff --git a/linux/drivers/media/video/saa7134/saa7134-empress.c b/linux/drivers/media/video/saa7134/saa7134-empress.c index 512c6173d..5c311210e 100644 --- a/linux/drivers/media/video/saa7134/saa7134-empress.c +++ b/linux/drivers/media/video/saa7134/saa7134-empress.c @@ -1,5 +1,4 @@ /* - * $Id: saa7134-empress.c,v 1.16 2006/01/01 17:17:39 mchehab Exp $ * * (c) 2004 Gerd Knorr [SuSE Labs] * diff --git a/linux/drivers/media/video/saa7134/saa7134-i2c.c b/linux/drivers/media/video/saa7134/saa7134-i2c.c index 1f9b91b21..f139f4af1 100644 --- a/linux/drivers/media/video/saa7134/saa7134-i2c.c +++ b/linux/drivers/media/video/saa7134/saa7134-i2c.c @@ -1,5 +1,4 @@ /* - * $Id: saa7134-i2c.c,v 1.29 2006/01/07 20:43:23 mchehab Exp $ * * device driver for philips saa7134 based TV cards * i2c interface support diff --git a/linux/drivers/media/video/saa7134/saa7134-input.c b/linux/drivers/media/video/saa7134/saa7134-input.c index 0543788bf..c06e42ca4 100644 --- a/linux/drivers/media/video/saa7134/saa7134-input.c +++ b/linux/drivers/media/video/saa7134/saa7134-input.c @@ -1,5 +1,4 @@ /* - * $Id: saa7134-input.c,v 1.54 2006/01/18 20:41:55 nsh Exp $ * * handle saa7134 IR remotes via linux kernel input layer. * diff --git a/linux/drivers/media/video/saa7134/saa7134-oss.c b/linux/drivers/media/video/saa7134/saa7134-oss.c index a88f8b5bb..c14f0975f 100644 --- a/linux/drivers/media/video/saa7134/saa7134-oss.c +++ b/linux/drivers/media/video/saa7134/saa7134-oss.c @@ -1,5 +1,4 @@ /* - * $Id: saa7134-oss.c,v 1.35 2006/01/01 17:17:39 mchehab Exp $ * * device driver for philips saa7134 based TV cards * oss dsp interface diff --git a/linux/drivers/media/video/saa7134/saa7134-reg.h b/linux/drivers/media/video/saa7134/saa7134-reg.h index c75936937..ac6431ba4 100644 --- a/linux/drivers/media/video/saa7134/saa7134-reg.h +++ b/linux/drivers/media/video/saa7134/saa7134-reg.h @@ -1,5 +1,4 @@ /* - * $Id: saa7134-reg.h,v 1.5 2005/10/16 12:13:58 mchehab Exp $ * * philips saa7134 registers */ diff --git a/linux/drivers/media/video/saa7134/saa7134-ts.c b/linux/drivers/media/video/saa7134/saa7134-ts.c index 1f9c2fe98..46413ebb0 100644 --- a/linux/drivers/media/video/saa7134/saa7134-ts.c +++ b/linux/drivers/media/video/saa7134/saa7134-ts.c @@ -1,5 +1,4 @@ /* - * $Id: saa7134-ts.c,v 1.19 2005/10/16 12:13:58 mchehab Exp $ * * device driver for philips saa7134 based TV cards * video4linux video interface diff --git a/linux/drivers/media/video/saa7134/saa7134-tvaudio.c b/linux/drivers/media/video/saa7134/saa7134-tvaudio.c index c750866b9..981fcbd77 100644 --- a/linux/drivers/media/video/saa7134/saa7134-tvaudio.c +++ b/linux/drivers/media/video/saa7134/saa7134-tvaudio.c @@ -1,5 +1,4 @@ /* - * $Id: saa7134-tvaudio.c,v 1.37 2006/01/14 23:26:44 hhackmann Exp $ * * device driver for philips saa7134 based TV cards * tv audio decoder (fm stereo, nicam, ...) diff --git a/linux/drivers/media/video/saa7134/saa7134-vbi.c b/linux/drivers/media/video/saa7134/saa7134-vbi.c index 44909bd1f..1a2ab1f88 100644 --- a/linux/drivers/media/video/saa7134/saa7134-vbi.c +++ b/linux/drivers/media/video/saa7134/saa7134-vbi.c @@ -1,5 +1,4 @@ /* - * $Id: saa7134-vbi.c,v 1.8 2005/07/15 21:44:14 mchehab Exp $ * * device driver for philips saa7134 based TV cards * video4linux video interface diff --git a/linux/drivers/media/video/saa7134/saa7134-video.c b/linux/drivers/media/video/saa7134/saa7134-video.c index 3bd76fe3a..8f5e4ec39 100644 --- a/linux/drivers/media/video/saa7134/saa7134-video.c +++ b/linux/drivers/media/video/saa7134/saa7134-video.c @@ -1,5 +1,4 @@ /* - * $Id: saa7134-video.c,v 1.50 2006/01/11 19:28:02 mchehab Exp $ * * device driver for philips saa7134 based TV cards * video4linux video interface diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index 8600c57b1..21562ed12 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -1,5 +1,4 @@ /* - * $Id: saa7134.h,v 1.89 2006/01/19 15:33:09 nsh Exp $ * * v4l2 device driver for philips saa7134 based TV cards * diff --git a/linux/drivers/media/video/tda8290.c b/linux/drivers/media/video/tda8290.c index c0f965b03..a6d238b73 100644 --- a/linux/drivers/media/video/tda8290.c +++ b/linux/drivers/media/video/tda8290.c @@ -1,5 +1,4 @@ /* - $Id: tda8290.c,v 1.33 2006/01/15 17:04:52 hverkuil Exp $ i2c tv tuner chip device driver controls the philips tda8290+75 tuner chip combo. diff --git a/linux/drivers/media/video/tea5767.c b/linux/drivers/media/video/tea5767.c index 43fe70fec..c537c90d4 100644 --- a/linux/drivers/media/video/tea5767.c +++ b/linux/drivers/media/video/tea5767.c @@ -2,7 +2,6 @@ * For Philips TEA5767 FM Chip used on some TV Cards like Prolink Pixelview * I2C address is allways 0xC0. * - * $Id: tea5767.c,v 1.36 2006/01/15 17:04:52 hverkuil Exp $ * * Copyright (c) 2005 Mauro Carvalho Chehab (mchehab@infradead.org) * This code is placed under the terms of the GNU General Public License diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index 35ba614c0..689f7b043 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -1,5 +1,4 @@ /* - * $Id: tuner-core.c,v 1.102 2006/01/23 02:16:19 mrechberger Exp $ * * i2c tv tuner chip device driver * core core, i.e. kernel interfaces, registering and so on diff --git a/linux/drivers/media/video/tuner-simple.c b/linux/drivers/media/video/tuner-simple.c index 57db5b1aa..0b82156ed 100644 --- a/linux/drivers/media/video/tuner-simple.c +++ b/linux/drivers/media/video/tuner-simple.c @@ -1,5 +1,4 @@ /* - * $Id: tuner-simple.c,v 1.73 2006/01/23 04:28:12 mkrufky Exp $ * * i2c tv tuner chip device driver * controls all those simple 4-control-bytes style tuners. diff --git a/linux/drivers/media/video/tuner-types.c b/linux/drivers/media/video/tuner-types.c index 560879ce9..409603c38 100644 --- a/linux/drivers/media/video/tuner-types.c +++ b/linux/drivers/media/video/tuner-types.c @@ -1,5 +1,4 @@ /* - * $Id: tuner-types.c,v 1.6 2006/01/23 05:38:16 mkrufky Exp $ * * i2c tv tuner chip device type database. * diff --git a/linux/drivers/media/video/tvmixer.c b/linux/drivers/media/video/tvmixer.c index 54927dd1b..3b59920ac 100644 --- a/linux/drivers/media/video/tvmixer.c +++ b/linux/drivers/media/video/tvmixer.c @@ -1,5 +1,4 @@ /* - * $Id: tvmixer.c,v 1.14 2006/01/08 12:05:34 mchehab Exp $ */ #include diff --git a/linux/drivers/media/video/v4l1-compat.c b/linux/drivers/media/video/v4l1-compat.c index ad94f3f26..ea5936087 100644 --- a/linux/drivers/media/video/v4l1-compat.c +++ b/linux/drivers/media/video/v4l1-compat.c @@ -1,5 +1,4 @@ /* - * $Id: v4l1-compat.c,v 1.16 2006/01/11 19:28:02 mchehab Exp $ * * Video for Linux Two * Backward Compatibility Layer diff --git a/linux/drivers/media/video/video-buf-dvb.c b/linux/drivers/media/video/video-buf-dvb.c index 1b9968a19..90d64d5a9 100644 --- a/linux/drivers/media/video/video-buf-dvb.c +++ b/linux/drivers/media/video/video-buf-dvb.c @@ -1,5 +1,4 @@ /* - * $Id: video-buf-dvb.c,v 1.13 2005/10/09 18:07:06 mchehab Exp $ * * some helper function for simple DVB cards which simply DMA the * complete transport stream and let the computer sort everything else diff --git a/linux/drivers/media/video/video-buf.c b/linux/drivers/media/video/video-buf.c index 4ae4e1e7a..05532ee28 100644 --- a/linux/drivers/media/video/video-buf.c +++ b/linux/drivers/media/video/video-buf.c @@ -1,5 +1,4 @@ /* - * $Id: video-buf.c,v 1.25 2006/01/11 19:28:02 mchehab Exp $ * * generic helper functions for video4linux capture buffers, to handle * memory management and PCI DMA. -- cgit v1.2.3 From 24bda047c0558518cdd4845894af9a62fe0cfe36 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Apr 2006 17:55:53 -0300 Subject: Merge: from V4L1 tree From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/Kconfig | 45 +- linux/drivers/media/common/Kconfig | 1 + linux/drivers/media/dvb/ttpci/Kconfig | 12 +- linux/drivers/media/radio/Kconfig | 30 +- linux/drivers/media/video/Kconfig | 78 +- linux/drivers/media/video/Makefile | 5 +- linux/drivers/media/video/bt8xx/Kconfig | 2 +- linux/drivers/media/video/cx88/cx88-video.c | 2 +- linux/drivers/media/video/em28xx/Kconfig | 2 +- linux/drivers/media/video/et61x251/Kconfig | 14 + linux/drivers/media/video/et61x251/Makefile | 4 + linux/drivers/media/video/et61x251/et61x251.h | 235 ++ linux/drivers/media/video/et61x251/et61x251_core.c | 2630 +++++++++++++ .../drivers/media/video/et61x251/et61x251_sensor.h | 117 + .../media/video/et61x251/et61x251_tas5130d1b.c | 141 + linux/drivers/media/video/ovcamchip/Makefile | 4 + linux/drivers/media/video/ovcamchip/ov6x20.c | 414 +++ linux/drivers/media/video/ovcamchip/ov6x30.c | 373 ++ linux/drivers/media/video/ovcamchip/ov76be.c | 302 ++ linux/drivers/media/video/ovcamchip/ov7x10.c | 334 ++ linux/drivers/media/video/ovcamchip/ov7x20.c | 454 +++ .../drivers/media/video/ovcamchip/ovcamchip_core.c | 443 +++ .../drivers/media/video/ovcamchip/ovcamchip_priv.h | 87 + linux/drivers/media/video/pvrusb2/Kconfig | 2 +- linux/drivers/media/video/pvrusb2/pvrusb2-audio.c | 2 +- linux/drivers/media/video/pvrusb2/pvrusb2-demod.c | 2 +- .../media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c | 2 +- linux/drivers/media/video/pvrusb2/pvrusb2-main.c | 2 +- linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c | 2 +- linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 2 +- .../media/video/pvrusb2/pvrusb2-video-v4l.c | 2 +- linux/drivers/media/video/pwc/Kconfig | 28 + linux/drivers/media/video/pwc/Makefile | 3 + linux/drivers/media/video/pwc/philips.txt | 236 ++ linux/drivers/media/video/pwc/pwc-ctrl.c | 1541 ++++++++ linux/drivers/media/video/pwc/pwc-if.c | 2205 +++++++++++ linux/drivers/media/video/pwc/pwc-ioctl.h | 292 ++ linux/drivers/media/video/pwc/pwc-kiara.c | 318 ++ linux/drivers/media/video/pwc/pwc-kiara.h | 45 + linux/drivers/media/video/pwc/pwc-misc.c | 140 + linux/drivers/media/video/pwc/pwc-nala.h | 66 + linux/drivers/media/video/pwc/pwc-timon.c | 316 ++ linux/drivers/media/video/pwc/pwc-timon.h | 61 + linux/drivers/media/video/pwc/pwc-uncompress.c | 146 + linux/drivers/media/video/pwc/pwc-uncompress.h | 41 + linux/drivers/media/video/pwc/pwc.h | 273 ++ linux/drivers/media/video/saa7134/saa7134-video.c | 2 +- linux/drivers/media/video/sn9c102/Kconfig | 11 + linux/drivers/media/video/sn9c102/Makefile | 7 + linux/drivers/media/video/sn9c102/sn9c102.h | 219 ++ linux/drivers/media/video/sn9c102/sn9c102_core.c | 2919 +++++++++++++++ .../drivers/media/video/sn9c102/sn9c102_hv7131d.c | 271 ++ linux/drivers/media/video/sn9c102/sn9c102_mi0343.c | 363 ++ linux/drivers/media/video/sn9c102/sn9c102_ov7630.c | 401 ++ .../drivers/media/video/sn9c102/sn9c102_pas106b.c | 307 ++ .../media/video/sn9c102/sn9c102_pas202bca.c | 238 ++ .../media/video/sn9c102/sn9c102_pas202bcb.c | 293 ++ linux/drivers/media/video/sn9c102/sn9c102_sensor.h | 390 ++ .../media/video/sn9c102/sn9c102_tas5110c1b.c | 159 + .../media/video/sn9c102/sn9c102_tas5130d1b.c | 169 + linux/drivers/media/video/usbvideo/Kconfig | 38 + linux/drivers/media/video/usbvideo/Makefile | 4 + linux/drivers/media/video/usbvideo/ibmcam.c | 3932 ++++++++++++++++++++ linux/drivers/media/video/usbvideo/konicawc.c | 978 +++++ linux/drivers/media/video/usbvideo/ultracam.c | 679 ++++ linux/drivers/media/video/usbvideo/usbvideo.c | 2190 +++++++++++ linux/drivers/media/video/usbvideo/usbvideo.h | 395 ++ linux/drivers/media/video/usbvideo/vicam.c | 1412 +++++++ linux/drivers/media/video/vivi.c | 3 +- linux/drivers/media/video/zc0301/Kconfig | 11 + linux/drivers/media/video/zc0301/Makefile | 3 + linux/drivers/media/video/zc0301/zc0301.h | 193 + linux/drivers/media/video/zc0301/zc0301_core.c | 2055 ++++++++++ .../drivers/media/video/zc0301/zc0301_pas202bcb.c | 361 ++ linux/drivers/media/video/zc0301/zc0301_sensor.h | 104 + 75 files changed, 29488 insertions(+), 75 deletions(-) create mode 100644 linux/drivers/media/video/et61x251/Kconfig create mode 100644 linux/drivers/media/video/et61x251/Makefile create mode 100644 linux/drivers/media/video/et61x251/et61x251.h create mode 100644 linux/drivers/media/video/et61x251/et61x251_core.c create mode 100644 linux/drivers/media/video/et61x251/et61x251_sensor.h create mode 100644 linux/drivers/media/video/et61x251/et61x251_tas5130d1b.c create mode 100644 linux/drivers/media/video/ovcamchip/Makefile create mode 100644 linux/drivers/media/video/ovcamchip/ov6x20.c create mode 100644 linux/drivers/media/video/ovcamchip/ov6x30.c create mode 100644 linux/drivers/media/video/ovcamchip/ov76be.c create mode 100644 linux/drivers/media/video/ovcamchip/ov7x10.c create mode 100644 linux/drivers/media/video/ovcamchip/ov7x20.c create mode 100644 linux/drivers/media/video/ovcamchip/ovcamchip_core.c create mode 100644 linux/drivers/media/video/ovcamchip/ovcamchip_priv.h create mode 100644 linux/drivers/media/video/pwc/Kconfig create mode 100644 linux/drivers/media/video/pwc/Makefile create mode 100644 linux/drivers/media/video/pwc/philips.txt create mode 100644 linux/drivers/media/video/pwc/pwc-ctrl.c create mode 100644 linux/drivers/media/video/pwc/pwc-if.c create mode 100644 linux/drivers/media/video/pwc/pwc-ioctl.h create mode 100644 linux/drivers/media/video/pwc/pwc-kiara.c create mode 100644 linux/drivers/media/video/pwc/pwc-kiara.h create mode 100644 linux/drivers/media/video/pwc/pwc-misc.c create mode 100644 linux/drivers/media/video/pwc/pwc-nala.h create mode 100644 linux/drivers/media/video/pwc/pwc-timon.c create mode 100644 linux/drivers/media/video/pwc/pwc-timon.h create mode 100644 linux/drivers/media/video/pwc/pwc-uncompress.c create mode 100644 linux/drivers/media/video/pwc/pwc-uncompress.h create mode 100644 linux/drivers/media/video/pwc/pwc.h create mode 100644 linux/drivers/media/video/sn9c102/Kconfig create mode 100644 linux/drivers/media/video/sn9c102/Makefile create mode 100644 linux/drivers/media/video/sn9c102/sn9c102.h create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_core.c create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_hv7131d.c create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_mi0343.c create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_ov7630.c create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_pas106b.c create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_pas202bca.c create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_pas202bcb.c create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_sensor.h create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c create mode 100644 linux/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c create mode 100644 linux/drivers/media/video/usbvideo/Kconfig create mode 100644 linux/drivers/media/video/usbvideo/Makefile create mode 100644 linux/drivers/media/video/usbvideo/ibmcam.c create mode 100644 linux/drivers/media/video/usbvideo/konicawc.c create mode 100644 linux/drivers/media/video/usbvideo/ultracam.c create mode 100644 linux/drivers/media/video/usbvideo/usbvideo.c create mode 100644 linux/drivers/media/video/usbvideo/usbvideo.h create mode 100644 linux/drivers/media/video/usbvideo/vicam.c create mode 100644 linux/drivers/media/video/zc0301/Kconfig create mode 100644 linux/drivers/media/video/zc0301/Makefile create mode 100644 linux/drivers/media/video/zc0301/zc0301.h create mode 100644 linux/drivers/media/video/zc0301/zc0301_core.c create mode 100644 linux/drivers/media/video/zc0301/zc0301_pas202bcb.c create mode 100644 linux/drivers/media/video/zc0301/zc0301_sensor.h (limited to 'linux/drivers') diff --git a/linux/drivers/media/Kconfig b/linux/drivers/media/Kconfig index fffc711c2..344d83aae 100644 --- a/linux/drivers/media/Kconfig +++ b/linux/drivers/media/Kconfig @@ -8,22 +8,54 @@ config VIDEO_DEV tristate "Video For Linux" ---help--- Support for audio/video capture and overlay devices and FM radio - cards. The exact capabilities of each device vary. User tools for - this are available from - . + cards. The exact capabilities of each device vary. This kernel includes support for the new Video for Linux Two API, (V4L2) as well as the original system. Drivers and applications need to be rewritten to use V4L2, but drivers for popular cards and applications for most video capture functions already exist. - Documentation for the original API is included in the file - . Documentation for V4L2 is - available on the web at . + Additional info and docs are available on the web at + + + Documentation for V4L2 is also available on the web at + . To compile this driver as a module, choose M here: the module will be called videodev. +config VIDEO_V4L1 + boolean "Enable Video For Linux API 1 (DEPRECATED)" + depends on VIDEO_DEV + select VIDEO_V4L1_COMPAT + default y + ---help--- + Enables a compatibility API used by most V4L2 devices to allow + its usage with legacy applications that supports only V4L1 api. + + If you are unsure as to whether this is required, answer Y. + +config VIDEO_V4L1_COMPAT + boolean "Enable Video For Linux API 1 compatible Layer" + depends on VIDEO_DEV + default y + ---help--- + This api were developed to be used at Kernel 2.2 and 2.4, but + lacks support for several video standards. There are several + drivers at kernel that still depends on it. + + Documentation for the original API is included in the file + . + + User tools for this are available from + . + + If you are unsure as to whether this is required, answer Y. + +config VIDEO_V4L2 + tristate + default y + source "drivers/media/video/Kconfig" source "drivers/media/radio/Kconfig" @@ -65,4 +97,3 @@ config USB_DABUSB module will be called dabusb. endmenu - diff --git a/linux/drivers/media/common/Kconfig b/linux/drivers/media/common/Kconfig index 6a901a026..9c45b983e 100644 --- a/linux/drivers/media/common/Kconfig +++ b/linux/drivers/media/common/Kconfig @@ -4,6 +4,7 @@ config VIDEO_SAA7146 config VIDEO_SAA7146_VV tristate + select VIDEO_V4L2 select VIDEO_BUF select VIDEO_VIDEOBUF select VIDEO_SAA7146 diff --git a/linux/drivers/media/dvb/ttpci/Kconfig b/linux/drivers/media/dvb/ttpci/Kconfig index 5b2aadb83..c26e23291 100644 --- a/linux/drivers/media/dvb/ttpci/Kconfig +++ b/linux/drivers/media/dvb/ttpci/Kconfig @@ -1,8 +1,7 @@ config DVB_AV7110 tristate "AV7110 cards" - depends on DVB_CORE && PCI + depends on DVB_CORE && PCI && VIDEO_V4L1 select FW_LOADER - select VIDEO_DEV select VIDEO_SAA7146_VV select DVB_VES1820 select DVB_VES1X93 @@ -59,7 +58,7 @@ config DVB_AV7110_OSD config DVB_BUDGET tristate "Budget cards" - depends on DVB_CORE && PCI + depends on DVB_CORE && PCI && VIDEO_V4L1 select VIDEO_SAA7146 select DVB_STV0299 select DVB_VES1X93 @@ -80,7 +79,7 @@ config DVB_BUDGET config DVB_BUDGET_CI tristate "Budget cards with onboard CI connector" - depends on DVB_CORE && PCI + depends on DVB_CORE && PCI && VIDEO_V4L1 select VIDEO_SAA7146 select DVB_STV0297 select DVB_STV0299 @@ -100,8 +99,7 @@ config DVB_BUDGET_CI config DVB_BUDGET_AV tristate "Budget cards with analog video inputs" - depends on DVB_CORE && PCI - select VIDEO_DEV + depends on DVB_CORE && PCI && VIDEO_V4L1 select VIDEO_SAA7146_VV select DVB_STV0299 select DVB_TDA1004X @@ -119,7 +117,7 @@ config DVB_BUDGET_AV config DVB_BUDGET_PATCH tristate "AV7110 cards with Budget Patch" - depends on DVB_CORE && DVB_BUDGET + depends on DVB_CORE && DVB_BUDGET && VIDEO_V4L1 select DVB_AV7110 select DVB_STV0299 select DVB_VES1X93 diff --git a/linux/drivers/media/radio/Kconfig b/linux/drivers/media/radio/Kconfig index c65bbc5fd..de3128a31 100644 --- a/linux/drivers/media/radio/Kconfig +++ b/linux/drivers/media/radio/Kconfig @@ -7,7 +7,7 @@ menu "Radio Adapters" config RADIO_CADET tristate "ADS Cadet AM/FM Tuner" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these AM/FM radio cards, and then fill in the port address below. @@ -25,7 +25,7 @@ config RADIO_CADET config RADIO_RTRACK tristate "AIMSlab RadioTrack (aka RadioReveal) support" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address below. @@ -59,7 +59,7 @@ config RADIO_RTRACK_PORT config RADIO_RTRACK2 tristate "AIMSlab RadioTrack II support" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have this FM radio card, and then fill in the port address below. @@ -82,7 +82,7 @@ config RADIO_RTRACK2_PORT config RADIO_AZTECH tristate "Aztech/Packard Bell Radio" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address below. @@ -106,7 +106,7 @@ config RADIO_AZTECH_PORT config RADIO_GEMTEK tristate "GemTek Radio Card support" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have this FM radio card, and then fill in the port address below. @@ -131,7 +131,7 @@ config RADIO_GEMTEK_PORT config RADIO_GEMTEK_PCI tristate "GemTek PCI Radio Card support" - depends on VIDEO_DEV && PCI + depends on VIDEO_V4L1 && PCI ---help--- Choose Y here if you have this PCI FM radio card. @@ -145,7 +145,7 @@ config RADIO_GEMTEK_PCI config RADIO_MAXIRADIO tristate "Guillemot MAXI Radio FM 2000 radio" - depends on VIDEO_DEV && PCI + depends on VIDEO_V4L1 && PCI ---help--- Choose Y here if you have this radio card. This card may also be found as Gemtek PCI FM. @@ -160,7 +160,7 @@ config RADIO_MAXIRADIO config RADIO_MAESTRO tristate "Maestro on board radio" - depends on VIDEO_DEV + depends on VIDEO_V4L1 ---help--- Say Y here to directly support the on-board radio tuner on the Maestro 2 or 2E sound card. @@ -175,7 +175,7 @@ config RADIO_MAESTRO config RADIO_MIROPCM20 tristate "miroSOUND PCM20 radio" - depends on ISA && VIDEO_DEV && SOUND_ACI_MIXER + depends on ISA && VIDEO_V4L1 && SOUND_ACI_MIXER ---help--- Choose Y here if you have this FM radio card. You also need to say Y to "ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20 radio)" (in "Sound") @@ -208,7 +208,7 @@ config RADIO_MIROPCM20_RDS config RADIO_SF16FMI tristate "SF16FMI Radio" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these FM radio cards. If you compile the driver into the kernel and your card is not PnP one, you @@ -225,7 +225,7 @@ config RADIO_SF16FMI config RADIO_SF16FMR2 tristate "SF16FMR2 Radio" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these FM radio cards. @@ -239,7 +239,7 @@ config RADIO_SF16FMR2 config RADIO_TERRATEC tristate "TerraTec ActiveRadio ISA Standalone" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have this FM radio card, and then fill in the port address below. (TODO) @@ -268,7 +268,7 @@ config RADIO_TERRATEC_PORT config RADIO_TRUST tristate "Trust FM radio card" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 help This is a driver for the Trust FM radio cards. Say Y if you have such a card and want to use it under Linux. @@ -286,7 +286,7 @@ config RADIO_TRUST_PORT config RADIO_TYPHOON tristate "Typhoon Radio (a.k.a. EcoRadio)" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address and the frequency used for muting below. @@ -330,7 +330,7 @@ config RADIO_TYPHOON_MUTEFREQ config RADIO_ZOLTRIX tristate "Zoltrix Radio" - depends on ISA && VIDEO_DEV + depends on ISA && VIDEO_V4L1 ---help--- Choose Y here if you have one of these FM radio cards, and then fill in the port address below. diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index 98f7201cd..e4dd4181d 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -2,10 +2,10 @@ # Multimedia Video device configuration # -menu "Video For Linux" +menu "Video Capture Adapters" depends on VIDEO_DEV -comment "Video Adapters" +comment "Video Capture Adapters" config VIDEO_ADV_DEBUG bool "Enable advanced debug functionality" @@ -16,11 +16,22 @@ config VIDEO_ADV_DEBUG V4L devices. In doubt, say N. +config VIDEO_VIVI + tristate "Virtual Video Driver" + depends on VIDEO_V4L2 + default n + ---help--- + Enables a virtual video driver. This device shows a color bar + and a timestamp, as a real device would generate by using V4L2 + api. + Say Y here if you want to test video apps or debug V4L devices. + In doubt, say N. + source "drivers/media/video/bt8xx/Kconfig" config VIDEO_SAA6588 tristate "SAA6588 Radio Chip RDS decoder support on BT848 cards" - depends on VIDEO_DEV && I2C && VIDEO_BT848 + depends on I2C && VIDEO_BT848 help Support for Radio Data System (RDS) decoder. This allows seeing @@ -32,7 +43,7 @@ config VIDEO_SAA6588 config VIDEO_PMS tristate "Mediavision Pro Movie Studio Video For Linux" - depends on VIDEO_DEV && ISA + depends on ISA && VIDEO_V4L1 help Say Y if you have such a thing. @@ -41,7 +52,7 @@ config VIDEO_PMS config VIDEO_PLANB tristate "PlanB Video-In on PowerMac" - depends on PPC_PMAC && VIDEO_DEV && BROKEN + depends on PPC_PMAC && VIDEO_V4L1 && BROKEN help PlanB is the V4L driver for the PowerMac 7x00/8x00 series video input hardware. If you want to experiment with this, say Y. @@ -52,7 +63,7 @@ config VIDEO_PLANB config VIDEO_BWQCAM tristate "Quickcam BW Video For Linux" - depends on VIDEO_DEV && PARPORT + depends on PARPORT && VIDEO_V4L1 help Say Y have if you the black and white version of the QuickCam camera. See the next option for the color version. @@ -62,7 +73,7 @@ config VIDEO_BWQCAM config VIDEO_CQCAM tristate "QuickCam Colour Video For Linux (EXPERIMENTAL)" - depends on EXPERIMENTAL && VIDEO_DEV && PARPORT + depends on EXPERIMENTAL && PARPORT && VIDEO_V4L1 help This is the video4linux driver for the colour version of the Connectix QuickCam. If you have one of these cameras, say Y here, @@ -73,7 +84,7 @@ config VIDEO_CQCAM config VIDEO_W9966 tristate "W9966CF Webcam (FlyCam Supra and others) Video For Linux" - depends on PARPORT_1284 && VIDEO_DEV && PARPORT + depends on PARPORT_1284 && PARPORT && VIDEO_V4L1 help Video4linux driver for Winbond's w9966 based Webcams. Currently tested with the LifeView FlyCam Supra. @@ -86,7 +97,7 @@ config VIDEO_W9966 config VIDEO_CPIA tristate "CPiA Video For Linux" - depends on VIDEO_DEV + depends on VIDEO_V4L1 ---help--- This is the video4linux driver for cameras based on Vision's CPiA (Colour Processor Interface ASIC), such as the Creative Labs Video @@ -123,7 +134,7 @@ source "drivers/media/video/cpia2/Kconfig" config VIDEO_SAA5246A tristate "SAA5246A, SAA5281 Teletext processor" - depends on VIDEO_DEV && I2C + depends on I2C && VIDEO_V4L1 help Support for I2C bus based teletext using the SAA5246A or SAA5281 chip. Useful only if you live in Europe. @@ -150,7 +161,7 @@ config TUNER_3036 config VIDEO_VINO tristate "SGI Vino Video For Linux (EXPERIMENTAL)" - depends on VIDEO_DEV && I2C && SGI_IP22 && EXPERIMENTAL + depends on I2C && SGI_IP22 && EXPERIMENTAL && VIDEO_V4L1 select I2C_ALGO_SGI help Say Y here to build in support for the Vino video input system found @@ -158,7 +169,7 @@ config VIDEO_VINO config VIDEO_STRADIS tristate "Stradis 4:2:2 MPEG-2 video driver (EXPERIMENTAL)" - depends on EXPERIMENTAL && VIDEO_DEV && PCI + depends on EXPERIMENTAL && PCI && VIDEO_V4L1 help Say Y here to enable support for the Stradis 4:2:2 MPEG-2 video driver for PCI. There is a product page at @@ -166,7 +177,7 @@ config VIDEO_STRADIS config VIDEO_ZORAN tristate "Zoran ZR36057/36067 Video For Linux" - depends on VIDEO_DEV && PCI && I2C_ALGOBIT + depends on PCI && I2C_ALGOBIT && VIDEO_V4L1 help Say Y for support for MJPEG capture cards based on the Zoran 36057/36067 PCI controller chipset. This includes the Iomega @@ -214,7 +225,7 @@ config VIDEO_ZORAN_LML33R10 config VIDEO_ZR36120 tristate "Zoran ZR36120/36125 Video For Linux" - depends on VIDEO_DEV && PCI && I2C && BROKEN + depends on PCI && I2C && VIDEO_V4L1 && BROKEN help Support for ZR36120/ZR36125 based frame grabber/overlay boards. This includes the Victor II, WaveWatcher, Video Wonder, Maxi-TV, @@ -226,7 +237,7 @@ config VIDEO_ZR36120 config VIDEO_MEYE tristate "Sony Vaio Picturebook Motion Eye Video For Linux" - depends on VIDEO_DEV && PCI && SONYPI + depends on PCI && SONYPI && VIDEO_V4L1 ---help--- This is the video4linux driver for the Motion Eye camera found in the Vaio Picturebook laptops. Please read the material in @@ -242,7 +253,7 @@ source "drivers/media/video/saa7134/Kconfig" config VIDEO_MXB tristate "Siemens-Nixdorf 'Multimedia eXtension Board'" - depends on VIDEO_DEV && PCI + depends on PCI && VIDEO_V4L1 select VIDEO_SAA7146_VV select VIDEO_TUNER ---help--- @@ -254,8 +265,9 @@ config VIDEO_MXB config VIDEO_DPC tristate "Philips-Semiconductors 'dpc7146 demonstration board'" - depends on VIDEO_DEV && PCI + depends on PCI && VIDEO_V4L1 select VIDEO_SAA7146_VV + select VIDEO_V4L2 ---help--- This is a video4linux driver for the 'dpc7146 demonstration board' by Philips-Semiconductors. It's the reference design @@ -268,8 +280,9 @@ config VIDEO_DPC config VIDEO_HEXIUM_ORION tristate "Hexium HV-PCI6 and Orion frame grabber" - depends on VIDEO_DEV && PCI + depends on PCI && VIDEO_V4L1 select VIDEO_SAA7146_VV + select VIDEO_V4L2 ---help--- This is a video4linux driver for the Hexium HV-PCI6 and Orion frame grabber cards by Hexium. @@ -279,8 +292,9 @@ config VIDEO_HEXIUM_ORION config VIDEO_HEXIUM_GEMINI tristate "Hexium Gemini frame grabber" - depends on VIDEO_DEV && PCI + depends on PCI && VIDEO_V4L1 select VIDEO_SAA7146_VV + select VIDEO_V4L2 ---help--- This is a video4linux driver for the Hexium Gemini frame grabber card by Hexium. Please note that the Gemini Dual @@ -293,7 +307,7 @@ source "drivers/media/video/cx88/Kconfig" config VIDEO_OVCAMCHIP tristate "OmniVision Camera Chip support" - depends on VIDEO_DEV && I2C + depends on I2C && VIDEO_V4L1 ---help--- Support for the OmniVision OV6xxx and OV7xxx series of camera chips. This driver is intended to be used with the ov511 and w9968cf USB @@ -304,7 +318,7 @@ config VIDEO_OVCAMCHIP config VIDEO_M32R_AR tristate "AR devices" - depends on M32R + depends on M32R && VIDEO_V4L1 ---help--- This is a video4linux driver for the Renesas AR (Artificial Retina) camera module. @@ -365,17 +379,17 @@ config VIDEO_WM8739 source "drivers/media/video/cx25840/Kconfig" config VIDEO_SAA711X - tristate "Philips SAA7113/4/5 video decoders" - depends on VIDEO_DEV && I2C && EXPERIMENTAL + tristate "Philips SAA7113/4/5 video decoders (OBSOLETED)" + depends on VIDEO_V4L1 && I2C && EXPERIMENTAL ---help--- - Support for the Philips SAA7113/4/5 video decoders. + Old support for the Philips SAA7113/4 video decoders. To compile this driver as a module, choose M here: the module will be called saa7115. config VIDEO_SAA7127 tristate "Philips SAA7127/9 digital video encoders" - depends on VIDEO_DEV && I2C && EXPERIMENTAL + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL ---help--- Support for the Philips SAA7127/9 digital video encoders. @@ -384,7 +398,7 @@ config VIDEO_SAA7127 config VIDEO_UPD64031A tristate "NEC Electronics uPD64031A Ghost Reduction" - depends on VIDEO_DEV && I2C && EXPERIMENTAL + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL ---help--- Support for the NEC Electronics uPD64031A Ghost Reduction video chip. It is most often found in NTSC TV cards made for @@ -396,7 +410,7 @@ config VIDEO_UPD64031A config VIDEO_UPD64083 tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation" - depends on VIDEO_DEV && I2C && EXPERIMENTAL + depends on VIDEO_V4L2 && I2C && EXPERIMENTAL ---help--- Support for the NEC Electronics uPD64083 3-Dimensional Y/C separation video chip. It is used to improve the quality of @@ -420,7 +434,7 @@ source "drivers/media/video/em28xx/Kconfig" config USB_DSBR tristate "D-Link USB FM radio support (EXPERIMENTAL)" - depends on USB && VIDEO_DEV && EXPERIMENTAL + depends on USB && VIDEO_V4L1 && EXPERIMENTAL ---help--- Say Y here if you want to connect this type of radio to your computer's USB port. Note that the audio is not digital, and @@ -436,7 +450,7 @@ source "drivers/media/video/et61x251/Kconfig" config USB_OV511 tristate "USB OV511 Camera support" - depends on USB && VIDEO_DEV + depends on USB && VIDEO_V4L1 ---help--- Say Y here if you want to connect this type of camera to your computer's USB port. See @@ -447,7 +461,7 @@ config USB_OV511 config USB_SE401 tristate "USB SE401 Camera support" - depends on USB && VIDEO_DEV + depends on USB && VIDEO_V4L1 ---help--- Say Y here if you want to connect this type of camera to your computer's USB port. See @@ -460,7 +474,7 @@ source "drivers/media/video/sn9c102/Kconfig" config USB_STV680 tristate "USB STV680 (Pencam) Camera support" - depends on USB && VIDEO_DEV + depends on USB && VIDEO_V4L1 ---help--- Say Y here if you want to connect this type of camera to your computer's USB port. This includes the Pencam line of cameras. @@ -472,7 +486,7 @@ config USB_STV680 config USB_W9968CF tristate "USB W996[87]CF JPEG Dual Mode Camera support" - depends on USB && VIDEO_DEV && I2C + depends on USB && VIDEO_V4L1 && I2C select VIDEO_OVCAMCHIP ---help--- Say Y here if you want support for cameras based on OV681 or diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index bfd5bce8e..d61bc458f 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -10,7 +10,8 @@ tuner-objs := tuner-core.o tuner-types.o tuner-simple.o \ msp3400-objs := msp3400-driver.o msp3400-kthreads.o -obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o v4l1-compat.o compat_ioctl32.o +obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o compat_ioctl32.o +obj-$(CONFIG_VIDEO_V4L1_COMPAT) += v4l1-compat.o obj-$(CONFIG_VIDEO_BT848) += bt8xx/ obj-$(CONFIG_VIDEO_BT848) += tvaudio.o tda7432.o tda9875.o ir-kbd-i2c.o @@ -85,4 +86,6 @@ obj-$(CONFIG_USB_IBMCAM) += usbvideo/ obj-$(CONFIG_USB_KONICAWC) += usbvideo/ obj-$(CONFIG_USB_VICAM) += usbvideo/ +obj-$(CONFIG_VIDEO_VIVI) += vivi.o + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff --git a/linux/drivers/media/video/bt8xx/Kconfig b/linux/drivers/media/video/bt8xx/Kconfig index 085477c12..153f6a4a9 100644 --- a/linux/drivers/media/video/bt8xx/Kconfig +++ b/linux/drivers/media/video/bt8xx/Kconfig @@ -1,6 +1,6 @@ config VIDEO_BT848 tristate "BT848 Video For Linux" - depends on VIDEO_DEV && PCI && I2C + depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L2 select I2C_ALGOBIT select FW_LOADER select VIDEO_BTCX diff --git a/linux/drivers/media/video/cx88/cx88-video.c b/linux/drivers/media/video/cx88/cx88-video.c index e17c91b4c..9c2f0dd39 100644 --- a/linux/drivers/media/video/cx88/cx88-video.c +++ b/linux/drivers/media/video/cx88/cx88-video.c @@ -38,7 +38,7 @@ #include "cx88.h" #include -#if 1 +#ifdef CONFIG_VIDEO_V4L1_COMPAT /* Include V4L1 specific functions. Should be removed soon */ #include #endif diff --git a/linux/drivers/media/video/em28xx/Kconfig b/linux/drivers/media/video/em28xx/Kconfig index 5a793ae7c..dfb15bfb8 100644 --- a/linux/drivers/media/video/em28xx/Kconfig +++ b/linux/drivers/media/video/em28xx/Kconfig @@ -1,6 +1,6 @@ config VIDEO_EM28XX tristate "Empia EM2800/2820/2840 USB video capture support" - depends on VIDEO_DEV && USB && I2C + depends on VIDEO_V4L1 && USB && I2C select VIDEO_BUF select VIDEO_TUNER select VIDEO_TVEEPROM diff --git a/linux/drivers/media/video/et61x251/Kconfig b/linux/drivers/media/video/et61x251/Kconfig new file mode 100644 index 000000000..c6bff7056 --- /dev/null +++ b/linux/drivers/media/video/et61x251/Kconfig @@ -0,0 +1,14 @@ +config USB_ET61X251 + tristate "USB ET61X[12]51 PC Camera Controller support" + depends on USB && VIDEO_V4L1 + ---help--- + Say Y here if you want support for cameras based on Etoms ET61X151 + or ET61X251 PC Camera Controllers. + + See for more info. + + This driver uses the Video For Linux API. You must say Y or M to + "Video For Linux" to use this driver. + + To compile this driver as a module, choose M here: the + module will be called et61x251. diff --git a/linux/drivers/media/video/et61x251/Makefile b/linux/drivers/media/video/et61x251/Makefile new file mode 100644 index 000000000..2ff4db9ec --- /dev/null +++ b/linux/drivers/media/video/et61x251/Makefile @@ -0,0 +1,4 @@ +et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o + +obj-$(CONFIG_USB_ET61X251) += et61x251.o + diff --git a/linux/drivers/media/video/et61x251/et61x251.h b/linux/drivers/media/video/et61x251/et61x251.h new file mode 100644 index 000000000..3ac2f508e --- /dev/null +++ b/linux/drivers/media/video/et61x251/et61x251.h @@ -0,0 +1,235 @@ +/*************************************************************************** + * V4L2 driver for ET61X[12]51 PC Camera Controllers * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _ET61X251_H_ +#define _ET61X251_H_ + +#include +#include +#include "compat.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "et61x251_sensor.h" + +/*****************************************************************************/ + +#define ET61X251_DEBUG +#define ET61X251_DEBUG_LEVEL 2 +#define ET61X251_MAX_DEVICES 64 +#define ET61X251_PRESERVE_IMGSCALE 0 +#define ET61X251_FORCE_MUNMAP 0 +#define ET61X251_MAX_FRAMES 32 +#define ET61X251_COMPRESSION_QUALITY 0 +#define ET61X251_URBS 2 +#define ET61X251_ISO_PACKETS 7 +#define ET61X251_ALTERNATE_SETTING 13 +#define ET61X251_URB_TIMEOUT msecs_to_jiffies(2 * ET61X251_ISO_PACKETS) +#define ET61X251_CTRL_TIMEOUT 100 +#define ET61X251_FRAME_TIMEOUT 2 + +/*****************************************************************************/ + +static const struct usb_device_id et61x251_id_table[] = { + { USB_DEVICE(0x102c, 0x6151), }, + { USB_DEVICE(0x102c, 0x6251), }, + { USB_DEVICE(0x102c, 0x6253), }, + { USB_DEVICE(0x102c, 0x6254), }, + { USB_DEVICE(0x102c, 0x6255), }, + { USB_DEVICE(0x102c, 0x6256), }, + { USB_DEVICE(0x102c, 0x6257), }, + { USB_DEVICE(0x102c, 0x6258), }, + { USB_DEVICE(0x102c, 0x6259), }, + { USB_DEVICE(0x102c, 0x625a), }, + { USB_DEVICE(0x102c, 0x625b), }, + { USB_DEVICE(0x102c, 0x625c), }, + { USB_DEVICE(0x102c, 0x625d), }, + { USB_DEVICE(0x102c, 0x625e), }, + { USB_DEVICE(0x102c, 0x625f), }, + { USB_DEVICE(0x102c, 0x6260), }, + { USB_DEVICE(0x102c, 0x6261), }, + { USB_DEVICE(0x102c, 0x6262), }, + { USB_DEVICE(0x102c, 0x6263), }, + { USB_DEVICE(0x102c, 0x6264), }, + { USB_DEVICE(0x102c, 0x6265), }, + { USB_DEVICE(0x102c, 0x6266), }, + { USB_DEVICE(0x102c, 0x6267), }, + { USB_DEVICE(0x102c, 0x6268), }, + { USB_DEVICE(0x102c, 0x6269), }, + { } +}; + +ET61X251_SENSOR_TABLE + +/*****************************************************************************/ + +enum et61x251_frame_state { + F_UNUSED, + F_QUEUED, + F_GRABBING, + F_DONE, + F_ERROR, +}; + +struct et61x251_frame_t { + void* bufmem; + struct v4l2_buffer buf; + enum et61x251_frame_state state; + struct list_head frame; + unsigned long vma_use_count; +}; + +enum et61x251_dev_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +enum et61x251_io_method { + IO_NONE, + IO_READ, + IO_MMAP, +}; + +enum et61x251_stream_state { + STREAM_OFF, + STREAM_INTERRUPT, + STREAM_ON, +}; + +struct et61x251_sysfs_attr { + u8 reg, i2c_reg; +}; + +struct et61x251_module_param { + u8 force_munmap; + u16 frame_timeout; +}; + +static DEFINE_MUTEX(et61x251_sysfs_lock); +static DECLARE_RWSEM(et61x251_disconnect); + +struct et61x251_device { + struct video_device* v4ldev; + + struct et61x251_sensor sensor; + + struct usb_device* usbdev; + struct urb* urb[ET61X251_URBS]; + void* transfer_buffer[ET61X251_URBS]; + u8* control_buffer; + + struct et61x251_frame_t *frame_current, frame[ET61X251_MAX_FRAMES]; + struct list_head inqueue, outqueue; + u32 frame_count, nbuffers, nreadbuffers; + + enum et61x251_io_method io; + enum et61x251_stream_state stream; + + struct v4l2_jpegcompression compression; + + struct et61x251_sysfs_attr sysfs; + struct et61x251_module_param module_param; + + enum et61x251_dev_state state; + u8 users; + + struct mutex dev_mutex, fileop_mutex; + spinlock_t queue_lock; + wait_queue_head_t open, wait_frame, wait_stream; +}; + +/*****************************************************************************/ + +struct et61x251_device* +et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id) +{ + if (usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id)) + return cam; + + return NULL; +} + + +void +et61x251_attach_sensor(struct et61x251_device* cam, + struct et61x251_sensor* sensor) +{ + memcpy(&cam->sensor, sensor, sizeof(struct et61x251_sensor)); +} + +/*****************************************************************************/ + +#undef DBG +#undef KDBG +#ifdef ET61X251_DEBUG +# define DBG(level, fmt, args...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1) \ + dev_err(&cam->usbdev->dev, fmt "\n", ## args); \ + else if ((level) == 2) \ + dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ + else if ((level) >= 3) \ + dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args); \ + } \ +} while (0) +# define KDBG(level, fmt, args...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1 || (level) == 2) \ + pr_info("et61x251: " fmt "\n", ## args); \ + else if ((level) == 3) \ + pr_debug("et61x251: [%s:%d] " fmt "\n", __FUNCTION__, \ + __LINE__ , ## args); \ + } \ +} while (0) +# define V4LDBG(level, name, cmd) \ +do { \ + if (debug >= (level)) \ + v4l_print_ioctl(name, cmd); \ +} while (0) +#else +# define DBG(level, fmt, args...) do {;} while(0) +# define KDBG(level, fmt, args...) do {;} while(0) +# define V4LDBG(level, name, cmd) do {;} while(0) +#endif + +#undef PDBG +#define PDBG(fmt, args...) \ +dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args) + +#undef PDBGG +#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ + +#endif /* _ET61X251_H_ */ diff --git a/linux/drivers/media/video/et61x251/et61x251_core.c b/linux/drivers/media/video/et61x251/et61x251_core.c new file mode 100644 index 000000000..dfc9dd732 --- /dev/null +++ b/linux/drivers/media/video/et61x251/et61x251_core.c @@ -0,0 +1,2630 @@ +/*************************************************************************** + * V4L2 driver for ET61X[12]51 PC Camera Controllers * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "et61x251.h" + +/*****************************************************************************/ + +#define ET61X251_MODULE_NAME "V4L2 driver for ET61X[12]51 " \ + "PC Camera Controllers" +#define ET61X251_MODULE_AUTHOR "(C) 2006 Luca Risolia" +#define ET61X251_AUTHOR_EMAIL "" +#define ET61X251_MODULE_LICENSE "GPL" +#define ET61X251_MODULE_VERSION "1:1.02" +#define ET61X251_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 2) + +/*****************************************************************************/ + +MODULE_DEVICE_TABLE(usb, et61x251_id_table); + +MODULE_AUTHOR(ET61X251_MODULE_AUTHOR " " ET61X251_AUTHOR_EMAIL); +MODULE_DESCRIPTION(ET61X251_MODULE_NAME); +MODULE_VERSION(ET61X251_MODULE_VERSION); +MODULE_LICENSE(ET61X251_MODULE_LICENSE); + +static short video_nr[] = {[0 ... ET61X251_MAX_DEVICES-1] = -1}; +module_param_array(video_nr, short, NULL, 0444); +MODULE_PARM_DESC(video_nr, + "\n<-1|n[,...]> Specify V4L2 minor mode number." + "\n -1 = use next available (default)" + "\n n = use minor number n (integer >= 0)" + "\nYou can specify up to " + __MODULE_STRING(ET61X251_MAX_DEVICES) " cameras this way." + "\nFor example:" + "\nvideo_nr=-1,2,-1 would assign minor number 2 to" + "\nthe second registered camera and use auto for the first" + "\none and for every other camera." + "\n"); + +static short force_munmap[] = {[0 ... ET61X251_MAX_DEVICES-1] = + ET61X251_FORCE_MUNMAP}; +module_param_array(force_munmap, bool, NULL, 0444); +MODULE_PARM_DESC(force_munmap, + "\n<0|1[,...]> Force the application to unmap previously" + "\nmapped buffer memory before calling any VIDIOC_S_CROP or" + "\nVIDIOC_S_FMT ioctl's. Not all the applications support" + "\nthis feature. This parameter is specific for each" + "\ndetected camera." + "\n 0 = do not force memory unmapping" + "\n 1 = force memory unmapping (save memory)" + "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." + "\n"); + +static unsigned int frame_timeout[] = {[0 ... ET61X251_MAX_DEVICES-1] = + ET61X251_FRAME_TIMEOUT}; +module_param_array(frame_timeout, uint, NULL, 0644); +MODULE_PARM_DESC(frame_timeout, + "\n Timeout for a video frame in seconds." + "\nThis parameter is specific for each detected camera." + "\nDefault value is " + __MODULE_STRING(ET61X251_FRAME_TIMEOUT)"." + "\n"); + +#ifdef ET61X251_DEBUG +static unsigned short debug = ET61X251_DEBUG_LEVEL; +module_param(debug, ushort, 0644); +MODULE_PARM_DESC(debug, + "\n Debugging information level, from 0 to 3:" + "\n0 = none (use carefully)" + "\n1 = critical errors" + "\n2 = significant informations" + "\n3 = more verbose messages" + "\nLevel 3 is useful for testing only, when only " + "one device is used." + "\nDefault value is "__MODULE_STRING(ET61X251_DEBUG_LEVEL)"." + "\n"); +#endif + +/*****************************************************************************/ + +static u32 +et61x251_request_buffers(struct et61x251_device* cam, u32 count, + enum et61x251_io_method io) +{ + struct v4l2_pix_format* p = &(cam->sensor.pix_format); + struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); + const size_t imagesize = cam->module_param.force_munmap || + io == IO_READ ? + (p->width * p->height * p->priv) / 8 : + (r->width * r->height * p->priv) / 8; + void* buff = NULL; + u32 i; + + if (count > ET61X251_MAX_FRAMES) + count = ET61X251_MAX_FRAMES; + + cam->nbuffers = count; + while (cam->nbuffers > 0) { + if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize)))) + break; + cam->nbuffers--; + } + + for (i = 0; i < cam->nbuffers; i++) { + cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); + cam->frame[i].buf.index = i; + cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); + cam->frame[i].buf.length = imagesize; + cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->frame[i].buf.sequence = 0; + cam->frame[i].buf.field = V4L2_FIELD_NONE; + cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; + cam->frame[i].buf.flags = 0; + } + + return cam->nbuffers; +} + + +static void et61x251_release_buffers(struct et61x251_device* cam) +{ + if (cam->nbuffers) { + vfree(cam->frame[0].bufmem); + cam->nbuffers = 0; + } + cam->frame_current = NULL; +} + + +static void et61x251_empty_framequeues(struct et61x251_device* cam) +{ + u32 i; + + INIT_LIST_HEAD(&cam->inqueue); + INIT_LIST_HEAD(&cam->outqueue); + + for (i = 0; i < ET61X251_MAX_FRAMES; i++) { + cam->frame[i].state = F_UNUSED; + cam->frame[i].buf.bytesused = 0; + } +} + + +static void et61x251_requeue_outqueue(struct et61x251_device* cam) +{ + struct et61x251_frame_t *i; + + list_for_each_entry(i, &cam->outqueue, frame) { + i->state = F_QUEUED; + list_add(&i->frame, &cam->inqueue); + } + + INIT_LIST_HEAD(&cam->outqueue); +} + + +static void et61x251_queue_unusedframes(struct et61x251_device* cam) +{ + unsigned long lock_flags; + u32 i; + + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].state == F_UNUSED) { + cam->frame[i].state = F_QUEUED; + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_add_tail(&cam->frame[i].frame, &cam->inqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + } +} + +/*****************************************************************************/ + +int et61x251_write_reg(struct et61x251_device* cam, u8 value, u16 index) +{ + struct usb_device* udev = cam->usbdev; + u8* buff = cam->control_buffer; + int res; + + *buff = value; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, + 0, index, buff, 1, ET61X251_CTRL_TIMEOUT); + if (res < 0) { + DBG(3, "Failed to write a register (value 0x%02X, index " + "0x%02X, error %d)", value, index, res); + return -1; + } + + return 0; +} + + +int et61x251_read_reg(struct et61x251_device* cam, u16 index) +{ + struct usb_device* udev = cam->usbdev; + u8* buff = cam->control_buffer; + int res; + + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, + 0, index, buff, 1, ET61X251_CTRL_TIMEOUT); + if (res < 0) + DBG(3, "Failed to read a register (index 0x%02X, error %d)", + index, res); + + return (res >= 0) ? (int)(*buff) : -1; +} + + +static int +et61x251_i2c_wait(struct et61x251_device* cam, struct et61x251_sensor* sensor) +{ + int i, r; + + for (i = 1; i <= 8; i++) { + if (sensor->interface == ET61X251_I2C_3WIRES) { + r = et61x251_read_reg(cam, 0x8e); + if (!(r & 0x02) && (r >= 0)) + return 0; + } else { + r = et61x251_read_reg(cam, 0x8b); + if (!(r & 0x01) && (r >= 0)) + return 0; + } + if (r < 0) + return -EIO; + udelay(8*8); /* minimum for sensors at 400kHz */ + } + + return -EBUSY; +} + + +int +et61x251_i2c_try_read(struct et61x251_device* cam, + struct et61x251_sensor* sensor, u8 address) +{ + struct usb_device* udev = cam->usbdev; + u8* data = cam->control_buffer; + int err = 0, res; + + data[0] = address; + data[1] = cam->sensor.i2c_slave_id; + data[2] = cam->sensor.rsta | 0x10; + data[3] = !(et61x251_read_reg(cam, 0x8b) & 0x02); + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, + 0, 0x88, data, 4, ET61X251_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += et61x251_i2c_wait(cam, sensor); + + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, + 0, 0x80, data, 8, ET61X251_CTRL_TIMEOUT); + if (res < 0) + err += res; + + if (err) + DBG(3, "I2C read failed for %s image sensor", sensor->name); + + PDBGG("I2C read: address 0x%02X, value: 0x%02X", address, data[0]); + + return err ? -1 : (int)data[0]; +} + + +int +et61x251_i2c_try_write(struct et61x251_device* cam, + struct et61x251_sensor* sensor, u8 address, u8 value) +{ + struct usb_device* udev = cam->usbdev; + u8* data = cam->control_buffer; + int err = 0, res; + + data[0] = address; + data[1] = cam->sensor.i2c_slave_id; + data[2] = cam->sensor.rsta | 0x12; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, + 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT); + if (res < 0) + err += res; + + data[0] = value; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, + 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += et61x251_i2c_wait(cam, sensor); + + if (err) + DBG(3, "I2C write failed for %s image sensor", sensor->name); + + PDBGG("I2C write: address 0x%02X, value: 0x%02X", address, value); + + return err ? -1 : 0; +} + + +int +et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2, + u8 data3, u8 data4, u8 data5, u8 data6, u8 data7, + u8 data8, u8 address) +{ + struct usb_device* udev = cam->usbdev; + u8* data = cam->control_buffer; + int err = 0, res; + + data[0] = data2; + data[1] = data3; + data[2] = data4; + data[3] = data5; + data[4] = data6; + data[5] = data7; + data[6] = data8; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, + 0, 0x81, data, n-1, ET61X251_CTRL_TIMEOUT); + if (res < 0) + err += res; + + data[0] = address; + data[1] = cam->sensor.i2c_slave_id; + data[2] = cam->sensor.rsta | 0x02 | (n << 4); + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, + 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT); + if (res < 0) + err += res; + + /* Start writing through the serial interface */ + data[0] = data1; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, + 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += et61x251_i2c_wait(cam, &cam->sensor); + + if (err) + DBG(3, "I2C raw write failed for %s image sensor", + cam->sensor.name); + + PDBGG("I2C raw write: %u bytes, address = 0x%02X, data1 = 0x%02X, " + "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X," + " data6 = 0x%02X, data7 = 0x%02X, data8 = 0x%02X", n, address, + data1, data2, data3, data4, data5, data6, data7, data8); + + return err ? -1 : 0; + +} + + +int et61x251_i2c_read(struct et61x251_device* cam, u8 address) +{ + return et61x251_i2c_try_read(cam, &cam->sensor, address); +} + + +int et61x251_i2c_write(struct et61x251_device* cam, u8 address, u8 value) +{ + return et61x251_i2c_try_write(cam, &cam->sensor, address, value); +} + +/*****************************************************************************/ + +static void et61x251_urb_complete(struct urb *urb, struct pt_regs* regs) +{ + struct et61x251_device* cam = urb->context; + struct et61x251_frame_t** f; + size_t imagesize; + u8 i; + int err = 0; + + if (urb->status == -ENOENT) + return; + + f = &cam->frame_current; + + if (cam->stream == STREAM_INTERRUPT) { + cam->stream = STREAM_OFF; + if ((*f)) + (*f)->state = F_QUEUED; + DBG(3, "Stream interrupted"); + wake_up(&cam->wait_stream); + } + + if (cam->state & DEV_DISCONNECTED) + return; + + if (cam->state & DEV_MISCONFIGURED) { + wake_up_interruptible(&cam->wait_frame); + return; + } + + if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) + goto resubmit_urb; + + if (!(*f)) + (*f) = list_entry(cam->inqueue.next, struct et61x251_frame_t, + frame); + + imagesize = (cam->sensor.pix_format.width * + cam->sensor.pix_format.height * + cam->sensor.pix_format.priv) / 8; + + for (i = 0; i < urb->number_of_packets; i++) { + unsigned int len, status; + void *pos; + u8* b1, * b2, sof; + const u8 VOID_BYTES = 6; + size_t imglen; + + len = urb->iso_frame_desc[i].actual_length; + status = urb->iso_frame_desc[i].status; + pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; + + if (status) { + DBG(3, "Error in isochronous frame"); + (*f)->state = F_ERROR; + continue; + } + + b1 = pos++; + b2 = pos++; + sof = ((*b1 & 0x3f) == 63); + imglen = ((*b1 & 0xc0) << 2) | *b2; + + PDBGG("Isochrnous frame: length %u, #%u i, image length %zu", + len, i, imglen); + + if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) +start_of_frame: + if (sof) { + (*f)->state = F_GRABBING; + (*f)->buf.bytesused = 0; + do_gettimeofday(&(*f)->buf.timestamp); + pos += 22; + DBG(3, "SOF detected: new video frame"); + } + + if ((*f)->state == F_GRABBING) { + if (sof && (*f)->buf.bytesused) { + if (cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_ET61X251) + goto end_of_frame; + else { + DBG(3, "Not expected SOF detected " + "after %lu bytes", + (unsigned long)(*f)->buf.bytesused); + (*f)->state = F_ERROR; + continue; + } + } + + if ((*f)->buf.bytesused + imglen > imagesize) { + DBG(3, "Video frame size exceeded"); + (*f)->state = F_ERROR; + continue; + } + + pos += VOID_BYTES; + + memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, imglen); + (*f)->buf.bytesused += imglen; + + if ((*f)->buf.bytesused == imagesize) { + u32 b; +end_of_frame: + b = (*f)->buf.bytesused; + (*f)->state = F_DONE; + (*f)->buf.sequence= ++cam->frame_count; + spin_lock(&cam->queue_lock); + list_move_tail(&(*f)->frame, &cam->outqueue); + if (!list_empty(&cam->inqueue)) + (*f) = list_entry(cam->inqueue.next, + struct et61x251_frame_t, + frame); + else + (*f) = NULL; + spin_unlock(&cam->queue_lock); + DBG(3, "Video frame captured: : %lu bytes", + (unsigned long)(b)); + + if (!(*f)) + goto resubmit_urb; + + if (sof && + cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_ET61X251) + goto start_of_frame; + } + } + } + +resubmit_urb: + urb->dev = cam->usbdev; + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0 && err != -EPERM) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "usb_submit_urb() failed"); + } + + wake_up_interruptible(&cam->wait_frame); +} + + +static int et61x251_start_transfer(struct et61x251_device* cam) +{ + struct usb_device *udev = cam->usbdev; + struct urb* urb; + const unsigned int wMaxPacketSize[] = {0, 256, 384, 512, 640, 768, 832, + 864, 896, 920, 956, 980, 1000, + 1022}; + const unsigned int psz = wMaxPacketSize[ET61X251_ALTERNATE_SETTING]; + s8 i, j; + int err = 0; + + for (i = 0; i < ET61X251_URBS; i++) { + cam->transfer_buffer[i] = kzalloc(ET61X251_ISO_PACKETS * psz, + GFP_KERNEL); + if (!cam->transfer_buffer[i]) { + err = -ENOMEM; + DBG(1, "Not enough memory"); + goto free_buffers; + } + } + + for (i = 0; i < ET61X251_URBS; i++) { + urb = usb_alloc_urb(ET61X251_ISO_PACKETS, GFP_KERNEL); + cam->urb[i] = urb; + if (!urb) { + err = -ENOMEM; + DBG(1, "usb_alloc_urb() failed"); + goto free_urbs; + } + urb->dev = udev; + urb->context = cam; + urb->pipe = usb_rcvisocpipe(udev, 1); + urb->transfer_flags = URB_ISO_ASAP; + urb->number_of_packets = ET61X251_ISO_PACKETS; + urb->complete = et61x251_urb_complete; + urb->transfer_buffer = cam->transfer_buffer[i]; + urb->transfer_buffer_length = psz * ET61X251_ISO_PACKETS; + urb->interval = 1; + for (j = 0; j < ET61X251_ISO_PACKETS; j++) { + urb->iso_frame_desc[j].offset = psz * j; + urb->iso_frame_desc[j].length = psz; + } + } + + err = et61x251_write_reg(cam, 0x01, 0x03); + err = et61x251_write_reg(cam, 0x00, 0x03); + err = et61x251_write_reg(cam, 0x08, 0x03); + if (err) { + err = -EIO; + DBG(1, "I/O hardware error"); + goto free_urbs; + } + + err = usb_set_interface(udev, 0, ET61X251_ALTERNATE_SETTING); + if (err) { + DBG(1, "usb_set_interface() failed"); + goto free_urbs; + } + + cam->frame_current = NULL; + + for (i = 0; i < ET61X251_URBS; i++) { + err = usb_submit_urb(cam->urb[i], GFP_KERNEL); + if (err) { + for (j = i-1; j >= 0; j--) + usb_kill_urb(cam->urb[j]); + DBG(1, "usb_submit_urb() failed, error %d", err); + goto free_urbs; + } + } + + return 0; + +free_urbs: + for (i = 0; (i < ET61X251_URBS) && cam->urb[i]; i++) + usb_free_urb(cam->urb[i]); + +free_buffers: + for (i = 0; (i < ET61X251_URBS) && cam->transfer_buffer[i]; i++) + kfree(cam->transfer_buffer[i]); + + return err; +} + + +static int et61x251_stop_transfer(struct et61x251_device* cam) +{ + struct usb_device *udev = cam->usbdev; + s8 i; + int err = 0; + + if (cam->state & DEV_DISCONNECTED) + return 0; + + for (i = ET61X251_URBS-1; i >= 0; i--) { + usb_kill_urb(cam->urb[i]); + usb_free_urb(cam->urb[i]); + kfree(cam->transfer_buffer[i]); + } + + err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ + if (err) + DBG(3, "usb_set_interface() failed"); + + return err; +} + + +static int et61x251_stream_interrupt(struct et61x251_device* cam) +{ + long timeout; + + cam->stream = STREAM_INTERRUPT; + timeout = wait_event_timeout(cam->wait_stream, + (cam->stream == STREAM_OFF) || + (cam->state & DEV_DISCONNECTED), + ET61X251_URB_TIMEOUT); + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + else if (cam->stream != STREAM_OFF) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "URB timeout reached. The camera is misconfigured. To " + "use it, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + return 0; +} + +/*****************************************************************************/ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static u8 et61x251_strtou8(const char* buff, size_t len, ssize_t* count) +{ + char str[5]; + char* endp; + unsigned long val; + + if (len < 4) { + strncpy(str, buff, len); + str[len+1] = '\0'; + } else { + strncpy(str, buff, 4); + str[4] = '\0'; + } + + val = simple_strtoul(str, &endp, 0); + + *count = 0; + if (val <= 0xff) + *count = (ssize_t)(endp - str); + if ((*count) && (len == *count+1) && (buff[*count] == '\n')) + *count += 1; + + return (u8)val; +} + +/* + NOTE 1: being inside one of the following methods implies that the v4l + device exists for sure (see kobjects and reference counters) + NOTE 2: buffers are PAGE_SIZE long +*/ + +static ssize_t et61x251_show_reg(struct class_device* cd, char* buf) +{ + struct et61x251_device* cam; + ssize_t count; + + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENODEV; + } + + count = sprintf(buf, "%u\n", cam->sysfs.reg); + + mutex_unlock(&et61x251_sysfs_lock); + + return count; +} + + +static ssize_t +et61x251_store_reg(struct class_device* cd, const char* buf, size_t len) +{ + struct et61x251_device* cam; + u8 index; + ssize_t count; + + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENODEV; + } + + index = et61x251_strtou8(buf, len, &count); + if (index > 0x8e || !count) { + mutex_unlock(&et61x251_sysfs_lock); + return -EINVAL; + } + + cam->sysfs.reg = index; + + DBG(2, "Moved ET61X[12]51 register index to 0x%02X", cam->sysfs.reg); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&et61x251_sysfs_lock); + + return count; +} + + +static ssize_t et61x251_show_val(struct class_device* cd, char* buf) +{ + struct et61x251_device* cam; + ssize_t count; + int val; + + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENODEV; + } + + if ((val = et61x251_read_reg(cam, cam->sysfs.reg)) < 0) { + mutex_unlock(&et61x251_sysfs_lock); + return -EIO; + } + + count = sprintf(buf, "%d\n", val); + + DBG(3, "Read bytes: %zd", count); + + mutex_unlock(&et61x251_sysfs_lock); + + return count; +} + + +static ssize_t +et61x251_store_val(struct class_device* cd, const char* buf, size_t len) +{ + struct et61x251_device* cam; + u8 value; + ssize_t count; + int err; + + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENODEV; + } + + value = et61x251_strtou8(buf, len, &count); + if (!count) { + mutex_unlock(&et61x251_sysfs_lock); + return -EINVAL; + } + + err = et61x251_write_reg(cam, value, cam->sysfs.reg); + if (err) { + mutex_unlock(&et61x251_sysfs_lock); + return -EIO; + } + + DBG(2, "Written ET61X[12]51 reg. 0x%02X, val. 0x%02X", + cam->sysfs.reg, value); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&et61x251_sysfs_lock); + + return count; +} + + +static ssize_t et61x251_show_i2c_reg(struct class_device* cd, char* buf) +{ + struct et61x251_device* cam; + ssize_t count; + + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENODEV; + } + + count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg); + + DBG(3, "Read bytes: %zd", count); + + mutex_unlock(&et61x251_sysfs_lock); + + return count; +} + + +static ssize_t +et61x251_store_i2c_reg(struct class_device* cd, const char* buf, size_t len) +{ + struct et61x251_device* cam; + u8 index; + ssize_t count; + + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENODEV; + } + + index = et61x251_strtou8(buf, len, &count); + if (!count) { + mutex_unlock(&et61x251_sysfs_lock); + return -EINVAL; + } + + cam->sysfs.i2c_reg = index; + + DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&et61x251_sysfs_lock); + + return count; +} + + +static ssize_t et61x251_show_i2c_val(struct class_device* cd, char* buf) +{ + struct et61x251_device* cam; + ssize_t count; + int val; + + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENODEV; + } + + if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENOSYS; + } + + if ((val = et61x251_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) { + mutex_unlock(&et61x251_sysfs_lock); + return -EIO; + } + + count = sprintf(buf, "%d\n", val); + + DBG(3, "Read bytes: %zd", count); + + mutex_unlock(&et61x251_sysfs_lock); + + return count; +} + + +static ssize_t +et61x251_store_i2c_val(struct class_device* cd, const char* buf, size_t len) +{ + struct et61x251_device* cam; + u8 value; + ssize_t count; + int err; + + if (mutex_lock_interruptible(&et61x251_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENODEV; + } + + if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) { + mutex_unlock(&et61x251_sysfs_lock); + return -ENOSYS; + } + + value = et61x251_strtou8(buf, len, &count); + if (!count) { + mutex_unlock(&et61x251_sysfs_lock); + return -EINVAL; + } + + err = et61x251_i2c_write(cam, cam->sysfs.i2c_reg, value); + if (err) { + mutex_unlock(&et61x251_sysfs_lock); + return -EIO; + } + + DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X", + cam->sysfs.i2c_reg, value); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&et61x251_sysfs_lock); + + return count; +} + + +static CLASS_DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, + et61x251_show_reg, et61x251_store_reg); +static CLASS_DEVICE_ATTR(val, S_IRUGO | S_IWUSR, + et61x251_show_val, et61x251_store_val); +static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, + et61x251_show_i2c_reg, et61x251_store_i2c_reg); +static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, + et61x251_show_i2c_val, et61x251_store_i2c_val); + + +static void et61x251_create_sysfs(struct et61x251_device* cam) +{ + struct video_device *v4ldev = cam->v4ldev; + + video_device_create_file(v4ldev, &class_device_attr_reg); + video_device_create_file(v4ldev, &class_device_attr_val); + if (cam->sensor.sysfs_ops) { + video_device_create_file(v4ldev, &class_device_attr_i2c_reg); + video_device_create_file(v4ldev, &class_device_attr_i2c_val); + } +} +#endif /* CONFIG_VIDEO_ADV_DEBUG */ + +/*****************************************************************************/ + +static int +et61x251_set_pix_format(struct et61x251_device* cam, + struct v4l2_pix_format* pix) +{ + int r, err = 0; + + if ((r = et61x251_read_reg(cam, 0x12)) < 0) + err += r; + if (pix->pixelformat == V4L2_PIX_FMT_ET61X251) + err += et61x251_write_reg(cam, r & 0xfd, 0x12); + else + err += et61x251_write_reg(cam, r | 0x02, 0x12); + + return err ? -EIO : 0; +} + + +static int +et61x251_set_compression(struct et61x251_device* cam, + struct v4l2_jpegcompression* compression) +{ + int r, err = 0; + + if ((r = et61x251_read_reg(cam, 0x12)) < 0) + err += r; + if (compression->quality == 0) + err += et61x251_write_reg(cam, r & 0xfb, 0x12); + else + err += et61x251_write_reg(cam, r | 0x04, 0x12); + + return err ? -EIO : 0; +} + + +static int et61x251_set_scale(struct et61x251_device* cam, u8 scale) +{ + int r = 0, err = 0; + + r = et61x251_read_reg(cam, 0x12); + if (r < 0) + err += r; + + if (scale == 1) + err += et61x251_write_reg(cam, r & ~0x01, 0x12); + else if (scale == 2) + err += et61x251_write_reg(cam, r | 0x01, 0x12); + + if (err) + return -EIO; + + PDBGG("Scaling factor: %u", scale); + + return 0; +} + + +static int +et61x251_set_crop(struct et61x251_device* cam, struct v4l2_rect* rect) +{ + struct et61x251_sensor* s = &cam->sensor; + u16 fmw_sx = (u16)(rect->left - s->cropcap.bounds.left + + s->active_pixel.left), + fmw_sy = (u16)(rect->top - s->cropcap.bounds.top + + s->active_pixel.top), + fmw_length = (u16)(rect->width), + fmw_height = (u16)(rect->height); + int err = 0; + + err += et61x251_write_reg(cam, fmw_sx & 0xff, 0x69); + err += et61x251_write_reg(cam, fmw_sy & 0xff, 0x6a); + err += et61x251_write_reg(cam, fmw_length & 0xff, 0x6b); + err += et61x251_write_reg(cam, fmw_height & 0xff, 0x6c); + err += et61x251_write_reg(cam, (fmw_sx >> 8) | ((fmw_sy & 0x300) >> 6) + | ((fmw_length & 0x300) >> 4) + | ((fmw_height & 0x300) >> 2), 0x6d); + if (err) + return -EIO; + + PDBGG("fmw_sx, fmw_sy, fmw_length, fmw_height: %u %u %u %u", + fmw_sx, fmw_sy, fmw_length, fmw_height); + + return 0; +} + + +static int et61x251_init(struct et61x251_device* cam) +{ + struct et61x251_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + struct v4l2_queryctrl *qctrl; + struct v4l2_rect* rect; + u8 i = 0; + int err = 0; + + if (!(cam->state & DEV_INITIALIZED)) { + init_waitqueue_head(&cam->open); + qctrl = s->qctrl; + rect = &(s->cropcap.defrect); + cam->compression.quality = ET61X251_COMPRESSION_QUALITY; + } else { /* use current values */ + qctrl = s->_qctrl; + rect = &(s->_rect); + } + + err += et61x251_set_scale(cam, rect->width / s->pix_format.width); + err += et61x251_set_crop(cam, rect); + if (err) + return err; + + if (s->init) { + err = s->init(cam); + if (err) { + DBG(3, "Sensor initialization failed"); + return err; + } + } + + err += et61x251_set_compression(cam, &cam->compression); + err += et61x251_set_pix_format(cam, &s->pix_format); + if (s->set_pix_format) + err += s->set_pix_format(cam, &s->pix_format); + if (err) + return err; + + if (s->pix_format.pixelformat == V4L2_PIX_FMT_ET61X251) + DBG(3, "Compressed video format is active, quality %d", + cam->compression.quality); + else + DBG(3, "Uncompressed video format is active"); + + if (s->set_crop) + if ((err = s->set_crop(cam, rect))) { + DBG(3, "set_crop() failed"); + return err; + } + + if (s->set_ctrl) { + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (s->qctrl[i].id != 0 && + !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { + ctrl.id = s->qctrl[i].id; + ctrl.value = qctrl[i].default_value; + err = s->set_ctrl(cam, &ctrl); + if (err) { + DBG(3, "Set %s control failed", + s->qctrl[i].name); + return err; + } + DBG(3, "Image sensor supports '%s' control", + s->qctrl[i].name); + } + } + + if (!(cam->state & DEV_INITIALIZED)) { + mutex_init(&cam->fileop_mutex); + spin_lock_init(&cam->queue_lock); + init_waitqueue_head(&cam->wait_frame); + init_waitqueue_head(&cam->wait_stream); + cam->nreadbuffers = 2; + memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); + memcpy(&(s->_rect), &(s->cropcap.defrect), + sizeof(struct v4l2_rect)); + cam->state |= DEV_INITIALIZED; + } + + DBG(2, "Initialization succeeded"); + return 0; +} + + +static void et61x251_release_resources(struct et61x251_device* cam) +{ + mutex_lock(&et61x251_sysfs_lock); + + DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor); + video_set_drvdata(cam->v4ldev, NULL); + video_unregister_device(cam->v4ldev); + + usb_put_dev(cam->usbdev); + + mutex_unlock(&et61x251_sysfs_lock); + + kfree(cam->control_buffer); +} + +/*****************************************************************************/ + +static int et61x251_open(struct inode* inode, struct file* filp) +{ + struct et61x251_device* cam; + int err = 0; + + /* + This is the only safe way to prevent race conditions with + disconnect + */ + if (!down_read_trylock(&et61x251_disconnect)) + return -ERESTARTSYS; + + cam = video_get_drvdata(video_devdata(filp)); + + if (mutex_lock_interruptible(&cam->dev_mutex)) { + up_read(&et61x251_disconnect); + return -ERESTARTSYS; + } + + if (cam->users) { + DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor); + if ((filp->f_flags & O_NONBLOCK) || + (filp->f_flags & O_NDELAY)) { + err = -EWOULDBLOCK; + goto out; + } + mutex_unlock(&cam->dev_mutex); + err = wait_event_interruptible_exclusive(cam->open, + cam->state & DEV_DISCONNECTED + || !cam->users); + if (err) { + up_read(&et61x251_disconnect); + return err; + } + if (cam->state & DEV_DISCONNECTED) { + up_read(&et61x251_disconnect); + return -ENODEV; + } + mutex_lock(&cam->dev_mutex); + } + + + if (cam->state & DEV_MISCONFIGURED) { + err = et61x251_init(cam); + if (err) { + DBG(1, "Initialization failed again. " + "I will retry on next open()."); + goto out; + } + cam->state &= ~DEV_MISCONFIGURED; + } + + if ((err = et61x251_start_transfer(cam))) + goto out; + + filp->private_data = cam; + cam->users++; + cam->io = IO_NONE; + cam->stream = STREAM_OFF; + cam->nbuffers = 0; + cam->frame_count = 0; + et61x251_empty_framequeues(cam); + + DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor); + +out: + mutex_unlock(&cam->dev_mutex); + up_read(&et61x251_disconnect); + return err; +} + + +static int et61x251_release(struct inode* inode, struct file* filp) +{ + struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + + mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */ + + et61x251_stop_transfer(cam); + + et61x251_release_buffers(cam); + + if (cam->state & DEV_DISCONNECTED) { + et61x251_release_resources(cam); + mutex_unlock(&cam->dev_mutex); + kfree(cam); + return 0; + } + + cam->users--; + wake_up_interruptible_nr(&cam->open, 1); + + DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor); + + mutex_unlock(&cam->dev_mutex); + + return 0; +} + + +static ssize_t +et61x251_read(struct file* filp, char __user * buf, + size_t count, loff_t* f_pos) +{ + struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + struct et61x251_frame_t* f, * i; + unsigned long lock_flags; + long timeout; + int err = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + if (cam->io == IO_MMAP) { + DBG(3, "Close and open the device again to choose the read " + "method"); + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + if (cam->io == IO_NONE) { + if (!et61x251_request_buffers(cam, cam->nreadbuffers, + IO_READ)) { + DBG(1, "read() failed, not enough memory"); + mutex_unlock(&cam->fileop_mutex); + return -ENOMEM; + } + cam->io = IO_READ; + cam->stream = STREAM_ON; + } + + if (list_empty(&cam->inqueue)) { + if (!list_empty(&cam->outqueue)) + et61x251_empty_framequeues(cam); + et61x251_queue_unusedframes(cam); + } + + if (!count) { + mutex_unlock(&cam->fileop_mutex); + return 0; + } + + if (list_empty(&cam->outqueue)) { + if (filp->f_flags & O_NONBLOCK) { + mutex_unlock(&cam->fileop_mutex); + return -EAGAIN; + } + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) { + mutex_unlock(&cam->fileop_mutex); + return timeout; + } + if (cam->state & DEV_DISCONNECTED) { + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + if (!timeout || (cam->state & DEV_MISCONFIGURED)) { + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + } + + f = list_entry(cam->outqueue.prev, struct et61x251_frame_t, frame); + + if (count > f->buf.bytesused) + count = f->buf.bytesused; + + if (copy_to_user(buf, f->bufmem, count)) { + err = -EFAULT; + goto exit; + } + *f_pos += count; + +exit: + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_for_each_entry(i, &cam->outqueue, frame) + i->state = F_UNUSED; + INIT_LIST_HEAD(&cam->outqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + et61x251_queue_unusedframes(cam); + + PDBGG("Frame #%lu, bytes read: %zu", + (unsigned long)f->buf.index, count); + + mutex_unlock(&cam->fileop_mutex); + + return err ? err : count; +} + + +static unsigned int et61x251_poll(struct file *filp, poll_table *wait) +{ + struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + struct et61x251_frame_t* f; + unsigned long lock_flags; + unsigned int mask = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return POLLERR; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + goto error; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + goto error; + } + + if (cam->io == IO_NONE) { + if (!et61x251_request_buffers(cam, cam->nreadbuffers, + IO_READ)) { + DBG(1, "poll() failed, not enough memory"); + goto error; + } + cam->io = IO_READ; + cam->stream = STREAM_ON; + } + + if (cam->io == IO_READ) { + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_for_each_entry(f, &cam->outqueue, frame) + f->state = F_UNUSED; + INIT_LIST_HEAD(&cam->outqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + et61x251_queue_unusedframes(cam); + } + + poll_wait(filp, &cam->wait_frame, wait); + + if (!list_empty(&cam->outqueue)) + mask |= POLLIN | POLLRDNORM; + + mutex_unlock(&cam->fileop_mutex); + + return mask; + +error: + mutex_unlock(&cam->fileop_mutex); + return POLLERR; +} + + +static void et61x251_vm_open(struct vm_area_struct* vma) +{ + struct et61x251_frame_t* f = vma->vm_private_data; + f->vma_use_count++; +} + + +static void et61x251_vm_close(struct vm_area_struct* vma) +{ + /* NOTE: buffers are not freed here */ + struct et61x251_frame_t* f = vma->vm_private_data; + f->vma_use_count--; +} + + +static struct vm_operations_struct et61x251_vm_ops = { + .open = et61x251_vm_open, + .close = et61x251_vm_close, +}; + + +static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma) +{ + struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + unsigned long size = vma->vm_end - vma->vm_start, + start = vma->vm_start; + void *pos; + u32 i; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || + size != PAGE_ALIGN(cam->frame[0].buf.length)) { + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + for (i = 0; i < cam->nbuffers; i++) { + if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff) + break; + } + if (i == cam->nbuffers) { + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_RESERVED; + + pos = cam->frame[i].bufmem; + while (size > 0) { /* size is page-aligned */ + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { + mutex_unlock(&cam->fileop_mutex); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + size -= PAGE_SIZE; + } + + vma->vm_ops = &et61x251_vm_ops; + vma->vm_private_data = &cam->frame[i]; + + et61x251_vm_open(vma); + + mutex_unlock(&cam->fileop_mutex); + + return 0; +} + +/*****************************************************************************/ + +static int +et61x251_vidioc_querycap(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_capability cap = { + .driver = "et61x251", + .version = ET61X251_MODULE_VERSION_CODE, + .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING, + }; + + strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); + if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) + strlcpy(cap.bus_info, cam->usbdev->dev.bus_id, + sizeof(cap.bus_info)); + + if (copy_to_user(arg, &cap, sizeof(cap))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_enuminput(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_input i; + + if (copy_from_user(&i, arg, sizeof(i))) + return -EFAULT; + + if (i.index) + return -EINVAL; + + memset(&i, 0, sizeof(i)); + strcpy(i.name, "Camera"); + i.type = V4L2_INPUT_TYPE_CAMERA; + + if (copy_to_user(arg, &i, sizeof(i))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_g_input(struct et61x251_device* cam, void __user * arg) +{ + int index = 0; + + if (copy_to_user(arg, &index, sizeof(index))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_s_input(struct et61x251_device* cam, void __user * arg) +{ + int index; + + if (copy_from_user(&index, arg, sizeof(index))) + return -EFAULT; + + if (index != 0) + return -EINVAL; + + return 0; +} + + +static int +et61x251_vidioc_query_ctrl(struct et61x251_device* cam, void __user * arg) +{ + struct et61x251_sensor* s = &cam->sensor; + struct v4l2_queryctrl qc; + u8 i; + + if (copy_from_user(&qc, arg, sizeof(qc))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (qc.id && qc.id == s->qctrl[i].id) { + memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); + if (copy_to_user(arg, &qc, sizeof(qc))) + return -EFAULT; + return 0; + } + + return -EINVAL; +} + + +static int +et61x251_vidioc_g_ctrl(struct et61x251_device* cam, void __user * arg) +{ + struct et61x251_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + int err = 0; + u8 i; + + if (!s->get_ctrl && !s->set_ctrl) + return -EINVAL; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + + if (!s->get_ctrl) { + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (ctrl.id == s->qctrl[i].id) { + ctrl.value = s->_qctrl[i].default_value; + goto exit; + } + return -EINVAL; + } else + err = s->get_ctrl(cam, &ctrl); + +exit: + if (copy_to_user(arg, &ctrl, sizeof(ctrl))) + return -EFAULT; + + return err; +} + + +static int +et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg) +{ + struct et61x251_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + u8 i; + int err = 0; + + if (!s->set_ctrl) + return -EINVAL; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (ctrl.id == s->qctrl[i].id) { + if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) + return -EINVAL; + if (ctrl.value < s->qctrl[i].minimum || + ctrl.value > s->qctrl[i].maximum) + return -ERANGE; + ctrl.value -= ctrl.value % s->qctrl[i].step; + break; + } + + if ((err = s->set_ctrl(cam, &ctrl))) + return err; + + s->_qctrl[i].default_value = ctrl.value; + + return 0; +} + + +static int +et61x251_vidioc_cropcap(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_cropcap* cc = &(cam->sensor.cropcap); + + cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cc->pixelaspect.numerator = 1; + cc->pixelaspect.denominator = 1; + + if (copy_to_user(arg, cc, sizeof(*cc))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_g_crop(struct et61x251_device* cam, void __user * arg) +{ + struct et61x251_sensor* s = &cam->sensor; + struct v4l2_crop crop = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + }; + + memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect)); + + if (copy_to_user(arg, &crop, sizeof(crop))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg) +{ + struct et61x251_sensor* s = &cam->sensor; + struct v4l2_crop crop; + struct v4l2_rect* rect; + struct v4l2_rect* bounds = &(s->cropcap.bounds); + struct v4l2_pix_format* pix_format = &(s->pix_format); + u8 scale; + const enum et61x251_stream_state stream = cam->stream; + const u32 nbuffers = cam->nbuffers; + u32 i; + int err = 0; + + if (copy_from_user(&crop, arg, sizeof(crop))) + return -EFAULT; + + rect = &(crop.c); + + if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (cam->module_param.force_munmap) + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_S_CROP failed. " + "Unmap the buffers first."); + return -EINVAL; + } + + /* Preserve R,G or B origin */ + rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L; + rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L; + + if (rect->width < 4) + rect->width = 4; + if (rect->height < 4) + rect->height = 4; + if (rect->width > bounds->width) + rect->width = bounds->width; + if (rect->height > bounds->height) + rect->height = bounds->height; + if (rect->left < bounds->left) + rect->left = bounds->left; + if (rect->top < bounds->top) + rect->top = bounds->top; + if (rect->left + rect->width > bounds->left + bounds->width) + rect->left = bounds->left+bounds->width - rect->width; + if (rect->top + rect->height > bounds->top + bounds->height) + rect->top = bounds->top+bounds->height - rect->height; + + rect->width &= ~3L; + rect->height &= ~3L; + + if (ET61X251_PRESERVE_IMGSCALE) { + /* Calculate the actual scaling factor */ + u32 a, b; + a = rect->width * rect->height; + b = pix_format->width * pix_format->height; + scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1; + } else + scale = 1; + + if (cam->stream == STREAM_ON) + if ((err = et61x251_stream_interrupt(cam))) + return err; + + if (copy_to_user(arg, &crop, sizeof(crop))) { + cam->stream = stream; + return -EFAULT; + } + + if (cam->module_param.force_munmap || cam->io == IO_READ) + et61x251_release_buffers(cam); + + err = et61x251_set_crop(cam, rect); + if (s->set_crop) + err += s->set_crop(cam, rect); + err += et61x251_set_scale(cam, scale); + + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + s->pix_format.width = rect->width/scale; + s->pix_format.height = rect->height/scale; + memcpy(&(s->_rect), rect, sizeof(*rect)); + + if ((cam->module_param.force_munmap || cam->io == IO_READ) && + nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -ENOMEM; + } + + if (cam->io == IO_READ) + et61x251_empty_framequeues(cam); + else if (cam->module_param.force_munmap) + et61x251_requeue_outqueue(cam); + + cam->stream = stream; + + return 0; +} + + +static int +et61x251_vidioc_enum_fmt(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_fmtdesc fmtd; + + if (copy_from_user(&fmtd, arg, sizeof(fmtd))) + return -EFAULT; + + if (fmtd.index == 0) { + strcpy(fmtd.description, "bayer rgb"); + fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8; + } else if (fmtd.index == 1) { + strcpy(fmtd.description, "compressed"); + fmtd.pixelformat = V4L2_PIX_FMT_ET61X251; + fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; + } else + return -EINVAL; + + fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + memset(&fmtd.reserved, 0, sizeof(fmtd.reserved)); + + if (copy_to_user(arg, &fmtd, sizeof(fmtd))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_g_fmt(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_format format; + struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format); + + if (copy_from_user(&format, arg, sizeof(format))) + return -EFAULT; + + if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_ET61X251) + ? 0 : (pfmt->width * pfmt->priv) / 8; + pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); + pfmt->field = V4L2_FIELD_NONE; + memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); + + if (copy_to_user(arg, &format, sizeof(format))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd, + void __user * arg) +{ + struct et61x251_sensor* s = &cam->sensor; + struct v4l2_format format; + struct v4l2_pix_format* pix; + struct v4l2_pix_format* pfmt = &(s->pix_format); + struct v4l2_rect* bounds = &(s->cropcap.bounds); + struct v4l2_rect rect; + u8 scale; + const enum et61x251_stream_state stream = cam->stream; + const u32 nbuffers = cam->nbuffers; + u32 i; + int err = 0; + + if (copy_from_user(&format, arg, sizeof(format))) + return -EFAULT; + + pix = &(format.fmt.pix); + + if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memcpy(&rect, &(s->_rect), sizeof(rect)); + + { /* calculate the actual scaling factor */ + u32 a, b; + a = rect.width * rect.height; + b = pix->width * pix->height; + scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1; + } + + rect.width = scale * pix->width; + rect.height = scale * pix->height; + + if (rect.width < 4) + rect.width = 4; + if (rect.height < 4) + rect.height = 4; + if (rect.width > bounds->left + bounds->width - rect.left) + rect.width = bounds->left + bounds->width - rect.left; + if (rect.height > bounds->top + bounds->height - rect.top) + rect.height = bounds->top + bounds->height - rect.top; + + rect.width &= ~3L; + rect.height &= ~3L; + + { /* adjust the scaling factor */ + u32 a, b; + a = rect.width * rect.height; + b = pix->width * pix->height; + scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1; + } + + pix->width = rect.width / scale; + pix->height = rect.height / scale; + + if (pix->pixelformat != V4L2_PIX_FMT_ET61X251 && + pix->pixelformat != V4L2_PIX_FMT_SBGGR8) + pix->pixelformat = pfmt->pixelformat; + pix->priv = pfmt->priv; /* bpp */ + pix->colorspace = pfmt->colorspace; + pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_ET61X251) + ? 0 : (pix->width * pix->priv) / 8; + pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); + pix->field = V4L2_FIELD_NONE; + + if (cmd == VIDIOC_TRY_FMT) { + if (copy_to_user(arg, &format, sizeof(format))) + return -EFAULT; + return 0; + } + + if (cam->module_param.force_munmap) + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_S_FMT failed. " + "Unmap the buffers first."); + return -EINVAL; + } + + if (cam->stream == STREAM_ON) + if ((err = et61x251_stream_interrupt(cam))) + return err; + + if (copy_to_user(arg, &format, sizeof(format))) { + cam->stream = stream; + return -EFAULT; + } + + if (cam->module_param.force_munmap || cam->io == IO_READ) + et61x251_release_buffers(cam); + + err += et61x251_set_pix_format(cam, pix); + err += et61x251_set_crop(cam, &rect); + if (s->set_pix_format) + err += s->set_pix_format(cam, pix); + if (s->set_crop) + err += s->set_crop(cam, &rect); + err += et61x251_set_scale(cam, scale); + + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + memcpy(pfmt, pix, sizeof(*pix)); + memcpy(&(s->_rect), &rect, sizeof(rect)); + + if ((cam->module_param.force_munmap || cam->io == IO_READ) && + nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -ENOMEM; + } + + if (cam->io == IO_READ) + et61x251_empty_framequeues(cam); + else if (cam->module_param.force_munmap) + et61x251_requeue_outqueue(cam); + + cam->stream = stream; + + return 0; +} + + +static int +et61x251_vidioc_g_jpegcomp(struct et61x251_device* cam, void __user * arg) +{ + if (copy_to_user(arg, &cam->compression, + sizeof(cam->compression))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_s_jpegcomp(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_jpegcompression jc; + const enum et61x251_stream_state stream = cam->stream; + int err = 0; + + if (copy_from_user(&jc, arg, sizeof(jc))) + return -EFAULT; + + if (jc.quality != 0 && jc.quality != 1) + return -EINVAL; + + if (cam->stream == STREAM_ON) + if ((err = et61x251_stream_interrupt(cam))) + return err; + + err += et61x251_set_compression(cam, &jc); + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware " + "problems. To use the camera, close and open " + "/dev/video%d again.", cam->v4ldev->minor); + return -EIO; + } + + cam->compression.quality = jc.quality; + + cam->stream = stream; + + return 0; +} + + +static int +et61x251_vidioc_reqbufs(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_requestbuffers rb; + u32 i; + int err; + + if (copy_from_user(&rb, arg, sizeof(rb))) + return -EFAULT; + + if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + rb.memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (cam->io == IO_READ) { + DBG(3, "Close and open the device again to choose the mmap " + "I/O method"); + return -EINVAL; + } + + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_REQBUFS failed. " + "Previous buffers are still mapped."); + return -EINVAL; + } + + if (cam->stream == STREAM_ON) + if ((err = et61x251_stream_interrupt(cam))) + return err; + + et61x251_empty_framequeues(cam); + + et61x251_release_buffers(cam); + if (rb.count) + rb.count = et61x251_request_buffers(cam, rb.count, IO_MMAP); + + if (copy_to_user(arg, &rb, sizeof(rb))) { + et61x251_release_buffers(cam); + cam->io = IO_NONE; + return -EFAULT; + } + + cam->io = rb.count ? IO_MMAP : IO_NONE; + + return 0; +} + + +static int +et61x251_vidioc_querybuf(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_buffer b; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + b.index >= cam->nbuffers || cam->io != IO_MMAP) + return -EINVAL; + + memcpy(&b, &cam->frame[b.index].buf, sizeof(b)); + + if (cam->frame[b.index].vma_use_count) + b.flags |= V4L2_BUF_FLAG_MAPPED; + + if (cam->frame[b.index].state == F_DONE) + b.flags |= V4L2_BUF_FLAG_DONE; + else if (cam->frame[b.index].state != F_UNUSED) + b.flags |= V4L2_BUF_FLAG_QUEUED; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_qbuf(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_buffer b; + unsigned long lock_flags; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + b.index >= cam->nbuffers || cam->io != IO_MMAP) + return -EINVAL; + + if (cam->frame[b.index].state != F_UNUSED) + return -EINVAL; + + cam->frame[b.index].state = F_QUEUED; + + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_add_tail(&cam->frame[b.index].frame, &cam->inqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + PDBGG("Frame #%lu queued", (unsigned long)b.index); + + return 0; +} + + +static int +et61x251_vidioc_dqbuf(struct et61x251_device* cam, struct file* filp, + void __user * arg) +{ + struct v4l2_buffer b; + struct et61x251_frame_t *f; + unsigned long lock_flags; + long timeout; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP) + return -EINVAL; + + if (list_empty(&cam->outqueue)) { + if (cam->stream == STREAM_OFF) + return -EINVAL; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) + return timeout; + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + if (!timeout || (cam->state & DEV_MISCONFIGURED)) + return -EIO; + } + + spin_lock_irqsave(&cam->queue_lock, lock_flags); + f = list_entry(cam->outqueue.next, struct et61x251_frame_t, frame); + list_del(cam->outqueue.next); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + f->state = F_UNUSED; + + memcpy(&b, &f->buf, sizeof(b)); + if (f->vma_use_count) + b.flags |= V4L2_BUF_FLAG_MAPPED; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index); + + return 0; +} + + +static int +et61x251_vidioc_streamon(struct et61x251_device* cam, void __user * arg) +{ + int type; + + if (copy_from_user(&type, arg, sizeof(type))) + return -EFAULT; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + if (list_empty(&cam->inqueue)) + return -EINVAL; + + cam->stream = STREAM_ON; + + DBG(3, "Stream on"); + + return 0; +} + + +static int +et61x251_vidioc_streamoff(struct et61x251_device* cam, void __user * arg) +{ + int type, err; + + if (copy_from_user(&type, arg, sizeof(type))) + return -EFAULT; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + if (cam->stream == STREAM_ON) + if ((err = et61x251_stream_interrupt(cam))) + return err; + + et61x251_empty_framequeues(cam); + + DBG(3, "Stream off"); + + return 0; +} + + +static int +et61x251_vidioc_g_parm(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_streamparm sp; + + if (copy_from_user(&sp, arg, sizeof(sp))) + return -EFAULT; + + if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + sp.parm.capture.extendedmode = 0; + sp.parm.capture.readbuffers = cam->nreadbuffers; + + if (copy_to_user(arg, &sp, sizeof(sp))) + return -EFAULT; + + return 0; +} + + +static int +et61x251_vidioc_s_parm(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_streamparm sp; + + if (copy_from_user(&sp, arg, sizeof(sp))) + return -EFAULT; + + if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + sp.parm.capture.extendedmode = 0; + + if (sp.parm.capture.readbuffers == 0) + sp.parm.capture.readbuffers = cam->nreadbuffers; + + if (sp.parm.capture.readbuffers > ET61X251_MAX_FRAMES) + sp.parm.capture.readbuffers = ET61X251_MAX_FRAMES; + + if (copy_to_user(arg, &sp, sizeof(sp))) + return -EFAULT; + + cam->nreadbuffers = sp.parm.capture.readbuffers; + + return 0; +} + + +static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp, + unsigned int cmd, void __user * arg) +{ + struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + + switch (cmd) { + + case VIDIOC_QUERYCAP: + return et61x251_vidioc_querycap(cam, arg); + + case VIDIOC_ENUMINPUT: + return et61x251_vidioc_enuminput(cam, arg); + + case VIDIOC_G_INPUT: + return et61x251_vidioc_g_input(cam, arg); + + case VIDIOC_S_INPUT: + return et61x251_vidioc_s_input(cam, arg); + + case VIDIOC_QUERYCTRL: + return et61x251_vidioc_query_ctrl(cam, arg); + + case VIDIOC_G_CTRL: + return et61x251_vidioc_g_ctrl(cam, arg); + + case VIDIOC_S_CTRL_OLD: + case VIDIOC_S_CTRL: + return et61x251_vidioc_s_ctrl(cam, arg); + + case VIDIOC_CROPCAP_OLD: + case VIDIOC_CROPCAP: + return et61x251_vidioc_cropcap(cam, arg); + + case VIDIOC_G_CROP: + return et61x251_vidioc_g_crop(cam, arg); + + case VIDIOC_S_CROP: + return et61x251_vidioc_s_crop(cam, arg); + + case VIDIOC_ENUM_FMT: + return et61x251_vidioc_enum_fmt(cam, arg); + + case VIDIOC_G_FMT: + return et61x251_vidioc_g_fmt(cam, arg); + + case VIDIOC_TRY_FMT: + case VIDIOC_S_FMT: + return et61x251_vidioc_try_s_fmt(cam, cmd, arg); + + case VIDIOC_G_JPEGCOMP: + return et61x251_vidioc_g_jpegcomp(cam, arg); + + case VIDIOC_S_JPEGCOMP: + return et61x251_vidioc_s_jpegcomp(cam, arg); + + case VIDIOC_REQBUFS: + return et61x251_vidioc_reqbufs(cam, arg); + + case VIDIOC_QUERYBUF: + return et61x251_vidioc_querybuf(cam, arg); + + case VIDIOC_QBUF: + return et61x251_vidioc_qbuf(cam, arg); + + case VIDIOC_DQBUF: + return et61x251_vidioc_dqbuf(cam, filp, arg); + + case VIDIOC_STREAMON: + return et61x251_vidioc_streamon(cam, arg); + + case VIDIOC_STREAMOFF: + return et61x251_vidioc_streamoff(cam, arg); + + case VIDIOC_G_PARM: + return et61x251_vidioc_g_parm(cam, arg); + + case VIDIOC_S_PARM_OLD: + case VIDIOC_S_PARM: + return et61x251_vidioc_s_parm(cam, arg); + + case VIDIOC_G_STD: + case VIDIOC_S_STD: + case VIDIOC_QUERYSTD: + case VIDIOC_ENUMSTD: + case VIDIOC_QUERYMENU: + return -EINVAL; + + default: + return -EINVAL; + + } +} + + +static int et61x251_ioctl(struct inode* inode, struct file* filp, + unsigned int cmd, unsigned long arg) +{ + struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + int err = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + V4LDBG(3, "et61x251", cmd); + + err = et61x251_ioctl_v4l2(inode, filp, cmd, (void __user *)arg); + + mutex_unlock(&cam->fileop_mutex); + + return err; +} + + +static struct file_operations et61x251_fops = { + .owner = THIS_MODULE, + .open = et61x251_open, + .release = et61x251_release, + .ioctl = et61x251_ioctl, + .read = et61x251_read, + .poll = et61x251_poll, + .mmap = et61x251_mmap, + .llseek = no_llseek, +}; + +/*****************************************************************************/ + +/* It exists a single interface only. We do not need to validate anything. */ +static int +et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct et61x251_device* cam; + static unsigned int dev_nr = 0; + unsigned int i; + int err = 0; + + if (!(cam = kzalloc(sizeof(struct et61x251_device), GFP_KERNEL))) + return -ENOMEM; + + cam->usbdev = udev; + + if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) { + DBG(1, "kmalloc() failed"); + err = -ENOMEM; + goto fail; + } + + if (!(cam->v4ldev = video_device_alloc())) { + DBG(1, "video_device_alloc() failed"); + err = -ENOMEM; + goto fail; + } + + mutex_init(&cam->dev_mutex); + + DBG(2, "ET61X[12]51 PC Camera Controller detected " + "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct); + + for (i = 0; et61x251_sensor_table[i]; i++) { + err = et61x251_sensor_table[i](cam); + if (!err) + break; + } + + if (!err) + DBG(2, "%s image sensor detected", cam->sensor.name); + else { + DBG(1, "No supported image sensor detected"); + err = -ENODEV; + goto fail; + } + + if (et61x251_init(cam)) { + DBG(1, "Initialization failed. I will retry on open()."); + cam->state |= DEV_MISCONFIGURED; + } + + strcpy(cam->v4ldev->name, "ET61X[12]51 PC Camera"); + cam->v4ldev->owner = THIS_MODULE; + cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; + cam->v4ldev->hardware = 0; + cam->v4ldev->fops = &et61x251_fops; + cam->v4ldev->minor = video_nr[dev_nr]; + cam->v4ldev->release = video_device_release; + video_set_drvdata(cam->v4ldev, cam); + + mutex_lock(&cam->dev_mutex); + + err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, + video_nr[dev_nr]); + if (err) { + DBG(1, "V4L2 device registration failed"); + if (err == -ENFILE && video_nr[dev_nr] == -1) + DBG(1, "Free /dev/videoX node not found"); + video_nr[dev_nr] = -1; + dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0; + mutex_unlock(&cam->dev_mutex); + goto fail; + } + + DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor); + + cam->module_param.force_munmap = force_munmap[dev_nr]; + cam->module_param.frame_timeout = frame_timeout[dev_nr]; + + dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + et61x251_create_sysfs(cam); + DBG(2, "Optional device control through 'sysfs' interface ready"); +#endif + + usb_set_intfdata(intf, cam); + + mutex_unlock(&cam->dev_mutex); + + return 0; + +fail: + if (cam) { + kfree(cam->control_buffer); + if (cam->v4ldev) + video_device_release(cam->v4ldev); + kfree(cam); + } + return err; +} + + +static void et61x251_usb_disconnect(struct usb_interface* intf) +{ + struct et61x251_device* cam = usb_get_intfdata(intf); + + if (!cam) + return; + + down_write(&et61x251_disconnect); + + mutex_lock(&cam->dev_mutex); + + DBG(2, "Disconnecting %s...", cam->v4ldev->name); + + wake_up_interruptible_all(&cam->open); + + if (cam->users) { + DBG(2, "Device /dev/video%d is open! Deregistration and " + "memory deallocation are deferred on close.", + cam->v4ldev->minor); + cam->state |= DEV_MISCONFIGURED; + et61x251_stop_transfer(cam); + cam->state |= DEV_DISCONNECTED; + wake_up_interruptible(&cam->wait_frame); + wake_up(&cam->wait_stream); + usb_get_dev(cam->usbdev); + } else { + cam->state |= DEV_DISCONNECTED; + et61x251_release_resources(cam); + } + + mutex_unlock(&cam->dev_mutex); + + if (!cam->users) + kfree(cam); + + up_write(&et61x251_disconnect); +} + + +static struct usb_driver et61x251_usb_driver = { + .name = "et61x251", + .id_table = et61x251_id_table, + .probe = et61x251_usb_probe, + .disconnect = et61x251_usb_disconnect, +}; + +/*****************************************************************************/ + +static int __init et61x251_module_init(void) +{ + int err = 0; + + KDBG(2, ET61X251_MODULE_NAME " v" ET61X251_MODULE_VERSION); + KDBG(3, ET61X251_MODULE_AUTHOR); + + if ((err = usb_register(&et61x251_usb_driver))) + KDBG(1, "usb_register() failed"); + + return err; +} + + +static void __exit et61x251_module_exit(void) +{ + usb_deregister(&et61x251_usb_driver); +} + + +module_init(et61x251_module_init); +module_exit(et61x251_module_exit); diff --git a/linux/drivers/media/video/et61x251/et61x251_sensor.h b/linux/drivers/media/video/et61x251/et61x251_sensor.h new file mode 100644 index 000000000..2adfc29ba --- /dev/null +++ b/linux/drivers/media/video/et61x251/et61x251_sensor.h @@ -0,0 +1,117 @@ +/*************************************************************************** + * API for image sensors connected to ET61X[12]51 PC Camera Controllers * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _ET61X251_SENSOR_H_ +#define _ET61X251_SENSOR_H_ + +#include +#include "compat.h" +#include +#include +#include +#include +#include + +struct et61x251_device; +struct et61x251_sensor; + +/*****************************************************************************/ + +extern int et61x251_probe_tas5130d1b(struct et61x251_device* cam); + +#define ET61X251_SENSOR_TABLE \ +/* Weak detections must go at the end of the list */ \ +static int (*et61x251_sensor_table[])(struct et61x251_device*) = { \ + &et61x251_probe_tas5130d1b, \ + NULL, \ +}; + +extern struct et61x251_device* +et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id); + +extern void +et61x251_attach_sensor(struct et61x251_device* cam, + struct et61x251_sensor* sensor); + +/*****************************************************************************/ + +extern int et61x251_write_reg(struct et61x251_device*, u8 value, u16 index); +extern int et61x251_read_reg(struct et61x251_device*, u16 index); +extern int et61x251_i2c_write(struct et61x251_device*, u8 address, u8 value); +extern int et61x251_i2c_read(struct et61x251_device*, u8 address); +extern int et61x251_i2c_try_write(struct et61x251_device*, + struct et61x251_sensor*, u8 address, + u8 value); +extern int et61x251_i2c_try_read(struct et61x251_device*, + struct et61x251_sensor*, u8 address); +extern int et61x251_i2c_raw_write(struct et61x251_device*, u8 n, u8 data1, + u8 data2, u8 data3, u8 data4, u8 data5, + u8 data6, u8 data7, u8 data8, u8 address); + +/*****************************************************************************/ + +enum et61x251_i2c_sysfs_ops { + ET61X251_I2C_READ = 0x01, + ET61X251_I2C_WRITE = 0x02, +}; + +enum et61x251_i2c_interface { + ET61X251_I2C_2WIRES, + ET61X251_I2C_3WIRES, +}; + +/* Repeat start condition when RSTA is high */ +enum et61x251_i2c_rsta { + ET61X251_I2C_RSTA_STOP = 0x00, /* stop then start */ + ET61X251_I2C_RSTA_REPEAT = 0x01, /* repeat start */ +}; + +#define ET61X251_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10 + +struct et61x251_sensor { + char name[32]; + + enum et61x251_i2c_sysfs_ops sysfs_ops; + + enum et61x251_i2c_interface interface; + u8 i2c_slave_id; + enum et61x251_i2c_rsta rsta; + struct v4l2_rect active_pixel; /* left and top define FVSX and FVSY */ + + struct v4l2_queryctrl qctrl[ET61X251_MAX_CTRLS]; + struct v4l2_cropcap cropcap; + struct v4l2_pix_format pix_format; + + int (*init)(struct et61x251_device* cam); + int (*get_ctrl)(struct et61x251_device* cam, + struct v4l2_control* ctrl); + int (*set_ctrl)(struct et61x251_device* cam, + const struct v4l2_control* ctrl); + int (*set_crop)(struct et61x251_device* cam, + const struct v4l2_rect* rect); + int (*set_pix_format)(struct et61x251_device* cam, + const struct v4l2_pix_format* pix); + + /* Private */ + struct v4l2_queryctrl _qctrl[ET61X251_MAX_CTRLS]; + struct v4l2_rect _rect; +}; + +#endif /* _ET61X251_SENSOR_H_ */ diff --git a/linux/drivers/media/video/et61x251/et61x251_tas5130d1b.c b/linux/drivers/media/video/et61x251/et61x251_tas5130d1b.c new file mode 100644 index 000000000..a7d65b82b --- /dev/null +++ b/linux/drivers/media/video/et61x251/et61x251_tas5130d1b.c @@ -0,0 +1,141 @@ +/*************************************************************************** + * Plug-in for TAS5130D1B image sensor connected to the ET61X[12]51 * + * PC Camera Controllers * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "et61x251_sensor.h" + + +static int tas5130d1b_init(struct et61x251_device* cam) +{ + int err = 0; + + err += et61x251_write_reg(cam, 0x14, 0x01); + err += et61x251_write_reg(cam, 0x1b, 0x02); + err += et61x251_write_reg(cam, 0x02, 0x12); + err += et61x251_write_reg(cam, 0x0e, 0x60); + err += et61x251_write_reg(cam, 0x80, 0x61); + err += et61x251_write_reg(cam, 0xf0, 0x62); + err += et61x251_write_reg(cam, 0x03, 0x63); + err += et61x251_write_reg(cam, 0x14, 0x64); + err += et61x251_write_reg(cam, 0xf4, 0x65); + err += et61x251_write_reg(cam, 0x01, 0x66); + err += et61x251_write_reg(cam, 0x05, 0x67); + err += et61x251_write_reg(cam, 0x8f, 0x68); + err += et61x251_write_reg(cam, 0x0f, 0x8d); + err += et61x251_write_reg(cam, 0x08, 0x8e); + + return err; +} + + +static int tas5130d1b_set_ctrl(struct et61x251_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err += et61x251_i2c_raw_write(cam, 2, 0x20, + 0xf6-ctrl->value, 0, 0, 0, + 0, 0, 0, 0); + break; + case V4L2_CID_EXPOSURE: + err += et61x251_i2c_raw_write(cam, 2, 0x40, + 0x47-ctrl->value, 0, 0, 0, + 0, 0, 0, 0); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static struct et61x251_sensor tas5130d1b = { + .name = "TAS5130D1B", + .interface = ET61X251_I2C_3WIRES, + .rsta = ET61X251_I2C_RSTA_STOP, + .active_pixel = { + .left = 106, + .top = 13, + }, + .init = &tas5130d1b_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xf6, + .step = 0x02, + .default_value = 0x0d, + .flags = 0, + }, + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x47, + .step = 0x01, + .default_value = 0x23, + .flags = 0, + }, + }, + .set_ctrl = &tas5130d1b_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, +}; + + +int et61x251_probe_tas5130d1b(struct et61x251_device* cam) +{ + const struct usb_device_id tas5130d1b_id_table[] = { + { USB_DEVICE(0x102c, 0x6251), }, + { } + }; + + /* Sensor detection is based on USB pid/vid */ + if (!et61x251_match_id(cam, tas5130d1b_id_table)) + return -ENODEV; + + et61x251_attach_sensor(cam, &tas5130d1b); + + return 0; +} diff --git a/linux/drivers/media/video/ovcamchip/Makefile b/linux/drivers/media/video/ovcamchip/Makefile new file mode 100644 index 000000000..cba4cdf20 --- /dev/null +++ b/linux/drivers/media/video/ovcamchip/Makefile @@ -0,0 +1,4 @@ +ovcamchip-objs := ovcamchip_core.o ov6x20.o ov6x30.o ov7x10.o ov7x20.o \ + ov76be.o + +obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip.o diff --git a/linux/drivers/media/video/ovcamchip/ov6x20.c b/linux/drivers/media/video/ovcamchip/ov6x20.c new file mode 100644 index 000000000..c04130dab --- /dev/null +++ b/linux/drivers/media/video/ovcamchip/ov6x20.c @@ -0,0 +1,414 @@ +/* OmniVision OV6620/OV6120 Camera Chip Support Code + * + * Copyright (c) 1999-2004 Mark McClelland + * http://alpha.dyndns.org/ov511/ + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. + */ + +#define DEBUG + +#include +#include "ovcamchip_priv.h" + +/* Registers */ +#define REG_GAIN 0x00 /* gain [5:0] */ +#define REG_BLUE 0x01 /* blue gain */ +#define REG_RED 0x02 /* red gain */ +#define REG_SAT 0x03 /* saturation */ +#define REG_CNT 0x05 /* Y contrast */ +#define REG_BRT 0x06 /* Y brightness */ +#define REG_WB_BLUE 0x0C /* WB blue ratio [5:0] */ +#define REG_WB_RED 0x0D /* WB red ratio [5:0] */ +#define REG_EXP 0x10 /* exposure */ + +/* Window parameters */ +#define HWSBASE 0x38 +#define HWEBASE 0x3A +#define VWSBASE 0x05 +#define VWEBASE 0x06 + +struct ov6x20 { + int auto_brt; + int auto_exp; + int backlight; + int bandfilt; + int mirror; +}; + +/* Initial values for use with OV511/OV511+ cameras */ +static struct ovcamchip_regvals regvals_init_6x20_511[] = { + { 0x12, 0x80 }, /* reset */ + { 0x11, 0x01 }, + { 0x03, 0x60 }, + { 0x05, 0x7f }, /* For when autoadjust is off */ + { 0x07, 0xa8 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0x0f, 0x15 }, /* COMS */ + { 0x10, 0x75 }, /* AEC Exposure time */ + { 0x12, 0x24 }, /* Enable AGC and AWB */ + { 0x14, 0x04 }, + { 0x16, 0x03 }, + { 0x26, 0xb2 }, /* BLC enable */ + /* 0x28: 0x05 Selects RGB format if RGB on */ + { 0x28, 0x05 }, + { 0x2a, 0x04 }, /* Disable framerate adjust */ + { 0x2d, 0x99 }, + { 0x33, 0xa0 }, /* Color Processing Parameter */ + { 0x34, 0xd2 }, /* Max A/D range */ + { 0x38, 0x8b }, + { 0x39, 0x40 }, + + { 0x3c, 0x39 }, /* Enable AEC mode changing */ + { 0x3c, 0x3c }, /* Change AEC mode */ + { 0x3c, 0x24 }, /* Disable AEC mode changing */ + + { 0x3d, 0x80 }, + /* These next two registers (0x4a, 0x4b) are undocumented. They + * control the color balance */ + { 0x4a, 0x80 }, + { 0x4b, 0x80 }, + { 0x4d, 0xd2 }, /* This reduces noise a bit */ + { 0x4e, 0xc1 }, + { 0x4f, 0x04 }, + { 0xff, 0xff }, /* END MARKER */ +}; + +/* Initial values for use with OV518 cameras */ +static struct ovcamchip_regvals regvals_init_6x20_518[] = { + { 0x12, 0x80 }, /* Do a reset */ + { 0x03, 0xc0 }, /* Saturation */ + { 0x05, 0x8a }, /* Contrast */ + { 0x0c, 0x24 }, /* AWB blue */ + { 0x0d, 0x24 }, /* AWB red */ + { 0x0e, 0x8d }, /* Additional 2x gain */ + { 0x0f, 0x25 }, /* Black expanding level = 1.3V */ + { 0x11, 0x01 }, /* Clock div. */ + { 0x12, 0x24 }, /* Enable AGC and AWB */ + { 0x13, 0x01 }, /* (default) */ + { 0x14, 0x80 }, /* Set reserved bit 7 */ + { 0x15, 0x01 }, /* (default) */ + { 0x16, 0x03 }, /* (default) */ + { 0x17, 0x38 }, /* (default) */ + { 0x18, 0xea }, /* (default) */ + { 0x19, 0x04 }, + { 0x1a, 0x93 }, + { 0x1b, 0x00 }, /* (default) */ + { 0x1e, 0xc4 }, /* (default) */ + { 0x1f, 0x04 }, /* (default) */ + { 0x20, 0x20 }, /* Enable 1st stage aperture correction */ + { 0x21, 0x10 }, /* Y offset */ + { 0x22, 0x88 }, /* U offset */ + { 0x23, 0xc0 }, /* Set XTAL power level */ + { 0x24, 0x53 }, /* AEC bright ratio */ + { 0x25, 0x7a }, /* AEC black ratio */ + { 0x26, 0xb2 }, /* BLC enable */ + { 0x27, 0xa2 }, /* Full output range */ + { 0x28, 0x01 }, /* (default) */ + { 0x29, 0x00 }, /* (default) */ + { 0x2a, 0x84 }, /* (default) */ + { 0x2b, 0xa8 }, /* Set custom frame rate */ + { 0x2c, 0xa0 }, /* (reserved) */ + { 0x2d, 0x95 }, /* Enable banding filter */ + { 0x2e, 0x88 }, /* V offset */ + { 0x33, 0x22 }, /* Luminance gamma on */ + { 0x34, 0xc7 }, /* A/D bias */ + { 0x36, 0x12 }, /* (reserved) */ + { 0x37, 0x63 }, /* (reserved) */ + { 0x38, 0x8b }, /* Quick AEC/AEB */ + { 0x39, 0x00 }, /* (default) */ + { 0x3a, 0x0f }, /* (default) */ + { 0x3b, 0x3c }, /* (default) */ + { 0x3c, 0x5c }, /* AEC controls */ + { 0x3d, 0x80 }, /* Drop 1 (bad) frame when AEC change */ + { 0x3e, 0x80 }, /* (default) */ + { 0x3f, 0x02 }, /* (default) */ + { 0x40, 0x10 }, /* (reserved) */ + { 0x41, 0x10 }, /* (reserved) */ + { 0x42, 0x00 }, /* (reserved) */ + { 0x43, 0x7f }, /* (reserved) */ + { 0x44, 0x80 }, /* (reserved) */ + { 0x45, 0x1c }, /* (reserved) */ + { 0x46, 0x1c }, /* (reserved) */ + { 0x47, 0x80 }, /* (reserved) */ + { 0x48, 0x5f }, /* (reserved) */ + { 0x49, 0x00 }, /* (reserved) */ + { 0x4a, 0x00 }, /* Color balance (undocumented) */ + { 0x4b, 0x80 }, /* Color balance (undocumented) */ + { 0x4c, 0x58 }, /* (reserved) */ + { 0x4d, 0xd2 }, /* U *= .938, V *= .838 */ + { 0x4e, 0xa0 }, /* (default) */ + { 0x4f, 0x04 }, /* UV 3-point average */ + { 0x50, 0xff }, /* (reserved) */ + { 0x51, 0x58 }, /* (reserved) */ + { 0x52, 0xc0 }, /* (reserved) */ + { 0x53, 0x42 }, /* (reserved) */ + { 0x27, 0xa6 }, /* Enable manual offset adj. (reg 21 & 22) */ + { 0x12, 0x20 }, + { 0x12, 0x24 }, + + { 0xff, 0xff }, /* END MARKER */ +}; + +/* This initializes the OV6x20 camera chip and relevant variables. */ +static int ov6x20_init(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov6x20 *s; + int rc; + + DDEBUG(4, &c->dev, "entered"); + + switch (c->adapter->id) { + case I2C_HW_SMBUS_OV511: + rc = ov_write_regvals(c, regvals_init_6x20_511); + break; + case I2C_HW_SMBUS_OV518: + rc = ov_write_regvals(c, regvals_init_6x20_518); + break; + default: + dev_err(&c->dev, "ov6x20: Unsupported adapter\n"); + rc = -ENODEV; + } + + if (rc < 0) + return rc; + + ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); + if (!s) + return -ENOMEM; + + s->auto_brt = 1; + s->auto_exp = 1; + + return rc; +} + +static int ov6x20_free(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + + kfree(ov->spriv); + return 0; +} + +static int ov6x20_set_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov6x20 *s = ov->spriv; + int rc; + int v = ctl->value; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_write(c, REG_CNT, v >> 8); + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_write(c, REG_BRT, v >> 8); + break; + case OVCAMCHIP_CID_SAT: + rc = ov_write(c, REG_SAT, v >> 8); + break; + case OVCAMCHIP_CID_HUE: + rc = ov_write(c, REG_RED, 0xFF - (v >> 8)); + if (rc < 0) + goto out; + + rc = ov_write(c, REG_BLUE, v >> 8); + break; + case OVCAMCHIP_CID_EXP: + rc = ov_write(c, REG_EXP, v); + break; + case OVCAMCHIP_CID_FREQ: + { + int sixty = (v == 60); + + rc = ov_write(c, 0x2b, sixty?0xa8:0x28); + if (rc < 0) + goto out; + + rc = ov_write(c, 0x2a, sixty?0x84:0xa4); + break; + } + case OVCAMCHIP_CID_BANDFILT: + rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); + s->bandfilt = v; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); + s->auto_brt = v; + break; + case OVCAMCHIP_CID_AUTOEXP: + rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01); + s->auto_exp = v; + break; + case OVCAMCHIP_CID_BACKLIGHT: + { + rc = ov_write_mask(c, 0x4e, v?0xe0:0xc0, 0xe0); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x0e, v?0x80:0x00, 0x80); + s->backlight = v; + break; + } + case OVCAMCHIP_CID_MIRROR: + rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); + s->mirror = v; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + +out: + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); + return rc; +} + +static int ov6x20_get_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov6x20 *s = ov->spriv; + int rc = 0; + unsigned char val = 0; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_read(c, REG_CNT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_read(c, REG_BRT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_SAT: + rc = ov_read(c, REG_SAT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_HUE: + rc = ov_read(c, REG_BLUE, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_EXP: + rc = ov_read(c, REG_EXP, &val); + ctl->value = val; + break; + case OVCAMCHIP_CID_BANDFILT: + ctl->value = s->bandfilt; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + ctl->value = s->auto_brt; + break; + case OVCAMCHIP_CID_AUTOEXP: + ctl->value = s->auto_exp; + break; + case OVCAMCHIP_CID_BACKLIGHT: + ctl->value = s->backlight; + break; + case OVCAMCHIP_CID_MIRROR: + ctl->value = s->mirror; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); + return rc; +} + +static int ov6x20_mode_init(struct i2c_client *c, struct ovcamchip_window *win) +{ + /******** QCIF-specific regs ********/ + + ov_write(c, 0x14, win->quarter?0x24:0x04); + + /******** Palette-specific regs ********/ + + /* OV518 needs 8 bit multiplexed in color mode, and 16 bit in B&W */ + if (c->adapter->id == I2C_HW_SMBUS_OV518) { + if (win->format == VIDEO_PALETTE_GREY) + ov_write_mask(c, 0x13, 0x00, 0x20); + else + ov_write_mask(c, 0x13, 0x20, 0x20); + } else { + if (win->format == VIDEO_PALETTE_GREY) + ov_write_mask(c, 0x13, 0x20, 0x20); + else + ov_write_mask(c, 0x13, 0x00, 0x20); + } + + /******** Clock programming ********/ + + /* The OV6620 needs special handling. This prevents the + * severe banding that normally occurs */ + + /* Clock down */ + ov_write(c, 0x2a, 0x04); + + ov_write(c, 0x11, win->clockdiv); + + ov_write(c, 0x2a, 0x84); + /* This next setting is critical. It seems to improve + * the gain or the contrast. The "reserved" bits seem + * to have some effect in this case. */ + ov_write(c, 0x2d, 0x85); /* FIXME: This messes up banding filter */ + + return 0; +} + +static int ov6x20_set_window(struct i2c_client *c, struct ovcamchip_window *win) +{ + int ret, hwscale, vwscale; + + ret = ov6x20_mode_init(c, win); + if (ret < 0) + return ret; + + if (win->quarter) { + hwscale = 0; + vwscale = 0; + } else { + hwscale = 1; + vwscale = 1; /* The datasheet says 0; it's wrong */ + } + + ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); + ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); + ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); + ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); + + return 0; +} + +static int ov6x20_command(struct i2c_client *c, unsigned int cmd, void *arg) +{ + switch (cmd) { + case OVCAMCHIP_CMD_S_CTRL: + return ov6x20_set_control(c, arg); + case OVCAMCHIP_CMD_G_CTRL: + return ov6x20_get_control(c, arg); + case OVCAMCHIP_CMD_S_MODE: + return ov6x20_set_window(c, arg); + default: + DDEBUG(2, &c->dev, "command not supported: %d", cmd); + return -ENOIOCTLCMD; + } +} + +struct ovcamchip_ops ov6x20_ops = { + .init = ov6x20_init, + .free = ov6x20_free, + .command = ov6x20_command, +}; diff --git a/linux/drivers/media/video/ovcamchip/ov6x30.c b/linux/drivers/media/video/ovcamchip/ov6x30.c new file mode 100644 index 000000000..73b94f51a --- /dev/null +++ b/linux/drivers/media/video/ovcamchip/ov6x30.c @@ -0,0 +1,373 @@ +/* OmniVision OV6630/OV6130 Camera Chip Support Code + * + * Copyright (c) 1999-2004 Mark McClelland + * http://alpha.dyndns.org/ov511/ + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. + */ + +#define DEBUG + +#include +#include "ovcamchip_priv.h" + +/* Registers */ +#define REG_GAIN 0x00 /* gain [5:0] */ +#define REG_BLUE 0x01 /* blue gain */ +#define REG_RED 0x02 /* red gain */ +#define REG_SAT 0x03 /* saturation [7:3] */ +#define REG_CNT 0x05 /* Y contrast [3:0] */ +#define REG_BRT 0x06 /* Y brightness */ +#define REG_SHARP 0x07 /* sharpness */ +#define REG_WB_BLUE 0x0C /* WB blue ratio [5:0] */ +#define REG_WB_RED 0x0D /* WB red ratio [5:0] */ +#define REG_EXP 0x10 /* exposure */ + +/* Window parameters */ +#define HWSBASE 0x38 +#define HWEBASE 0x3A +#define VWSBASE 0x05 +#define VWEBASE 0x06 + +struct ov6x30 { + int auto_brt; + int auto_exp; + int backlight; + int bandfilt; + int mirror; +}; + +static struct ovcamchip_regvals regvals_init_6x30[] = { + { 0x12, 0x80 }, /* reset */ + { 0x00, 0x1f }, /* Gain */ + { 0x01, 0x99 }, /* Blue gain */ + { 0x02, 0x7c }, /* Red gain */ + { 0x03, 0xc0 }, /* Saturation */ + { 0x05, 0x0a }, /* Contrast */ + { 0x06, 0x95 }, /* Brightness */ + { 0x07, 0x2d }, /* Sharpness */ + { 0x0c, 0x20 }, + { 0x0d, 0x20 }, + { 0x0e, 0x20 }, + { 0x0f, 0x05 }, + { 0x10, 0x9a }, /* "exposure check" */ + { 0x11, 0x00 }, /* Pixel clock = fastest */ + { 0x12, 0x24 }, /* Enable AGC and AWB */ + { 0x13, 0x21 }, + { 0x14, 0x80 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x38 }, + { 0x18, 0xea }, + { 0x19, 0x04 }, + { 0x1a, 0x93 }, + { 0x1b, 0x00 }, + { 0x1e, 0xc4 }, + { 0x1f, 0x04 }, + { 0x20, 0x20 }, + { 0x21, 0x10 }, + { 0x22, 0x88 }, + { 0x23, 0xc0 }, /* Crystal circuit power level */ + { 0x25, 0x9a }, /* Increase AEC black pixel ratio */ + { 0x26, 0xb2 }, /* BLC enable */ + { 0x27, 0xa2 }, + { 0x28, 0x00 }, + { 0x29, 0x00 }, + { 0x2a, 0x84 }, /* (keep) */ + { 0x2b, 0xa8 }, /* (keep) */ + { 0x2c, 0xa0 }, + { 0x2d, 0x95 }, /* Enable auto-brightness */ + { 0x2e, 0x88 }, + { 0x33, 0x26 }, + { 0x34, 0x03 }, + { 0x36, 0x8f }, + { 0x37, 0x80 }, + { 0x38, 0x83 }, + { 0x39, 0x80 }, + { 0x3a, 0x0f }, + { 0x3b, 0x3c }, + { 0x3c, 0x1a }, + { 0x3d, 0x80 }, + { 0x3e, 0x80 }, + { 0x3f, 0x0e }, + { 0x40, 0x00 }, /* White bal */ + { 0x41, 0x00 }, /* White bal */ + { 0x42, 0x80 }, + { 0x43, 0x3f }, /* White bal */ + { 0x44, 0x80 }, + { 0x45, 0x20 }, + { 0x46, 0x20 }, + { 0x47, 0x80 }, + { 0x48, 0x7f }, + { 0x49, 0x00 }, + { 0x4a, 0x00 }, + { 0x4b, 0x80 }, + { 0x4c, 0xd0 }, + { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */ + { 0x4e, 0x40 }, + { 0x4f, 0x07 }, /* UV average mode, color killer: strongest */ + { 0x50, 0xff }, + { 0x54, 0x23 }, /* Max AGC gain: 18dB */ + { 0x55, 0xff }, + { 0x56, 0x12 }, + { 0x57, 0x81 }, /* (default) */ + { 0x58, 0x75 }, + { 0x59, 0x01 }, /* AGC dark current compensation: +1 */ + { 0x5a, 0x2c }, + { 0x5b, 0x0f }, /* AWB chrominance levels */ + { 0x5c, 0x10 }, + { 0x3d, 0x80 }, + { 0x27, 0xa6 }, + /* Toggle AWB off and on */ + { 0x12, 0x20 }, + { 0x12, 0x24 }, + + { 0xff, 0xff }, /* END MARKER */ +}; + +/* This initializes the OV6x30 camera chip and relevant variables. */ +static int ov6x30_init(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov6x30 *s; + int rc; + + DDEBUG(4, &c->dev, "entered"); + + rc = ov_write_regvals(c, regvals_init_6x30); + if (rc < 0) + return rc; + + ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); + if (!s) + return -ENOMEM; + + s->auto_brt = 1; + s->auto_exp = 1; + + return rc; +} + +static int ov6x30_free(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + + kfree(ov->spriv); + return 0; +} + +static int ov6x30_set_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov6x30 *s = ov->spriv; + int rc; + int v = ctl->value; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_write_mask(c, REG_CNT, v >> 12, 0x0f); + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_write(c, REG_BRT, v >> 8); + break; + case OVCAMCHIP_CID_SAT: + rc = ov_write(c, REG_SAT, v >> 8); + break; + case OVCAMCHIP_CID_HUE: + rc = ov_write(c, REG_RED, 0xFF - (v >> 8)); + if (rc < 0) + goto out; + + rc = ov_write(c, REG_BLUE, v >> 8); + break; + case OVCAMCHIP_CID_EXP: + rc = ov_write(c, REG_EXP, v); + break; + case OVCAMCHIP_CID_FREQ: + { + int sixty = (v == 60); + + rc = ov_write(c, 0x2b, sixty?0xa8:0x28); + if (rc < 0) + goto out; + + rc = ov_write(c, 0x2a, sixty?0x84:0xa4); + break; + } + case OVCAMCHIP_CID_BANDFILT: + rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); + s->bandfilt = v; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); + s->auto_brt = v; + break; + case OVCAMCHIP_CID_AUTOEXP: + rc = ov_write_mask(c, 0x28, v?0x00:0x10, 0x10); + s->auto_exp = v; + break; + case OVCAMCHIP_CID_BACKLIGHT: + { + rc = ov_write_mask(c, 0x4e, v?0x80:0x60, 0xe0); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x28, v?0x02:0x00, 0x02); + s->backlight = v; + break; + } + case OVCAMCHIP_CID_MIRROR: + rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); + s->mirror = v; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + +out: + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); + return rc; +} + +static int ov6x30_get_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov6x30 *s = ov->spriv; + int rc = 0; + unsigned char val = 0; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_read(c, REG_CNT, &val); + ctl->value = (val & 0x0f) << 12; + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_read(c, REG_BRT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_SAT: + rc = ov_read(c, REG_SAT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_HUE: + rc = ov_read(c, REG_BLUE, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_EXP: + rc = ov_read(c, REG_EXP, &val); + ctl->value = val; + break; + case OVCAMCHIP_CID_BANDFILT: + ctl->value = s->bandfilt; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + ctl->value = s->auto_brt; + break; + case OVCAMCHIP_CID_AUTOEXP: + ctl->value = s->auto_exp; + break; + case OVCAMCHIP_CID_BACKLIGHT: + ctl->value = s->backlight; + break; + case OVCAMCHIP_CID_MIRROR: + ctl->value = s->mirror; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); + return rc; +} + +static int ov6x30_mode_init(struct i2c_client *c, struct ovcamchip_window *win) +{ + /******** QCIF-specific regs ********/ + + ov_write_mask(c, 0x14, win->quarter?0x20:0x00, 0x20); + + /******** Palette-specific regs ********/ + + if (win->format == VIDEO_PALETTE_GREY) { + if (c->adapter->id == I2C_HW_SMBUS_OV518) { + /* Do nothing - we're already in 8-bit mode */ + } else { + ov_write_mask(c, 0x13, 0x20, 0x20); + } + } else { + /* The OV518 needs special treatment. Although both the OV518 + * and the OV6630 support a 16-bit video bus, only the 8 bit Y + * bus is actually used. The UV bus is tied to ground. + * Therefore, the OV6630 needs to be in 8-bit multiplexed + * output mode */ + + if (c->adapter->id == I2C_HW_SMBUS_OV518) { + /* Do nothing - we want to stay in 8-bit mode */ + /* Warning: Messing with reg 0x13 breaks OV518 color */ + } else { + ov_write_mask(c, 0x13, 0x00, 0x20); + } + } + + /******** Clock programming ********/ + + ov_write(c, 0x11, win->clockdiv); + + return 0; +} + +static int ov6x30_set_window(struct i2c_client *c, struct ovcamchip_window *win) +{ + int ret, hwscale, vwscale; + + ret = ov6x30_mode_init(c, win); + if (ret < 0) + return ret; + + if (win->quarter) { + hwscale = 0; + vwscale = 0; + } else { + hwscale = 1; + vwscale = 1; /* The datasheet says 0; it's wrong */ + } + + ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); + ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); + ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); + ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); + + return 0; +} + +static int ov6x30_command(struct i2c_client *c, unsigned int cmd, void *arg) +{ + switch (cmd) { + case OVCAMCHIP_CMD_S_CTRL: + return ov6x30_set_control(c, arg); + case OVCAMCHIP_CMD_G_CTRL: + return ov6x30_get_control(c, arg); + case OVCAMCHIP_CMD_S_MODE: + return ov6x30_set_window(c, arg); + default: + DDEBUG(2, &c->dev, "command not supported: %d", cmd); + return -ENOIOCTLCMD; + } +} + +struct ovcamchip_ops ov6x30_ops = { + .init = ov6x30_init, + .free = ov6x30_free, + .command = ov6x30_command, +}; diff --git a/linux/drivers/media/video/ovcamchip/ov76be.c b/linux/drivers/media/video/ovcamchip/ov76be.c new file mode 100644 index 000000000..11f6be924 --- /dev/null +++ b/linux/drivers/media/video/ovcamchip/ov76be.c @@ -0,0 +1,302 @@ +/* OmniVision OV76BE Camera Chip Support Code + * + * Copyright (c) 1999-2004 Mark McClelland + * http://alpha.dyndns.org/ov511/ + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. + */ + +#define DEBUG + +#include +#include "ovcamchip_priv.h" + +/* OV7610 registers: Since the OV76BE is undocumented, we'll settle for these + * for now. */ +#define REG_GAIN 0x00 /* gain [5:0] */ +#define REG_BLUE 0x01 /* blue channel balance */ +#define REG_RED 0x02 /* red channel balance */ +#define REG_SAT 0x03 /* saturation */ +#define REG_CNT 0x05 /* Y contrast */ +#define REG_BRT 0x06 /* Y brightness */ +#define REG_BLUE_BIAS 0x0C /* blue channel bias [5:0] */ +#define REG_RED_BIAS 0x0D /* red channel bias [5:0] */ +#define REG_GAMMA_COEFF 0x0E /* gamma settings */ +#define REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */ +#define REG_EXP 0x10 /* manual exposure setting */ +#define REG_CLOCK 0x11 /* polarity/clock prescaler */ +#define REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */ +#define REG_HWIN_START 0x17 /* horizontal window start */ +#define REG_HWIN_END 0x18 /* horizontal window end */ +#define REG_VWIN_START 0x19 /* vertical window start */ +#define REG_VWIN_END 0x1A /* vertical window end */ +#define REG_PIXEL_SHIFT 0x1B /* pixel shift */ +#define REG_YOFFSET 0x21 /* Y channel offset */ +#define REG_UOFFSET 0x22 /* U channel offset */ +#define REG_ECW 0x24 /* exposure white level for AEC */ +#define REG_ECB 0x25 /* exposure black level for AEC */ +#define REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */ +#define REG_FRAMERATE_L 0x2B /* frame rate LSB */ +#define REG_ALC 0x2C /* Auto Level Control settings */ +#define REG_VOFFSET 0x2E /* V channel offset adjustment */ +#define REG_ARRAY_BIAS 0x2F /* array bias -- don't change */ +#define REG_YGAMMA 0x33 /* misc gamma settings [7:6] */ +#define REG_BIAS_ADJUST 0x34 /* misc bias settings */ + +/* Window parameters */ +#define HWSBASE 0x38 +#define HWEBASE 0x3a +#define VWSBASE 0x05 +#define VWEBASE 0x05 + +struct ov76be { + int auto_brt; + int auto_exp; + int bandfilt; + int mirror; +}; + +/* NOTE: These are the same as the 7x10 settings, but should eventually be + * optimized for the OV76BE */ +static struct ovcamchip_regvals regvals_init_76be[] = { + { 0x10, 0xff }, + { 0x16, 0x03 }, + { 0x28, 0x24 }, + { 0x2b, 0xac }, + { 0x12, 0x00 }, + { 0x38, 0x81 }, + { 0x28, 0x24 }, /* 0c */ + { 0x0f, 0x85 }, /* lg's setting */ + { 0x15, 0x01 }, + { 0x20, 0x1c }, + { 0x23, 0x2a }, + { 0x24, 0x10 }, + { 0x25, 0x8a }, + { 0x26, 0xa2 }, + { 0x27, 0xc2 }, + { 0x2a, 0x04 }, + { 0x2c, 0xfe }, + { 0x2d, 0x93 }, + { 0x30, 0x71 }, + { 0x31, 0x60 }, + { 0x32, 0x26 }, + { 0x33, 0x20 }, + { 0x34, 0x48 }, + { 0x12, 0x24 }, + { 0x11, 0x01 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0xff, 0xff }, /* END MARKER */ +}; + +/* This initializes the OV76be camera chip and relevant variables. */ +static int ov76be_init(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov76be *s; + int rc; + + DDEBUG(4, &c->dev, "entered"); + + rc = ov_write_regvals(c, regvals_init_76be); + if (rc < 0) + return rc; + + ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); + if (!s) + return -ENOMEM; + + s->auto_brt = 1; + s->auto_exp = 1; + + return rc; +} + +static int ov76be_free(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + + kfree(ov->spriv); + return 0; +} + +static int ov76be_set_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov76be *s = ov->spriv; + int rc; + int v = ctl->value; + + switch (ctl->id) { + case OVCAMCHIP_CID_BRIGHT: + rc = ov_write(c, REG_BRT, v >> 8); + break; + case OVCAMCHIP_CID_SAT: + rc = ov_write(c, REG_SAT, v >> 8); + break; + case OVCAMCHIP_CID_EXP: + rc = ov_write(c, REG_EXP, v); + break; + case OVCAMCHIP_CID_FREQ: + { + int sixty = (v == 60); + + rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80); + if (rc < 0) + goto out; + + rc = ov_write(c, 0x2b, sixty?0x00:0xac); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x76, 0x01, 0x01); + break; + } + case OVCAMCHIP_CID_BANDFILT: + rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); + s->bandfilt = v; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); + s->auto_brt = v; + break; + case OVCAMCHIP_CID_AUTOEXP: + rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01); + s->auto_exp = v; + break; + case OVCAMCHIP_CID_MIRROR: + rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); + s->mirror = v; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + +out: + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); + return rc; +} + +static int ov76be_get_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov76be *s = ov->spriv; + int rc = 0; + unsigned char val = 0; + + switch (ctl->id) { + case OVCAMCHIP_CID_BRIGHT: + rc = ov_read(c, REG_BRT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_SAT: + rc = ov_read(c, REG_SAT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_EXP: + rc = ov_read(c, REG_EXP, &val); + ctl->value = val; + break; + case OVCAMCHIP_CID_BANDFILT: + ctl->value = s->bandfilt; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + ctl->value = s->auto_brt; + break; + case OVCAMCHIP_CID_AUTOEXP: + ctl->value = s->auto_exp; + break; + case OVCAMCHIP_CID_MIRROR: + ctl->value = s->mirror; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); + return rc; +} + +static int ov76be_mode_init(struct i2c_client *c, struct ovcamchip_window *win) +{ + int qvga = win->quarter; + + /******** QVGA-specific regs ********/ + + ov_write(c, 0x14, qvga?0xa4:0x84); + + /******** Palette-specific regs ********/ + + if (win->format == VIDEO_PALETTE_GREY) { + ov_write_mask(c, 0x0e, 0x40, 0x40); + ov_write_mask(c, 0x13, 0x20, 0x20); + } else { + ov_write_mask(c, 0x0e, 0x00, 0x40); + ov_write_mask(c, 0x13, 0x00, 0x20); + } + + /******** Clock programming ********/ + + ov_write(c, 0x11, win->clockdiv); + + /******** Resolution-specific ********/ + + if (win->width == 640 && win->height == 480) + ov_write(c, 0x35, 0x9e); + else + ov_write(c, 0x35, 0x1e); + + return 0; +} + +static int ov76be_set_window(struct i2c_client *c, struct ovcamchip_window *win) +{ + int ret, hwscale, vwscale; + + ret = ov76be_mode_init(c, win); + if (ret < 0) + return ret; + + if (win->quarter) { + hwscale = 1; + vwscale = 0; + } else { + hwscale = 2; + vwscale = 1; + } + + ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); + ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); + ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); + ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); + + return 0; +} + +static int ov76be_command(struct i2c_client *c, unsigned int cmd, void *arg) +{ + switch (cmd) { + case OVCAMCHIP_CMD_S_CTRL: + return ov76be_set_control(c, arg); + case OVCAMCHIP_CMD_G_CTRL: + return ov76be_get_control(c, arg); + case OVCAMCHIP_CMD_S_MODE: + return ov76be_set_window(c, arg); + default: + DDEBUG(2, &c->dev, "command not supported: %d", cmd); + return -ENOIOCTLCMD; + } +} + +struct ovcamchip_ops ov76be_ops = { + .init = ov76be_init, + .free = ov76be_free, + .command = ov76be_command, +}; diff --git a/linux/drivers/media/video/ovcamchip/ov7x10.c b/linux/drivers/media/video/ovcamchip/ov7x10.c new file mode 100644 index 000000000..5206e7913 --- /dev/null +++ b/linux/drivers/media/video/ovcamchip/ov7x10.c @@ -0,0 +1,334 @@ +/* OmniVision OV7610/OV7110 Camera Chip Support Code + * + * Copyright (c) 1999-2004 Mark McClelland + * http://alpha.dyndns.org/ov511/ + * + * Color fixes by by Orion Sky Lawlor (2/26/2000) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. + */ + +#define DEBUG + +#include +#include "ovcamchip_priv.h" + +/* Registers */ +#define REG_GAIN 0x00 /* gain [5:0] */ +#define REG_BLUE 0x01 /* blue channel balance */ +#define REG_RED 0x02 /* red channel balance */ +#define REG_SAT 0x03 /* saturation */ +#define REG_CNT 0x05 /* Y contrast */ +#define REG_BRT 0x06 /* Y brightness */ +#define REG_BLUE_BIAS 0x0C /* blue channel bias [5:0] */ +#define REG_RED_BIAS 0x0D /* red channel bias [5:0] */ +#define REG_GAMMA_COEFF 0x0E /* gamma settings */ +#define REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */ +#define REG_EXP 0x10 /* manual exposure setting */ +#define REG_CLOCK 0x11 /* polarity/clock prescaler */ +#define REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */ +#define REG_HWIN_START 0x17 /* horizontal window start */ +#define REG_HWIN_END 0x18 /* horizontal window end */ +#define REG_VWIN_START 0x19 /* vertical window start */ +#define REG_VWIN_END 0x1A /* vertical window end */ +#define REG_PIXEL_SHIFT 0x1B /* pixel shift */ +#define REG_YOFFSET 0x21 /* Y channel offset */ +#define REG_UOFFSET 0x22 /* U channel offset */ +#define REG_ECW 0x24 /* exposure white level for AEC */ +#define REG_ECB 0x25 /* exposure black level for AEC */ +#define REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */ +#define REG_FRAMERATE_L 0x2B /* frame rate LSB */ +#define REG_ALC 0x2C /* Auto Level Control settings */ +#define REG_VOFFSET 0x2E /* V channel offset adjustment */ +#define REG_ARRAY_BIAS 0x2F /* array bias -- don't change */ +#define REG_YGAMMA 0x33 /* misc gamma settings [7:6] */ +#define REG_BIAS_ADJUST 0x34 /* misc bias settings */ + +/* Window parameters */ +#define HWSBASE 0x38 +#define HWEBASE 0x3a +#define VWSBASE 0x05 +#define VWEBASE 0x05 + +struct ov7x10 { + int auto_brt; + int auto_exp; + int bandfilt; + int mirror; +}; + +/* Lawrence Glaister reports: + * + * Register 0x0f in the 7610 has the following effects: + * + * 0x85 (AEC method 1): Best overall, good contrast range + * 0x45 (AEC method 2): Very overexposed + * 0xa5 (spec sheet default): Ok, but the black level is + * shifted resulting in loss of contrast + * 0x05 (old driver setting): very overexposed, too much + * contrast + */ +static struct ovcamchip_regvals regvals_init_7x10[] = { + { 0x10, 0xff }, + { 0x16, 0x03 }, + { 0x28, 0x24 }, + { 0x2b, 0xac }, + { 0x12, 0x00 }, + { 0x38, 0x81 }, + { 0x28, 0x24 }, /* 0c */ + { 0x0f, 0x85 }, /* lg's setting */ + { 0x15, 0x01 }, + { 0x20, 0x1c }, + { 0x23, 0x2a }, + { 0x24, 0x10 }, + { 0x25, 0x8a }, + { 0x26, 0xa2 }, + { 0x27, 0xc2 }, + { 0x2a, 0x04 }, + { 0x2c, 0xfe }, + { 0x2d, 0x93 }, + { 0x30, 0x71 }, + { 0x31, 0x60 }, + { 0x32, 0x26 }, + { 0x33, 0x20 }, + { 0x34, 0x48 }, + { 0x12, 0x24 }, + { 0x11, 0x01 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0xff, 0xff }, /* END MARKER */ +}; + +/* This initializes the OV7x10 camera chip and relevant variables. */ +static int ov7x10_init(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov7x10 *s; + int rc; + + DDEBUG(4, &c->dev, "entered"); + + rc = ov_write_regvals(c, regvals_init_7x10); + if (rc < 0) + return rc; + + ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); + if (!s) + return -ENOMEM; + + s->auto_brt = 1; + s->auto_exp = 1; + + return rc; +} + +static int ov7x10_free(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + + kfree(ov->spriv); + return 0; +} + +static int ov7x10_set_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov7x10 *s = ov->spriv; + int rc; + int v = ctl->value; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_write(c, REG_CNT, v >> 8); + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_write(c, REG_BRT, v >> 8); + break; + case OVCAMCHIP_CID_SAT: + rc = ov_write(c, REG_SAT, v >> 8); + break; + case OVCAMCHIP_CID_HUE: + rc = ov_write(c, REG_RED, 0xFF - (v >> 8)); + if (rc < 0) + goto out; + + rc = ov_write(c, REG_BLUE, v >> 8); + break; + case OVCAMCHIP_CID_EXP: + rc = ov_write(c, REG_EXP, v); + break; + case OVCAMCHIP_CID_FREQ: + { + int sixty = (v == 60); + + rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80); + if (rc < 0) + goto out; + + rc = ov_write(c, 0x2b, sixty?0x00:0xac); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x13, 0x10, 0x10); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x13, 0x00, 0x10); + break; + } + case OVCAMCHIP_CID_BANDFILT: + rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); + s->bandfilt = v; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); + s->auto_brt = v; + break; + case OVCAMCHIP_CID_AUTOEXP: + rc = ov_write_mask(c, 0x29, v?0x00:0x80, 0x80); + s->auto_exp = v; + break; + case OVCAMCHIP_CID_MIRROR: + rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); + s->mirror = v; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + +out: + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); + return rc; +} + +static int ov7x10_get_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov7x10 *s = ov->spriv; + int rc = 0; + unsigned char val = 0; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_read(c, REG_CNT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_read(c, REG_BRT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_SAT: + rc = ov_read(c, REG_SAT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_HUE: + rc = ov_read(c, REG_BLUE, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_EXP: + rc = ov_read(c, REG_EXP, &val); + ctl->value = val; + break; + case OVCAMCHIP_CID_BANDFILT: + ctl->value = s->bandfilt; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + ctl->value = s->auto_brt; + break; + case OVCAMCHIP_CID_AUTOEXP: + ctl->value = s->auto_exp; + break; + case OVCAMCHIP_CID_MIRROR: + ctl->value = s->mirror; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); + return rc; +} + +static int ov7x10_mode_init(struct i2c_client *c, struct ovcamchip_window *win) +{ + int qvga = win->quarter; + + /******** QVGA-specific regs ********/ + + ov_write(c, 0x14, qvga?0x24:0x04); + + /******** Palette-specific regs ********/ + + if (win->format == VIDEO_PALETTE_GREY) { + ov_write_mask(c, 0x0e, 0x40, 0x40); + ov_write_mask(c, 0x13, 0x20, 0x20); + } else { + ov_write_mask(c, 0x0e, 0x00, 0x40); + ov_write_mask(c, 0x13, 0x00, 0x20); + } + + /******** Clock programming ********/ + + ov_write(c, 0x11, win->clockdiv); + + /******** Resolution-specific ********/ + + if (win->width == 640 && win->height == 480) + ov_write(c, 0x35, 0x9e); + else + ov_write(c, 0x35, 0x1e); + + return 0; +} + +static int ov7x10_set_window(struct i2c_client *c, struct ovcamchip_window *win) +{ + int ret, hwscale, vwscale; + + ret = ov7x10_mode_init(c, win); + if (ret < 0) + return ret; + + if (win->quarter) { + hwscale = 1; + vwscale = 0; + } else { + hwscale = 2; + vwscale = 1; + } + + ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); + ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); + ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); + ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); + + return 0; +} + +static int ov7x10_command(struct i2c_client *c, unsigned int cmd, void *arg) +{ + switch (cmd) { + case OVCAMCHIP_CMD_S_CTRL: + return ov7x10_set_control(c, arg); + case OVCAMCHIP_CMD_G_CTRL: + return ov7x10_get_control(c, arg); + case OVCAMCHIP_CMD_S_MODE: + return ov7x10_set_window(c, arg); + default: + DDEBUG(2, &c->dev, "command not supported: %d", cmd); + return -ENOIOCTLCMD; + } +} + +struct ovcamchip_ops ov7x10_ops = { + .init = ov7x10_init, + .free = ov7x10_free, + .command = ov7x10_command, +}; diff --git a/linux/drivers/media/video/ovcamchip/ov7x20.c b/linux/drivers/media/video/ovcamchip/ov7x20.c new file mode 100644 index 000000000..8e26ae338 --- /dev/null +++ b/linux/drivers/media/video/ovcamchip/ov7x20.c @@ -0,0 +1,454 @@ +/* OmniVision OV7620/OV7120 Camera Chip Support Code + * + * Copyright (c) 1999-2004 Mark McClelland + * http://alpha.dyndns.org/ov511/ + * + * OV7620 fixes by Charl P. Botha + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. + */ + +#define DEBUG + +#include +#include "ovcamchip_priv.h" + +/* Registers */ +#define REG_GAIN 0x00 /* gain [5:0] */ +#define REG_BLUE 0x01 /* blue gain */ +#define REG_RED 0x02 /* red gain */ +#define REG_SAT 0x03 /* saturation */ +#define REG_BRT 0x06 /* Y brightness */ +#define REG_SHARP 0x07 /* analog sharpness */ +#define REG_BLUE_BIAS 0x0C /* WB blue ratio [5:0] */ +#define REG_RED_BIAS 0x0D /* WB red ratio [5:0] */ +#define REG_EXP 0x10 /* exposure */ + +/* Default control settings. Values are in terms of V4L2 controls. */ +#define OV7120_DFL_BRIGHT 0x60 +#define OV7620_DFL_BRIGHT 0x60 +#define OV7120_DFL_SAT 0xb0 +#define OV7620_DFL_SAT 0xc0 +#define DFL_AUTO_EXP 1 +#define DFL_AUTO_GAIN 1 +#define OV7120_DFL_GAIN 0x00 +#define OV7620_DFL_GAIN 0x00 +/* NOTE: Since autoexposure is the default, these aren't programmed into the + * OV7x20 chip. They are just here because V4L2 expects a default */ +#define OV7120_DFL_EXP 0x7f +#define OV7620_DFL_EXP 0x7f + +/* Window parameters */ +#define HWSBASE 0x2F /* From 7620.SET (spec is wrong) */ +#define HWEBASE 0x2F +#define VWSBASE 0x05 +#define VWEBASE 0x05 + +struct ov7x20 { + int auto_brt; + int auto_exp; + int auto_gain; + int backlight; + int bandfilt; + int mirror; +}; + +/* Contrast look-up table */ +static unsigned char ctab[] = { + 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57, + 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff +}; + +/* Settings for (Black & White) OV7120 camera chip */ +static struct ovcamchip_regvals regvals_init_7120[] = { + { 0x12, 0x80 }, /* reset */ + { 0x13, 0x00 }, /* Autoadjust off */ + { 0x12, 0x20 }, /* Disable AWB */ + { 0x13, DFL_AUTO_GAIN?0x01:0x00 }, /* Autoadjust on (if desired) */ + { 0x00, OV7120_DFL_GAIN }, + { 0x01, 0x80 }, + { 0x02, 0x80 }, + { 0x03, OV7120_DFL_SAT }, + { 0x06, OV7120_DFL_BRIGHT }, + { 0x07, 0x00 }, + { 0x0c, 0x20 }, + { 0x0d, 0x20 }, + { 0x11, 0x01 }, + { 0x14, 0x84 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x2f }, + { 0x18, 0xcf }, + { 0x19, 0x06 }, + { 0x1a, 0xf5 }, + { 0x1b, 0x00 }, + { 0x20, 0x08 }, + { 0x21, 0x80 }, + { 0x22, 0x80 }, + { 0x23, 0x00 }, + { 0x26, 0xa0 }, + { 0x27, 0xfa }, + { 0x28, 0x20 }, /* DON'T set bit 6. It is for the OV7620 only */ + { 0x29, DFL_AUTO_EXP?0x00:0x80 }, + { 0x2a, 0x10 }, + { 0x2b, 0x00 }, + { 0x2c, 0x88 }, + { 0x2d, 0x95 }, + { 0x2e, 0x80 }, + { 0x2f, 0x44 }, + { 0x60, 0x20 }, + { 0x61, 0x02 }, + { 0x62, 0x5f }, + { 0x63, 0xd5 }, + { 0x64, 0x57 }, + { 0x65, 0x83 }, /* OV says "don't change this value" */ + { 0x66, 0x55 }, + { 0x67, 0x92 }, + { 0x68, 0xcf }, + { 0x69, 0x76 }, + { 0x6a, 0x22 }, + { 0x6b, 0xe2 }, + { 0x6c, 0x40 }, + { 0x6d, 0x48 }, + { 0x6e, 0x80 }, + { 0x6f, 0x0d }, + { 0x70, 0x89 }, + { 0x71, 0x00 }, + { 0x72, 0x14 }, + { 0x73, 0x54 }, + { 0x74, 0xa0 }, + { 0x75, 0x8e }, + { 0x76, 0x00 }, + { 0x77, 0xff }, + { 0x78, 0x80 }, + { 0x79, 0x80 }, + { 0x7a, 0x80 }, + { 0x7b, 0xe6 }, + { 0x7c, 0x00 }, + { 0x24, 0x3a }, + { 0x25, 0x60 }, + { 0xff, 0xff }, /* END MARKER */ +}; + +/* Settings for (color) OV7620 camera chip */ +static struct ovcamchip_regvals regvals_init_7620[] = { + { 0x12, 0x80 }, /* reset */ + { 0x00, OV7620_DFL_GAIN }, + { 0x01, 0x80 }, + { 0x02, 0x80 }, + { 0x03, OV7620_DFL_SAT }, + { 0x06, OV7620_DFL_BRIGHT }, + { 0x07, 0x00 }, + { 0x0c, 0x24 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0x11, 0x01 }, + { 0x12, 0x24 }, + { 0x13, DFL_AUTO_GAIN?0x01:0x00 }, + { 0x14, 0x84 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x2f }, + { 0x18, 0xcf }, + { 0x19, 0x06 }, + { 0x1a, 0xf5 }, + { 0x1b, 0x00 }, + { 0x20, 0x18 }, + { 0x21, 0x80 }, + { 0x22, 0x80 }, + { 0x23, 0x00 }, + { 0x26, 0xa2 }, + { 0x27, 0xea }, + { 0x28, 0x20 }, + { 0x29, DFL_AUTO_EXP?0x00:0x80 }, + { 0x2a, 0x10 }, + { 0x2b, 0x00 }, + { 0x2c, 0x88 }, + { 0x2d, 0x91 }, + { 0x2e, 0x80 }, + { 0x2f, 0x44 }, + { 0x60, 0x27 }, + { 0x61, 0x02 }, + { 0x62, 0x5f }, + { 0x63, 0xd5 }, + { 0x64, 0x57 }, + { 0x65, 0x83 }, + { 0x66, 0x55 }, + { 0x67, 0x92 }, + { 0x68, 0xcf }, + { 0x69, 0x76 }, + { 0x6a, 0x22 }, + { 0x6b, 0x00 }, + { 0x6c, 0x02 }, + { 0x6d, 0x44 }, + { 0x6e, 0x80 }, + { 0x6f, 0x1d }, + { 0x70, 0x8b }, + { 0x71, 0x00 }, + { 0x72, 0x14 }, + { 0x73, 0x54 }, + { 0x74, 0x00 }, + { 0x75, 0x8e }, + { 0x76, 0x00 }, + { 0x77, 0xff }, + { 0x78, 0x80 }, + { 0x79, 0x80 }, + { 0x7a, 0x80 }, + { 0x7b, 0xe2 }, + { 0x7c, 0x00 }, + { 0xff, 0xff }, /* END MARKER */ +}; + +/* Returns index into the specified look-up table, with 'n' elements, for which + * the value is greater than or equal to "val". If a match isn't found, (n-1) + * is returned. The entries in the table must be in ascending order. */ +static inline int ov7x20_lut_find(unsigned char lut[], int n, unsigned char val) +{ + int i = 0; + + while (lut[i] < val && i < n) + i++; + + return i; +} + +/* This initializes the OV7x20 camera chip and relevant variables. */ +static int ov7x20_init(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov7x20 *s; + int rc; + + DDEBUG(4, &c->dev, "entered"); + + if (ov->mono) + rc = ov_write_regvals(c, regvals_init_7120); + else + rc = ov_write_regvals(c, regvals_init_7620); + + if (rc < 0) + return rc; + + ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); + if (!s) + return -ENOMEM; + + s->auto_brt = 1; + s->auto_exp = DFL_AUTO_EXP; + s->auto_gain = DFL_AUTO_GAIN; + + return 0; +} + +static int ov7x20_free(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + + kfree(ov->spriv); + return 0; +} + +static int ov7x20_set_v4l1_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov7x20 *s = ov->spriv; + int rc; + int v = ctl->value; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + { + /* Use Y gamma control instead. Bit 0 enables it. */ + rc = ov_write(c, 0x64, ctab[v >> 12]); + break; + } + case OVCAMCHIP_CID_BRIGHT: + /* 7620 doesn't like manual changes when in auto mode */ + if (!s->auto_brt) + rc = ov_write(c, REG_BRT, v >> 8); + else + rc = 0; + break; + case OVCAMCHIP_CID_SAT: + rc = ov_write(c, REG_SAT, v >> 8); + break; + case OVCAMCHIP_CID_EXP: + if (!s->auto_exp) + rc = ov_write(c, REG_EXP, v); + else + rc = -EBUSY; + break; + case OVCAMCHIP_CID_FREQ: + { + int sixty = (v == 60); + + rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80); + if (rc < 0) + goto out; + + rc = ov_write(c, 0x2b, sixty?0x00:0xac); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x76, 0x01, 0x01); + break; + } + case OVCAMCHIP_CID_BANDFILT: + rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); + s->bandfilt = v; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); + s->auto_brt = v; + break; + case OVCAMCHIP_CID_AUTOEXP: + rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01); + s->auto_exp = v; + break; + case OVCAMCHIP_CID_BACKLIGHT: + { + rc = ov_write_mask(c, 0x68, v?0xe0:0xc0, 0xe0); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x28, v?0x02:0x00, 0x02); + s->backlight = v; + break; + } + case OVCAMCHIP_CID_MIRROR: + rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); + s->mirror = v; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + +out: + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); + return rc; +} + +static int ov7x20_get_v4l1_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov7x20 *s = ov->spriv; + int rc = 0; + unsigned char val = 0; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_read(c, 0x64, &val); + ctl->value = ov7x20_lut_find(ctab, 16, val) << 12; + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_read(c, REG_BRT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_SAT: + rc = ov_read(c, REG_SAT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_EXP: + rc = ov_read(c, REG_EXP, &val); + ctl->value = val; + break; + case OVCAMCHIP_CID_BANDFILT: + ctl->value = s->bandfilt; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + ctl->value = s->auto_brt; + break; + case OVCAMCHIP_CID_AUTOEXP: + ctl->value = s->auto_exp; + break; + case OVCAMCHIP_CID_BACKLIGHT: + ctl->value = s->backlight; + break; + case OVCAMCHIP_CID_MIRROR: + ctl->value = s->mirror; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); + return rc; +} + +static int ov7x20_mode_init(struct i2c_client *c, struct ovcamchip_window *win) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + int qvga = win->quarter; + + /******** QVGA-specific regs ********/ + ov_write_mask(c, 0x14, qvga?0x20:0x00, 0x20); + ov_write_mask(c, 0x28, qvga?0x00:0x20, 0x20); + ov_write(c, 0x24, qvga?0x20:0x3a); + ov_write(c, 0x25, qvga?0x30:0x60); + ov_write_mask(c, 0x2d, qvga?0x40:0x00, 0x40); + if (!ov->mono) + ov_write_mask(c, 0x67, qvga?0xf0:0x90, 0xf0); + ov_write_mask(c, 0x74, qvga?0x20:0x00, 0x20); + + /******** Clock programming ********/ + + ov_write(c, 0x11, win->clockdiv); + + return 0; +} + +static int ov7x20_set_window(struct i2c_client *c, struct ovcamchip_window *win) +{ + int ret, hwscale, vwscale; + + ret = ov7x20_mode_init(c, win); + if (ret < 0) + return ret; + + if (win->quarter) { + hwscale = 1; + vwscale = 0; + } else { + hwscale = 2; + vwscale = 1; + } + + ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); + ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); + ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); + ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); + + return 0; +} + +static int ov7x20_command(struct i2c_client *c, unsigned int cmd, void *arg) +{ + switch (cmd) { + case OVCAMCHIP_CMD_S_CTRL: + return ov7x20_set_v4l1_control(c, arg); + case OVCAMCHIP_CMD_G_CTRL: + return ov7x20_get_v4l1_control(c, arg); + case OVCAMCHIP_CMD_S_MODE: + return ov7x20_set_window(c, arg); + default: + DDEBUG(2, &c->dev, "command not supported: %d", cmd); + return -ENOIOCTLCMD; + } +} + +struct ovcamchip_ops ov7x20_ops = { + .init = ov7x20_init, + .free = ov7x20_free, + .command = ov7x20_command, +}; diff --git a/linux/drivers/media/video/ovcamchip/ovcamchip_core.c b/linux/drivers/media/video/ovcamchip/ovcamchip_core.c new file mode 100644 index 000000000..3fe9fa04c --- /dev/null +++ b/linux/drivers/media/video/ovcamchip/ovcamchip_core.c @@ -0,0 +1,443 @@ +/* Shared Code for OmniVision Camera Chip Drivers + * + * Copyright (c) 2004 Mark McClelland + * http://alpha.dyndns.org/ov511/ + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include "ovcamchip_priv.h" + +#define DRIVER_VERSION "v2.27 for Linux 2.6" +#define DRIVER_AUTHOR "Mark McClelland " +#define DRIVER_DESC "OV camera chip I2C driver" + +#define PINFO(fmt, args...) printk(KERN_INFO "ovcamchip: " fmt "\n" , ## args); +#define PERROR(fmt, args...) printk(KERN_ERR "ovcamchip: " fmt "\n" , ## args); + +#ifdef DEBUG +int ovcamchip_debug = 0; +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, + "Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=all"); +#endif + +/* By default, let bridge driver tell us if chip is monochrome. mono=0 + * will ignore that and always treat chips as color. mono=1 will force + * monochrome mode for all chips. */ +static int mono = -1; +module_param(mono, int, 0); +MODULE_PARM_DESC(mono, + "1=chips are monochrome (OVx1xx), 0=force color, -1=autodetect (default)"); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* Registers common to all chips, that are needed for detection */ +#define GENERIC_REG_ID_HIGH 0x1C /* manufacturer ID MSB */ +#define GENERIC_REG_ID_LOW 0x1D /* manufacturer ID LSB */ +#define GENERIC_REG_COM_I 0x29 /* misc ID bits */ + +extern struct ovcamchip_ops ov6x20_ops; +extern struct ovcamchip_ops ov6x30_ops; +extern struct ovcamchip_ops ov7x10_ops; +extern struct ovcamchip_ops ov7x20_ops; +extern struct ovcamchip_ops ov76be_ops; + +static char *chip_names[NUM_CC_TYPES] = { + [CC_UNKNOWN] = "Unknown chip", + [CC_OV76BE] = "OV76BE", + [CC_OV7610] = "OV7610", + [CC_OV7620] = "OV7620", + [CC_OV7620AE] = "OV7620AE", + [CC_OV6620] = "OV6620", + [CC_OV6630] = "OV6630", + [CC_OV6630AE] = "OV6630AE", + [CC_OV6630AF] = "OV6630AF", +}; + +/* Forward declarations */ +static struct i2c_driver driver; +static struct i2c_client client_template; + +/* ----------------------------------------------------------------------- */ + +int ov_write_regvals(struct i2c_client *c, struct ovcamchip_regvals *rvals) +{ + int rc; + + while (rvals->reg != 0xff) { + rc = ov_write(c, rvals->reg, rvals->val); + if (rc < 0) + return rc; + rvals++; + } + + return 0; +} + +/* Writes bits at positions specified by mask to an I2C reg. Bits that are in + * the same position as 1's in "mask" are cleared and set to "value". Bits + * that are in the same position as 0's in "mask" are preserved, regardless + * of their respective state in "value". + */ +int ov_write_mask(struct i2c_client *c, + unsigned char reg, + unsigned char value, + unsigned char mask) +{ + int rc; + unsigned char oldval, newval; + + if (mask == 0xff) { + newval = value; + } else { + rc = ov_read(c, reg, &oldval); + if (rc < 0) + return rc; + + oldval &= (~mask); /* Clear the masked bits */ + value &= mask; /* Enforce mask on value */ + newval = oldval | value; /* Set the desired bits */ + } + + return ov_write(c, reg, newval); +} + +/* ----------------------------------------------------------------------- */ + +/* Reset the chip and ensure that I2C is synchronized. Returns <0 if failure. + */ +static int init_camchip(struct i2c_client *c) +{ + int i, success; + unsigned char high, low; + + /* Reset the chip */ + ov_write(c, 0x12, 0x80); + + /* Wait for it to initialize */ + msleep(150); + + for (i = 0, success = 0; i < I2C_DETECT_RETRIES && !success; i++) { + if (ov_read(c, GENERIC_REG_ID_HIGH, &high) >= 0) { + if (ov_read(c, GENERIC_REG_ID_LOW, &low) >= 0) { + if (high == 0x7F && low == 0xA2) { + success = 1; + continue; + } + } + } + + /* Reset the chip */ + ov_write(c, 0x12, 0x80); + + /* Wait for it to initialize */ + msleep(150); + + /* Dummy read to sync I2C */ + ov_read(c, 0x00, &low); + } + + if (!success) + return -EIO; + + PDEBUG(1, "I2C synced in %d attempt(s)", i); + + return 0; +} + +/* This detects the OV7610, OV7620, or OV76BE chip. */ +static int ov7xx0_detect(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + int rc; + unsigned char val; + + PDEBUG(4, ""); + + /* Detect chip (sub)type */ + rc = ov_read(c, GENERIC_REG_COM_I, &val); + if (rc < 0) { + PERROR("Error detecting ov7xx0 type"); + return rc; + } + + if ((val & 3) == 3) { + PINFO("Camera chip is an OV7610"); + ov->subtype = CC_OV7610; + } else if ((val & 3) == 1) { + rc = ov_read(c, 0x15, &val); + if (rc < 0) { + PERROR("Error detecting ov7xx0 type"); + return rc; + } + + if (val & 1) { + PINFO("Camera chip is an OV7620AE"); + /* OV7620 is a close enough match for now. There are + * some definite differences though, so this should be + * fixed */ + ov->subtype = CC_OV7620; + } else { + PINFO("Camera chip is an OV76BE"); + ov->subtype = CC_OV76BE; + } + } else if ((val & 3) == 0) { + PINFO("Camera chip is an OV7620"); + ov->subtype = CC_OV7620; + } else { + PERROR("Unknown camera chip version: %d", val & 3); + return -ENOSYS; + } + + if (ov->subtype == CC_OV76BE) + ov->sops = &ov76be_ops; + else if (ov->subtype == CC_OV7620) + ov->sops = &ov7x20_ops; + else + ov->sops = &ov7x10_ops; + + return 0; +} + +/* This detects the OV6620, OV6630, OV6630AE, or OV6630AF chip. */ +static int ov6xx0_detect(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + int rc; + unsigned char val; + + PDEBUG(4, ""); + + /* Detect chip (sub)type */ + rc = ov_read(c, GENERIC_REG_COM_I, &val); + if (rc < 0) { + PERROR("Error detecting ov6xx0 type"); + return -1; + } + + if ((val & 3) == 0) { + ov->subtype = CC_OV6630; + PINFO("Camera chip is an OV6630"); + } else if ((val & 3) == 1) { + ov->subtype = CC_OV6620; + PINFO("Camera chip is an OV6620"); + } else if ((val & 3) == 2) { + ov->subtype = CC_OV6630; + PINFO("Camera chip is an OV6630AE"); + } else if ((val & 3) == 3) { + ov->subtype = CC_OV6630; + PINFO("Camera chip is an OV6630AF"); + } + + if (ov->subtype == CC_OV6620) + ov->sops = &ov6x20_ops; + else + ov->sops = &ov6x30_ops; + + return 0; +} + +static int ovcamchip_detect(struct i2c_client *c) +{ + /* Ideally we would just try a single register write and see if it NAKs. + * That isn't possible since the OV518 can't report I2C transaction + * failures. So, we have to try to initialize the chip (i.e. reset it + * and check the ID registers) to detect its presence. */ + + /* Test for 7xx0 */ + PDEBUG(3, "Testing for 0V7xx0"); + c->addr = OV7xx0_SID; + if (init_camchip(c) < 0) { + /* Test for 6xx0 */ + PDEBUG(3, "Testing for 0V6xx0"); + c->addr = OV6xx0_SID; + if (init_camchip(c) < 0) { + return -ENODEV; + } else { + if (ov6xx0_detect(c) < 0) { + PERROR("Failed to init OV6xx0"); + return -EIO; + } + } + } else { + if (ov7xx0_detect(c) < 0) { + PERROR("Failed to init OV7xx0"); + return -EIO; + } + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int ovcamchip_attach(struct i2c_adapter *adap) +{ + int rc = 0; + struct ovcamchip *ov; + struct i2c_client *c; + + /* I2C is not a PnP bus, so we can never be certain that we're talking + * to the right chip. To prevent damage to EEPROMS and such, only + * attach to adapters that are known to contain OV camera chips. */ + + switch (adap->id) { + case I2C_HW_SMBUS_OV511: + case I2C_HW_SMBUS_OV518: + case I2C_HW_SMBUS_OVFX2: + case I2C_HW_SMBUS_W9968CF: + PDEBUG(1, "Adapter ID 0x%06x accepted", adap->id); + break; + default: + PDEBUG(1, "Adapter ID 0x%06x rejected", adap->id); + return -ENODEV; + } + + c = kmalloc(sizeof *c, GFP_KERNEL); + if (!c) { + rc = -ENOMEM; + goto no_client; + } + memcpy(c, &client_template, sizeof *c); + c->adapter = adap; + strcpy(c->name, "OV????"); + + ov = kzalloc(sizeof *ov, GFP_KERNEL); + if (!ov) { + rc = -ENOMEM; + goto no_ov; + } + i2c_set_clientdata(c, ov); + + rc = ovcamchip_detect(c); + if (rc < 0) + goto error; + + strcpy(c->name, chip_names[ov->subtype]); + + PDEBUG(1, "Camera chip detection complete"); + + i2c_attach_client(c); + + return rc; +error: + kfree(ov); +no_ov: + kfree(c); +no_client: + PDEBUG(1, "returning %d", rc); + return rc; +} + +static int ovcamchip_detach(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + int rc; + + rc = ov->sops->free(c); + if (rc < 0) + return rc; + + i2c_detach_client(c); + + kfree(ov); + kfree(c); + return 0; +} + +static int ovcamchip_command(struct i2c_client *c, unsigned int cmd, void *arg) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + + if (!ov->initialized && + cmd != OVCAMCHIP_CMD_Q_SUBTYPE && + cmd != OVCAMCHIP_CMD_INITIALIZE) { + dev_err(&c->dev, "ERROR: Camera chip not initialized yet!\n"); + return -EPERM; + } + + switch (cmd) { + case OVCAMCHIP_CMD_Q_SUBTYPE: + { + *(int *)arg = ov->subtype; + return 0; + } + case OVCAMCHIP_CMD_INITIALIZE: + { + int rc; + + if (mono == -1) + ov->mono = *(int *)arg; + else + ov->mono = mono; + + if (ov->mono) { + if (ov->subtype != CC_OV7620) + dev_warn(&c->dev, "Warning: Monochrome not " + "implemented for this chip\n"); + else + dev_info(&c->dev, "Initializing chip as " + "monochrome\n"); + } + + rc = ov->sops->init(c); + if (rc < 0) + return rc; + + ov->initialized = 1; + return 0; + } + default: + return ov->sops->command(c, cmd, arg); + } +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver driver = { + .driver = { + .name = "ovcamchip", + }, + .id = I2C_DRIVERID_OVCAMCHIP, + .class = I2C_CLASS_CAM_DIGITAL, + .attach_adapter = ovcamchip_attach, + .detach_client = ovcamchip_detach, + .command = ovcamchip_command, +}; + +static struct i2c_client client_template = { + .name = "(unset)", + .driver = &driver, +}; + +static int __init ovcamchip_init(void) +{ +#ifdef DEBUG + ovcamchip_debug = debug; +#endif + + PINFO(DRIVER_VERSION " : " DRIVER_DESC); + return i2c_add_driver(&driver); +} + +static void __exit ovcamchip_exit(void) +{ + i2c_del_driver(&driver); +} + +module_init(ovcamchip_init); +module_exit(ovcamchip_exit); diff --git a/linux/drivers/media/video/ovcamchip/ovcamchip_priv.h b/linux/drivers/media/video/ovcamchip/ovcamchip_priv.h new file mode 100644 index 000000000..1231335a9 --- /dev/null +++ b/linux/drivers/media/video/ovcamchip/ovcamchip_priv.h @@ -0,0 +1,87 @@ +/* OmniVision* camera chip driver private definitions for core code and + * chip-specific code + * + * Copyright (c) 1999-2004 Mark McClelland + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. + * + * * OmniVision is a trademark of OmniVision Technologies, Inc. This driver + * is not sponsored or developed by them. + */ + +#ifndef __LINUX_OVCAMCHIP_PRIV_H +#define __LINUX_OVCAMCHIP_PRIV_H + +#include + +#ifdef DEBUG +extern int ovcamchip_debug; +#endif + +#define PDEBUG(level, fmt, args...) \ + if (ovcamchip_debug >= (level)) pr_debug("[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args) + +#define DDEBUG(level, dev, fmt, args...) \ + if (ovcamchip_debug >= (level)) dev_dbg(dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args) + +/* Number of times to retry chip detection. Increase this if you are getting + * "Failed to init camera chip" */ +#define I2C_DETECT_RETRIES 10 + +struct ovcamchip_regvals { + unsigned char reg; + unsigned char val; +}; + +struct ovcamchip_ops { + int (*init)(struct i2c_client *); + int (*free)(struct i2c_client *); + int (*command)(struct i2c_client *, unsigned int, void *); +}; + +struct ovcamchip { + struct ovcamchip_ops *sops; + void *spriv; /* Private data for OV7x10.c etc... */ + int subtype; /* = SEN_OV7610 etc... */ + int mono; /* Monochrome chip? (invalid until init) */ + int initialized; /* OVCAMCHIP_CMD_INITIALIZE was successful */ +}; + +/* --------------------------------- */ +/* I2C I/O */ +/* --------------------------------- */ + +static inline int ov_read(struct i2c_client *c, unsigned char reg, + unsigned char *value) +{ + int rc; + + rc = i2c_smbus_read_byte_data(c, reg); + *value = (unsigned char) rc; + return rc; +} + +static inline int ov_write(struct i2c_client *c, unsigned char reg, + unsigned char value ) +{ + return i2c_smbus_write_byte_data(c, reg, value); +} + +/* --------------------------------- */ +/* FUNCTION PROTOTYPES */ +/* --------------------------------- */ + +/* Functions in ovcamchip_core.c */ + +extern int ov_write_regvals(struct i2c_client *c, + struct ovcamchip_regvals *rvals); + +extern int ov_write_mask(struct i2c_client *c, unsigned char reg, + unsigned char value, unsigned char mask); + +#endif diff --git a/linux/drivers/media/video/pvrusb2/Kconfig b/linux/drivers/media/video/pvrusb2/Kconfig index 89031a2f5..207efa6c0 100644 --- a/linux/drivers/media/video/pvrusb2/Kconfig +++ b/linux/drivers/media/video/pvrusb2/Kconfig @@ -1,6 +1,6 @@ config VIDEO_PVRUSB2 tristate "Hauppauge WinTV-PVR USB2 support" - depends on VIDEO_DEV && USB && I2C && EXPERIMENTAL + depends on VIDEO_V4L1 && USB && I2C && EXPERIMENTAL select FW_LOADER select VIDEO_TUNER select VIDEO_TVEEPROM diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c index 7f29a8821..7d5997bdc 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -24,7 +24,7 @@ #include "pvrusb2-audio.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" -#include +#include #include #include diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-demod.c b/linux/drivers/media/video/pvrusb2/pvrusb2-demod.c index dca787dfa..f082ecee2 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-demod.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-demod.c @@ -26,7 +26,7 @@ #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" #include "compat.h" -#include +#include #include #include diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c index b3d2ce926..fb83cd2c9 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c @@ -23,7 +23,7 @@ #include "pvrusb2-i2c-cmd-v4l2.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" -#include +#include #include diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c index 40fb6ae41..988f32ad4 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include "pvrusb2-hdw.h" #include "pvrusb2-context.h" diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c b/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c index f829c0acc..8bf7cc142 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c @@ -26,7 +26,7 @@ #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" #include "compat.h" -#include +#include #include #include diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 56d15c10d..51491e67c 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -30,7 +30,7 @@ #include "pvrusb2-debug.h" #include "pvrusb2-v4l2.h" #include "pvrusb2-ioread.h" -#include +#include #include diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c index c55f3c653..dde031bb8 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c @@ -34,7 +34,7 @@ #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" -#include +#include #include #include #include diff --git a/linux/drivers/media/video/pwc/Kconfig b/linux/drivers/media/video/pwc/Kconfig new file mode 100644 index 000000000..53cbc950f --- /dev/null +++ b/linux/drivers/media/video/pwc/Kconfig @@ -0,0 +1,28 @@ +config USB_PWC + tristate "USB Philips Cameras" + depends on USB && VIDEO_V4L1 + ---help--- + Say Y or M here if you want to use one of these Philips & OEM + webcams: + * Philips PCA645, PCA646 + * Philips PCVC675, PCVC680, PCVC690 + * Philips PCVC720/40, PCVC730, PCVC740, PCVC750 + * Askey VC010 + * Logitech QuickCam Pro 3000, 4000, 'Zoom', 'Notebook Pro' + and 'Orbit'/'Sphere' + * Samsung MPC-C10, MPC-C30 + * Creative Webcam 5, Pro Ex + * SOTEC Afina Eye + * Visionite VCS-UC300, VCS-UM100 + + The PCA635, PCVC665 and PCVC720/20 are not supported by this driver + and never will be, but the 665 and 720/20 are supported by other + drivers. + + See for more information and + installation instructions. + + The built-in microphone is enabled by selecting USB Audio support. + + To compile this driver as a module, choose M here: the + module will be called pwc. diff --git a/linux/drivers/media/video/pwc/Makefile b/linux/drivers/media/video/pwc/Makefile new file mode 100644 index 000000000..33d60126c --- /dev/null +++ b/linux/drivers/media/video/pwc/Makefile @@ -0,0 +1,3 @@ +pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o pwc-timon.o pwc-kiara.o + +obj-$(CONFIG_USB_PWC) += pwc.o diff --git a/linux/drivers/media/video/pwc/philips.txt b/linux/drivers/media/video/pwc/philips.txt new file mode 100644 index 000000000..11f751a6b --- /dev/null +++ b/linux/drivers/media/video/pwc/philips.txt @@ -0,0 +1,236 @@ +This file contains some additional information for the Philips and OEM webcams. +E-mail: webcam@smcc.demon.nl Last updated: 2004-01-19 +Site: http://www.smcc.demon.nl/webcam/ + +As of this moment, the following cameras are supported: + * Philips PCA645 + * Philips PCA646 + * Philips PCVC675 + * Philips PCVC680 + * Philips PCVC690 + * Philips PCVC720/40 + * Philips PCVC730 + * Philips PCVC740 + * Philips PCVC750 + * Askey VC010 + * Creative Labs Webcam 5 + * Creative Labs Webcam Pro Ex + * Logitech QuickCam 3000 Pro + * Logitech QuickCam 4000 Pro + * Logitech QuickCam Notebook Pro + * Logitech QuickCam Zoom + * Logitech QuickCam Orbit + * Logitech QuickCam Sphere + * Samsung MPC-C10 + * Samsung MPC-C30 + * Sotec Afina Eye + * AME CU-001 + * Visionite VCS-UM100 + * Visionite VCS-UC300 + +The main webpage for the Philips driver is at the address above. It contains +a lot of extra information, a FAQ, and the binary plugin 'PWCX'. This plugin +contains decompression routines that allow you to use higher image sizes and +framerates; in addition the webcam uses less bandwidth on the USB bus (handy +if you want to run more than 1 camera simultaneously). These routines fall +under a NDA, and may therefor not be distributed as source; however, its use +is completely optional. + +You can build this code either into your kernel, or as a module. I recommend +the latter, since it makes troubleshooting a lot easier. The built-in +microphone is supported through the USB Audio class. + +When you load the module you can set some default settings for the +camera; some programs depend on a particular image-size or -format and +don't know how to set it properly in the driver. The options are: + +size + Can be one of 'sqcif', 'qsif', 'qcif', 'sif', 'cif' or + 'vga', for an image size of resp. 128x96, 160x120, 176x144, + 320x240, 352x288 and 640x480 (of course, only for those cameras that + support these resolutions). + +fps + Specifies the desired framerate. Is an integer in the range of 4-30. + +fbufs + This paramter specifies the number of internal buffers to use for storing + frames from the cam. This will help if the process that reads images from + the cam is a bit slow or momentarely busy. However, on slow machines it + only introduces lag, so choose carefully. The default is 3, which is + reasonable. You can set it between 2 and 5. + +mbufs + This is an integer between 1 and 10. It will tell the module the number of + buffers to reserve for mmap(), VIDIOCCGMBUF, VIDIOCMCAPTURE and friends. + The default is 2, which is adequate for most applications (double + buffering). + + Should you experience a lot of 'Dumping frame...' messages during + grabbing with a tool that uses mmap(), you might want to increase if. + However, it doesn't really buffer images, it just gives you a bit more + slack when your program is behind. But you need a multi-threaded or + forked program to really take advantage of these buffers. + + The absolute maximum is 10, but don't set it too high! Every buffer takes + up 460 KB of RAM, so unless you have a lot of memory setting this to + something more than 4 is an absolute waste. This memory is only + allocated during open(), so nothing is wasted when the camera is not in + use. + +power_save + When power_save is enabled (set to 1), the module will try to shut down + the cam on close() and re-activate on open(). This will save power and + turn off the LED. Not all cameras support this though (the 645 and 646 + don't have power saving at all), and some models don't work either (they + will shut down, but never wake up). Consider this experimental. By + default this option is disabled. + +compression (only useful with the plugin) + With this option you can control the compression factor that the camera + uses to squeeze the image through the USB bus. You can set the + parameter between 0 and 3: + 0 = prefer uncompressed images; if the requested mode is not available + in an uncompressed format, the driver will silently switch to low + compression. + 1 = low compression. + 2 = medium compression. + 3 = high compression. + + High compression takes less bandwidth of course, but it could also + introduce some unwanted artefacts. The default is 2, medium compression. + See the FAQ on the website for an overview of which modes require + compression. + + The compression parameter does not apply to the 645 and 646 cameras + and OEM models derived from those (only a few). Most cams honour this + parameter. + +leds + This settings takes 2 integers, that define the on/off time for the LED + (in milliseconds). One of the interesting things that you can do with + this is let the LED blink while the camera is in use. This: + + leds=500,500 + + will blink the LED once every second. But with: + + leds=0,0 + + the LED never goes on, making it suitable for silent surveillance. + + By default the camera's LED is on solid while in use, and turned off + when the camera is not used anymore. + + This parameter works only with the ToUCam range of cameras (720, 730, 740, + 750) and OEMs. For other cameras this command is silently ignored, and + the LED cannot be controlled. + + Finally: this parameters does not take effect UNTIL the first time you + open the camera device. Until then, the LED remains on. + +dev_hint + A long standing problem with USB devices is their dynamic nature: you + never know what device a camera gets assigned; it depends on module load + order, the hub configuration, the order in which devices are plugged in, + and the phase of the moon (i.e. it can be random). With this option you + can give the driver a hint as to what video device node (/dev/videoX) it + should use with a specific camera. This is also handy if you have two + cameras of the same model. + + A camera is specified by its type (the number from the camera model, + like PCA645, PCVC750VC, etc) and optionally the serial number (visible + in /proc/bus/usb/devices). A hint consists of a string with the following + format: + + [type[.serialnumber]:]node + + The square brackets mean that both the type and the serialnumber are + optional, but a serialnumber cannot be specified without a type (which + would be rather pointless). The serialnumber is separated from the type + by a '.'; the node number by a ':'. + + This somewhat cryptic syntax is best explained by a few examples: + + dev_hint=3,5 The first detected cam gets assigned + /dev/video3, the second /dev/video5. Any + other cameras will get the first free + available slot (see below). + + dev_hint=645:1,680:2 The PCA645 camera will get /dev/video1, + and a PCVC680 /dev/video2. + + dev_hint=645.0123:3,645.4567:0 The PCA645 camera with serialnumber + 0123 goes to /dev/video3, the same + camera model with the 4567 serial + gets /dev/video0. + + dev_hint=750:1,4,5,6 The PCVC750 camera will get /dev/video1, the + next 3 Philips cams will use /dev/video4 + through /dev/video6. + + Some points worth knowing: + - Serialnumbers are case sensitive and must be written full, including + leading zeroes (it's treated as a string). + - If a device node is already occupied, registration will fail and + the webcam is not available. + - You can have up to 64 video devices; be sure to make enough device + nodes in /dev if you want to spread the numbers (this does not apply + to devfs). After /dev/video9 comes /dev/video10 (not /dev/videoA). + - If a camera does not match any dev_hint, it will simply get assigned + the first available device node, just as it used to be. + +trace + In order to better detect problems, it is now possible to turn on a + 'trace' of some of the calls the module makes; it logs all items in your + kernel log at debug level. + + The trace variable is a bitmask; each bit represents a certain feature. + If you want to trace something, look up the bit value(s) in the table + below, add the values together and supply that to the trace variable. + + Value Value Description Default + (dec) (hex) + 1 0x1 Module initialization; this will log messages On + while loading and unloading the module + + 2 0x2 probe() and disconnect() traces On + + 4 0x4 Trace open() and close() calls Off + + 8 0x8 read(), mmap() and associated ioctl() calls Off + + 16 0x10 Memory allocation of buffers, etc. Off + + 32 0x20 Showing underflow, overflow and Dumping frame On + messages + + 64 0x40 Show viewport and image sizes Off + + 128 0x80 PWCX debugging Off + + For example, to trace the open() & read() fuctions, sum 8 + 4 = 12, + so you would supply trace=12 during insmod or modprobe. If + you want to turn the initialization and probing tracing off, set trace=0. + The default value for trace is 35 (0x23). + + + +Example: + + # modprobe pwc size=cif fps=15 power_save=1 + +The fbufs, mbufs and trace parameters are global and apply to all connected +cameras. Each camera has its own set of buffers. + +size and fps only specify defaults when you open() the device; this is to +accommodate some tools that don't set the size. You can change these +settings after open() with the Video4Linux ioctl() calls. The default of +defaults is QCIF size at 10 fps. + +The compression parameter is semiglobal; it sets the initial compression +preference for all camera's, but this parameter can be set per camera with +the VIDIOCPWCSCQUAL ioctl() call. + +All parameters are optional. + diff --git a/linux/drivers/media/video/pwc/pwc-ctrl.c b/linux/drivers/media/video/pwc/pwc-ctrl.c new file mode 100644 index 000000000..4ba549bfa --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-ctrl.c @@ -0,0 +1,1541 @@ +/* Driver for Philips webcam + Functions that send various control messages to the webcam, including + video modes. + (C) 1999-2003 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + Changes + 2001/08/03 Alvarado Added methods for changing white balance and + red/green gains + */ + +/* Control functions for the cam; brightness, contrast, video mode, etc. */ + +#ifdef __KERNEL__ +#include +#endif +#include + +#include "pwc.h" +#include "pwc-ioctl.h" +#include "pwc-uncompress.h" +#include "pwc-kiara.h" +#include "pwc-timon.h" + +/* Request types: video */ +#define SET_LUM_CTL 0x01 +#define GET_LUM_CTL 0x02 +#define SET_CHROM_CTL 0x03 +#define GET_CHROM_CTL 0x04 +#define SET_STATUS_CTL 0x05 +#define GET_STATUS_CTL 0x06 +#define SET_EP_STREAM_CTL 0x07 +#define GET_EP_STREAM_CTL 0x08 +#define SET_MPT_CTL 0x0D +#define GET_MPT_CTL 0x0E + +/* Selectors for the Luminance controls [GS]ET_LUM_CTL */ +#define AGC_MODE_FORMATTER 0x2000 +#define PRESET_AGC_FORMATTER 0x2100 +#define SHUTTER_MODE_FORMATTER 0x2200 +#define PRESET_SHUTTER_FORMATTER 0x2300 +#define PRESET_CONTOUR_FORMATTER 0x2400 +#define AUTO_CONTOUR_FORMATTER 0x2500 +#define BACK_LIGHT_COMPENSATION_FORMATTER 0x2600 +#define CONTRAST_FORMATTER 0x2700 +#define DYNAMIC_NOISE_CONTROL_FORMATTER 0x2800 +#define FLICKERLESS_MODE_FORMATTER 0x2900 +#define AE_CONTROL_SPEED 0x2A00 +#define BRIGHTNESS_FORMATTER 0x2B00 +#define GAMMA_FORMATTER 0x2C00 + +/* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */ +#define WB_MODE_FORMATTER 0x1000 +#define AWB_CONTROL_SPEED_FORMATTER 0x1100 +#define AWB_CONTROL_DELAY_FORMATTER 0x1200 +#define PRESET_MANUAL_RED_GAIN_FORMATTER 0x1300 +#define PRESET_MANUAL_BLUE_GAIN_FORMATTER 0x1400 +#define COLOUR_MODE_FORMATTER 0x1500 +#define SATURATION_MODE_FORMATTER1 0x1600 +#define SATURATION_MODE_FORMATTER2 0x1700 + +/* Selectors for the Status controls [GS]ET_STATUS_CTL */ +#define SAVE_USER_DEFAULTS_FORMATTER 0x0200 +#define RESTORE_USER_DEFAULTS_FORMATTER 0x0300 +#define RESTORE_FACTORY_DEFAULTS_FORMATTER 0x0400 +#define READ_AGC_FORMATTER 0x0500 +#define READ_SHUTTER_FORMATTER 0x0600 +#define READ_RED_GAIN_FORMATTER 0x0700 +#define READ_BLUE_GAIN_FORMATTER 0x0800 +#define SENSOR_TYPE_FORMATTER1 0x0C00 +#define READ_RAW_Y_MEAN_FORMATTER 0x3100 +#define SET_POWER_SAVE_MODE_FORMATTER 0x3200 +#define MIRROR_IMAGE_FORMATTER 0x3300 +#define LED_FORMATTER 0x3400 +#define SENSOR_TYPE_FORMATTER2 0x3700 + +/* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */ +#define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100 + +/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */ +#define PT_RELATIVE_CONTROL_FORMATTER 0x01 +#define PT_RESET_CONTROL_FORMATTER 0x02 +#define PT_STATUS_FORMATTER 0x03 + +static const char *size2name[PSZ_MAX] = +{ + "subQCIF", + "QSIF", + "QCIF", + "SIF", + "CIF", + "VGA", +}; + +/********/ + +/* Entries for the Nala (645/646) camera; the Nala doesn't have compression + preferences, so you either get compressed or non-compressed streams. + + An alternate value of 0 means this mode is not available at all. + */ + +struct Nala_table_entry { + char alternate; /* USB alternate setting */ + int compressed; /* Compressed yes/no */ + + unsigned char mode[3]; /* precomputed mode table */ +}; + +static struct Nala_table_entry Nala_table[PSZ_MAX][8] = +{ +#include "pwc-nala.h" +}; + + +/****************************************************************************/ + + +#define SendControlMsg(request, value, buflen) \ + usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), \ + request, \ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \ + value, \ + pdev->vcinterface, \ + &buf, buflen, 500) + +#define RecvControlMsg(request, value, buflen) \ + usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), \ + request, \ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \ + value, \ + pdev->vcinterface, \ + &buf, buflen, 500) + + +#if PWC_DEBUG +void pwc_hexdump(void *p, int len) +{ + int i; + unsigned char *s; + char buf[100], *d; + + s = (unsigned char *)p; + d = buf; + *d = '\0'; + Debug("Doing hexdump @ %p, %d bytes.\n", p, len); + for (i = 0; i < len; i++) { + d += sprintf(d, "%02X ", *s++); + if ((i & 0xF) == 0xF) { + Debug("%s\n", buf); + d = buf; + *d = '\0'; + } + } + if ((i & 0xF) != 0) + Debug("%s\n", buf); +} +#endif + +static inline int send_video_command(struct usb_device *udev, int index, void *buf, int buflen) +{ + return usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + SET_EP_STREAM_CTL, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + VIDEO_OUTPUT_CONTROL_FORMATTER, + index, + buf, buflen, 1000); +} + + + +static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames) +{ + unsigned char buf[3]; + int ret, fps; + struct Nala_table_entry *pEntry; + int frames2frames[31] = + { /* closest match of framerate */ + 0, 0, 0, 0, 4, /* 0-4 */ + 5, 5, 7, 7, 10, /* 5-9 */ + 10, 10, 12, 12, 15, /* 10-14 */ + 15, 15, 15, 20, 20, /* 15-19 */ + 20, 20, 20, 24, 24, /* 20-24 */ + 24, 24, 24, 24, 24, /* 25-29 */ + 24 /* 30 */ + }; + int frames2table[31] = + { 0, 0, 0, 0, 0, /* 0-4 */ + 1, 1, 1, 2, 2, /* 5-9 */ + 3, 3, 4, 4, 4, /* 10-14 */ + 5, 5, 5, 5, 5, /* 15-19 */ + 6, 6, 6, 6, 7, /* 20-24 */ + 7, 7, 7, 7, 7, /* 25-29 */ + 7 /* 30 */ + }; + + if (size < 0 || size > PSZ_CIF || frames < 4 || frames > 25) + return -EINVAL; + frames = frames2frames[frames]; + fps = frames2table[frames]; + pEntry = &Nala_table[size][fps]; + if (pEntry->alternate == 0) + return -EINVAL; + + if (pEntry->compressed) + return -ENOENT; /* Not supported. */ + + memcpy(buf, pEntry->mode, 3); + ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 3); + if (ret < 0) { + Debug("Failed to send video command... %d\n", ret); + return ret; + } + if (pEntry->compressed && pdev->vpalette != VIDEO_PALETTE_RAW) + { + switch(pdev->type) { + case 645: + case 646: +/* pwc_dec1_init(pdev->type, pdev->release, buf, pdev->decompress_data); */ + break; + + case 675: + case 680: + case 690: + case 720: + case 730: + case 740: + case 750: +/* pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); */ + break; + } + } + + pdev->cmd_len = 3; + memcpy(pdev->cmd_buf, buf, 3); + + /* Set various parameters */ + pdev->vframes = frames; + pdev->vsize = size; + pdev->valternate = pEntry->alternate; + pdev->image = pwc_image_sizes[size]; + pdev->frame_size = (pdev->image.x * pdev->image.y * 3) / 2; + if (pEntry->compressed) { + if (pdev->release < 5) { /* 4 fold compression */ + pdev->vbandlength = 528; + pdev->frame_size /= 4; + } + else { + pdev->vbandlength = 704; + pdev->frame_size /= 3; + } + } + else + pdev->vbandlength = 0; + return 0; +} + + +static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) +{ + unsigned char buf[13]; + const struct Timon_table_entry *pChoose; + int ret, fps; + + if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) + return -EINVAL; + if (size == PSZ_VGA && frames > 15) + return -EINVAL; + fps = (frames / 5) - 1; + + /* Find a supported framerate with progressively higher compression ratios + if the preferred ratio is not available. + */ + pChoose = NULL; + while (compression <= 3) { + pChoose = &Timon_table[size][fps][compression]; + if (pChoose->alternate != 0) + break; + compression++; + } + if (pChoose == NULL || pChoose->alternate == 0) + return -ENOENT; /* Not supported. */ + + memcpy(buf, pChoose->mode, 13); + if (snapshot) + buf[0] |= 0x80; + ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 13); + if (ret < 0) + return ret; + +/* if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) + pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); */ + + pdev->cmd_len = 13; + memcpy(pdev->cmd_buf, buf, 13); + + /* Set various parameters */ + pdev->vframes = frames; + pdev->vsize = size; + pdev->vsnapshot = snapshot; + pdev->valternate = pChoose->alternate; + pdev->image = pwc_image_sizes[size]; + pdev->vbandlength = pChoose->bandlength; + if (pChoose->bandlength > 0) + pdev->frame_size = (pChoose->bandlength * pdev->image.y) / 4; + else + pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; + return 0; +} + + +static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) +{ + const struct Kiara_table_entry *pChoose = NULL; + int fps, ret; + unsigned char buf[12]; + struct Kiara_table_entry RawEntry = {6, 773, 1272, {0xAD, 0xF4, 0x10, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}}; + + if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) + return -EINVAL; + if (size == PSZ_VGA && frames > 15) + return -EINVAL; + fps = (frames / 5) - 1; + + /* special case: VGA @ 5 fps and snapshot is raw bayer mode */ + if (size == PSZ_VGA && frames == 5 && snapshot) + { + /* Only available in case the raw palette is selected or + we have the decompressor available. This mode is + only available in compressed form + */ + if (pdev->vpalette == VIDEO_PALETTE_RAW) + { + Info("Choosing VGA/5 BAYER mode (%d).\n", pdev->vpalette); + pChoose = &RawEntry; + } + else + { + Info("VGA/5 BAYER mode _must_ have a decompressor available, or use RAW palette.\n"); + } + } + else + { + /* Find a supported framerate with progressively higher compression ratios + if the preferred ratio is not available. + Skip this step when using RAW modes. + */ + while (compression <= 3) { + pChoose = &Kiara_table[size][fps][compression]; + if (pChoose->alternate != 0) + break; + compression++; + } + } + if (pChoose == NULL || pChoose->alternate == 0) + return -ENOENT; /* Not supported. */ + + Debug("Using alternate setting %d.\n", pChoose->alternate); + + /* usb_control_msg won't take staticly allocated arrays as argument?? */ + memcpy(buf, pChoose->mode, 12); + if (snapshot) + buf[0] |= 0x80; + + /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */ + ret = send_video_command(pdev->udev, 4 /* pdev->vendpoint */, buf, 12); + if (ret < 0) + return ret; + +/* if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) + pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); */ + + pdev->cmd_len = 12; + memcpy(pdev->cmd_buf, buf, 12); + /* All set and go */ + pdev->vframes = frames; + pdev->vsize = size; + pdev->vsnapshot = snapshot; + pdev->valternate = pChoose->alternate; + pdev->image = pwc_image_sizes[size]; + pdev->vbandlength = pChoose->bandlength; + if (pdev->vbandlength > 0) + pdev->frame_size = (pdev->vbandlength * pdev->image.y) / 4; + else + pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; + return 0; +} + + + +static void pwc_set_image_buffer_size(struct pwc_device *pdev) +{ + int i, factor = 0, filler = 0; + + /* for PALETTE_YUV420P */ + switch(pdev->vpalette) + { + case VIDEO_PALETTE_YUV420P: + factor = 6; + filler = 128; + break; + case VIDEO_PALETTE_RAW: + factor = 6; /* can be uncompressed YUV420P */ + filler = 0; + break; + } + + /* Set sizes in bytes */ + pdev->image.size = pdev->image.x * pdev->image.y * factor / 4; + pdev->view.size = pdev->view.x * pdev->view.y * factor / 4; + + /* Align offset, or you'll get some very weird results in + YUV420 mode... x must be multiple of 4 (to get the Y's in + place), and y even (or you'll mixup U & V). This is less of a + problem for YUV420P. + */ + pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC; + pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE; + + /* Fill buffers with gray or black */ + for (i = 0; i < MAX_IMAGES; i++) { + if (pdev->image_ptr[i] != NULL) + memset(pdev->image_ptr[i], filler, pdev->view.size); + } +} + + + +/** + @pdev: device structure + @width: viewport width + @height: viewport height + @frame: framerate, in fps + @compression: preferred compression ratio + @snapshot: snapshot mode or streaming + */ +int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot) +{ + int ret, size; + + Trace(TRACE_FLOW, "set_video_mode(%dx%d @ %d, palette %d).\n", width, height, frames, pdev->vpalette); + size = pwc_decode_size(pdev, width, height); + if (size < 0) { + Debug("Could not find suitable size.\n"); + return -ERANGE; + } + Debug("decode_size = %d.\n", size); + + ret = -EINVAL; + switch(pdev->type) { + case 645: + case 646: + ret = set_video_mode_Nala(pdev, size, frames); + break; + + case 675: + case 680: + case 690: + ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot); + break; + + case 720: + case 730: + case 740: + case 750: + ret = set_video_mode_Kiara(pdev, size, frames, compression, snapshot); + break; + } + if (ret < 0) { + if (ret == -ENOENT) + Info("Video mode %s@%d fps is only supported with the decompressor module (pwcx).\n", size2name[size], frames); + else { + Err("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); + } + return ret; + } + pdev->view.x = width; + pdev->view.y = height; + pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; + pwc_set_image_buffer_size(pdev); + Trace(TRACE_SIZE, "Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y); + return 0; +} + + +/* BRIGHTNESS */ + +int pwc_get_brightness(struct pwc_device *pdev) +{ + char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, BRIGHTNESS_FORMATTER, 1); + if (ret < 0) + return ret; + return buf << 9; +} + +int pwc_set_brightness(struct pwc_device *pdev, int value) +{ + char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + buf = (value >> 9) & 0x7f; + return SendControlMsg(SET_LUM_CTL, BRIGHTNESS_FORMATTER, 1); +} + +/* CONTRAST */ + +int pwc_get_contrast(struct pwc_device *pdev) +{ + char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, CONTRAST_FORMATTER, 1); + if (ret < 0) + return ret; + return buf << 10; +} + +int pwc_set_contrast(struct pwc_device *pdev, int value) +{ + char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + buf = (value >> 10) & 0x3f; + return SendControlMsg(SET_LUM_CTL, CONTRAST_FORMATTER, 1); +} + +/* GAMMA */ + +int pwc_get_gamma(struct pwc_device *pdev) +{ + char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, GAMMA_FORMATTER, 1); + if (ret < 0) + return ret; + return buf << 11; +} + +int pwc_set_gamma(struct pwc_device *pdev, int value) +{ + char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + buf = (value >> 11) & 0x1f; + return SendControlMsg(SET_LUM_CTL, GAMMA_FORMATTER, 1); +} + + +/* SATURATION */ + +int pwc_get_saturation(struct pwc_device *pdev) +{ + char buf; + int ret; + + if (pdev->type < 675) + return -1; + ret = RecvControlMsg(GET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1); + if (ret < 0) + return ret; + return 32768 + buf * 327; +} + +int pwc_set_saturation(struct pwc_device *pdev, int value) +{ + char buf; + + if (pdev->type < 675) + return -EINVAL; + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + /* saturation ranges from -100 to +100 */ + buf = (value - 32768) / 327; + return SendControlMsg(SET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1); +} + +/* AGC */ + +static inline int pwc_set_agc(struct pwc_device *pdev, int mode, int value) +{ + char buf; + int ret; + + if (mode) + buf = 0x0; /* auto */ + else + buf = 0xff; /* fixed */ + + ret = SendControlMsg(SET_LUM_CTL, AGC_MODE_FORMATTER, 1); + + if (!mode && ret >= 0) { + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + buf = (value >> 10) & 0x3F; + ret = SendControlMsg(SET_LUM_CTL, PRESET_AGC_FORMATTER, 1); + } + if (ret < 0) + return ret; + return 0; +} + +static inline int pwc_get_agc(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, AGC_MODE_FORMATTER, 1); + if (ret < 0) + return ret; + + if (buf != 0) { /* fixed */ + ret = RecvControlMsg(GET_LUM_CTL, PRESET_AGC_FORMATTER, 1); + if (ret < 0) + return ret; + if (buf > 0x3F) + buf = 0x3F; + *value = (buf << 10); + } + else { /* auto */ + ret = RecvControlMsg(GET_STATUS_CTL, READ_AGC_FORMATTER, 1); + if (ret < 0) + return ret; + /* Gah... this value ranges from 0x00 ... 0x9F */ + if (buf > 0x9F) + buf = 0x9F; + *value = -(48 + buf * 409); + } + + return 0; +} + +static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value) +{ + char buf[2]; + int speed, ret; + + + if (mode) + buf[0] = 0x0; /* auto */ + else + buf[0] = 0xff; /* fixed */ + + ret = SendControlMsg(SET_LUM_CTL, SHUTTER_MODE_FORMATTER, 1); + + if (!mode && ret >= 0) { + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + switch(pdev->type) { + case 675: + case 680: + case 690: + /* speed ranges from 0x0 to 0x290 (656) */ + speed = (value / 100); + buf[1] = speed >> 8; + buf[0] = speed & 0xff; + break; + case 720: + case 730: + case 740: + case 750: + /* speed seems to range from 0x0 to 0xff */ + buf[1] = 0; + buf[0] = value >> 8; + break; + } + + ret = SendControlMsg(SET_LUM_CTL, PRESET_SHUTTER_FORMATTER, 2); + } + return ret; +} + + +/* POWER */ + +int pwc_camera_power(struct pwc_device *pdev, int power) +{ + char buf; + + if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6)) + return 0; /* Not supported by Nala or Timon < release 6 */ + + if (power) + buf = 0x00; /* active */ + else + buf = 0xFF; /* power save */ + return SendControlMsg(SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER, 1); +} + + + +/* private calls */ + +static inline int pwc_restore_user(struct pwc_device *pdev) +{ + char buf; /* dummy */ + return SendControlMsg(SET_STATUS_CTL, RESTORE_USER_DEFAULTS_FORMATTER, 0); +} + +static inline int pwc_save_user(struct pwc_device *pdev) +{ + char buf; /* dummy */ + return SendControlMsg(SET_STATUS_CTL, SAVE_USER_DEFAULTS_FORMATTER, 0); +} + +static inline int pwc_restore_factory(struct pwc_device *pdev) +{ + char buf; /* dummy */ + return SendControlMsg(SET_STATUS_CTL, RESTORE_FACTORY_DEFAULTS_FORMATTER, 0); +} + + /* ************************************************* */ + /* Patch by Alvarado: (not in the original version */ + + /* + * the camera recognizes modes from 0 to 4: + * + * 00: indoor (incandescant lighting) + * 01: outdoor (sunlight) + * 02: fluorescent lighting + * 03: manual + * 04: auto + */ +static inline int pwc_set_awb(struct pwc_device *pdev, int mode) +{ + char buf; + int ret; + + if (mode < 0) + mode = 0; + + if (mode > 4) + mode = 4; + + buf = mode & 0x07; /* just the lowest three bits */ + + ret = SendControlMsg(SET_CHROM_CTL, WB_MODE_FORMATTER, 1); + + if (ret < 0) + return ret; + return 0; +} + +static inline int pwc_get_awb(struct pwc_device *pdev) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, WB_MODE_FORMATTER, 1); + + if (ret < 0) + return ret; + return buf; +} + +static inline int pwc_set_red_gain(struct pwc_device *pdev, int value) +{ + unsigned char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + /* only the msb is considered */ + buf = value >> 8; + return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1); +} + +static inline int pwc_get_red_gain(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 8; + return 0; +} + + +static inline int pwc_set_blue_gain(struct pwc_device *pdev, int value) +{ + unsigned char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + /* only the msb is considered */ + buf = value >> 8; + return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1); +} + +static inline int pwc_get_blue_gain(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 8; + return 0; +} + + +/* The following two functions are different, since they only read the + internal red/blue gains, which may be different from the manual + gains set or read above. + */ +static inline int pwc_read_red_gain(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_STATUS_CTL, READ_RED_GAIN_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 8; + return 0; +} + +static inline int pwc_read_blue_gain(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_STATUS_CTL, READ_BLUE_GAIN_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 8; + return 0; +} + + +static inline int pwc_set_wb_speed(struct pwc_device *pdev, int speed) +{ + unsigned char buf; + + /* useful range is 0x01..0x20 */ + buf = speed / 0x7f0; + return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1); +} + +static inline int pwc_get_wb_speed(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf * 0x7f0; + return 0; +} + + +static inline int pwc_set_wb_delay(struct pwc_device *pdev, int delay) +{ + unsigned char buf; + + /* useful range is 0x01..0x3F */ + buf = (delay >> 10); + return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1); +} + +static inline int pwc_get_wb_delay(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 10; + return 0; +} + + +int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) +{ + unsigned char buf[2]; + + if (pdev->type < 730) + return 0; + on_value /= 100; + off_value /= 100; + if (on_value < 0) + on_value = 0; + if (on_value > 0xff) + on_value = 0xff; + if (off_value < 0) + off_value = 0; + if (off_value > 0xff) + off_value = 0xff; + + buf[0] = on_value; + buf[1] = off_value; + + return SendControlMsg(SET_STATUS_CTL, LED_FORMATTER, 2); +} + +static int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) +{ + unsigned char buf[2]; + int ret; + + if (pdev->type < 730) { + *on_value = -1; + *off_value = -1; + return 0; + } + + ret = RecvControlMsg(GET_STATUS_CTL, LED_FORMATTER, 2); + if (ret < 0) + return ret; + *on_value = buf[0] * 100; + *off_value = buf[1] * 100; + return 0; +} + +static inline int pwc_set_contour(struct pwc_device *pdev, int contour) +{ + unsigned char buf; + int ret; + + if (contour < 0) + buf = 0xff; /* auto contour on */ + else + buf = 0x0; /* auto contour off */ + ret = SendControlMsg(SET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1); + if (ret < 0) + return ret; + + if (contour < 0) + return 0; + if (contour > 0xffff) + contour = 0xffff; + + buf = (contour >> 10); /* contour preset is [0..3f] */ + ret = SendControlMsg(SET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1); + if (ret < 0) + return ret; + return 0; +} + +static inline int pwc_get_contour(struct pwc_device *pdev, int *contour) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1); + if (ret < 0) + return ret; + + if (buf == 0) { + /* auto mode off, query current preset value */ + ret = RecvControlMsg(GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1); + if (ret < 0) + return ret; + *contour = buf << 10; + } + else + *contour = -1; + return 0; +} + + +static inline int pwc_set_backlight(struct pwc_device *pdev, int backlight) +{ + unsigned char buf; + + if (backlight) + buf = 0xff; + else + buf = 0x0; + return SendControlMsg(SET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); +} + +static inline int pwc_get_backlight(struct pwc_device *pdev, int *backlight) +{ + int ret; + unsigned char buf; + + ret = RecvControlMsg(GET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); + if (ret < 0) + return ret; + *backlight = buf; + return 0; +} + + +static inline int pwc_set_flicker(struct pwc_device *pdev, int flicker) +{ + unsigned char buf; + + if (flicker) + buf = 0xff; + else + buf = 0x0; + return SendControlMsg(SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); +} + +static inline int pwc_get_flicker(struct pwc_device *pdev, int *flicker) +{ + int ret; + unsigned char buf; + + ret = RecvControlMsg(GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); + if (ret < 0) + return ret; + *flicker = buf; + return 0; +} + + +static inline int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise) +{ + unsigned char buf; + + if (noise < 0) + noise = 0; + if (noise > 3) + noise = 3; + buf = noise; + return SendControlMsg(SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); +} + +static inline int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise) +{ + int ret; + unsigned char buf; + + ret = RecvControlMsg(GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); + if (ret < 0) + return ret; + *noise = buf; + return 0; +} + +static int pwc_mpt_reset(struct pwc_device *pdev, int flags) +{ + unsigned char buf; + + buf = flags & 0x03; // only lower two bits are currently used + return SendControlMsg(SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, 1); +} + +static inline int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) +{ + unsigned char buf[4]; + + /* set new relative angle; angles are expressed in degrees * 100, + but cam as .5 degree resolution, hence divide by 200. Also + the angle must be multiplied by 64 before it's send to + the cam (??) + */ + pan = 64 * pan / 100; + tilt = -64 * tilt / 100; /* positive tilt is down, which is not what the user would expect */ + buf[0] = pan & 0xFF; + buf[1] = (pan >> 8) & 0xFF; + buf[2] = tilt & 0xFF; + buf[3] = (tilt >> 8) & 0xFF; + return SendControlMsg(SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, 4); +} + +static inline int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status) +{ + int ret; + unsigned char buf[5]; + + ret = RecvControlMsg(GET_MPT_CTL, PT_STATUS_FORMATTER, 5); + if (ret < 0) + return ret; + status->status = buf[0] & 0x7; // 3 bits are used for reporting + status->time_pan = (buf[1] << 8) + buf[2]; + status->time_tilt = (buf[3] << 8) + buf[4]; + return 0; +} + + +int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) +{ + unsigned char buf; + int ret = -1, request; + + if (pdev->type < 675) + request = SENSOR_TYPE_FORMATTER1; + else if (pdev->type < 730) + return -1; /* The Vesta series doesn't have this call */ + else + request = SENSOR_TYPE_FORMATTER2; + + ret = RecvControlMsg(GET_STATUS_CTL, request, 1); + if (ret < 0) + return ret; + if (pdev->type < 675) + *sensor = buf | 0x100; + else + *sensor = buf; + return 0; +} + + + /* End of Add-Ons */ + /* ************************************************* */ + + +int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) +{ + int ret = 0; + + switch(cmd) { + case VIDIOCPWCRUSER: + { + if (pwc_restore_user(pdev)) + ret = -EINVAL; + break; + } + + case VIDIOCPWCSUSER: + { + if (pwc_save_user(pdev)) + ret = -EINVAL; + break; + } + + case VIDIOCPWCFACTORY: + { + if (pwc_restore_factory(pdev)) + ret = -EINVAL; + break; + } + + case VIDIOCPWCSCQUAL: + { + int *qual = arg; + + if (*qual < 0 || *qual > 3) + ret = -EINVAL; + else + ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, *qual, pdev->vsnapshot); + if (ret >= 0) + pdev->vcompression = *qual; + break; + } + + case VIDIOCPWCGCQUAL: + { + int *qual = arg; + *qual = pdev->vcompression; + break; + } + + case VIDIOCPWCPROBE: + { + struct pwc_probe *probe = arg; + strcpy(probe->name, pdev->vdev->name); + probe->type = pdev->type; + break; + } + + case VIDIOCPWCGSERIAL: + { + struct pwc_serial *serial = arg; + strcpy(serial->serial, pdev->serial); + break; + } + + case VIDIOCPWCSAGC: + { + int *agc = arg; + if (pwc_set_agc(pdev, *agc < 0 ? 1 : 0, *agc)) + ret = -EINVAL; + break; + } + + case VIDIOCPWCGAGC: + { + int *agc = arg; + + if (pwc_get_agc(pdev, agc)) + ret = -EINVAL; + break; + } + + case VIDIOCPWCSSHUTTER: + { + int *shutter_speed = arg; + ret = pwc_set_shutter_speed(pdev, *shutter_speed < 0 ? 1 : 0, *shutter_speed); + break; + } + + case VIDIOCPWCSAWB: + { + struct pwc_whitebalance *wb = arg; + + ret = pwc_set_awb(pdev, wb->mode); + if (ret >= 0 && wb->mode == PWC_WB_MANUAL) { + pwc_set_red_gain(pdev, wb->manual_red); + pwc_set_blue_gain(pdev, wb->manual_blue); + } + break; + } + + case VIDIOCPWCGAWB: + { + struct pwc_whitebalance *wb = arg; + + memset(wb, 0, sizeof(struct pwc_whitebalance)); + wb->mode = pwc_get_awb(pdev); + if (wb->mode < 0) + ret = -EINVAL; + else { + if (wb->mode == PWC_WB_MANUAL) { + ret = pwc_get_red_gain(pdev, &wb->manual_red); + if (ret < 0) + break; + ret = pwc_get_blue_gain(pdev, &wb->manual_blue); + if (ret < 0) + break; + } + if (wb->mode == PWC_WB_AUTO) { + ret = pwc_read_red_gain(pdev, &wb->read_red); + if (ret < 0) + break; + ret = pwc_read_blue_gain(pdev, &wb->read_blue); + if (ret < 0) + break; + } + } + break; + } + + case VIDIOCPWCSAWBSPEED: + { + struct pwc_wb_speed *wbs = arg; + + if (wbs->control_speed > 0) { + ret = pwc_set_wb_speed(pdev, wbs->control_speed); + } + if (wbs->control_delay > 0) { + ret = pwc_set_wb_delay(pdev, wbs->control_delay); + } + break; + } + + case VIDIOCPWCGAWBSPEED: + { + struct pwc_wb_speed *wbs = arg; + + ret = pwc_get_wb_speed(pdev, &wbs->control_speed); + if (ret < 0) + break; + ret = pwc_get_wb_delay(pdev, &wbs->control_delay); + if (ret < 0) + break; + break; + } + + case VIDIOCPWCSLED: + { + struct pwc_leds *leds = arg; + ret = pwc_set_leds(pdev, leds->led_on, leds->led_off); + break; + } + + + case VIDIOCPWCGLED: + { + struct pwc_leds *leds = arg; + ret = pwc_get_leds(pdev, &leds->led_on, &leds->led_off); + break; + } + + case VIDIOCPWCSCONTOUR: + { + int *contour = arg; + ret = pwc_set_contour(pdev, *contour); + break; + } + + case VIDIOCPWCGCONTOUR: + { + int *contour = arg; + ret = pwc_get_contour(pdev, contour); + break; + } + + case VIDIOCPWCSBACKLIGHT: + { + int *backlight = arg; + ret = pwc_set_backlight(pdev, *backlight); + break; + } + + case VIDIOCPWCGBACKLIGHT: + { + int *backlight = arg; + ret = pwc_get_backlight(pdev, backlight); + break; + } + + case VIDIOCPWCSFLICKER: + { + int *flicker = arg; + ret = pwc_set_flicker(pdev, *flicker); + break; + } + + case VIDIOCPWCGFLICKER: + { + int *flicker = arg; + ret = pwc_get_flicker(pdev, flicker); + break; + } + + case VIDIOCPWCSDYNNOISE: + { + int *dynnoise = arg; + ret = pwc_set_dynamic_noise(pdev, *dynnoise); + break; + } + + case VIDIOCPWCGDYNNOISE: + { + int *dynnoise = arg; + ret = pwc_get_dynamic_noise(pdev, dynnoise); + break; + } + + case VIDIOCPWCGREALSIZE: + { + struct pwc_imagesize *size = arg; + size->width = pdev->image.x; + size->height = pdev->image.y; + break; + } + + case VIDIOCPWCMPTRESET: + { + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + int *flags = arg; + + ret = pwc_mpt_reset(pdev, *flags); + if (ret >= 0) + { + pdev->pan_angle = 0; + pdev->tilt_angle = 0; + } + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTGRANGE: + { + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + struct pwc_mpt_range *range = arg; + *range = pdev->angle_range; + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTSANGLE: + { + int new_pan, new_tilt; + + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + struct pwc_mpt_angles *angles = arg; + /* The camera can only set relative angles, so + do some calculations when getting an absolute angle . + */ + if (angles->absolute) + { + new_pan = angles->pan; + new_tilt = angles->tilt; + } + else + { + new_pan = pdev->pan_angle + angles->pan; + new_tilt = pdev->tilt_angle + angles->tilt; + } + /* check absolute ranges */ + if (new_pan < pdev->angle_range.pan_min || + new_pan > pdev->angle_range.pan_max || + new_tilt < pdev->angle_range.tilt_min || + new_tilt > pdev->angle_range.tilt_max) + { + ret = -ERANGE; + } + else + { + /* go to relative range, check again */ + new_pan -= pdev->pan_angle; + new_tilt -= pdev->tilt_angle; + /* angles are specified in degrees * 100, thus the limit = 36000 */ + if (new_pan < -36000 || new_pan > 36000 || new_tilt < -36000 || new_tilt > 36000) + ret = -ERANGE; + } + if (ret == 0) /* no errors so far */ + { + ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt); + if (ret >= 0) + { + pdev->pan_angle += new_pan; + pdev->tilt_angle += new_tilt; + } + if (ret == -EPIPE) /* stall -> out of range */ + ret = -ERANGE; + } + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTGANGLE: + { + + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + struct pwc_mpt_angles *angles = arg; + + angles->absolute = 1; + angles->pan = pdev->pan_angle; + angles->tilt = pdev->tilt_angle; + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTSTATUS: + { + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + struct pwc_mpt_status *status = arg; + ret = pwc_mpt_get_status(pdev, status); + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCGVIDCMD: + { + struct pwc_video_command *cmd = arg; + + cmd->type = pdev->type; + cmd->release = pdev->release; + cmd->command_len = pdev->cmd_len; + memcpy(&cmd->command_buf, pdev->cmd_buf, pdev->cmd_len); + cmd->bandlength = pdev->vbandlength; + cmd->frame_size = pdev->frame_size; + break; + } + /* + case VIDIOCPWCGVIDTABLE: + { + struct pwc_table_init_buffer *table = arg; + table->len = pdev->cmd_len; + memcpy(&table->buffer, pdev->decompress_data, pdev->decompressor->table_size); + break; + } + */ + + default: + ret = -ENOIOCTLCMD; + break; + } + + if (ret > 0) + return 0; + return ret; +} + + + diff --git a/linux/drivers/media/video/pwc/pwc-if.c b/linux/drivers/media/video/pwc/pwc-if.c new file mode 100644 index 000000000..41418294a --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-if.c @@ -0,0 +1,2205 @@ +/* Linux driver for Philips webcam + USB and Video4Linux interface part. + (C) 1999-2004 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +/* + This code forms the interface between the USB layers and the Philips + specific stuff. Some adanved stuff of the driver falls under an + NDA, signed between me and Philips B.V., Eindhoven, the Netherlands, and + is thus not distributed in source form. The binary pwcx.o module + contains the code that falls under the NDA. + + In case you're wondering: 'pwc' stands for "Philips WebCam", but + I really didn't want to type 'philips_web_cam' every time (I'm lazy as + any Linux kernel hacker, but I don't like uncomprehensible abbreviations + without explanation). + + Oh yes, convention: to disctinguish between all the various pointers to + device-structures, I use these names for the pointer variables: + udev: struct usb_device * + vdev: struct video_device * + pdev: struct pwc_devive * +*/ + +/* Contributors: + - Alvarado: adding whitebalance code + - Alistar Moire: QuickCam 3000 Pro device/product ID + - Tony Hoyle: Creative Labs Webcam 5 device/product ID + - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged + - Jk Fang: Sotec Afina Eye ID + - Xavier Roche: QuickCam Pro 4000 ID + - Jens Knudsen: QuickCam Zoom ID + - J. Debert: QuickCam for Notebooks ID +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pwc.h" +#include "pwc-ioctl.h" +#include "pwc-kiara.h" +#include "pwc-timon.h" +#include "pwc-uncompress.h" + +/* Function prototypes and driver templates */ + +/* hotplug device table support */ +static struct usb_device_id pwc_device_table [] = { + { USB_DEVICE(0x0471, 0x0302) }, /* Philips models */ + { USB_DEVICE(0x0471, 0x0303) }, + { USB_DEVICE(0x0471, 0x0304) }, + { USB_DEVICE(0x0471, 0x0307) }, + { USB_DEVICE(0x0471, 0x0308) }, + { USB_DEVICE(0x0471, 0x030C) }, + { USB_DEVICE(0x0471, 0x0310) }, + { USB_DEVICE(0x0471, 0x0311) }, + { USB_DEVICE(0x0471, 0x0312) }, + { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */ + { USB_DEVICE(0x069A, 0x0001) }, /* Askey */ + { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */ + { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */ + { USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */ + { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */ + { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */ + { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */ + { USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */ + { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */ + { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */ + { USB_DEVICE(0x055D, 0x9000) }, /* Samsung */ + { USB_DEVICE(0x055D, 0x9001) }, + { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */ + { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */ + { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */ + { USB_DEVICE(0x06BE, 0x8116) }, /* new Afina Eye */ + { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */ + { USB_DEVICE(0x0d81, 0x1900) }, + { } +}; +MODULE_DEVICE_TABLE(usb, pwc_device_table); + +static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id); +static void usb_pwc_disconnect(struct usb_interface *intf); + +static struct usb_driver pwc_driver = { + .name = "Philips webcam", /* name */ + .id_table = pwc_device_table, + .probe = usb_pwc_probe, /* probe() */ + .disconnect = usb_pwc_disconnect, /* disconnect() */ +}; + +#define MAX_DEV_HINTS 20 +#define MAX_ISOC_ERRORS 20 + +static int default_size = PSZ_QCIF; +static int default_fps = 10; +static int default_fbufs = 3; /* Default number of frame buffers */ +static int default_mbufs = 2; /* Default number of mmap() buffers */ + int pwc_trace = TRACE_MODULE | TRACE_FLOW | TRACE_PWCX; +static int power_save = 0; +static int led_on = 100, led_off = 0; /* defaults to LED that is on while in use */ +static int pwc_preferred_compression = 2; /* 0..3 = uncompressed..high */ +static struct { + int type; + char serial_number[30]; + int device_node; + struct pwc_device *pdev; +} device_hint[MAX_DEV_HINTS]; + +/***/ + +static int pwc_video_open(struct inode *inode, struct file *file); +static int pwc_video_close(struct inode *inode, struct file *file); +static ssize_t pwc_video_read(struct file *file, char __user * buf, + size_t count, loff_t *ppos); +static unsigned int pwc_video_poll(struct file *file, poll_table *wait); +static int pwc_video_ioctl(struct inode *inode, struct file *file, + unsigned int ioctlnr, unsigned long arg); +static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma); + +static struct file_operations pwc_fops = { + .owner = THIS_MODULE, + .open = pwc_video_open, + .release = pwc_video_close, + .read = pwc_video_read, + .poll = pwc_video_poll, + .mmap = pwc_video_mmap, + .ioctl = pwc_video_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; +static struct video_device pwc_template = { + .owner = THIS_MODULE, + .name = "Philips Webcam", /* Filled in later */ + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_PWC, + .release = video_device_release, + .fops = &pwc_fops, + .minor = -1, +}; + +/***************************************************************************/ + +/* Okay, this is some magic that I worked out and the reasoning behind it... + + The biggest problem with any USB device is of course: "what to do + when the user unplugs the device while it is in use by an application?" + We have several options: + 1) Curse them with the 7 plagues when they do (requires divine intervention) + 2) Tell them not to (won't work: they'll do it anyway) + 3) Oops the kernel (this will have a negative effect on a user's uptime) + 4) Do something sensible. + + Of course, we go for option 4. + + It happens that this device will be linked to two times, once from + usb_device and once from the video_device in their respective 'private' + pointers. This is done when the device is probed() and all initialization + succeeded. The pwc_device struct links back to both structures. + + When a device is unplugged while in use it will be removed from the + list of known USB devices; I also de-register it as a V4L device, but + unfortunately I can't free the memory since the struct is still in use + by the file descriptor. This free-ing is then deferend until the first + opportunity. Crude, but it works. + + A small 'advantage' is that if a user unplugs the cam and plugs it back + in, it should get assigned the same video device minor, but unfortunately + it's non-trivial to re-link the cam back to the video device... (that + would surely be magic! :)) +*/ + +/***************************************************************************/ +/* Private functions */ + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the area. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long kva, ret; + + kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ + ret = __pa(kva); + return ret; +} + +static void * rvmalloc(unsigned long size) +{ + void * mem; + unsigned long adr; + + size=PAGE_ALIGN(size); + mem=vmalloc_32(size); + if (mem) + { + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr=(unsigned long) mem; + while (size > 0) + { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + } + return mem; +} + +static void rvfree(void * mem, unsigned long size) +{ + unsigned long adr; + + if (mem) + { + adr=(unsigned long) mem; + while ((long) size > 0) + { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + vfree(mem); + } +} + + + + +static int pwc_allocate_buffers(struct pwc_device *pdev) +{ + int i; + void *kbuf; + + Trace(TRACE_MEMORY, ">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev); + + if (pdev == NULL) + return -ENXIO; + +#ifdef PWC_MAGIC + if (pdev->magic != PWC_MAGIC) { + Err("allocate_buffers(): magic failed.\n"); + return -ENXIO; + } +#endif + /* Allocate Isochronous pipe buffers */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + if (pdev->sbuf[i].data == NULL) { + kbuf = kmalloc(ISO_BUFFER_SIZE, GFP_KERNEL); + if (kbuf == NULL) { + Err("Failed to allocate iso buffer %d.\n", i); + return -ENOMEM; + } + Trace(TRACE_MEMORY, "Allocated iso buffer at %p.\n", kbuf); + pdev->sbuf[i].data = kbuf; + memset(kbuf, 0, ISO_BUFFER_SIZE); + } + } + + /* Allocate frame buffer structure */ + if (pdev->fbuf == NULL) { + kbuf = kmalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL); + if (kbuf == NULL) { + Err("Failed to allocate frame buffer structure.\n"); + return -ENOMEM; + } + Trace(TRACE_MEMORY, "Allocated frame buffer structure at %p.\n", kbuf); + pdev->fbuf = kbuf; + memset(kbuf, 0, default_fbufs * sizeof(struct pwc_frame_buf)); + } + /* create frame buffers, and make circular ring */ + for (i = 0; i < default_fbufs; i++) { + if (pdev->fbuf[i].data == NULL) { + kbuf = vmalloc(PWC_FRAME_SIZE); /* need vmalloc since frame buffer > 128K */ + if (kbuf == NULL) { + Err("Failed to allocate frame buffer %d.\n", i); + return -ENOMEM; + } + Trace(TRACE_MEMORY, "Allocated frame buffer %d at %p.\n", i, kbuf); + pdev->fbuf[i].data = kbuf; + memset(kbuf, 128, PWC_FRAME_SIZE); + } + } + + /* Allocate decompressor table space */ + kbuf = NULL; + switch (pdev->type) + { + case 675: + case 680: + case 690: + case 720: + case 730: + case 740: + case 750: +#if 0 + Trace(TRACE_MEMORY,"private_data(%zu)\n",sizeof(struct pwc_dec23_private)); + kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); /* Timon & Kiara */ + break; + case 645: + case 646: + /* TODO & FIXME */ + kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); + break; +#endif + ; + } + pdev->decompress_data = kbuf; + + /* Allocate image buffer; double buffer for mmap() */ + kbuf = rvmalloc(default_mbufs * pdev->len_per_image); + if (kbuf == NULL) { + Err("Failed to allocate image buffer(s). needed (%d)\n",default_mbufs * pdev->len_per_image); + return -ENOMEM; + } + Trace(TRACE_MEMORY, "Allocated image buffer at %p.\n", kbuf); + pdev->image_data = kbuf; + for (i = 0; i < default_mbufs; i++) + pdev->image_ptr[i] = kbuf + i * pdev->len_per_image; + for (; i < MAX_IMAGES; i++) + pdev->image_ptr[i] = NULL; + + kbuf = NULL; + + Trace(TRACE_MEMORY, "<< pwc_allocate_buffers()\n"); + return 0; +} + +static void pwc_free_buffers(struct pwc_device *pdev) +{ + int i; + + Trace(TRACE_MEMORY, "Entering free_buffers(%p).\n", pdev); + + if (pdev == NULL) + return; +#ifdef PWC_MAGIC + if (pdev->magic != PWC_MAGIC) { + Err("free_buffers(): magic failed.\n"); + return; + } +#endif + + /* Release Iso-pipe buffers */ + for (i = 0; i < MAX_ISO_BUFS; i++) + if (pdev->sbuf[i].data != NULL) { + Trace(TRACE_MEMORY, "Freeing ISO buffer at %p.\n", pdev->sbuf[i].data); + kfree(pdev->sbuf[i].data); + pdev->sbuf[i].data = NULL; + } + + /* The same for frame buffers */ + if (pdev->fbuf != NULL) { + for (i = 0; i < default_fbufs; i++) { + if (pdev->fbuf[i].data != NULL) { + Trace(TRACE_MEMORY, "Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data); + vfree(pdev->fbuf[i].data); + pdev->fbuf[i].data = NULL; + } + } + kfree(pdev->fbuf); + pdev->fbuf = NULL; + } + + /* Intermediate decompression buffer & tables */ + if (pdev->decompress_data != NULL) { + Trace(TRACE_MEMORY, "Freeing decompression buffer at %p.\n", pdev->decompress_data); + kfree(pdev->decompress_data); + pdev->decompress_data = NULL; + } + pdev->decompressor = NULL; + + /* Release image buffers */ + if (pdev->image_data != NULL) { + Trace(TRACE_MEMORY, "Freeing image buffer at %p.\n", pdev->image_data); + rvfree(pdev->image_data, default_mbufs * pdev->len_per_image); + } + pdev->image_data = NULL; + + Trace(TRACE_MEMORY, "Leaving free_buffers().\n"); +} + +/* The frame & image buffer mess. + + Yes, this is a mess. Well, it used to be simple, but alas... In this + module, 3 buffers schemes are used to get the data from the USB bus to + the user program. The first scheme involves the ISO buffers (called thus + since they transport ISO data from the USB controller), and not really + interesting. Suffices to say the data from this buffer is quickly + gathered in an interrupt handler (pwc_isoc_handler) and placed into the + frame buffer. + + The frame buffer is the second scheme, and is the central element here. + It collects the data from a single frame from the camera (hence, the + name). Frames are delimited by the USB camera with a short USB packet, + so that's easy to detect. The frame buffers form a list that is filled + by the camera+USB controller and drained by the user process through + either read() or mmap(). + + The image buffer is the third scheme, in which frames are decompressed + and converted into planar format. For mmap() there is more than + one image buffer available. + + The frame buffers provide the image buffering. In case the user process + is a bit slow, this introduces lag and some undesired side-effects. + The problem arises when the frame buffer is full. I used to drop the last + frame, which makes the data in the queue stale very quickly. But dropping + the frame at the head of the queue proved to be a litte bit more difficult. + I tried a circular linked scheme, but this introduced more problems than + it solved. + + Because filling and draining are completely asynchronous processes, this + requires some fiddling with pointers and mutexes. + + Eventually, I came up with a system with 2 lists: an 'empty' frame list + and a 'full' frame list: + * Initially, all frame buffers but one are on the 'empty' list; the one + remaining buffer is our initial fill frame. + * If a frame is needed for filling, we try to take it from the 'empty' + list, unless that list is empty, in which case we take the buffer at + the head of the 'full' list. + * When our fill buffer has been filled, it is appended to the 'full' + list. + * If a frame is needed by read() or mmap(), it is taken from the head of + the 'full' list, handled, and then appended to the 'empty' list. If no + buffer is present on the 'full' list, we wait. + The advantage is that the buffer that is currently being decompressed/ + converted, is on neither list, and thus not in our way (any other scheme + I tried had the problem of old data lingering in the queue). + + Whatever strategy you choose, it always remains a tradeoff: with more + frame buffers the chances of a missed frame are reduced. On the other + hand, on slower machines it introduces lag because the queue will + always be full. + */ + +/** + \brief Find next frame buffer to fill. Take from empty or full list, whichever comes first. + */ +static inline int pwc_next_fill_frame(struct pwc_device *pdev) +{ + int ret; + unsigned long flags; + + ret = 0; + spin_lock_irqsave(&pdev->ptrlock, flags); + if (pdev->fill_frame != NULL) { + /* append to 'full' list */ + if (pdev->full_frames == NULL) { + pdev->full_frames = pdev->fill_frame; + pdev->full_frames_tail = pdev->full_frames; + } + else { + pdev->full_frames_tail->next = pdev->fill_frame; + pdev->full_frames_tail = pdev->fill_frame; + } + } + if (pdev->empty_frames != NULL) { + /* We have empty frames available. That's easy */ + pdev->fill_frame = pdev->empty_frames; + pdev->empty_frames = pdev->empty_frames->next; + } + else { + /* Hmm. Take it from the full list */ +#if PWC_DEBUG + /* sanity check */ + if (pdev->full_frames == NULL) { + Err("Neither empty or full frames available!\n"); + spin_unlock_irqrestore(&pdev->ptrlock, flags); + return -EINVAL; + } +#endif + pdev->fill_frame = pdev->full_frames; + pdev->full_frames = pdev->full_frames->next; + ret = 1; + } + pdev->fill_frame->next = NULL; +#if PWC_DEBUG + Trace(TRACE_SEQUENCE, "Assigning sequence number %d.\n", pdev->sequence); + pdev->fill_frame->sequence = pdev->sequence++; +#endif + spin_unlock_irqrestore(&pdev->ptrlock, flags); + return ret; +} + + +/** + \brief Reset all buffers, pointers and lists, except for the image_used[] buffer. + + If the image_used[] buffer is cleared too, mmap()/VIDIOCSYNC will run into trouble. + */ +static void pwc_reset_buffers(struct pwc_device *pdev) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&pdev->ptrlock, flags); + pdev->full_frames = NULL; + pdev->full_frames_tail = NULL; + for (i = 0; i < default_fbufs; i++) { + pdev->fbuf[i].filled = 0; + if (i > 0) + pdev->fbuf[i].next = &pdev->fbuf[i - 1]; + else + pdev->fbuf->next = NULL; + } + pdev->empty_frames = &pdev->fbuf[default_fbufs - 1]; + pdev->empty_frames_tail = pdev->fbuf; + pdev->read_frame = NULL; + pdev->fill_frame = pdev->empty_frames; + pdev->empty_frames = pdev->empty_frames->next; + + pdev->image_read_pos = 0; + pdev->fill_image = 0; + spin_unlock_irqrestore(&pdev->ptrlock, flags); +} + + +/** + \brief Do all the handling for getting one frame: get pointer, decompress, advance pointers. + */ +static int pwc_handle_frame(struct pwc_device *pdev) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&pdev->ptrlock, flags); + /* First grab our read_frame; this is removed from all lists, so + we can release the lock after this without problems */ + if (pdev->read_frame != NULL) { + /* This can't theoretically happen */ + Err("Huh? Read frame still in use?\n"); + } + else { + if (pdev->full_frames == NULL) { + Err("Woops. No frames ready.\n"); + } + else { + pdev->read_frame = pdev->full_frames; + pdev->full_frames = pdev->full_frames->next; + pdev->read_frame->next = NULL; + } + + if (pdev->read_frame != NULL) { +#if PWC_DEBUG + Trace(TRACE_SEQUENCE, "Decompressing frame %d\n", pdev->read_frame->sequence); +#endif + /* Decompression is a lenghty process, so it's outside of the lock. + This gives the isoc_handler the opportunity to fill more frames + in the mean time. + */ + spin_unlock_irqrestore(&pdev->ptrlock, flags); + ret = pwc_decompress(pdev); + spin_lock_irqsave(&pdev->ptrlock, flags); + + /* We're done with read_buffer, tack it to the end of the empty buffer list */ + if (pdev->empty_frames == NULL) { + pdev->empty_frames = pdev->read_frame; + pdev->empty_frames_tail = pdev->empty_frames; + } + else { + pdev->empty_frames_tail->next = pdev->read_frame; + pdev->empty_frames_tail = pdev->read_frame; + } + pdev->read_frame = NULL; + } + } + spin_unlock_irqrestore(&pdev->ptrlock, flags); + return ret; +} + +/** + \brief Advance pointers of image buffer (after each user request) +*/ +static inline void pwc_next_image(struct pwc_device *pdev) +{ + pdev->image_used[pdev->fill_image] = 0; + pdev->fill_image = (pdev->fill_image + 1) % default_mbufs; +} + + +/* This gets called for the Isochronous pipe (video). This is done in + * interrupt time, so it has to be fast, not crash, and not stall. Neat. + */ +static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs) +{ + struct pwc_device *pdev; + int i, fst, flen; + int awake; + struct pwc_frame_buf *fbuf; + unsigned char *fillptr = NULL, *iso_buf = NULL; + + awake = 0; + pdev = (struct pwc_device *)urb->context; + if (pdev == NULL) { + Err("isoc_handler() called with NULL device?!\n"); + return; + } +#ifdef PWC_MAGIC + if (pdev->magic != PWC_MAGIC) { + Err("isoc_handler() called with bad magic!\n"); + return; + } +#endif + if (urb->status == -ENOENT || urb->status == -ECONNRESET) { + Trace(TRACE_OPEN, "pwc_isoc_handler(): URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a"); + return; + } + if (urb->status != -EINPROGRESS && urb->status != 0) { + const char *errmsg; + + errmsg = "Unknown"; + switch(urb->status) { + case -ENOSR: errmsg = "Buffer error (overrun)"; break; + case -EPIPE: errmsg = "Stalled (device not responding)"; break; + case -EOVERFLOW: errmsg = "Babble (bad cable?)"; break; + case -EPROTO: errmsg = "Bit-stuff error (bad cable?)"; break; + case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break; + case -ETIMEDOUT: errmsg = "NAK (device does not respond)"; break; + } + Trace(TRACE_FLOW, "pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg); + /* Give up after a number of contiguous errors on the USB bus. + Appearantly something is wrong so we simulate an unplug event. + */ + if (++pdev->visoc_errors > MAX_ISOC_ERRORS) + { + Info("Too many ISOC errors, bailing out.\n"); + pdev->error_status = EIO; + awake = 1; + wake_up_interruptible(&pdev->frameq); + } + goto handler_end; // ugly, but practical + } + + fbuf = pdev->fill_frame; + if (fbuf == NULL) { + Err("pwc_isoc_handler without valid fill frame.\n"); + awake = 1; + goto handler_end; + } + else { + fillptr = fbuf->data + fbuf->filled; + } + + /* Reset ISOC error counter. We did get here, after all. */ + pdev->visoc_errors = 0; + + /* vsync: 0 = don't copy data + 1 = sync-hunt + 2 = synched + */ + /* Compact data */ + for (i = 0; i < urb->number_of_packets; i++) { + fst = urb->iso_frame_desc[i].status; + flen = urb->iso_frame_desc[i].actual_length; + iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + if (fst == 0) { + if (flen > 0) { /* if valid data... */ + if (pdev->vsync > 0) { /* ...and we are not sync-hunting... */ + pdev->vsync = 2; + + /* ...copy data to frame buffer, if possible */ + if (flen + fbuf->filled > pdev->frame_total_size) { + Trace(TRACE_FLOW, "Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, pdev->frame_total_size); + pdev->vsync = 0; /* Hmm, let's wait for an EOF (end-of-frame) */ + pdev->vframes_error++; + } + else { + memmove(fillptr, iso_buf, flen); + fillptr += flen; + } + } + fbuf->filled += flen; + } /* ..flen > 0 */ + + if (flen < pdev->vlast_packet_size) { + /* Shorter packet... We probably have the end of an image-frame; + wake up read() process and let select()/poll() do something. + Decompression is done in user time over there. + */ + if (pdev->vsync == 2) { + /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus + frames on the USB wire after an exposure change. This conditition is + however detected in the cam and a bit is set in the header. + */ + if (pdev->type == 730) { + unsigned char *ptr = (unsigned char *)fbuf->data; + + if (ptr[1] == 1 && ptr[0] & 0x10) { +#if PWC_DEBUG + Debug("Hyundai CMOS sensor bug. Dropping frame %d.\n", fbuf->sequence); +#endif + pdev->drop_frames += 2; + pdev->vframes_error++; + } + if ((ptr[0] ^ pdev->vmirror) & 0x01) { + if (ptr[0] & 0x01) + Info("Snapshot button pressed.\n"); + else + Info("Snapshot button released.\n"); + } + if ((ptr[0] ^ pdev->vmirror) & 0x02) { + if (ptr[0] & 0x02) + Info("Image is mirrored.\n"); + else + Info("Image is normal.\n"); + } + pdev->vmirror = ptr[0] & 0x03; + /* Sometimes the trailer of the 730 is still sent as a 4 byte packet + after a short frame; this condition is filtered out specifically. A 4 byte + frame doesn't make sense anyway. + So we get either this sequence: + drop_bit set -> 4 byte frame -> short frame -> good frame + Or this one: + drop_bit set -> short frame -> good frame + So we drop either 3 or 2 frames in all! + */ + if (fbuf->filled == 4) + pdev->drop_frames++; + } + + /* In case we were instructed to drop the frame, do so silently. + The buffer pointers are not updated either (but the counters are reset below). + */ + if (pdev->drop_frames > 0) + pdev->drop_frames--; + else { + /* Check for underflow first */ + if (fbuf->filled < pdev->frame_total_size) { + Trace(TRACE_FLOW, "Frame buffer underflow (%d bytes); discarded.\n", fbuf->filled); + pdev->vframes_error++; + } + else { + /* Send only once per EOF */ + awake = 1; /* delay wake_ups */ + + /* Find our next frame to fill. This will always succeed, since we + * nick a frame from either empty or full list, but if we had to + * take it from the full list, it means a frame got dropped. + */ + if (pwc_next_fill_frame(pdev)) { + pdev->vframes_dumped++; + if ((pdev->vframe_count > FRAME_LOWMARK) && (pwc_trace & TRACE_FLOW)) { + if (pdev->vframes_dumped < 20) + Trace(TRACE_FLOW, "Dumping frame %d.\n", pdev->vframe_count); + if (pdev->vframes_dumped == 20) + Trace(TRACE_FLOW, "Dumping frame %d (last message).\n", pdev->vframe_count); + } + } + fbuf = pdev->fill_frame; + } + } /* !drop_frames */ + pdev->vframe_count++; + } + fbuf->filled = 0; + fillptr = fbuf->data; + pdev->vsync = 1; + } /* .. flen < last_packet_size */ + pdev->vlast_packet_size = flen; + } /* ..status == 0 */ +#if PWC_DEBUG + /* This is normally not interesting to the user, unless you are really debugging something */ + else { + static int iso_error = 0; + iso_error++; + if (iso_error < 20) + Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst); + } +#endif + } + +handler_end: + if (awake) + wake_up_interruptible(&pdev->frameq); + + urb->dev = pdev->udev; + i = usb_submit_urb(urb, GFP_ATOMIC); + if (i != 0) + Err("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i); +} + + +static int pwc_isoc_init(struct pwc_device *pdev) +{ + struct usb_device *udev; + struct urb *urb; + int i, j, ret; + + struct usb_interface *intf; + struct usb_host_interface *idesc = NULL; + + if (pdev == NULL) + return -EFAULT; + if (pdev->iso_init) + return 0; + pdev->vsync = 0; + udev = pdev->udev; + + /* Get the current alternate interface, adjust packet size */ + if (!udev->actconfig) + return -EFAULT; + + intf = usb_ifnum_to_if(udev, 0); + if (intf) + idesc = usb_altnum_to_altsetting(intf, pdev->valternate); + + if (!idesc) + return -EFAULT; + + /* Search video endpoint */ + pdev->vmax_packet_size = -1; + for (i = 0; i < idesc->desc.bNumEndpoints; i++) + if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) { + pdev->vmax_packet_size = le16_to_cpu(idesc->endpoint[i].desc.wMaxPacketSize); + break; + } + + if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) { + Err("Failed to find packet size for video endpoint in current alternate setting.\n"); + return -ENFILE; /* Odd error, that should be noticeable */ + } + + /* Set alternate interface */ + ret = 0; + Trace(TRACE_OPEN, "Setting alternate interface %d\n", pdev->valternate); + ret = usb_set_interface(pdev->udev, 0, pdev->valternate); + if (ret < 0) + return ret; + + for (i = 0; i < MAX_ISO_BUFS; i++) { + urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); + if (urb == NULL) { + Err("Failed to allocate urb %d\n", i); + ret = -ENOMEM; + break; + } + pdev->sbuf[i].urb = urb; + Trace(TRACE_MEMORY, "Allocated URB at 0x%p\n", urb); + } + if (ret) { + /* De-allocate in reverse order */ + while (i >= 0) { + if (pdev->sbuf[i].urb != NULL) + usb_free_urb(pdev->sbuf[i].urb); + pdev->sbuf[i].urb = NULL; + i--; + } + return ret; + } + + /* init URB structure */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + urb = pdev->sbuf[i].urb; + + urb->interval = 1; // devik + urb->dev = udev; + urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = pdev->sbuf[i].data; + urb->transfer_buffer_length = ISO_BUFFER_SIZE; + urb->complete = pwc_isoc_handler; + urb->context = pdev; + urb->start_frame = 0; + urb->number_of_packets = ISO_FRAMES_PER_DESC; + for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { + urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE; + urb->iso_frame_desc[j].length = pdev->vmax_packet_size; + } + } + + /* link */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + ret = usb_submit_urb(pdev->sbuf[i].urb, GFP_KERNEL); + if (ret) + Err("isoc_init() submit_urb %d failed with error %d\n", i, ret); + else + Trace(TRACE_MEMORY, "URB 0x%p submitted.\n", pdev->sbuf[i].urb); + } + + /* All is done... */ + pdev->iso_init = 1; + Trace(TRACE_OPEN, "<< pwc_isoc_init()\n"); + return 0; +} + +static void pwc_isoc_cleanup(struct pwc_device *pdev) +{ + int i; + + Trace(TRACE_OPEN, ">> pwc_isoc_cleanup()\n"); + if (pdev == NULL) + return; + + /* Unlinking ISOC buffers one by one */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + struct urb *urb; + + urb = pdev->sbuf[i].urb; + if (urb != 0) { + if (pdev->iso_init) { + Trace(TRACE_MEMORY, "Unlinking URB %p\n", urb); + usb_kill_urb(urb); + } + Trace(TRACE_MEMORY, "Freeing URB\n"); + usb_free_urb(urb); + pdev->sbuf[i].urb = NULL; + } + } + + /* Stop camera, but only if we are sure the camera is still there (unplug + is signalled by EPIPE) + */ + if (pdev->error_status && pdev->error_status != EPIPE) { + Trace(TRACE_OPEN, "Setting alternate interface 0.\n"); + usb_set_interface(pdev->udev, 0, 0); + } + + pdev->iso_init = 0; + Trace(TRACE_OPEN, "<< pwc_isoc_cleanup()\n"); +} + +int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot) +{ + int ret, start; + + /* Stop isoc stuff */ + pwc_isoc_cleanup(pdev); + /* Reset parameters */ + pwc_reset_buffers(pdev); + /* Try to set video mode... */ + start = ret = pwc_set_video_mode(pdev, width, height, new_fps, new_compression, new_snapshot); + if (ret) { + Trace(TRACE_FLOW, "pwc_set_video_mode attempt 1 failed.\n"); + /* That failed... restore old mode (we know that worked) */ + start = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); + if (start) { + Trace(TRACE_FLOW, "pwc_set_video_mode attempt 2 failed.\n"); + } + } + if (start == 0) + { + if (pwc_isoc_init(pdev) < 0) + { + Info("Failed to restart ISOC transfers in pwc_try_video_mode.\n"); + ret = -EAGAIN; /* let's try again, who knows if it works a second time */ + } + } + pdev->drop_frames++; /* try to avoid garbage during switch */ + return ret; /* Return original error code */ +} + + +/***************************************************************************/ +/* Video4Linux functions */ + +static int pwc_video_open(struct inode *inode, struct file *file) +{ + int i; + struct video_device *vdev = video_devdata(file); + struct pwc_device *pdev; + + Trace(TRACE_OPEN, ">> video_open called(vdev = 0x%p).\n", vdev); + + pdev = (struct pwc_device *)vdev->priv; + if (pdev == NULL) + BUG(); + if (pdev->vopen) + return -EBUSY; + + down(&pdev->modlock); + if (!pdev->usb_init) { + Trace(TRACE_OPEN, "Doing first time initialization.\n"); + pdev->usb_init = 1; + + if (pwc_trace & TRACE_OPEN) + { + /* Query sensor type */ + const char *sensor_type = NULL; + int ret; + + ret = pwc_get_cmos_sensor(pdev, &i); + if (ret >= 0) + { + switch(i) { + case 0x00: sensor_type = "Hyundai CMOS sensor"; break; + case 0x20: sensor_type = "Sony CCD sensor + TDA8787"; break; + case 0x2E: sensor_type = "Sony CCD sensor + Exas 98L59"; break; + case 0x2F: sensor_type = "Sony CCD sensor + ADI 9804"; break; + case 0x30: sensor_type = "Sharp CCD sensor + TDA8787"; break; + case 0x3E: sensor_type = "Sharp CCD sensor + Exas 98L59"; break; + case 0x3F: sensor_type = "Sharp CCD sensor + ADI 9804"; break; + case 0x40: sensor_type = "UPA 1021 sensor"; break; + case 0x100: sensor_type = "VGA sensor"; break; + case 0x101: sensor_type = "PAL MR sensor"; break; + default: sensor_type = "unknown type of sensor"; break; + } + } + if (sensor_type != NULL) + Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev->name, sensor_type, i); + } + } + + /* Turn on camera */ + if (power_save) { + i = pwc_camera_power(pdev, 1); + if (i < 0) + Info("Failed to restore power to the camera! (%d)\n", i); + } + /* Set LED on/off time */ + if (pwc_set_leds(pdev, led_on, led_off) < 0) + Info("Failed to set LED on/off time.\n"); + + pwc_construct(pdev); /* set min/max sizes correct */ + + /* So far, so good. Allocate memory. */ + i = pwc_allocate_buffers(pdev); + if (i < 0) { + Trace(TRACE_OPEN, "Failed to allocate buffer memory.\n"); + up(&pdev->modlock); + return i; + } + + /* Reset buffers & parameters */ + pwc_reset_buffers(pdev); + for (i = 0; i < default_mbufs; i++) + pdev->image_used[i] = 0; + pdev->vframe_count = 0; + pdev->vframes_dumped = 0; + pdev->vframes_error = 0; + pdev->visoc_errors = 0; + pdev->error_status = 0; +#if PWC_DEBUG + pdev->sequence = 0; +#endif + pwc_construct(pdev); /* set min/max sizes correct */ + + /* Set some defaults */ + pdev->vsnapshot = 0; + + /* Start iso pipe for video; first try the last used video size + (or the default one); if that fails try QCIF/10 or QSIF/10; + it that fails too, give up. + */ + i = pwc_set_video_mode(pdev, pwc_image_sizes[pdev->vsize].x, pwc_image_sizes[pdev->vsize].y, pdev->vframes, pdev->vcompression, 0); + if (i) { + Trace(TRACE_OPEN, "First attempt at set_video_mode failed.\n"); + if (pdev->type == 730 || pdev->type == 740 || pdev->type == 750) + i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QSIF].x, pwc_image_sizes[PSZ_QSIF].y, 10, pdev->vcompression, 0); + else + i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QCIF].x, pwc_image_sizes[PSZ_QCIF].y, 10, pdev->vcompression, 0); + } + if (i) { + Trace(TRACE_OPEN, "Second attempt at set_video_mode failed.\n"); + up(&pdev->modlock); + return i; + } + + i = pwc_isoc_init(pdev); + if (i) { + Trace(TRACE_OPEN, "Failed to init ISOC stuff = %d.\n", i); + up(&pdev->modlock); + return i; + } + + pdev->vopen++; + file->private_data = vdev; + up(&pdev->modlock); + Trace(TRACE_OPEN, "<< video_open() returns 0.\n"); + return 0; +} + +/* Note that all cleanup is done in the reverse order as in _open */ +static int pwc_video_close(struct inode *inode, struct file *file) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + int i; + + Trace(TRACE_OPEN, ">> video_close called(vdev = 0x%p).\n", vdev); + + pdev = (struct pwc_device *)vdev->priv; + if (pdev->vopen == 0) + Info("video_close() called on closed device?\n"); + + /* Dump statistics, but only if a reasonable amount of frames were + processed (to prevent endless log-entries in case of snap-shot + programs) + */ + if (pdev->vframe_count > 20) + Info("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error); + + switch (pdev->type) + { + case 675: + case 680: + case 690: + case 720: + case 730: + case 740: + case 750: +/* pwc_dec23_exit(); *//* Timon & Kiara */ + break; + case 645: + case 646: +/* pwc_dec1_exit(); */ + break; + } + + pwc_isoc_cleanup(pdev); + pwc_free_buffers(pdev); + + /* Turn off LEDS and power down camera, but only when not unplugged */ + if (pdev->error_status != EPIPE) { + /* Turn LEDs off */ + if (pwc_set_leds(pdev, 0, 0) < 0) + Info("Failed to set LED on/off time.\n"); + if (power_save) { + i = pwc_camera_power(pdev, 0); + if (i < 0) + Err("Failed to power down camera (%d)\n", i); + } + } + pdev->vopen = 0; + Trace(TRACE_OPEN, "<< video_close()\n"); + return 0; +} + +/* + * FIXME: what about two parallel reads ???? + * ANSWER: Not supported. You can't open the device more than once, + despite what the V4L1 interface says. First, I don't see + the need, second there's no mechanism of alerting the + 2nd/3rd/... process of events like changing image size. + And I don't see the point of blocking that for the + 2nd/3rd/... process. + In multi-threaded environments reading parallel from any + device is tricky anyhow. + */ + +static ssize_t pwc_video_read(struct file *file, char __user * buf, + size_t count, loff_t *ppos) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + int noblock = file->f_flags & O_NONBLOCK; + DECLARE_WAITQUEUE(wait, current); + int bytes_to_read; + + Trace(TRACE_READ, "video_read(0x%p, %p, %zu) called.\n", vdev, buf, count); + if (vdev == NULL) + return -EFAULT; + pdev = vdev->priv; + if (pdev == NULL) + return -EFAULT; + if (pdev->error_status) + return -pdev->error_status; /* Something happened, report what. */ + + /* In case we're doing partial reads, we don't have to wait for a frame */ + if (pdev->image_read_pos == 0) { + /* Do wait queueing according to the (doc)book */ + add_wait_queue(&pdev->frameq, &wait); + while (pdev->full_frames == NULL) { + /* Check for unplugged/etc. here */ + if (pdev->error_status) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -pdev->error_status ; + } + if (noblock) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -EWOULDBLOCK; + } + if (signal_pending(current)) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -ERESTARTSYS; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + + /* Decompress and release frame */ + if (pwc_handle_frame(pdev)) + return -EFAULT; + } + + Trace(TRACE_READ, "Copying data to user space.\n"); + if (pdev->vpalette == VIDEO_PALETTE_RAW) + bytes_to_read = pdev->frame_size; + else + bytes_to_read = pdev->view.size; + + /* copy bytes to user space; we allow for partial reads */ + if (count + pdev->image_read_pos > bytes_to_read) + count = bytes_to_read - pdev->image_read_pos; + if (copy_to_user(buf, pdev->image_ptr[pdev->fill_image] + pdev->image_read_pos, count)) + return -EFAULT; + pdev->image_read_pos += count; + if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */ + pdev->image_read_pos = 0; + pwc_next_image(pdev); + } + return count; +} + +static unsigned int pwc_video_poll(struct file *file, poll_table *wait) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + + if (vdev == NULL) + return -EFAULT; + pdev = vdev->priv; + if (pdev == NULL) + return -EFAULT; + + poll_wait(file, &pdev->frameq, wait); + if (pdev->error_status) + return POLLERR; + if (pdev->full_frames != NULL) /* we have frames waiting */ + return (POLLIN | POLLRDNORM); + + return 0; +} + +static int pwc_video_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + DECLARE_WAITQUEUE(wait, current); + + if (vdev == NULL) + return -EFAULT; + pdev = vdev->priv; + if (pdev == NULL) + return -EFAULT; + + switch (cmd) { + /* Query cabapilities */ + case VIDIOCGCAP: + { + struct video_capability *caps = arg; + + strcpy(caps->name, vdev->name); + caps->type = VID_TYPE_CAPTURE; + caps->channels = 1; + caps->audios = 1; + caps->minwidth = pdev->view_min.x; + caps->minheight = pdev->view_min.y; + caps->maxwidth = pdev->view_max.x; + caps->maxheight = pdev->view_max.y; + break; + } + + /* Channel functions (simulate 1 channel) */ + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + + if (v->channel != 0) + return -EINVAL; + v->flags = 0; + v->tuners = 0; + v->type = VIDEO_TYPE_CAMERA; + strcpy(v->name, "Webcam"); + return 0; + } + + case VIDIOCSCHAN: + { + /* The spec says the argument is an integer, but + the bttv driver uses a video_channel arg, which + makes sense becasue it also has the norm flag. + */ + struct video_channel *v = arg; + if (v->channel != 0) + return -EINVAL; + return 0; + } + + + /* Picture functions; contrast etc. */ + case VIDIOCGPICT: + { + struct video_picture *p = arg; + int val; + + val = pwc_get_brightness(pdev); + if (val >= 0) + p->brightness = val; + else + p->brightness = 0xffff; + val = pwc_get_contrast(pdev); + if (val >= 0) + p->contrast = val; + else + p->contrast = 0xffff; + /* Gamma, Whiteness, what's the difference? :) */ + val = pwc_get_gamma(pdev); + if (val >= 0) + p->whiteness = val; + else + p->whiteness = 0xffff; + val = pwc_get_saturation(pdev); + if (val >= 0) + p->colour = val; + else + p->colour = 0xffff; + p->depth = 24; + p->palette = pdev->vpalette; + p->hue = 0xFFFF; /* N/A */ + break; + } + + case VIDIOCSPICT: + { + struct video_picture *p = arg; + /* + * FIXME: Suppose we are mid read + ANSWER: No problem: the firmware of the camera + can handle brightness/contrast/etc + changes at _any_ time, and the palette + is used exactly once in the uncompress + routine. + */ + pwc_set_brightness(pdev, p->brightness); + pwc_set_contrast(pdev, p->contrast); + pwc_set_gamma(pdev, p->whiteness); + pwc_set_saturation(pdev, p->colour); + if (p->palette && p->palette != pdev->vpalette) { + switch (p->palette) { + case VIDEO_PALETTE_YUV420P: + case VIDEO_PALETTE_RAW: + pdev->vpalette = p->palette; + return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); + break; + default: + return -EINVAL; + break; + } + } + break; + } + + /* Window/size parameters */ + case VIDIOCGWIN: + { + struct video_window *vw = arg; + + vw->x = 0; + vw->y = 0; + vw->width = pdev->view.x; + vw->height = pdev->view.y; + vw->chromakey = 0; + vw->flags = (pdev->vframes << PWC_FPS_SHIFT) | + (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0); + break; + } + + case VIDIOCSWIN: + { + struct video_window *vw = arg; + int fps, snapshot, ret; + + fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT; + snapshot = vw->flags & PWC_FPS_SNAPSHOT; + if (fps == 0) + fps = pdev->vframes; + if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot) + return 0; + ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot); + if (ret) + return ret; + break; + } + + /* We don't have overlay support (yet) */ + case VIDIOCGFBUF: + { + struct video_buffer *vb = arg; + + memset(vb,0,sizeof(*vb)); + break; + } + + /* mmap() functions */ + case VIDIOCGMBUF: + { + /* Tell the user program how much memory is needed for a mmap() */ + struct video_mbuf *vm = arg; + int i; + + memset(vm, 0, sizeof(*vm)); + vm->size = default_mbufs * pdev->len_per_image; + vm->frames = default_mbufs; /* double buffering should be enough for most applications */ + for (i = 0; i < default_mbufs; i++) + vm->offsets[i] = i * pdev->len_per_image; + break; + } + + case VIDIOCMCAPTURE: + { + /* Start capture into a given image buffer (called 'frame' in video_mmap structure) */ + struct video_mmap *vm = arg; + + Trace(TRACE_READ, "VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format); + if (vm->frame < 0 || vm->frame >= default_mbufs) + return -EINVAL; + + /* xawtv is nasty. It probes the available palettes + by setting a very small image size and trying + various palettes... The driver doesn't support + such small images, so I'm working around it. + */ + if (vm->format) + { + switch (vm->format) + { + case VIDEO_PALETTE_YUV420P: + case VIDEO_PALETTE_RAW: + break; + default: + return -EINVAL; + break; + } + } + + if ((vm->width != pdev->view.x || vm->height != pdev->view.y) && + (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) { + int ret; + + Trace(TRACE_OPEN, "VIDIOCMCAPTURE: changing size to please xawtv :-(.\n"); + ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot); + if (ret) + return ret; + } /* ... size mismatch */ + + /* FIXME: should we lock here? */ + if (pdev->image_used[vm->frame]) + return -EBUSY; /* buffer wasn't available. Bummer */ + pdev->image_used[vm->frame] = 1; + + /* Okay, we're done here. In the SYNC call we wait until a + frame comes available, then expand image into the given + buffer. + In contrast to the CPiA cam the Philips cams deliver a + constant stream, almost like a grabber card. Also, + we have separate buffers for the rawdata and the image, + meaning we can nearly always expand into the requested buffer. + */ + Trace(TRACE_READ, "VIDIOCMCAPTURE done.\n"); + break; + } + + case VIDIOCSYNC: + { + /* The doc says: "Whenever a buffer is used it should + call VIDIOCSYNC to free this frame up and continue." + + The only odd thing about this whole procedure is + that MCAPTURE flags the buffer as "in use", and + SYNC immediately unmarks it, while it isn't + after SYNC that you know that the buffer actually + got filled! So you better not start a CAPTURE in + the same frame immediately (use double buffering). + This is not a problem for this cam, since it has + extra intermediate buffers, but a hardware + grabber card will then overwrite the buffer + you're working on. + */ + int *mbuf = arg; + int ret; + + Trace(TRACE_READ, "VIDIOCSYNC called (%d).\n", *mbuf); + + /* bounds check */ + if (*mbuf < 0 || *mbuf >= default_mbufs) + return -EINVAL; + /* check if this buffer was requested anyway */ + if (pdev->image_used[*mbuf] == 0) + return -EINVAL; + + /* Add ourselves to the frame wait-queue. + + FIXME: needs auditing for safety. + QUESTION: In what respect? I think that using the + frameq is safe now. + */ + add_wait_queue(&pdev->frameq, &wait); + while (pdev->full_frames == NULL) { + if (pdev->error_status) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -pdev->error_status; + } + + if (signal_pending(current)) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -ERESTARTSYS; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + + /* The frame is ready. Expand in the image buffer + requested by the user. I don't care if you + mmap() 5 buffers and request data in this order: + buffer 4 2 3 0 1 2 3 0 4 3 1 . . . + Grabber hardware may not be so forgiving. + */ + Trace(TRACE_READ, "VIDIOCSYNC: frame ready.\n"); + pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */ + /* Decompress, etc */ + ret = pwc_handle_frame(pdev); + pdev->image_used[*mbuf] = 0; + if (ret) + return -EFAULT; + break; + } + + case VIDIOCGAUDIO: + { + struct video_audio *v = arg; + + strcpy(v->name, "Microphone"); + v->audio = -1; /* unknown audio minor */ + v->flags = 0; + v->mode = VIDEO_SOUND_MONO; + v->volume = 0; + v->bass = 0; + v->treble = 0; + v->balance = 0x8000; + v->step = 1; + break; + } + + case VIDIOCSAUDIO: + { + /* Dummy: nothing can be set */ + break; + } + + case VIDIOCGUNIT: + { + struct video_unit *vu = arg; + + vu->video = pdev->vdev->minor & 0x3F; + vu->audio = -1; /* not known yet */ + vu->vbi = -1; + vu->radio = -1; + vu->teletext = -1; + break; + } + default: + return pwc_ioctl(pdev, cmd, arg); + } /* ..switch */ + return 0; +} + +static int pwc_video_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl); +} + + +static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end-vma->vm_start; + unsigned long page, pos; + + Trace(TRACE_MEMORY, "mmap(0x%p, 0x%lx, %lu) called.\n", vdev, start, size); + pdev = vdev->priv; + + vma->vm_flags |= VM_IO; + + pos = (unsigned long)pdev->image_data; + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + +/***************************************************************************/ +/* USB functions */ + +/* This function gets called when a new device is plugged in or the usb core + * is loaded. + */ + +static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct pwc_device *pdev = NULL; + int vendor_id, product_id, type_id; + int i, hint; + int features = 0; + int video_nr = -1; /* default: use next available device */ + char serial_number[30], *name; + + /* Check if we can handle this device */ + Trace(TRACE_PROBE, "probe() called [%04X %04X], if %d\n", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + intf->altsetting->desc.bInterfaceNumber); + + /* the interfaces are probed one by one. We are only interested in the + video interface (0) now. + Interface 1 is the Audio Control, and interface 2 Audio itself. + */ + if (intf->altsetting->desc.bInterfaceNumber > 0) + return -ENODEV; + + vendor_id = le16_to_cpu(udev->descriptor.idVendor); + product_id = le16_to_cpu(udev->descriptor.idProduct); + + if (vendor_id == 0x0471) { + switch (product_id) { + case 0x0302: + Info("Philips PCA645VC USB webcam detected.\n"); + name = "Philips 645 webcam"; + type_id = 645; + break; + case 0x0303: + Info("Philips PCA646VC USB webcam detected.\n"); + name = "Philips 646 webcam"; + type_id = 646; + break; + case 0x0304: + Info("Askey VC010 type 2 USB webcam detected.\n"); + name = "Askey VC010 webcam"; + type_id = 646; + break; + case 0x0307: + Info("Philips PCVC675K (Vesta) USB webcam detected.\n"); + name = "Philips 675 webcam"; + type_id = 675; + break; + case 0x0308: + Info("Philips PCVC680K (Vesta Pro) USB webcam detected.\n"); + name = "Philips 680 webcam"; + type_id = 680; + break; + case 0x030C: + Info("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n"); + name = "Philips 690 webcam"; + type_id = 690; + break; + case 0x0310: + Info("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n"); + name = "Philips 730 webcam"; + type_id = 730; + break; + case 0x0311: + Info("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n"); + name = "Philips 740 webcam"; + type_id = 740; + break; + case 0x0312: + Info("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n"); + name = "Philips 750 webcam"; + type_id = 750; + break; + case 0x0313: + Info("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n"); + name = "Philips 720K/40 webcam"; + type_id = 720; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x069A) { + switch(product_id) { + case 0x0001: + Info("Askey VC010 type 1 USB webcam detected.\n"); + name = "Askey VC010 webcam"; + type_id = 645; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x046d) { + switch(product_id) { + case 0x08b0: + Info("Logitech QuickCam Pro 3000 USB webcam detected.\n"); + name = "Logitech QuickCam Pro 3000"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b1: + Info("Logitech QuickCam Notebook Pro USB webcam detected.\n"); + name = "Logitech QuickCam Notebook Pro"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b2: + Info("Logitech QuickCam 4000 Pro USB webcam detected.\n"); + name = "Logitech QuickCam Pro 4000"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b3: + Info("Logitech QuickCam Zoom USB webcam detected.\n"); + name = "Logitech QuickCam Zoom"; + type_id = 740; /* CCD sensor */ + break; + case 0x08B4: + Info("Logitech QuickCam Zoom (new model) USB webcam detected.\n"); + name = "Logitech QuickCam Zoom"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b5: + Info("Logitech QuickCam Orbit/Sphere USB webcam detected.\n"); + name = "Logitech QuickCam Orbit"; + type_id = 740; /* CCD sensor */ + features |= FEATURE_MOTOR_PANTILT; + break; + case 0x08b6: + case 0x08b7: + case 0x08b8: + Info("Logitech QuickCam detected (reserved ID).\n"); + name = "Logitech QuickCam (res.)"; + type_id = 730; /* Assuming CMOS */ + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x055d) { + /* I don't know the difference between the C10 and the C30; + I suppose the difference is the sensor, but both cameras + work equally well with a type_id of 675 + */ + switch(product_id) { + case 0x9000: + Info("Samsung MPC-C10 USB webcam detected.\n"); + name = "Samsung MPC-C10"; + type_id = 675; + break; + case 0x9001: + Info("Samsung MPC-C30 USB webcam detected.\n"); + name = "Samsung MPC-C30"; + type_id = 675; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x041e) { + switch(product_id) { + case 0x400c: + Info("Creative Labs Webcam 5 detected.\n"); + name = "Creative Labs Webcam 5"; + type_id = 730; + break; + case 0x4011: + Info("Creative Labs Webcam Pro Ex detected.\n"); + name = "Creative Labs Webcam Pro Ex"; + type_id = 740; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x04cc) { + switch(product_id) { + case 0x8116: + Info("Sotec Afina Eye USB webcam detected.\n"); + name = "Sotec Afina Eye"; + type_id = 730; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x06be) { + switch(product_id) { + case 0x8116: + /* This is essentially the same cam as the Sotec Afina Eye */ + Info("AME Co. Afina Eye USB webcam detected.\n"); + name = "AME Co. Afina Eye"; + type_id = 750; + break; + default: + return -ENODEV; + break; + } + + } + else if (vendor_id == 0x0d81) { + switch(product_id) { + case 0x1900: + Info("Visionite VCS-UC300 USB webcam detected.\n"); + name = "Visionite VCS-UC300"; + type_id = 740; /* CCD sensor */ + break; + case 0x1910: + Info("Visionite VCS-UM100 USB webcam detected.\n"); + name = "Visionite VCS-UM100"; + type_id = 730; /* CMOS sensor */ + break; + default: + return -ENODEV; + break; + } + } + else + return -ENODEV; /* Not any of the know types; but the list keeps growing. */ + + memset(serial_number, 0, 30); + usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29); + Trace(TRACE_PROBE, "Device serial number is %s\n", serial_number); + + if (udev->descriptor.bNumConfigurations > 1) + Info("Warning: more than 1 configuration available.\n"); + + /* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */ + pdev = kzalloc(sizeof(struct pwc_device), GFP_KERNEL); + if (pdev == NULL) { + Err("Oops, could not allocate memory for pwc_device.\n"); + return -ENOMEM; + } + pdev->type = type_id; + pdev->vsize = default_size; + pdev->vframes = default_fps; + strcpy(pdev->serial, serial_number); + pdev->features = features; + if (vendor_id == 0x046D && product_id == 0x08B5) + { + /* Logitech QuickCam Orbit + The ranges have been determined experimentally; they may differ from cam to cam. + Also, the exact ranges left-right and up-down are different for my cam + */ + pdev->angle_range.pan_min = -7000; + pdev->angle_range.pan_max = 7000; + pdev->angle_range.tilt_min = -3000; + pdev->angle_range.tilt_max = 2500; + } + + init_MUTEX(&pdev->modlock); + spin_lock_init(&pdev->ptrlock); + + pdev->udev = udev; + init_waitqueue_head(&pdev->frameq); + pdev->vcompression = pwc_preferred_compression; + + /* Allocate video_device structure */ + pdev->vdev = video_device_alloc(); + if (pdev->vdev == 0) + { + Err("Err, cannot allocate video_device struture. Failing probe."); + kfree(pdev); + return -ENOMEM; + } + memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template)); + strcpy(pdev->vdev->name, name); + pdev->vdev->owner = THIS_MODULE; + video_set_drvdata(pdev->vdev, pdev); + + pdev->release = le16_to_cpu(udev->descriptor.bcdDevice); + Trace(TRACE_PROBE, "Release: %04x\n", pdev->release); + + /* Now search device_hint[] table for a match, so we can hint a node number. */ + for (hint = 0; hint < MAX_DEV_HINTS; hint++) { + if (((device_hint[hint].type == -1) || (device_hint[hint].type == pdev->type)) && + (device_hint[hint].pdev == NULL)) { + /* so far, so good... try serial number */ + if ((device_hint[hint].serial_number[0] == '*') || !strcmp(device_hint[hint].serial_number, serial_number)) { + /* match! */ + video_nr = device_hint[hint].device_node; + Trace(TRACE_PROBE, "Found hint, will try to register as /dev/video%d\n", video_nr); + break; + } + } + } + + pdev->vdev->release = video_device_release; + i = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr); + if (i < 0) { + Err("Failed to register as video device (%d).\n", i); + video_device_release(pdev->vdev); /* Drip... drip... drip... */ + kfree(pdev); /* Oops, no memory leaks please */ + return -EIO; + } + else { + Info("Registered as /dev/video%d.\n", pdev->vdev->minor & 0x3F); + } + + /* occupy slot */ + if (hint < MAX_DEV_HINTS) + device_hint[hint].pdev = pdev; + + Trace(TRACE_PROBE, "probe() function returning struct at 0x%p.\n", pdev); + usb_set_intfdata (intf, pdev); + return 0; +} + +/* The user janked out the cable... */ +static void usb_pwc_disconnect(struct usb_interface *intf) +{ + struct pwc_device *pdev; + int hint; + + lock_kernel(); + pdev = usb_get_intfdata (intf); + usb_set_intfdata (intf, NULL); + if (pdev == NULL) { + Err("pwc_disconnect() Called without private pointer.\n"); + goto disconnect_out; + } + if (pdev->udev == NULL) { + Err("pwc_disconnect() already called for %p\n", pdev); + goto disconnect_out; + } + if (pdev->udev != interface_to_usbdev(intf)) { + Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n"); + goto disconnect_out; + } +#ifdef PWC_MAGIC + if (pdev->magic != PWC_MAGIC) { + Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n"); + goto disconnect_out; + } +#endif + + /* We got unplugged; this is signalled by an EPIPE error code */ + if (pdev->vopen) { + Info("Disconnected while webcam is in use!\n"); + pdev->error_status = EPIPE; + } + + /* Alert waiting processes */ + wake_up_interruptible(&pdev->frameq); + /* Wait until device is closed */ + while (pdev->vopen) + schedule(); + /* Device is now closed, so we can safely unregister it */ + Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n"); + video_unregister_device(pdev->vdev); + + /* Free memory (don't set pdev to 0 just yet) */ + kfree(pdev); + +disconnect_out: + /* search device_hint[] table if we occupy a slot, by any chance */ + for (hint = 0; hint < MAX_DEV_HINTS; hint++) + if (device_hint[hint].pdev == pdev) + device_hint[hint].pdev = NULL; + + unlock_kernel(); +} + + +/* *grunt* We have to do atoi ourselves :-( */ +static int pwc_atoi(const char *s) +{ + int k = 0; + + k = 0; + while (*s != '\0' && *s >= '0' && *s <= '9') { + k = 10 * k + (*s - '0'); + s++; + } + return k; +} + + +/* + * Initialization code & module stuff + */ + +static char size[10]; +static int fps = 0; +static int fbufs = 0; +static int mbufs = 0; +static int trace = -1; +static int compression = -1; +static int leds[2] = { -1, -1 }; +static char *dev_hint[MAX_DEV_HINTS] = { }; + +module_param_string(size, size, sizeof(size), 0); +MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif, vga"); +module_param(fps, int, 0000); +MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30"); +module_param(fbufs, int, 0000); +MODULE_PARM_DESC(fbufs, "Number of internal frame buffers to reserve"); +module_param(mbufs, int, 0000); +MODULE_PARM_DESC(mbufs, "Number of external (mmap()ed) image buffers"); +module_param(trace, int, 0000); +MODULE_PARM_DESC(trace, "For debugging purposes"); +module_param(power_save, bool, 0000); +MODULE_PARM_DESC(power_save, "Turn power save feature in camera on or off"); +module_param(compression, int, 0000); +MODULE_PARM_DESC(compression, "Preferred compression quality. Range 0 (uncompressed) to 3 (high compression)"); +module_param_array(leds, int, NULL, 0000); +MODULE_PARM_DESC(leds, "LED on,off time in milliseconds"); +module_param_array(dev_hint, charp, NULL, 0000); +MODULE_PARM_DESC(dev_hint, "Device node hints"); + +MODULE_DESCRIPTION("Philips & OEM USB webcam driver"); +MODULE_AUTHOR("Luc Saillard "); +MODULE_LICENSE("GPL"); + +static int __init usb_pwc_init(void) +{ + int i, sz; + char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" }; + + Info("Philips webcam module version " PWC_VERSION " loaded.\n"); + Info("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n"); + Info("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n"); + Info("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n"); + + if (fps) { + if (fps < 4 || fps > 30) { + Err("Framerate out of bounds (4-30).\n"); + return -EINVAL; + } + default_fps = fps; + Info("Default framerate set to %d.\n", default_fps); + } + + if (size[0]) { + /* string; try matching with array */ + for (sz = 0; sz < PSZ_MAX; sz++) { + if (!strcmp(sizenames[sz], size)) { /* Found! */ + default_size = sz; + break; + } + } + if (sz == PSZ_MAX) { + Err("Size not recognized; try size=[sqcif | qsif | qcif | sif | cif | vga].\n"); + return -EINVAL; + } + Info("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y); + } + if (mbufs) { + if (mbufs < 1 || mbufs > MAX_IMAGES) { + Err("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES); + return -EINVAL; + } + default_mbufs = mbufs; + Info("Number of image buffers set to %d.\n", default_mbufs); + } + if (fbufs) { + if (fbufs < 2 || fbufs > MAX_FRAMES) { + Err("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES); + return -EINVAL; + } + default_fbufs = fbufs; + Info("Number of frame buffers set to %d.\n", default_fbufs); + } + if (trace >= 0) { + Info("Trace options: 0x%04x\n", trace); + pwc_trace = trace; + } + if (compression >= 0) { + if (compression > 3) { + Err("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n"); + return -EINVAL; + } + pwc_preferred_compression = compression; + Info("Preferred compression set to %d.\n", pwc_preferred_compression); + } + if (power_save) + Info("Enabling power save on open/close.\n"); + if (leds[0] >= 0) + led_on = leds[0]; + if (leds[1] >= 0) + led_off = leds[1]; + + /* Big device node whoopla. Basically, it allows you to assign a + device node (/dev/videoX) to a camera, based on its type + & serial number. The format is [type[.serialnumber]:]node. + + Any camera that isn't matched by these rules gets the next + available free device node. + */ + for (i = 0; i < MAX_DEV_HINTS; i++) { + char *s, *colon, *dot; + + /* This loop also initializes the array */ + device_hint[i].pdev = NULL; + s = dev_hint[i]; + if (s != NULL && *s != '\0') { + device_hint[i].type = -1; /* wildcard */ + strcpy(device_hint[i].serial_number, "*"); + + /* parse string: chop at ':' & '/' */ + colon = dot = s; + while (*colon != '\0' && *colon != ':') + colon++; + while (*dot != '\0' && *dot != '.') + dot++; + /* Few sanity checks */ + if (*dot != '\0' && dot > colon) { + Err("Malformed camera hint: the colon must be after the dot.\n"); + return -EINVAL; + } + + if (*colon == '\0') { + /* No colon */ + if (*dot != '\0') { + Err("Malformed camera hint: no colon + device node given.\n"); + return -EINVAL; + } + else { + /* No type or serial number specified, just a number. */ + device_hint[i].device_node = pwc_atoi(s); + } + } + else { + /* There's a colon, so we have at least a type and a device node */ + device_hint[i].type = pwc_atoi(s); + device_hint[i].device_node = pwc_atoi(colon + 1); + if (*dot != '\0') { + /* There's a serial number as well */ + int k; + + dot++; + k = 0; + while (*dot != ':' && k < 29) { + device_hint[i].serial_number[k++] = *dot; + dot++; + } + device_hint[i].serial_number[k] = '\0'; + } + } +#if PWC_DEBUG + Debug("device_hint[%d]:\n", i); + Debug(" type : %d\n", device_hint[i].type); + Debug(" serial# : %s\n", device_hint[i].serial_number); + Debug(" node : %d\n", device_hint[i].device_node); +#endif + } + else + device_hint[i].type = 0; /* not filled */ + } /* ..for MAX_DEV_HINTS */ + + Trace(TRACE_PROBE, "Registering driver at address 0x%p.\n", &pwc_driver); + return usb_register(&pwc_driver); +} + +static void __exit usb_pwc_exit(void) +{ + Trace(TRACE_MODULE, "Deregistering driver.\n"); + usb_deregister(&pwc_driver); + Info("Philips webcam module removed.\n"); +} + +module_init(usb_pwc_init); +module_exit(usb_pwc_exit); + diff --git a/linux/drivers/media/video/pwc/pwc-ioctl.h b/linux/drivers/media/video/pwc/pwc-ioctl.h new file mode 100644 index 000000000..784bc7252 --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-ioctl.h @@ -0,0 +1,292 @@ +#ifndef PWC_IOCTL_H +#define PWC_IOCTL_H + +/* (C) 2001-2004 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This is pwc-ioctl.h belonging to PWC 8.12.1 + It contains structures and defines to communicate from user space + directly to the driver. + */ + +/* + Changes + 2001/08/03 Alvarado Added ioctl constants to access methods for + changing white balance and red/blue gains + 2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE + 2003/12/13 Nemosft Unv. Some modifications to make interfacing to + PWCX easier + */ + +/* These are private ioctl() commands, specific for the Philips webcams. + They contain functions not found in other webcams, and settings not + specified in the Video4Linux API. + + The #define names are built up like follows: + VIDIOC VIDeo IOCtl prefix + PWC Philps WebCam + G optional: Get + S optional: Set + ... the function + */ + + + /* Enumeration of image sizes */ +#define PSZ_SQCIF 0x00 +#define PSZ_QSIF 0x01 +#define PSZ_QCIF 0x02 +#define PSZ_SIF 0x03 +#define PSZ_CIF 0x04 +#define PSZ_VGA 0x05 +#define PSZ_MAX 6 + + +/* The frame rate is encoded in the video_window.flags parameter using + the upper 16 bits, since some flags are defined nowadays. The following + defines provide a mask and shift to filter out this value. + + In 'Snapshot' mode the camera freezes its automatic exposure and colour + balance controls. + */ +#define PWC_FPS_SHIFT 16 +#define PWC_FPS_MASK 0x00FF0000 +#define PWC_FPS_FRMASK 0x003F0000 +#define PWC_FPS_SNAPSHOT 0x00400000 + + +/* structure for transferring x & y coordinates */ +struct pwc_coord +{ + int x, y; /* guess what */ + int size; /* size, or offset */ +}; + + +/* Used with VIDIOCPWCPROBE */ +struct pwc_probe +{ + char name[32]; + int type; +}; + +struct pwc_serial +{ + char serial[30]; /* String with serial number. Contains terminating 0 */ +}; + +/* pwc_whitebalance.mode values */ +#define PWC_WB_INDOOR 0 +#define PWC_WB_OUTDOOR 1 +#define PWC_WB_FL 2 +#define PWC_WB_MANUAL 3 +#define PWC_WB_AUTO 4 + +/* Used with VIDIOCPWC[SG]AWB (Auto White Balance). + Set mode to one of the PWC_WB_* values above. + *red and *blue are the respective gains of these colour components inside + the camera; range 0..65535 + When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read; + otherwise undefined. + 'read_red' and 'read_blue' are read-only. +*/ +struct pwc_whitebalance +{ + int mode; + int manual_red, manual_blue; /* R/W */ + int read_red, read_blue; /* R/O */ +}; + +/* + 'control_speed' and 'control_delay' are used in automatic whitebalance mode, + and tell the camera how fast it should react to changes in lighting, and + with how much delay. Valid values are 0..65535. +*/ +struct pwc_wb_speed +{ + int control_speed; + int control_delay; + +}; + +/* Used with VIDIOCPWC[SG]LED */ +struct pwc_leds +{ + int led_on; /* Led on-time; range = 0..25000 */ + int led_off; /* Led off-time; range = 0..25000 */ +}; + +/* Image size (used with GREALSIZE) */ +struct pwc_imagesize +{ + int width; + int height; +}; + +/* Defines and structures for Motorized Pan & Tilt */ +#define PWC_MPT_PAN 0x01 +#define PWC_MPT_TILT 0x02 +#define PWC_MPT_TIMEOUT 0x04 /* for status */ + +/* Set angles; when absolute != 0, the angle is absolute and the + driver calculates the relative offset for you. This can only + be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns + absolute angles. + */ +struct pwc_mpt_angles +{ + int absolute; /* write-only */ + int pan; /* degrees * 100 */ + int tilt; /* degress * 100 */ +}; + +/* Range of angles of the camera, both horizontally and vertically. + */ +struct pwc_mpt_range +{ + int pan_min, pan_max; /* degrees * 100 */ + int tilt_min, tilt_max; +}; + +struct pwc_mpt_status +{ + int status; + int time_pan; + int time_tilt; +}; + + +/* This is used for out-of-kernel decompression. With it, you can get + all the necessary information to initialize and use the decompressor + routines in standalone applications. + */ +struct pwc_video_command +{ + int type; /* camera type (645, 675, 730, etc.) */ + int release; /* release number */ + + int size; /* one of PSZ_* */ + int alternate; + int command_len; /* length of USB video command */ + unsigned char command_buf[13]; /* Actual USB video command */ + int bandlength; /* >0 = compressed */ + int frame_size; /* Size of one (un)compressed frame */ +}; + +/* Flags for PWCX subroutines. Not all modules honour all flags. */ +#define PWCX_FLAG_PLANAR 0x0001 +#define PWCX_FLAG_BAYER 0x0008 + + +/* IOCTL definitions */ + + /* Restore user settings */ +#define VIDIOCPWCRUSER _IO('v', 192) + /* Save user settings */ +#define VIDIOCPWCSUSER _IO('v', 193) + /* Restore factory settings */ +#define VIDIOCPWCFACTORY _IO('v', 194) + + /* You can manipulate the compression factor. A compression preference of 0 + means use uncompressed modes when available; 1 is low compression, 2 is + medium and 3 is high compression preferred. Of course, the higher the + compression, the lower the bandwidth used but more chance of artefacts + in the image. The driver automatically chooses a higher compression when + the preferred mode is not available. + */ + /* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */ +#define VIDIOCPWCSCQUAL _IOW('v', 195, int) + /* Get preferred compression quality */ +#define VIDIOCPWCGCQUAL _IOR('v', 195, int) + + +/* Retrieve serial number of camera */ +#define VIDIOCPWCGSERIAL _IOR('v', 198, struct pwc_serial) + + /* This is a probe function; since so many devices are supported, it + becomes difficult to include all the names in programs that want to + check for the enhanced Philips stuff. So in stead, try this PROBE; + it returns a structure with the original name, and the corresponding + Philips type. + To use, fill the structure with zeroes, call PROBE and if that succeeds, + compare the name with that returned from VIDIOCGCAP; they should be the + same. If so, you can be assured it is a Philips (OEM) cam and the type + is valid. + */ +#define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe) + + /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */ +#define VIDIOCPWCSAGC _IOW('v', 200, int) + /* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCGAGC _IOR('v', 200, int) + /* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCSSHUTTER _IOW('v', 201, int) + + /* Color compensation (Auto White Balance) */ +#define VIDIOCPWCSAWB _IOW('v', 202, struct pwc_whitebalance) +#define VIDIOCPWCGAWB _IOR('v', 202, struct pwc_whitebalance) + + /* Auto WB speed */ +#define VIDIOCPWCSAWBSPEED _IOW('v', 203, struct pwc_wb_speed) +#define VIDIOCPWCGAWBSPEED _IOR('v', 203, struct pwc_wb_speed) + + /* LEDs on/off/blink; int range 0..65535 */ +#define VIDIOCPWCSLED _IOW('v', 205, struct pwc_leds) +#define VIDIOCPWCGLED _IOR('v', 205, struct pwc_leds) + + /* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */ +#define VIDIOCPWCSCONTOUR _IOW('v', 206, int) +#define VIDIOCPWCGCONTOUR _IOR('v', 206, int) + + /* Backlight compensation; 0 = off, otherwise on */ +#define VIDIOCPWCSBACKLIGHT _IOW('v', 207, int) +#define VIDIOCPWCGBACKLIGHT _IOR('v', 207, int) + + /* Flickerless mode; = 0 off, otherwise on */ +#define VIDIOCPWCSFLICKER _IOW('v', 208, int) +#define VIDIOCPWCGFLICKER _IOR('v', 208, int) + + /* Dynamic noise reduction; 0 off, 3 = high noise reduction */ +#define VIDIOCPWCSDYNNOISE _IOW('v', 209, int) +#define VIDIOCPWCGDYNNOISE _IOR('v', 209, int) + + /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */ +#define VIDIOCPWCGREALSIZE _IOR('v', 210, struct pwc_imagesize) + + /* Motorized pan & tilt functions */ +#define VIDIOCPWCMPTRESET _IOW('v', 211, int) +#define VIDIOCPWCMPTGRANGE _IOR('v', 211, struct pwc_mpt_range) +#define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status) + + /* Get the USB set-video command; needed for initializing libpwcx */ +#define VIDIOCPWCGVIDCMD _IOR('v', 215, struct pwc_video_command) +struct pwc_table_init_buffer { + int len; + char *buffer; + +}; +#define VIDIOCPWCGVIDTABLE _IOR('v', 216, struct pwc_table_init_buffer) + +#endif diff --git a/linux/drivers/media/video/pwc/pwc-kiara.c b/linux/drivers/media/video/pwc/pwc-kiara.c new file mode 100644 index 000000000..4c96037f7 --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-kiara.c @@ -0,0 +1,318 @@ +/* Linux driver for Philips webcam + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +/* This tables contains entries for the 730/740/750 (Kiara) camera, with + 4 different qualities (no compression, low, medium, high). + It lists the bandwidth requirements for said mode by its alternate interface + number. An alternate of 0 means that the mode is unavailable. + + There are 6 * 4 * 4 entries: + 6 different resolutions subqcif, qsif, qcif, sif, cif, vga + 6 framerates: 5, 10, 15, 20, 25, 30 + 4 compression modi: none, low, medium, high + + When an uncompressed mode is not available, the next available compressed mode + will be chosen (unless the decompressor is absent). Sometimes there are only + 1 or 2 compressed modes available; in that case entries are duplicated. +*/ + + +#include "pwc-kiara.h" +#include "pwc-uncompress.h" + +const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4] = +{ + /* SQCIF */ + { + /* 5 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 10 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 15 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, + /* QSIF */ + { + /* 5 fps */ + { + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + }, + /* 10 fps */ + { + {2, 291, 0, {0x1C, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0x01, 0x80}}, + {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, + {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, + {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, + }, + /* 15 fps */ + { + {3, 437, 0, {0x1B, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x01, 0x80}}, + {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}}, + {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}}, + {1, 192, 420, {0x13, 0xF4, 0x30, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x18, 0xC0, 0x00, 0x80}}, + }, + /* 20 fps */ + { + {4, 589, 0, {0x1A, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4D, 0x02, 0x80}}, + {3, 448, 730, {0x12, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xC0, 0x01, 0x80}}, + {2, 292, 476, {0x12, 0xF4, 0x30, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0x01, 0x80}}, + {1, 192, 312, {0x12, 0xF4, 0x50, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0x00, 0x80}}, + }, + /* 25 fps */ + { + {5, 703, 0, {0x19, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x02, 0x80}}, + {3, 447, 610, {0x11, 0xF4, 0x30, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x28, 0xBF, 0x01, 0x80}}, + {2, 292, 398, {0x11, 0xF4, 0x50, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x28, 0x24, 0x01, 0x80}}, + {1, 193, 262, {0x11, 0xF4, 0x50, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x28, 0xC1, 0x00, 0x80}}, + }, + /* 30 fps */ + { + {8, 874, 0, {0x18, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x6A, 0x03, 0x80}}, + {5, 704, 730, {0x10, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x28, 0xC0, 0x02, 0x80}}, + {3, 448, 492, {0x10, 0xF4, 0x30, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x28, 0xC0, 0x01, 0x80}}, + {2, 292, 320, {0x10, 0xF4, 0x50, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x28, 0x24, 0x01, 0x80}}, + }, + }, + /* QCIF */ + { + /* 5 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 10 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 15 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, + /* SIF */ + { + /* 5 fps */ + { + {4, 582, 0, {0x0D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x02, 0x80}}, + {3, 387, 1276, {0x05, 0xF4, 0x30, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x01, 0x80}}, + {2, 291, 960, {0x05, 0xF4, 0x30, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0x01, 0x80}}, + {1, 191, 630, {0x05, 0xF4, 0x50, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x18, 0xBF, 0x00, 0x80}}, + }, + /* 10 fps */ + { + {0, }, + {6, 775, 1278, {0x04, 0xF4, 0x30, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x03, 0x80}}, + {3, 447, 736, {0x04, 0xF4, 0x30, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x28, 0xBF, 0x01, 0x80}}, + {2, 292, 480, {0x04, 0xF4, 0x70, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x28, 0x24, 0x01, 0x80}}, + }, + /* 15 fps */ + { + {0, }, + {9, 955, 1050, {0x03, 0xF4, 0x30, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x03, 0x80}}, + {4, 592, 650, {0x03, 0xF4, 0x30, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x50, 0x02, 0x80}}, + {3, 448, 492, {0x03, 0xF4, 0x50, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x38, 0xC0, 0x01, 0x80}}, + }, + /* 20 fps */ + { + {0, }, + {9, 958, 782, {0x02, 0xF4, 0x30, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x03, 0x80}}, + {5, 703, 574, {0x02, 0xF4, 0x50, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x02, 0x80}}, + {3, 446, 364, {0x02, 0xF4, 0x90, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x38, 0xBE, 0x01, 0x80}}, + }, + /* 25 fps */ + { + {0, }, + {9, 958, 654, {0x01, 0xF4, 0x30, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x03, 0x80}}, + {6, 776, 530, {0x01, 0xF4, 0x50, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x03, 0x80}}, + {4, 592, 404, {0x01, 0xF4, 0x70, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x48, 0x50, 0x02, 0x80}}, + }, + /* 30 fps */ + { + {0, }, + {9, 957, 526, {0x00, 0xF4, 0x50, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x03, 0x80}}, + {6, 775, 426, {0x00, 0xF4, 0x70, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x03, 0x80}}, + {4, 590, 324, {0x00, 0x7A, 0x88, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x50, 0x4E, 0x02, 0x80}}, + }, + }, + /* CIF */ + { + /* 5 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 10 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 15 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, + /* VGA */ + { + /* 5 fps */ + { + {0, }, + {6, 773, 1272, {0x25, 0xF4, 0x30, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}}, + {4, 592, 976, {0x25, 0xF4, 0x50, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x02, 0x80}}, + {3, 448, 738, {0x25, 0xF4, 0x90, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x01, 0x80}}, + }, + /* 10 fps */ + { + {0, }, + {9, 956, 788, {0x24, 0xF4, 0x70, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x03, 0x80}}, + {6, 776, 640, {0x24, 0xF4, 0xB0, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x03, 0x80}}, + {4, 592, 488, {0x24, 0x7A, 0xE8, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x02, 0x80}}, + }, + /* 15 fps */ + { + {0, }, + {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}}, + {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}}, + {8, 895, 492, {0x23, 0x7A, 0xE8, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x03, 0x80}}, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, +}; + diff --git a/linux/drivers/media/video/pwc/pwc-kiara.h b/linux/drivers/media/video/pwc/pwc-kiara.h new file mode 100644 index 000000000..12929abbb --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-kiara.h @@ -0,0 +1,45 @@ +/* Linux driver for Philips webcam + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Entries for the Kiara (730/740/750) camera */ + +#ifndef PWC_KIARA_H +#define PWC_KIARA_H + +#include "pwc-ioctl.h" + +struct Kiara_table_entry +{ + char alternate; /* USB alternate interface */ + unsigned short packetsize; /* Normal packet size */ + unsigned short bandlength; /* Bandlength when decompressing */ + unsigned char mode[12]; /* precomputed mode settings for cam */ +}; + +const extern struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4]; +const extern unsigned int KiaraRomTable[8][2][16][8]; + +#endif + + diff --git a/linux/drivers/media/video/pwc/pwc-misc.c b/linux/drivers/media/video/pwc/pwc-misc.c new file mode 100644 index 000000000..58fe79747 --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-misc.c @@ -0,0 +1,140 @@ +/* Linux driver for Philips webcam + Various miscellaneous functions and tables. + (C) 1999-2003 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include + +#include "pwc.h" + +struct pwc_coord pwc_image_sizes[PSZ_MAX] = +{ + { 128, 96, 0 }, + { 160, 120, 0 }, + { 176, 144, 0 }, + { 320, 240, 0 }, + { 352, 288, 0 }, + { 640, 480, 0 }, +}; + +/* x,y -> PSZ_ */ +int pwc_decode_size(struct pwc_device *pdev, int width, int height) +{ + int i, find; + + /* Make sure we don't go beyond our max size. + NB: we have different limits for RAW and normal modes. In case + you don't have the decompressor loaded or use RAW mode, + the maximum viewable size is smaller. + */ + if (pdev->vpalette == VIDEO_PALETTE_RAW) + { + if (width > pdev->abs_max.x || height > pdev->abs_max.y) + { + Debug("VIDEO_PALETTE_RAW: going beyond abs_max.\n"); + return -1; + } + } + else + { + if (width > pdev->view_max.x || height > pdev->view_max.y) + { + Debug("VIDEO_PALETTE_ not RAW: going beyond view_max.\n"); + return -1; + } + } + + /* Find the largest size supported by the camera that fits into the + requested size. + */ + find = -1; + for (i = 0; i < PSZ_MAX; i++) { + if (pdev->image_mask & (1 << i)) { + if (pwc_image_sizes[i].x <= width && pwc_image_sizes[i].y <= height) + find = i; + } + } + return find; +} + +/* initialize variables depending on type and decompressor*/ +void pwc_construct(struct pwc_device *pdev) +{ + switch(pdev->type) { + case 645: + case 646: + pdev->view_min.x = 128; + pdev->view_min.y = 96; + pdev->view_max.x = 352; + pdev->view_max.y = 288; + pdev->abs_max.x = 352; + pdev->abs_max.y = 288; + pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QCIF | 1 << PSZ_CIF; + pdev->vcinterface = 2; + pdev->vendpoint = 4; + pdev->frame_header_size = 0; + pdev->frame_trailer_size = 0; + break; + case 675: + case 680: + case 690: + pdev->view_min.x = 128; + pdev->view_min.y = 96; + /* Anthill bug #38: PWC always reports max size, even without PWCX */ + pdev->view_max.x = 640; + pdev->view_max.y = 480; + pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA; + pdev->abs_max.x = 640; + pdev->abs_max.y = 480; + pdev->vcinterface = 3; + pdev->vendpoint = 4; + pdev->frame_header_size = 0; + pdev->frame_trailer_size = 0; + break; + case 720: + case 730: + case 740: + case 750: + pdev->view_min.x = 160; + pdev->view_min.y = 120; + pdev->view_max.x = 640; + pdev->view_max.y = 480; + pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA; + pdev->abs_max.x = 640; + pdev->abs_max.y = 480; + pdev->vcinterface = 3; + pdev->vendpoint = 5; + pdev->frame_header_size = TOUCAM_HEADER_SIZE; + pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE; + break; + } + Debug("type = %d\n",pdev->type); + pdev->vpalette = VIDEO_PALETTE_YUV420P; /* default */ + pdev->view_min.size = pdev->view_min.x * pdev->view_min.y; + pdev->view_max.size = pdev->view_max.x * pdev->view_max.y; + /* length of image, in YUV format; always allocate enough memory. */ + pdev->len_per_image = (pdev->abs_max.x * pdev->abs_max.y * 3) / 2; +} + + diff --git a/linux/drivers/media/video/pwc/pwc-nala.h b/linux/drivers/media/video/pwc/pwc-nala.h new file mode 100644 index 000000000..168c73ef7 --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-nala.h @@ -0,0 +1,66 @@ + /* SQCIF */ + { + {0, 0, {0x04, 0x01, 0x03}}, + {8, 0, {0x05, 0x01, 0x03}}, + {7, 0, {0x08, 0x01, 0x03}}, + {7, 0, {0x0A, 0x01, 0x03}}, + {6, 0, {0x0C, 0x01, 0x03}}, + {5, 0, {0x0F, 0x01, 0x03}}, + {4, 0, {0x14, 0x01, 0x03}}, + {3, 0, {0x18, 0x01, 0x03}}, + }, + /* QSIF */ + { + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + }, + /* QCIF */ + { + {0, 0, {0x04, 0x01, 0x02}}, + {8, 0, {0x05, 0x01, 0x02}}, + {7, 0, {0x08, 0x01, 0x02}}, + {6, 0, {0x0A, 0x01, 0x02}}, + {5, 0, {0x0C, 0x01, 0x02}}, + {4, 0, {0x0F, 0x01, 0x02}}, + {1, 0, {0x14, 0x01, 0x02}}, + {1, 0, {0x18, 0x01, 0x02}}, + }, + /* SIF */ + { + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + }, + /* CIF */ + { + {4, 0, {0x04, 0x01, 0x01}}, + {7, 1, {0x05, 0x03, 0x01}}, + {6, 1, {0x08, 0x03, 0x01}}, + {4, 1, {0x0A, 0x03, 0x01}}, + {3, 1, {0x0C, 0x03, 0x01}}, + {2, 1, {0x0F, 0x03, 0x01}}, + {0}, + {0}, + }, + /* VGA */ + { + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + }, diff --git a/linux/drivers/media/video/pwc/pwc-timon.c b/linux/drivers/media/video/pwc/pwc-timon.c new file mode 100644 index 000000000..175250d08 --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-timon.c @@ -0,0 +1,316 @@ +/* Linux driver for Philips webcam + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +/* This tables contains entries for the 675/680/690 (Timon) camera, with + 4 different qualities (no compression, low, medium, high). + It lists the bandwidth requirements for said mode by its alternate interface + number. An alternate of 0 means that the mode is unavailable. + + There are 6 * 4 * 4 entries: + 6 different resolutions subqcif, qsif, qcif, sif, cif, vga + 6 framerates: 5, 10, 15, 20, 25, 30 + 4 compression modi: none, low, medium, high + + When an uncompressed mode is not available, the next available compressed mode + will be chosen (unless the decompressor is absent). Sometimes there are only + 1 or 2 compressed modes available; in that case entries are duplicated. +*/ + +#include "pwc-timon.h" + +const struct Timon_table_entry Timon_table[PSZ_MAX][6][4] = +{ + /* SQCIF */ + { + /* 5 fps */ + { + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + }, + /* 10 fps */ + { + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + }, + /* 15 fps */ + { + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + }, + /* 20 fps */ + { + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + }, + /* 25 fps */ + { + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + }, + /* 30 fps */ + { + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + }, + }, + /* QSIF */ + { + /* 5 fps */ + { + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + }, + /* 10 fps */ + { + {2, 291, 0, {0x2C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0xA1, 0xC0, 0x02}}, + {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + }, + /* 15 fps */ + { + {3, 437, 0, {0x2B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x6D, 0xC0, 0x02}}, + {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {1, 191, 420, {0x2B, 0xF4, 0x0D, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + }, + /* 20 fps */ + { + {4, 588, 0, {0x2A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4C, 0x52, 0xC0, 0x02}}, + {3, 447, 730, {0x2A, 0xF4, 0x05, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 476, {0x2A, 0xF4, 0x0D, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, + {1, 192, 312, {0x2A, 0xF4, 0x1D, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}}, + }, + /* 25 fps */ + { + {5, 703, 0, {0x29, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x42, 0xC0, 0x02}}, + {3, 447, 610, {0x29, 0xF4, 0x05, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 398, {0x29, 0xF4, 0x0D, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, + {1, 192, 262, {0x29, 0xF4, 0x25, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}}, + }, + /* 30 fps */ + { + {8, 873, 0, {0x28, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x69, 0x37, 0xC0, 0x02}}, + {5, 704, 774, {0x28, 0xF4, 0x05, 0x18, 0x21, 0x17, 0x59, 0x0F, 0x18, 0xC0, 0x42, 0xC0, 0x02}}, + {3, 448, 492, {0x28, 0xF4, 0x05, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x18, 0xC0, 0x69, 0xC0, 0x02}}, + {2, 291, 320, {0x28, 0xF4, 0x1D, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}}, + }, + }, + /* QCIF */ + { + /* 5 fps */ + { + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + }, + /* 10 fps */ + { + {3, 385, 0, {0x0C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x81, 0x79, 0xC0, 0x02}}, + {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {1, 194, 532, {0x0C, 0xF4, 0x05, 0x10, 0x9A, 0x0F, 0xBE, 0x1B, 0x08, 0xC2, 0xF0, 0xC0, 0x02}}, + }, + /* 15 fps */ + { + {4, 577, 0, {0x0B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x41, 0x52, 0xC0, 0x02}}, + {3, 447, 818, {0x0B, 0xF4, 0x05, 0x19, 0x89, 0x18, 0xAD, 0x0F, 0x10, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 534, {0x0B, 0xF4, 0x05, 0x10, 0xA3, 0x0F, 0xC7, 0x19, 0x10, 0x24, 0xA1, 0xC0, 0x02}}, + {1, 195, 356, {0x0B, 0xF4, 0x15, 0x0B, 0x11, 0x0A, 0x35, 0x1E, 0x10, 0xC3, 0xF0, 0xC0, 0x02}}, + }, + /* 20 fps */ + { + {6, 776, 0, {0x0A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x08, 0x3F, 0xC0, 0x02}}, + {4, 591, 804, {0x0A, 0xF4, 0x05, 0x19, 0x1E, 0x18, 0x42, 0x0F, 0x18, 0x4F, 0x4E, 0xC0, 0x02}}, + {3, 447, 608, {0x0A, 0xF4, 0x05, 0x12, 0xFD, 0x12, 0x21, 0x15, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 291, 396, {0x0A, 0xF4, 0x15, 0x0C, 0x5E, 0x0B, 0x82, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}}, + }, + /* 25 fps */ + { + {9, 928, 0, {0x09, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA0, 0x33, 0xC0, 0x02}}, + {5, 703, 800, {0x09, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x10, 0x18, 0xBF, 0x42, 0xC0, 0x02}}, + {3, 447, 508, {0x09, 0xF4, 0x0D, 0x0F, 0xD2, 0x0E, 0xF6, 0x1B, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 332, {0x09, 0xF4, 0x1D, 0x0A, 0x5A, 0x09, 0x7E, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, + }, + /* 30 fps */ + { + {0, }, + {9, 956, 876, {0x08, 0xF4, 0x05, 0x1B, 0x58, 0x1A, 0x7C, 0x0E, 0x20, 0xBC, 0x33, 0x10, 0x02}}, + {4, 592, 542, {0x08, 0xF4, 0x05, 0x10, 0xE4, 0x10, 0x08, 0x17, 0x20, 0x50, 0x4E, 0x10, 0x02}}, + {2, 291, 266, {0x08, 0xF4, 0x25, 0x08, 0x48, 0x07, 0x6C, 0x1E, 0x20, 0x23, 0xA1, 0x10, 0x02}}, + }, + }, + /* SIF */ + { + /* 5 fps */ + { + {4, 582, 0, {0x35, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x52, 0x60, 0x02}}, + {3, 387, 1276, {0x35, 0xF4, 0x05, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x79, 0x60, 0x02}}, + {2, 291, 960, {0x35, 0xF4, 0x0D, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0xA1, 0x60, 0x02}}, + {1, 191, 630, {0x35, 0xF4, 0x1D, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x08, 0xBF, 0xF4, 0x60, 0x02}}, + }, + /* 10 fps */ + { + {0, }, + {6, 775, 1278, {0x34, 0xF4, 0x05, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x3F, 0x10, 0x02}}, + {3, 447, 736, {0x34, 0xF4, 0x15, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x18, 0xBF, 0x69, 0x10, 0x02}}, + {2, 291, 480, {0x34, 0xF4, 0x2D, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x18, 0x23, 0xA1, 0x10, 0x02}}, + }, + /* 15 fps */ + { + {0, }, + {9, 955, 1050, {0x33, 0xF4, 0x05, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x33, 0x10, 0x02}}, + {4, 591, 650, {0x33, 0xF4, 0x15, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x4F, 0x4E, 0x10, 0x02}}, + {3, 448, 492, {0x33, 0xF4, 0x25, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x28, 0xC0, 0x69, 0x10, 0x02}}, + }, + /* 20 fps */ + { + {0, }, + {9, 958, 782, {0x32, 0xF4, 0x0D, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x33, 0xD0, 0x02}}, + {5, 703, 574, {0x32, 0xF4, 0x1D, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x42, 0xD0, 0x02}}, + {3, 446, 364, {0x32, 0xF4, 0x3D, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x30, 0xBE, 0x69, 0xD0, 0x02}}, + }, + /* 25 fps */ + { + {0, }, + {9, 958, 654, {0x31, 0xF4, 0x15, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x33, 0x90, 0x02}}, + {6, 776, 530, {0x31, 0xF4, 0x25, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x3F, 0x90, 0x02}}, + {4, 592, 404, {0x31, 0xF4, 0x35, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x38, 0x50, 0x4E, 0x90, 0x02}}, + }, + /* 30 fps */ + { + {0, }, + {9, 957, 526, {0x30, 0xF4, 0x25, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x33, 0x60, 0x02}}, + {6, 775, 426, {0x30, 0xF4, 0x35, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x3F, 0x60, 0x02}}, + {4, 590, 324, {0x30, 0x7A, 0x4B, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x40, 0x4E, 0x52, 0x60, 0x02}}, + }, + }, + /* CIF */ + { + /* 5 fps */ + { + {6, 771, 0, {0x15, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x3F, 0x80, 0x02}}, + {4, 465, 1278, {0x15, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x03, 0x18, 0xD1, 0x65, 0x80, 0x02}}, + {2, 291, 800, {0x15, 0xF4, 0x15, 0x18, 0xF4, 0x17, 0x3C, 0x05, 0x18, 0x23, 0xA1, 0x80, 0x02}}, + {1, 193, 528, {0x15, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x18, 0xC1, 0xF4, 0x80, 0x02}}, + }, + /* 10 fps */ + { + {0, }, + {9, 932, 1278, {0x14, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x04, 0x30, 0xA4, 0x33, 0x10, 0x02}}, + {4, 591, 812, {0x14, 0xF4, 0x15, 0x19, 0x56, 0x17, 0x9E, 0x06, 0x28, 0x4F, 0x4E, 0x10, 0x02}}, + {2, 291, 400, {0x14, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x28, 0x23, 0xA1, 0x10, 0x02}}, + }, + /* 15 fps */ + { + {0, }, + {9, 956, 876, {0x13, 0xF4, 0x0D, 0x1B, 0x58, 0x19, 0xA0, 0x05, 0x38, 0xBC, 0x33, 0x60, 0x02}}, + {5, 703, 644, {0x13, 0xF4, 0x1D, 0x14, 0x1C, 0x12, 0x64, 0x08, 0x38, 0xBF, 0x42, 0x60, 0x02}}, + {3, 448, 410, {0x13, 0xF4, 0x3D, 0x0C, 0xC4, 0x0B, 0x0C, 0x0E, 0x38, 0xC0, 0x69, 0x60, 0x02}}, + }, + /* 20 fps */ + { + {0, }, + {9, 956, 650, {0x12, 0xF4, 0x1D, 0x14, 0x4A, 0x12, 0x92, 0x09, 0x48, 0xBC, 0x33, 0x10, 0x03}}, + {6, 776, 528, {0x12, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x40, 0x08, 0x3F, 0x10, 0x03}}, + {4, 591, 402, {0x12, 0xF4, 0x3D, 0x0C, 0x8F, 0x0A, 0xD7, 0x0E, 0x40, 0x4F, 0x4E, 0x10, 0x03}}, + }, + /* 25 fps */ + { + {0, }, + {9, 956, 544, {0x11, 0xF4, 0x25, 0x10, 0xF4, 0x0F, 0x3C, 0x0A, 0x48, 0xBC, 0x33, 0xC0, 0x02}}, + {7, 840, 478, {0x11, 0xF4, 0x2D, 0x0E, 0xEB, 0x0D, 0x33, 0x0B, 0x48, 0x48, 0x3B, 0xC0, 0x02}}, + {5, 703, 400, {0x11, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x48, 0xBF, 0x42, 0xC0, 0x02}}, + }, + /* 30 fps */ + { + {0, }, + {9, 956, 438, {0x10, 0xF4, 0x35, 0x0D, 0xAC, 0x0B, 0xF4, 0x0D, 0x50, 0xBC, 0x33, 0x10, 0x02}}, + {7, 838, 384, {0x10, 0xF4, 0x45, 0x0B, 0xFD, 0x0A, 0x45, 0x0F, 0x50, 0x46, 0x3B, 0x10, 0x02}}, + {6, 773, 354, {0x10, 0x7A, 0x4B, 0x0B, 0x0C, 0x09, 0x80, 0x10, 0x50, 0x05, 0x3F, 0x10, 0x02}}, + }, + }, + /* VGA */ + { + /* 5 fps */ + { + {0, }, + {6, 773, 1272, {0x1D, 0xF4, 0x15, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x3F, 0x10, 0x02}}, + {4, 592, 976, {0x1D, 0xF4, 0x25, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x4E, 0x10, 0x02}}, + {3, 448, 738, {0x1D, 0xF4, 0x3D, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x69, 0x10, 0x02}}, + }, + /* 10 fps */ + { + {0, }, + {9, 956, 788, {0x1C, 0xF4, 0x35, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x33, 0x10, 0x02}}, + {6, 776, 640, {0x1C, 0x7A, 0x53, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x3F, 0x10, 0x02}}, + {4, 592, 488, {0x1C, 0x7A, 0x6B, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x4E, 0x10, 0x02}}, + }, + /* 15 fps */ + { + {0, }, + {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}}, + {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}}, + {8, 895, 492, {0x1B, 0x7A, 0x6B, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x37, 0x80, 0x02}}, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, +}; + diff --git a/linux/drivers/media/video/pwc/pwc-timon.h b/linux/drivers/media/video/pwc/pwc-timon.h new file mode 100644 index 000000000..a86b3782a --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-timon.h @@ -0,0 +1,61 @@ +/* Linux driver for Philips webcam + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + + +/* This tables contains entries for the 675/680/690 (Timon) camera, with + 4 different qualities (no compression, low, medium, high). + It lists the bandwidth requirements for said mode by its alternate interface + number. An alternate of 0 means that the mode is unavailable. + + There are 6 * 4 * 4 entries: + 6 different resolutions subqcif, qsif, qcif, sif, cif, vga + 6 framerates: 5, 10, 15, 20, 25, 30 + 4 compression modi: none, low, medium, high + + When an uncompressed mode is not available, the next available compressed mode + will be chosen (unless the decompressor is absent). Sometimes there are only + 1 or 2 compressed modes available; in that case entries are duplicated. +*/ + +#ifndef PWC_TIMON_H +#define PWC_TIMON_H + +#include "pwc-ioctl.h" + +struct Timon_table_entry +{ + char alternate; /* USB alternate interface */ + unsigned short packetsize; /* Normal packet size */ + unsigned short bandlength; /* Bandlength when decompressing */ + unsigned char mode[13]; /* precomputed mode settings for cam */ +}; + +const extern struct Timon_table_entry Timon_table[PSZ_MAX][6][4]; +const extern unsigned int TimonRomTable [16][2][16][8]; + + +#endif + + diff --git a/linux/drivers/media/video/pwc/pwc-uncompress.c b/linux/drivers/media/video/pwc/pwc-uncompress.c new file mode 100644 index 000000000..b37a89a16 --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-uncompress.c @@ -0,0 +1,146 @@ +/* Linux driver for Philips webcam + Decompression frontend. + (C) 1999-2003 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include + +#include "pwc.h" +#include "pwc-uncompress.h" + +int pwc_decompress(struct pwc_device *pdev) +{ + struct pwc_frame_buf *fbuf; + int n, line, col, stride; + void *yuv, *image; + u16 *src; + u16 *dsty, *dstu, *dstv; + + if (pdev == NULL) + return -EFAULT; +#if defined(__KERNEL__) && defined(PWC_MAGIC) + if (pdev->magic != PWC_MAGIC) { + Err("pwc_decompress(): magic failed.\n"); + return -EFAULT; + } +#endif + + fbuf = pdev->read_frame; + if (fbuf == NULL) + return -EFAULT; + image = pdev->image_ptr[pdev->fill_image]; + if (!image) + return -EFAULT; + + yuv = fbuf->data + pdev->frame_header_size; /* Skip header */ + + /* Raw format; that's easy... */ + if (pdev->vpalette == VIDEO_PALETTE_RAW) + { + memcpy(image, yuv, pdev->frame_size); + return 0; + } + + if (pdev->vbandlength == 0) { + /* Uncompressed mode. We copy the data into the output buffer, + using the viewport size (which may be larger than the image + size). Unfortunately we have to do a bit of byte stuffing + to get the desired output format/size. + */ + /* + * We do some byte shuffling here to go from the + * native format to YUV420P. + */ + src = (u16 *)yuv; + n = pdev->view.x * pdev->view.y; + + /* offset in Y plane */ + stride = pdev->view.x * pdev->offset.y + pdev->offset.x; + dsty = (u16 *)(image + stride); + + /* offsets in U/V planes */ + stride = pdev->view.x * pdev->offset.y / 4 + pdev->offset.x / 2; + dstu = (u16 *)(image + n + stride); + dstv = (u16 *)(image + n + n / 4 + stride); + + /* increment after each line */ + stride = (pdev->view.x - pdev->image.x) / 2; /* u16 is 2 bytes */ + + for (line = 0; line < pdev->image.y; line++) { + for (col = 0; col < pdev->image.x; col += 4) { + *dsty++ = *src++; + *dsty++ = *src++; + if (line & 1) + *dstv++ = *src++; + else + *dstu++ = *src++; + } + dsty += stride; + if (line & 1) + dstv += (stride >> 1); + else + dstu += (stride >> 1); + } + } + else { + /* Compressed; the decompressor routines will write the data + in planar format immediately. + */ + int flags; + + flags = PWCX_FLAG_PLANAR; + if (pdev->vsize == PSZ_VGA && pdev->vframes == 5 && pdev->vsnapshot) + { + printk(KERN_ERR "pwc: Mode Bayer is not supported for now\n"); + flags |= PWCX_FLAG_BAYER; + return -ENXIO; /* No such device or address: missing decompressor */ + } + +#if 0 + switch (pdev->type) + { + case 675: + case 680: + case 690: + case 720: + case 730: + case 740: + case 750: + pwc_dec23_decompress(&pdev->image, &pdev->view, + &pdev->offset, yuv, image, flags, + pdev->decompress_data, pdev->vbandlength); + break; + case 645: + case 646: + /* TODO & FIXME */ + return -ENXIO; /* Missing decompressor */ + break; + } +#endif + } + return 0; +} + + diff --git a/linux/drivers/media/video/pwc/pwc-uncompress.h b/linux/drivers/media/video/pwc/pwc-uncompress.h new file mode 100644 index 000000000..f75e1b6cb --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-uncompress.h @@ -0,0 +1,41 @@ +/* (C) 1999-2003 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This file is the bridge between the kernel module and the plugin; it + describes the structures and datatypes used in both modules. Any + significant change should be reflected by increasing the + pwc_decompressor_version major number. + */ +#ifndef PWC_UNCOMPRESS_H +#define PWC_UNCOMPRESS_H + +#include + +#include "pwc-ioctl.h" + +/* from pwc-dec.h */ +#define PWCX_FLAG_PLANAR 0x0001 +/* */ + +#endif diff --git a/linux/drivers/media/video/pwc/pwc.h b/linux/drivers/media/video/pwc/pwc.h new file mode 100644 index 000000000..74d003695 --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc.h @@ -0,0 +1,273 @@ +/* (C) 1999-2003 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef PWC_H +#define PWC_H + +#include +#include +#include +#include +#include "compat.h" +#include +#include +#include +#include +#include + +#include "pwc-uncompress.h" +#include "pwc-ioctl.h" + +/* Defines and structures for the Philips webcam */ +/* Used for checking memory corruption/pointer validation */ +#define PWC_MAGIC 0x89DC10ABUL +#undef PWC_MAGIC + +/* Turn some debugging options on/off */ +#define PWC_DEBUG 0 + +/* Trace certain actions in the driver */ +#define TRACE_MODULE 0x0001 +#define TRACE_PROBE 0x0002 +#define TRACE_OPEN 0x0004 +#define TRACE_READ 0x0008 +#define TRACE_MEMORY 0x0010 +#define TRACE_FLOW 0x0020 +#define TRACE_SIZE 0x0040 +#define TRACE_PWCX 0x0080 +#define TRACE_SEQUENCE 0x1000 + +#define Trace(R, A...) if (pwc_trace & R) printk(KERN_DEBUG PWC_NAME " " A) +#define Debug(A...) printk(KERN_DEBUG PWC_NAME " " A) +#define Info(A...) printk(KERN_INFO PWC_NAME " " A) +#define Err(A...) printk(KERN_ERR PWC_NAME " " A) + + +/* Defines for ToUCam cameras */ +#define TOUCAM_HEADER_SIZE 8 +#define TOUCAM_TRAILER_SIZE 4 + +#define FEATURE_MOTOR_PANTILT 0x0001 + +/* Version block */ +#define PWC_MAJOR 9 +#define PWC_MINOR 0 +#define PWC_VERSION "9.0.2-unofficial" +#define PWC_NAME "pwc" + +/* Turn certain features on/off */ +#define PWC_INT_PIPE 0 + +/* Ignore errors in the first N frames, to allow for startup delays */ +#define FRAME_LOWMARK 5 + +/* Size and number of buffers for the ISO pipe. */ +#define MAX_ISO_BUFS 2 +#define ISO_FRAMES_PER_DESC 10 +#define ISO_MAX_FRAME_SIZE 960 +#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) + +/* Frame buffers: contains compressed or uncompressed video data. */ +#define MAX_FRAMES 5 +/* Maximum size after decompression is 640x480 YUV data, 1.5 * 640 * 480 */ +#define PWC_FRAME_SIZE (460800 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE) + +/* Absolute maximum number of buffers available for mmap() */ +#define MAX_IMAGES 10 + +/* The following structures were based on cpia.h. Why reinvent the wheel? :-) */ +struct pwc_iso_buf +{ + void *data; + int length; + int read; + struct urb *urb; +}; + +/* intermediate buffers with raw data from the USB cam */ +struct pwc_frame_buf +{ + void *data; + volatile int filled; /* number of bytes filled */ + struct pwc_frame_buf *next; /* list */ +#if PWC_DEBUG + int sequence; /* Sequence number */ +#endif +}; + +struct pwc_device +{ + struct video_device *vdev; +#ifdef PWC_MAGIC + int magic; +#endif + /* Pointer to our usb_device */ + struct usb_device *udev; + + int type; /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */ + int release; /* release number */ + int features; /* feature bits */ + char serial[30]; /* serial number (string) */ + int error_status; /* set when something goes wrong with the cam (unplugged, USB errors) */ + int usb_init; /* set when the cam has been initialized over USB */ + + /*** Video data ***/ + int vopen; /* flag */ + int vendpoint; /* video isoc endpoint */ + int vcinterface; /* video control interface */ + int valternate; /* alternate interface needed */ + int vframes, vsize; /* frames-per-second & size (see PSZ_*) */ + int vpalette; /* palette: 420P, RAW or RGBBAYER */ + int vframe_count; /* received frames */ + int vframes_dumped; /* counter for dumped frames */ + int vframes_error; /* frames received in error */ + int vmax_packet_size; /* USB maxpacket size */ + int vlast_packet_size; /* for frame synchronisation */ + int visoc_errors; /* number of contiguous ISOC errors */ + int vcompression; /* desired compression factor */ + int vbandlength; /* compressed band length; 0 is uncompressed */ + char vsnapshot; /* snapshot mode */ + char vsync; /* used by isoc handler */ + char vmirror; /* for ToUCaM series */ + + int cmd_len; + unsigned char cmd_buf[13]; + + /* The image acquisition requires 3 to 4 steps: + 1. data is gathered in short packets from the USB controller + 2. data is synchronized and packed into a frame buffer + 3a. in case data is compressed, decompress it directly into image buffer + 3b. in case data is uncompressed, copy into image buffer with viewport + 4. data is transferred to the user process + + Note that MAX_ISO_BUFS != MAX_FRAMES != MAX_IMAGES.... + We have in effect a back-to-back-double-buffer system. + */ + /* 1: isoc */ + struct pwc_iso_buf sbuf[MAX_ISO_BUFS]; + char iso_init; + + /* 2: frame */ + struct pwc_frame_buf *fbuf; /* all frames */ + struct pwc_frame_buf *empty_frames, *empty_frames_tail; /* all empty frames */ + struct pwc_frame_buf *full_frames, *full_frames_tail; /* all filled frames */ + struct pwc_frame_buf *fill_frame; /* frame currently being filled */ + struct pwc_frame_buf *read_frame; /* frame currently read by user process */ + int frame_header_size, frame_trailer_size; + int frame_size; + int frame_total_size; /* including header & trailer */ + int drop_frames; +#if PWC_DEBUG + int sequence; /* Debugging aid */ +#endif + + /* 3: decompression */ + struct pwc_decompressor *decompressor; /* function block with decompression routines */ + void *decompress_data; /* private data for decompression engine */ + + /* 4: image */ + /* We have an 'image' and a 'view', where 'image' is the fixed-size image + as delivered by the camera, and 'view' is the size requested by the + program. The camera image is centered in this viewport, laced with + a gray or black border. view_min <= image <= view <= view_max; + */ + int image_mask; /* bitmask of supported sizes */ + struct pwc_coord view_min, view_max; /* minimum and maximum viewable sizes */ + struct pwc_coord abs_max; /* maximum supported size with compression */ + struct pwc_coord image, view; /* image and viewport size */ + struct pwc_coord offset; /* offset within the viewport */ + + void *image_data; /* total buffer, which is subdivided into ... */ + void *image_ptr[MAX_IMAGES]; /* ...several images... */ + int fill_image; /* ...which are rotated. */ + int len_per_image; /* length per image */ + int image_read_pos; /* In case we read data in pieces, keep track of were we are in the imagebuffer */ + int image_used[MAX_IMAGES]; /* For MCAPTURE and SYNC */ + + struct semaphore modlock; /* to prevent races in video_open(), etc */ + spinlock_t ptrlock; /* for manipulating the buffer pointers */ + + /*** motorized pan/tilt feature */ + struct pwc_mpt_range angle_range; + int pan_angle; /* in degrees * 100 */ + int tilt_angle; /* absolute angle; 0,0 is home position */ + + /*** Misc. data ***/ + wait_queue_head_t frameq; /* When waiting for a frame to finish... */ +#if PWC_INT_PIPE + void *usb_int_handler; /* for the interrupt endpoint */ +#endif +}; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Global variable */ +extern int pwc_trace; + +/** functions in pwc-if.c */ +int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot); + +/** Functions in pwc-misc.c */ +/* sizes in pixels */ +extern struct pwc_coord pwc_image_sizes[PSZ_MAX]; + +int pwc_decode_size(struct pwc_device *pdev, int width, int height); +void pwc_construct(struct pwc_device *pdev); + +/** Functions in pwc-ctrl.c */ +/* Request a certain video mode. Returns < 0 if not possible */ +extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot); + +/* Various controls; should be obvious. Value 0..65535, or < 0 on error */ +extern int pwc_get_brightness(struct pwc_device *pdev); +extern int pwc_set_brightness(struct pwc_device *pdev, int value); +extern int pwc_get_contrast(struct pwc_device *pdev); +extern int pwc_set_contrast(struct pwc_device *pdev, int value); +extern int pwc_get_gamma(struct pwc_device *pdev); +extern int pwc_set_gamma(struct pwc_device *pdev, int value); +extern int pwc_get_saturation(struct pwc_device *pdev); +extern int pwc_set_saturation(struct pwc_device *pdev, int value); +extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value); +extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor); + +/* Power down or up the camera; not supported by all models */ +extern int pwc_camera_power(struct pwc_device *pdev, int power); + +/* Private ioctl()s; see pwc-ioctl.h */ +extern int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg); + + +/** pwc-uncompress.c */ +/* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */ +extern int pwc_decompress(struct pwc_device *pdev); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/linux/drivers/media/video/saa7134/saa7134-video.c b/linux/drivers/media/video/saa7134/saa7134-video.c index 8f5e4ec39..165d0afe2 100644 --- a/linux/drivers/media/video/saa7134/saa7134-video.c +++ b/linux/drivers/media/video/saa7134/saa7134-video.c @@ -32,7 +32,7 @@ #include "saa7134.h" #include -#if 1 +#ifdef CONFIG_VIDEO_V4L1_COMPAT /* Include V4L1 specific functions. Should be removed soon */ #include #endif diff --git a/linux/drivers/media/video/sn9c102/Kconfig b/linux/drivers/media/video/sn9c102/Kconfig new file mode 100644 index 000000000..cf552e6b8 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/Kconfig @@ -0,0 +1,11 @@ +config USB_SN9C102 + tristate "USB SN9C10x PC Camera Controller support" + depends on USB && VIDEO_V4L1 + ---help--- + Say Y here if you want support for cameras based on SONiX SN9C101, + SN9C102 or SN9C103 PC Camera Controllers. + + See for more info. + + To compile this driver as a module, choose M here: the + module will be called sn9c102. diff --git a/linux/drivers/media/video/sn9c102/Makefile b/linux/drivers/media/video/sn9c102/Makefile new file mode 100644 index 000000000..536ad3098 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/Makefile @@ -0,0 +1,7 @@ +sn9c102-objs := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o \ + sn9c102_ov7630.o sn9c102_pas106b.o sn9c102_pas202bca.o \ + sn9c102_pas202bcb.o sn9c102_tas5110c1b.o \ + sn9c102_tas5130d1b.o + +obj-$(CONFIG_USB_SN9C102) += sn9c102.o + diff --git a/linux/drivers/media/video/sn9c102/sn9c102.h b/linux/drivers/media/video/sn9c102/sn9c102.h new file mode 100644 index 000000000..3f644c63f --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102.h @@ -0,0 +1,219 @@ +/*************************************************************************** + * V4L2 driver for SN9C10x PC Camera Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _SN9C102_H_ +#define _SN9C102_H_ + +#include +#include +#include "compat.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sn9c102_sensor.h" + +/*****************************************************************************/ + +#define SN9C102_DEBUG +#define SN9C102_DEBUG_LEVEL 2 +#define SN9C102_MAX_DEVICES 64 +#define SN9C102_PRESERVE_IMGSCALE 0 +#define SN9C102_FORCE_MUNMAP 0 +#define SN9C102_MAX_FRAMES 32 +#define SN9C102_URBS 2 +#define SN9C102_ISO_PACKETS 7 +#define SN9C102_ALTERNATE_SETTING 8 +#define SN9C102_URB_TIMEOUT msecs_to_jiffies(2 * SN9C102_ISO_PACKETS) +#define SN9C102_CTRL_TIMEOUT 300 +#define SN9C102_FRAME_TIMEOUT 2 + +/*****************************************************************************/ + +enum sn9c102_bridge { + BRIDGE_SN9C101 = 0x01, + BRIDGE_SN9C102 = 0x02, + BRIDGE_SN9C103 = 0x04, +}; + +SN9C102_ID_TABLE +SN9C102_SENSOR_TABLE + +enum sn9c102_frame_state { + F_UNUSED, + F_QUEUED, + F_GRABBING, + F_DONE, + F_ERROR, +}; + +struct sn9c102_frame_t { + void* bufmem; + struct v4l2_buffer buf; + enum sn9c102_frame_state state; + struct list_head frame; + unsigned long vma_use_count; +}; + +enum sn9c102_dev_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +enum sn9c102_io_method { + IO_NONE, + IO_READ, + IO_MMAP, +}; + +enum sn9c102_stream_state { + STREAM_OFF, + STREAM_INTERRUPT, + STREAM_ON, +}; + +typedef char sn9c103_sof_header_t[18]; +typedef char sn9c102_sof_header_t[12]; +typedef char sn9c102_eof_header_t[4]; + +struct sn9c102_sysfs_attr { + u8 reg, i2c_reg; + sn9c103_sof_header_t frame_header; +}; + +struct sn9c102_module_param { + u8 force_munmap; + u16 frame_timeout; +}; + +static DEFINE_MUTEX(sn9c102_sysfs_lock); +static DECLARE_RWSEM(sn9c102_disconnect); + +struct sn9c102_device { + struct video_device* v4ldev; + + enum sn9c102_bridge bridge; + struct sn9c102_sensor sensor; + + struct usb_device* usbdev; + struct urb* urb[SN9C102_URBS]; + void* transfer_buffer[SN9C102_URBS]; + u8* control_buffer; + + struct sn9c102_frame_t *frame_current, frame[SN9C102_MAX_FRAMES]; + struct list_head inqueue, outqueue; + u32 frame_count, nbuffers, nreadbuffers; + + enum sn9c102_io_method io; + enum sn9c102_stream_state stream; + + struct v4l2_jpegcompression compression; + + struct sn9c102_sysfs_attr sysfs; + sn9c103_sof_header_t sof_header; + u16 reg[63]; + + struct sn9c102_module_param module_param; + + enum sn9c102_dev_state state; + u8 users; + + struct mutex dev_mutex, fileop_mutex; + spinlock_t queue_lock; + wait_queue_head_t open, wait_frame, wait_stream; +}; + +/*****************************************************************************/ + +struct sn9c102_device* +sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id) +{ + if (usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id)) + return cam; + + return NULL; +} + + +void +sn9c102_attach_sensor(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor) +{ + memcpy(&cam->sensor, sensor, sizeof(struct sn9c102_sensor)); +} + +/*****************************************************************************/ + +#undef DBG +#undef KDBG +#ifdef SN9C102_DEBUG +# define DBG(level, fmt, args...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1) \ + dev_err(&cam->usbdev->dev, fmt "\n", ## args); \ + else if ((level) == 2) \ + dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ + else if ((level) >= 3) \ + dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args); \ + } \ +} while (0) +# define V4LDBG(level, name, cmd) \ +do { \ + if (debug >= (level)) \ + v4l_print_ioctl(name, cmd); \ +} while (0) +# define KDBG(level, fmt, args...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1 || (level) == 2) \ + pr_info("sn9c102: " fmt "\n", ## args); \ + else if ((level) == 3) \ + pr_debug("sn9c102: [%s:%d] " fmt "\n", __FUNCTION__, \ + __LINE__ , ## args); \ + } \ +} while (0) +#else +# define DBG(level, fmt, args...) do {;} while(0) +# define V4LDBG(level, name, cmd) do {;} while(0) +# define KDBG(level, fmt, args...) do {;} while(0) +#endif + +#undef PDBG +#define PDBG(fmt, args...) \ +dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args) + +#undef PDBGG +#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ + +#endif /* _SN9C102_H_ */ diff --git a/linux/drivers/media/video/sn9c102/sn9c102_core.c b/linux/drivers/media/video/sn9c102/sn9c102_core.c new file mode 100644 index 000000000..ea4394dc9 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_core.c @@ -0,0 +1,2919 @@ +/*************************************************************************** + * V4L2 driver for SN9C10x PC Camera Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sn9c102.h" + +/*****************************************************************************/ + +#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10x PC Camera Controllers" +#define SN9C102_MODULE_AUTHOR "(C) 2004-2006 Luca Risolia" +#define SN9C102_AUTHOR_EMAIL "" +#define SN9C102_MODULE_LICENSE "GPL" +#define SN9C102_MODULE_VERSION "1:1.27" +#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 27) + +/*****************************************************************************/ + +MODULE_DEVICE_TABLE(usb, sn9c102_id_table); + +MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL); +MODULE_DESCRIPTION(SN9C102_MODULE_NAME); +MODULE_VERSION(SN9C102_MODULE_VERSION); +MODULE_LICENSE(SN9C102_MODULE_LICENSE); + +static short video_nr[] = {[0 ... SN9C102_MAX_DEVICES-1] = -1}; +module_param_array(video_nr, short, NULL, 0444); +MODULE_PARM_DESC(video_nr, + "\n<-1|n[,...]> Specify V4L2 minor mode number." + "\n -1 = use next available (default)" + "\n n = use minor number n (integer >= 0)" + "\nYou can specify up to "__MODULE_STRING(SN9C102_MAX_DEVICES) + " cameras this way." + "\nFor example:" + "\nvideo_nr=-1,2,-1 would assign minor number 2 to" + "\nthe second camera and use auto for the first" + "\none and for every other camera." + "\n"); + +static short force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] = + SN9C102_FORCE_MUNMAP}; +module_param_array(force_munmap, bool, NULL, 0444); +MODULE_PARM_DESC(force_munmap, + "\n<0|1[,...]> Force the application to unmap previously" + "\nmapped buffer memory before calling any VIDIOC_S_CROP or" + "\nVIDIOC_S_FMT ioctl's. Not all the applications support" + "\nthis feature. This parameter is specific for each" + "\ndetected camera." + "\n 0 = do not force memory unmapping" + "\n 1 = force memory unmapping (save memory)" + "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." + "\n"); + +static unsigned int frame_timeout[] = {[0 ... SN9C102_MAX_DEVICES-1] = + SN9C102_FRAME_TIMEOUT}; +module_param_array(frame_timeout, uint, NULL, 0644); +MODULE_PARM_DESC(frame_timeout, + "\n Timeout for a video frame in seconds." + "\nThis parameter is specific for each detected camera." + "\nDefault value is "__MODULE_STRING(SN9C102_FRAME_TIMEOUT)"." + "\n"); + +#ifdef SN9C102_DEBUG +static unsigned short debug = SN9C102_DEBUG_LEVEL; +module_param(debug, ushort, 0644); +MODULE_PARM_DESC(debug, + "\n Debugging information level, from 0 to 3:" + "\n0 = none (use carefully)" + "\n1 = critical errors" + "\n2 = significant informations" + "\n3 = more verbose messages" + "\nLevel 3 is useful for testing only, when only " + "one device is used." + "\nDefault value is "__MODULE_STRING(SN9C102_DEBUG_LEVEL)"." + "\n"); +#endif + +/*****************************************************************************/ + +static sn9c102_sof_header_t sn9c102_sof_header[] = { + {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x00}, + {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01}, +}; + +static sn9c103_sof_header_t sn9c103_sof_header[] = { + {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x20}, +}; + +static sn9c102_eof_header_t sn9c102_eof_header[] = { + {0x00, 0x00, 0x00, 0x00}, + {0x40, 0x00, 0x00, 0x00}, + {0x80, 0x00, 0x00, 0x00}, + {0xc0, 0x00, 0x00, 0x00}, +}; + +/*****************************************************************************/ + +static u32 +sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, + enum sn9c102_io_method io) +{ + struct v4l2_pix_format* p = &(cam->sensor.pix_format); + struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); + const size_t imagesize = cam->module_param.force_munmap || + io == IO_READ ? + (p->width * p->height * p->priv) / 8 : + (r->width * r->height * p->priv) / 8; + void* buff = NULL; + u32 i; + + if (count > SN9C102_MAX_FRAMES) + count = SN9C102_MAX_FRAMES; + + cam->nbuffers = count; + while (cam->nbuffers > 0) { + if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize)))) + break; + cam->nbuffers--; + } + + for (i = 0; i < cam->nbuffers; i++) { + cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); + cam->frame[i].buf.index = i; + cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); + cam->frame[i].buf.length = imagesize; + cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->frame[i].buf.sequence = 0; + cam->frame[i].buf.field = V4L2_FIELD_NONE; + cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; + cam->frame[i].buf.flags = 0; + } + + return cam->nbuffers; +} + + +static void sn9c102_release_buffers(struct sn9c102_device* cam) +{ + if (cam->nbuffers) { + vfree(cam->frame[0].bufmem); + cam->nbuffers = 0; + } + cam->frame_current = NULL; +} + + +static void sn9c102_empty_framequeues(struct sn9c102_device* cam) +{ + u32 i; + + INIT_LIST_HEAD(&cam->inqueue); + INIT_LIST_HEAD(&cam->outqueue); + + for (i = 0; i < SN9C102_MAX_FRAMES; i++) { + cam->frame[i].state = F_UNUSED; + cam->frame[i].buf.bytesused = 0; + } +} + + +static void sn9c102_requeue_outqueue(struct sn9c102_device* cam) +{ + struct sn9c102_frame_t *i; + + list_for_each_entry(i, &cam->outqueue, frame) { + i->state = F_QUEUED; + list_add(&i->frame, &cam->inqueue); + } + + INIT_LIST_HEAD(&cam->outqueue); +} + + +static void sn9c102_queue_unusedframes(struct sn9c102_device* cam) +{ + unsigned long lock_flags; + u32 i; + + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].state == F_UNUSED) { + cam->frame[i].state = F_QUEUED; + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_add_tail(&cam->frame[i].frame, &cam->inqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + } +} + +/*****************************************************************************/ + +int sn9c102_write_regs(struct sn9c102_device* cam, u8* buff, u16 index) +{ + struct usb_device* udev = cam->usbdev; + int i, res; + + if (index + sizeof(buff) >= ARRAY_SIZE(cam->reg)) + return -1; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + index, 0, buff, sizeof(buff), + SN9C102_CTRL_TIMEOUT*sizeof(buff)); + if (res < 0) { + DBG(3, "Failed to write registers (index 0x%02X, error %d)", + index, res); + return -1; + } + + for (i = 0; i < sizeof(buff); i++) + cam->reg[index+i] = buff[i]; + + return 0; +} + + +int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index) +{ + struct usb_device* udev = cam->usbdev; + u8* buff = cam->control_buffer; + int res; + + if (index >= ARRAY_SIZE(cam->reg)) + return -1; + + *buff = value; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + index, 0, buff, 1, SN9C102_CTRL_TIMEOUT); + if (res < 0) { + DBG(3, "Failed to write a register (value 0x%02X, index " + "0x%02X, error %d)", value, index, res); + return -1; + } + + cam->reg[index] = value; + + return 0; +} + + +/* NOTE: reading some registers always returns 0 */ +static int sn9c102_read_reg(struct sn9c102_device* cam, u16 index) +{ + struct usb_device* udev = cam->usbdev; + u8* buff = cam->control_buffer; + int res; + + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, + index, 0, buff, 1, SN9C102_CTRL_TIMEOUT); + if (res < 0) + DBG(3, "Failed to read a register (index 0x%02X, error %d)", + index, res); + + return (res >= 0) ? (int)(*buff) : -1; +} + + +int sn9c102_pread_reg(struct sn9c102_device* cam, u16 index) +{ + if (index >= ARRAY_SIZE(cam->reg)) + return -1; + + return cam->reg[index]; +} + + +static int +sn9c102_i2c_wait(struct sn9c102_device* cam, struct sn9c102_sensor* sensor) +{ + int i, r; + + for (i = 1; i <= 5; i++) { + r = sn9c102_read_reg(cam, 0x08); + if (r < 0) + return -EIO; + if (r & 0x04) + return 0; + if (sensor->frequency & SN9C102_I2C_400KHZ) + udelay(5*16); + else + udelay(16*16); + } + return -EBUSY; +} + + +static int +sn9c102_i2c_detect_read_error(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor) +{ + int r; + r = sn9c102_read_reg(cam, 0x08); + return (r < 0 || (r >= 0 && !(r & 0x08))) ? -EIO : 0; +} + + +static int +sn9c102_i2c_detect_write_error(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor) +{ + int r; + r = sn9c102_read_reg(cam, 0x08); + return (r < 0 || (r >= 0 && (r & 0x08))) ? -EIO : 0; +} + + +int +sn9c102_i2c_try_raw_read(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor, u8 data0, u8 data1, + u8 n, u8 buffer[]) +{ + struct usb_device* udev = cam->usbdev; + u8* data = cam->control_buffer; + int err = 0, res; + + /* Write cycle */ + data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | + ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | 0x10; + data[1] = data0; /* I2C slave id */ + data[2] = data1; /* address */ + data[7] = 0x10; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_wait(cam, sensor); + + /* Read cycle - n bytes */ + data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | + ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | + (n << 4) | 0x02; + data[1] = data0; + data[7] = 0x10; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_wait(cam, sensor); + + /* The first read byte will be placed in data[4] */ + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, + 0x0a, 0, data, 5, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_detect_read_error(cam, sensor); + + PDBGG("I2C read: address 0x%02X, first read byte: 0x%02X", data1, + data[4]); + + if (err) { + DBG(3, "I2C read failed for %s image sensor", sensor->name); + return -1; + } + + if (buffer) + memcpy(buffer, data, sizeof(buffer)); + + return (int)data[4]; +} + + +int +sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor, u8 n, u8 data0, + u8 data1, u8 data2, u8 data3, u8 data4, u8 data5) +{ + struct usb_device* udev = cam->usbdev; + u8* data = cam->control_buffer; + int err = 0, res; + + /* Write cycle. It usually is address + value */ + data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) | + ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) + | ((n - 1) << 4); + data[1] = data0; + data[2] = data1; + data[3] = data2; + data[4] = data3; + data[5] = data4; + data[6] = data5; + data[7] = 0x14; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, + 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); + if (res < 0) + err += res; + + err += sn9c102_i2c_wait(cam, sensor); + err += sn9c102_i2c_detect_write_error(cam, sensor); + + if (err) + DBG(3, "I2C write failed for %s image sensor", sensor->name); + + PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, " + "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X", + n, data0, data1, data2, data3, data4, data5); + + return err ? -1 : 0; +} + + +int +sn9c102_i2c_try_read(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor, u8 address) +{ + return sn9c102_i2c_try_raw_read(cam, sensor, sensor->i2c_slave_id, + address, 1, NULL); +} + + +int +sn9c102_i2c_try_write(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor, u8 address, u8 value) +{ + return sn9c102_i2c_try_raw_write(cam, sensor, 3, + sensor->i2c_slave_id, address, + value, 0, 0, 0); +} + + +int sn9c102_i2c_read(struct sn9c102_device* cam, u8 address) +{ + return sn9c102_i2c_try_read(cam, &cam->sensor, address); +} + + +int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value) +{ + return sn9c102_i2c_try_write(cam, &cam->sensor, address, value); +} + +/*****************************************************************************/ + +static void* +sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len) +{ + size_t soflen = 0, i; + u8 j, n = 0; + + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + soflen = sizeof(sn9c102_sof_header_t); + n = sizeof(sn9c102_sof_header) / soflen; + break; + case BRIDGE_SN9C103: + soflen = sizeof(sn9c103_sof_header_t); + n = sizeof(sn9c103_sof_header) / soflen; + } + + for (i = 0; (len >= soflen) && (i <= len - soflen); i++) + for (j = 0; j < n; j++) + /* The invariable part of the header is 6 bytes long */ + if ((cam->bridge != BRIDGE_SN9C103 && + !memcmp(mem + i, sn9c102_sof_header[j], 6)) || + (cam->bridge == BRIDGE_SN9C103 && + !memcmp(mem + i, sn9c103_sof_header[j], 6))) { + memcpy(cam->sof_header, mem + i, soflen); + /* Skip the header */ + return mem + i + soflen; + } + + return NULL; +} + + +static void* +sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len) +{ + size_t eoflen = sizeof(sn9c102_eof_header_t), i; + unsigned j, n = sizeof(sn9c102_eof_header) / eoflen; + + if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) + return NULL; /* EOF header does not exist in compressed data */ + + for (i = 0; (len >= eoflen) && (i <= len - eoflen); i++) + for (j = 0; j < n; j++) + if (!memcmp(mem + i, sn9c102_eof_header[j], eoflen)) + return mem + i; + + return NULL; +} + + +static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs) +{ + struct sn9c102_device* cam = urb->context; + struct sn9c102_frame_t** f; + size_t imagesize, soflen; + u8 i; + int err = 0; + + if (urb->status == -ENOENT) + return; + + f = &cam->frame_current; + + if (cam->stream == STREAM_INTERRUPT) { + cam->stream = STREAM_OFF; + if ((*f)) + (*f)->state = F_QUEUED; + DBG(3, "Stream interrupted"); + wake_up(&cam->wait_stream); + } + + if (cam->state & DEV_DISCONNECTED) + return; + + if (cam->state & DEV_MISCONFIGURED) { + wake_up_interruptible(&cam->wait_frame); + return; + } + + if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) + goto resubmit_urb; + + if (!(*f)) + (*f) = list_entry(cam->inqueue.next, struct sn9c102_frame_t, + frame); + + imagesize = (cam->sensor.pix_format.width * + cam->sensor.pix_format.height * + cam->sensor.pix_format.priv) / 8; + + soflen = (cam->bridge) == BRIDGE_SN9C103 ? + sizeof(sn9c103_sof_header_t) : + sizeof(sn9c102_sof_header_t); + + for (i = 0; i < urb->number_of_packets; i++) { + unsigned int img, len, status; + void *pos, *sof, *eof; + + len = urb->iso_frame_desc[i].actual_length; + status = urb->iso_frame_desc[i].status; + pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; + + if (status) { + DBG(3, "Error in isochronous frame"); + (*f)->state = F_ERROR; + continue; + } + + PDBGG("Isochrnous frame: length %u, #%u i", len, i); + +redo: + sof = sn9c102_find_sof_header(cam, pos, len); + if (likely(!sof)) { + eof = sn9c102_find_eof_header(cam, pos, len); + if ((*f)->state == F_GRABBING) { +end_of_frame: + img = len; + + if (eof) + img = (eof > pos) ? eof - pos - 1 : 0; + + if ((*f)->buf.bytesused+img > imagesize) { + u32 b; + b = (*f)->buf.bytesused + img - + imagesize; + img = imagesize - (*f)->buf.bytesused; + DBG(3, "Expected EOF not found: " + "video frame cut"); + if (eof) + DBG(3, "Exceeded limit: +%u " + "bytes", (unsigned)(b)); + } + + memcpy((*f)->bufmem + (*f)->buf.bytesused, pos, + img); + + if ((*f)->buf.bytesused == 0) + do_gettimeofday(&(*f)->buf.timestamp); + + (*f)->buf.bytesused += img; + + if ((*f)->buf.bytesused == imagesize || + (cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_SN9C10X && eof)) { + u32 b; + b = (*f)->buf.bytesused; + (*f)->state = F_DONE; + (*f)->buf.sequence= ++cam->frame_count; + spin_lock(&cam->queue_lock); + list_move_tail(&(*f)->frame, + &cam->outqueue); + if (!list_empty(&cam->inqueue)) + (*f) = list_entry( + cam->inqueue.next, + struct sn9c102_frame_t, + frame ); + else + (*f) = NULL; + spin_unlock(&cam->queue_lock); + memcpy(cam->sysfs.frame_header, + cam->sof_header, soflen); + DBG(3, "Video frame captured: %lu " + "bytes", (unsigned long)(b)); + + if (!(*f)) + goto resubmit_urb; + + } else if (eof) { + (*f)->state = F_ERROR; + DBG(3, "Not expected EOF after %lu " + "bytes of image data", + (unsigned long) + ((*f)->buf.bytesused)); + } + + if (sof) /* (1) */ + goto start_of_frame; + + } else if (eof) { + DBG(3, "EOF without SOF"); + continue; + + } else { + PDBGG("Ignoring pointless isochronous frame"); + continue; + } + + } else if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) { +start_of_frame: + (*f)->state = F_GRABBING; + (*f)->buf.bytesused = 0; + len -= (sof - pos); + pos = sof; + DBG(3, "SOF detected: new video frame"); + if (len) + goto redo; + + } else if ((*f)->state == F_GRABBING) { + eof = sn9c102_find_eof_header(cam, pos, len); + if (eof && eof < sof) + goto end_of_frame; /* (1) */ + else { + if (cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_SN9C10X) { + eof = sof - soflen; + goto end_of_frame; + } else { + DBG(3, "SOF before expected EOF after " + "%lu bytes of image data", + (unsigned long) + ((*f)->buf.bytesused)); + goto start_of_frame; + } + } + } + } + +resubmit_urb: + urb->dev = cam->usbdev; + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0 && err != -EPERM) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "usb_submit_urb() failed"); + } + + wake_up_interruptible(&cam->wait_frame); +} + + +static int sn9c102_start_transfer(struct sn9c102_device* cam) +{ + struct usb_device *udev = cam->usbdev; + struct urb* urb; + const unsigned int sn9c102_wMaxPacketSize[] = {0, 128, 256, 384, 512, + 680, 800, 900, 1023}; + const unsigned int sn9c103_wMaxPacketSize[] = {0, 128, 256, 384, 512, + 680, 800, 900, 1003}; + const unsigned int psz = (cam->bridge == BRIDGE_SN9C103) ? + sn9c103_wMaxPacketSize[SN9C102_ALTERNATE_SETTING] : + sn9c102_wMaxPacketSize[SN9C102_ALTERNATE_SETTING]; + s8 i, j; + int err = 0; + + for (i = 0; i < SN9C102_URBS; i++) { + cam->transfer_buffer[i] = kzalloc(SN9C102_ISO_PACKETS * psz, + GFP_KERNEL); + if (!cam->transfer_buffer[i]) { + err = -ENOMEM; + DBG(1, "Not enough memory"); + goto free_buffers; + } + } + + for (i = 0; i < SN9C102_URBS; i++) { + urb = usb_alloc_urb(SN9C102_ISO_PACKETS, GFP_KERNEL); + cam->urb[i] = urb; + if (!urb) { + err = -ENOMEM; + DBG(1, "usb_alloc_urb() failed"); + goto free_urbs; + } + urb->dev = udev; + urb->context = cam; + urb->pipe = usb_rcvisocpipe(udev, 1); + urb->transfer_flags = URB_ISO_ASAP; + urb->number_of_packets = SN9C102_ISO_PACKETS; + urb->complete = sn9c102_urb_complete; + urb->transfer_buffer = cam->transfer_buffer[i]; + urb->transfer_buffer_length = psz * SN9C102_ISO_PACKETS; + urb->interval = 1; + for (j = 0; j < SN9C102_ISO_PACKETS; j++) { + urb->iso_frame_desc[j].offset = psz * j; + urb->iso_frame_desc[j].length = psz; + } + } + + /* Enable video */ + if (!(cam->reg[0x01] & 0x04)) { + err = sn9c102_write_reg(cam, cam->reg[0x01] | 0x04, 0x01); + if (err) { + err = -EIO; + DBG(1, "I/O hardware error"); + goto free_urbs; + } + } + + err = usb_set_interface(udev, 0, SN9C102_ALTERNATE_SETTING); + if (err) { + DBG(1, "usb_set_interface() failed"); + goto free_urbs; + } + + cam->frame_current = NULL; + + for (i = 0; i < SN9C102_URBS; i++) { + err = usb_submit_urb(cam->urb[i], GFP_KERNEL); + if (err) { + for (j = i-1; j >= 0; j--) + usb_kill_urb(cam->urb[j]); + DBG(1, "usb_submit_urb() failed, error %d", err); + goto free_urbs; + } + } + + return 0; + +free_urbs: + for (i = 0; (i < SN9C102_URBS) && cam->urb[i]; i++) + usb_free_urb(cam->urb[i]); + +free_buffers: + for (i = 0; (i < SN9C102_URBS) && cam->transfer_buffer[i]; i++) + kfree(cam->transfer_buffer[i]); + + return err; +} + + +static int sn9c102_stop_transfer(struct sn9c102_device* cam) +{ + struct usb_device *udev = cam->usbdev; + s8 i; + int err = 0; + + if (cam->state & DEV_DISCONNECTED) + return 0; + + for (i = SN9C102_URBS-1; i >= 0; i--) { + usb_kill_urb(cam->urb[i]); + usb_free_urb(cam->urb[i]); + kfree(cam->transfer_buffer[i]); + } + + err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ + if (err) + DBG(3, "usb_set_interface() failed"); + + return err; +} + + +static int sn9c102_stream_interrupt(struct sn9c102_device* cam) +{ + long timeout; + + cam->stream = STREAM_INTERRUPT; + timeout = wait_event_timeout(cam->wait_stream, + (cam->stream == STREAM_OFF) || + (cam->state & DEV_DISCONNECTED), + SN9C102_URB_TIMEOUT); + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + else if (cam->stream != STREAM_OFF) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "URB timeout reached. The camera is misconfigured. " + "To use it, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + return 0; +} + +/*****************************************************************************/ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static u8 sn9c102_strtou8(const char* buff, size_t len, ssize_t* count) +{ + char str[5]; + char* endp; + unsigned long val; + + if (len < 4) { + strncpy(str, buff, len); + str[len+1] = '\0'; + } else { + strncpy(str, buff, 4); + str[4] = '\0'; + } + + val = simple_strtoul(str, &endp, 0); + + *count = 0; + if (val <= 0xff) + *count = (ssize_t)(endp - str); + if ((*count) && (len == *count+1) && (buff[*count] == '\n')) + *count += 1; + + return (u8)val; +} + +/* + NOTE 1: being inside one of the following methods implies that the v4l + device exists for sure (see kobjects and reference counters) + NOTE 2: buffers are PAGE_SIZE long +*/ + +static ssize_t sn9c102_show_reg(struct class_device* cd, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + count = sprintf(buf, "%u\n", cam->sysfs.reg); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_reg(struct class_device* cd, const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u8 index; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + index = sn9c102_strtou8(buf, len, &count); + if (index > 0x1f || !count) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EINVAL; + } + + cam->sysfs.reg = index; + + DBG(2, "Moved SN9C10X register index to 0x%02X", cam->sysfs.reg); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t sn9c102_show_val(struct class_device* cd, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + int val; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + if ((val = sn9c102_read_reg(cam, cam->sysfs.reg)) < 0) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EIO; + } + + count = sprintf(buf, "%d\n", val); + + DBG(3, "Read bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_val(struct class_device* cd, const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u8 value; + ssize_t count; + int err; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + value = sn9c102_strtou8(buf, len, &count); + if (!count) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EINVAL; + } + + err = sn9c102_write_reg(cam, value, cam->sysfs.reg); + if (err) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EIO; + } + + DBG(2, "Written SN9C10X reg. 0x%02X, val. 0x%02X", + cam->sysfs.reg, value); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t sn9c102_show_i2c_reg(struct class_device* cd, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg); + + DBG(3, "Read bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_i2c_reg(struct class_device* cd, const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u8 index; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + index = sn9c102_strtou8(buf, len, &count); + if (!count) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EINVAL; + } + + cam->sysfs.i2c_reg = index; + + DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + int val; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + if (!(cam->sensor.sysfs_ops & SN9C102_I2C_READ)) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENOSYS; + } + + if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EIO; + } + + count = sprintf(buf, "%d\n", val); + + DBG(3, "Read bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len) +{ + struct sn9c102_device* cam; + u8 value; + ssize_t count; + int err; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + if (!(cam->sensor.sysfs_ops & SN9C102_I2C_WRITE)) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENOSYS; + } + + value = sn9c102_strtou8(buf, len, &count); + if (!count) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EINVAL; + } + + err = sn9c102_i2c_write(cam, cam->sysfs.i2c_reg, value); + if (err) { + mutex_unlock(&sn9c102_sysfs_lock); + return -EIO; + } + + DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X", + cam->sysfs.i2c_reg, value); + DBG(3, "Written bytes: %zd", count); + + mutex_unlock(&sn9c102_sysfs_lock); + + return count; +} + + +static ssize_t +sn9c102_store_green(struct class_device* cd, const char* buf, size_t len) +{ + struct sn9c102_device* cam; + enum sn9c102_bridge bridge; + ssize_t res = 0; + u8 value; + ssize_t count; + + if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) + return -ERESTARTSYS; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) { + mutex_unlock(&sn9c102_sysfs_lock); + return -ENODEV; + } + + bridge = cam->bridge; + + mutex_unlock(&sn9c102_sysfs_lock); + + value = sn9c102_strtou8(buf, len, &count); + if (!count) + return -EINVAL; + + switch (bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + if (value > 0x0f) + return -EINVAL; + if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0) + res = sn9c102_store_val(cd, buf, len); + break; + case BRIDGE_SN9C103: + if (value > 0x7f) + return -EINVAL; + if ((res = sn9c102_store_reg(cd, "0x04", 4)) >= 0) + res = sn9c102_store_val(cd, buf, len); + break; + } + + return res; +} + + +static ssize_t +sn9c102_store_blue(struct class_device* cd, const char* buf, size_t len) +{ + ssize_t res = 0; + u8 value; + ssize_t count; + + value = sn9c102_strtou8(buf, len, &count); + if (!count || value > 0x7f) + return -EINVAL; + + if ((res = sn9c102_store_reg(cd, "0x06", 4)) >= 0) + res = sn9c102_store_val(cd, buf, len); + + return res; +} + + +static ssize_t +sn9c102_store_red(struct class_device* cd, const char* buf, size_t len) +{ + ssize_t res = 0; + u8 value; + ssize_t count; + + value = sn9c102_strtou8(buf, len, &count); + if (!count || value > 0x7f) + return -EINVAL; + + if ((res = sn9c102_store_reg(cd, "0x05", 4)) >= 0) + res = sn9c102_store_val(cd, buf, len); + + return res; +} + + +static ssize_t sn9c102_show_frame_header(struct class_device* cd, char* buf) +{ + struct sn9c102_device* cam; + ssize_t count; + + cam = video_get_drvdata(to_video_device(cd)); + if (!cam) + return -ENODEV; + + count = sizeof(cam->sysfs.frame_header); + memcpy(buf, cam->sysfs.frame_header, count); + + DBG(3, "Frame header, read bytes: %zd", count); + + return count; +} + + +static CLASS_DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, + sn9c102_show_reg, sn9c102_store_reg); +static CLASS_DEVICE_ATTR(val, S_IRUGO | S_IWUSR, + sn9c102_show_val, sn9c102_store_val); +static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, + sn9c102_show_i2c_reg, sn9c102_store_i2c_reg); +static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, + sn9c102_show_i2c_val, sn9c102_store_i2c_val); +static CLASS_DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green); +static CLASS_DEVICE_ATTR(blue, S_IWUGO, NULL, sn9c102_store_blue); +static CLASS_DEVICE_ATTR(red, S_IWUGO, NULL, sn9c102_store_red); +static CLASS_DEVICE_ATTR(frame_header, S_IRUGO, + sn9c102_show_frame_header, NULL); + + +static void sn9c102_create_sysfs(struct sn9c102_device* cam) +{ + struct video_device *v4ldev = cam->v4ldev; + + video_device_create_file(v4ldev, &class_device_attr_reg); + video_device_create_file(v4ldev, &class_device_attr_val); + video_device_create_file(v4ldev, &class_device_attr_frame_header); + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) + video_device_create_file(v4ldev, &class_device_attr_green); + else if (cam->bridge == BRIDGE_SN9C103) { + video_device_create_file(v4ldev, &class_device_attr_blue); + video_device_create_file(v4ldev, &class_device_attr_red); + } + if (cam->sensor.sysfs_ops) { + video_device_create_file(v4ldev, &class_device_attr_i2c_reg); + video_device_create_file(v4ldev, &class_device_attr_i2c_val); + } +} +#endif /* CONFIG_VIDEO_ADV_DEBUG */ + +/*****************************************************************************/ + +static int +sn9c102_set_pix_format(struct sn9c102_device* cam, struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, 0x18); + else + err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, 0x18); + + return err ? -EIO : 0; +} + + +static int +sn9c102_set_compression(struct sn9c102_device* cam, + struct v4l2_jpegcompression* compression) +{ + int err = 0; + + if (compression->quality == 0) + err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01, 0x17); + else if (compression->quality == 1) + err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe, 0x17); + + return err ? -EIO : 0; +} + + +static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale) +{ + u8 r = 0; + int err = 0; + + if (scale == 1) + r = cam->reg[0x18] & 0xcf; + else if (scale == 2) { + r = cam->reg[0x18] & 0xcf; + r |= 0x10; + } else if (scale == 4) + r = cam->reg[0x18] | 0x20; + + err += sn9c102_write_reg(cam, r, 0x18); + if (err) + return -EIO; + + PDBGG("Scaling factor: %u", scale); + + return 0; +} + + +static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &cam->sensor; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left), + v_start = (u8)(rect->top - s->cropcap.bounds.top), + h_size = (u8)(rect->width / 16), + v_size = (u8)(rect->height / 16); + int err = 0; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + err += sn9c102_write_reg(cam, h_size, 0x15); + err += sn9c102_write_reg(cam, v_size, 0x16); + if (err) + return -EIO; + + PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size " + "%u %u %u %u", h_start, v_start, h_size, v_size); + + return 0; +} + + +static int sn9c102_init(struct sn9c102_device* cam) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + struct v4l2_queryctrl *qctrl; + struct v4l2_rect* rect; + u8 i = 0; + int err = 0; + + if (!(cam->state & DEV_INITIALIZED)) { + init_waitqueue_head(&cam->open); + qctrl = s->qctrl; + rect = &(s->cropcap.defrect); + } else { /* use current values */ + qctrl = s->_qctrl; + rect = &(s->_rect); + } + + err += sn9c102_set_scale(cam, rect->width / s->pix_format.width); + err += sn9c102_set_crop(cam, rect); + if (err) + return err; + + if (s->init) { + err = s->init(cam); + if (err) { + DBG(3, "Sensor initialization failed"); + return err; + } + } + + if (!(cam->state & DEV_INITIALIZED)) + cam->compression.quality = cam->reg[0x17] & 0x01 ? 0 : 1; + else + err += sn9c102_set_compression(cam, &cam->compression); + err += sn9c102_set_pix_format(cam, &s->pix_format); + if (s->set_pix_format) + err += s->set_pix_format(cam, &s->pix_format); + if (err) + return err; + + if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) + DBG(3, "Compressed video format is active, quality %d", + cam->compression.quality); + else + DBG(3, "Uncompressed video format is active"); + + if (s->set_crop) + if ((err = s->set_crop(cam, rect))) { + DBG(3, "set_crop() failed"); + return err; + } + + if (s->set_ctrl) { + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (s->qctrl[i].id != 0 && + !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { + ctrl.id = s->qctrl[i].id; + ctrl.value = qctrl[i].default_value; + err = s->set_ctrl(cam, &ctrl); + if (err) { + DBG(3, "Set %s control failed", + s->qctrl[i].name); + return err; + } + DBG(3, "Image sensor supports '%s' control", + s->qctrl[i].name); + } + } + + if (!(cam->state & DEV_INITIALIZED)) { + mutex_init(&cam->fileop_mutex); + spin_lock_init(&cam->queue_lock); + init_waitqueue_head(&cam->wait_frame); + init_waitqueue_head(&cam->wait_stream); + cam->nreadbuffers = 2; + memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); + memcpy(&(s->_rect), &(s->cropcap.defrect), + sizeof(struct v4l2_rect)); + cam->state |= DEV_INITIALIZED; + } + + DBG(2, "Initialization succeeded"); + return 0; +} + + +static void sn9c102_release_resources(struct sn9c102_device* cam) +{ + mutex_lock(&sn9c102_sysfs_lock); + + DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor); + video_set_drvdata(cam->v4ldev, NULL); + video_unregister_device(cam->v4ldev); + + usb_put_dev(cam->usbdev); + + mutex_unlock(&sn9c102_sysfs_lock); + + kfree(cam->control_buffer); +} + +/*****************************************************************************/ + +static int sn9c102_open(struct inode* inode, struct file* filp) +{ + struct sn9c102_device* cam; + int err = 0; + + /* + This is the only safe way to prevent race conditions with + disconnect + */ + if (!down_read_trylock(&sn9c102_disconnect)) + return -ERESTARTSYS; + + cam = video_get_drvdata(video_devdata(filp)); + + if (mutex_lock_interruptible(&cam->dev_mutex)) { + up_read(&sn9c102_disconnect); + return -ERESTARTSYS; + } + + if (cam->users) { + DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor); + if ((filp->f_flags & O_NONBLOCK) || + (filp->f_flags & O_NDELAY)) { + err = -EWOULDBLOCK; + goto out; + } + mutex_unlock(&cam->dev_mutex); + err = wait_event_interruptible_exclusive(cam->open, + cam->state & DEV_DISCONNECTED + || !cam->users); + if (err) { + up_read(&sn9c102_disconnect); + return err; + } + if (cam->state & DEV_DISCONNECTED) { + up_read(&sn9c102_disconnect); + return -ENODEV; + } + mutex_lock(&cam->dev_mutex); + } + + + if (cam->state & DEV_MISCONFIGURED) { + err = sn9c102_init(cam); + if (err) { + DBG(1, "Initialization failed again. " + "I will retry on next open()."); + goto out; + } + cam->state &= ~DEV_MISCONFIGURED; + } + + if ((err = sn9c102_start_transfer(cam))) + goto out; + + filp->private_data = cam; + cam->users++; + cam->io = IO_NONE; + cam->stream = STREAM_OFF; + cam->nbuffers = 0; + cam->frame_count = 0; + sn9c102_empty_framequeues(cam); + + DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor); + +out: + mutex_unlock(&cam->dev_mutex); + up_read(&sn9c102_disconnect); + return err; +} + + +static int sn9c102_release(struct inode* inode, struct file* filp) +{ + struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + + mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */ + + sn9c102_stop_transfer(cam); + + sn9c102_release_buffers(cam); + + if (cam->state & DEV_DISCONNECTED) { + sn9c102_release_resources(cam); + mutex_unlock(&cam->dev_mutex); + kfree(cam); + return 0; + } + + cam->users--; + wake_up_interruptible_nr(&cam->open, 1); + + DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor); + + mutex_unlock(&cam->dev_mutex); + + return 0; +} + + +static ssize_t +sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) +{ + struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + struct sn9c102_frame_t* f, * i; + unsigned long lock_flags; + long timeout; + int err = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + if (cam->io == IO_MMAP) { + DBG(3, "Close and open the device again to choose " + "the read method"); + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + if (cam->io == IO_NONE) { + if (!sn9c102_request_buffers(cam,cam->nreadbuffers, IO_READ)) { + DBG(1, "read() failed, not enough memory"); + mutex_unlock(&cam->fileop_mutex); + return -ENOMEM; + } + cam->io = IO_READ; + cam->stream = STREAM_ON; + } + + if (list_empty(&cam->inqueue)) { + if (!list_empty(&cam->outqueue)) + sn9c102_empty_framequeues(cam); + sn9c102_queue_unusedframes(cam); + } + + if (!count) { + mutex_unlock(&cam->fileop_mutex); + return 0; + } + + if (list_empty(&cam->outqueue)) { + if (filp->f_flags & O_NONBLOCK) { + mutex_unlock(&cam->fileop_mutex); + return -EAGAIN; + } + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) { + mutex_unlock(&cam->fileop_mutex); + return timeout; + } + if (cam->state & DEV_DISCONNECTED) { + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + if (!timeout || (cam->state & DEV_MISCONFIGURED)) { + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + } + + f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame); + + if (count > f->buf.bytesused) + count = f->buf.bytesused; + + if (copy_to_user(buf, f->bufmem, count)) { + err = -EFAULT; + goto exit; + } + *f_pos += count; + +exit: + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_for_each_entry(i, &cam->outqueue, frame) + i->state = F_UNUSED; + INIT_LIST_HEAD(&cam->outqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + sn9c102_queue_unusedframes(cam); + + PDBGG("Frame #%lu, bytes read: %zu", + (unsigned long)f->buf.index, count); + + mutex_unlock(&cam->fileop_mutex); + + return count; +} + + +static unsigned int sn9c102_poll(struct file *filp, poll_table *wait) +{ + struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + struct sn9c102_frame_t* f; + unsigned long lock_flags; + unsigned int mask = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return POLLERR; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + goto error; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + goto error; + } + + if (cam->io == IO_NONE) { + if (!sn9c102_request_buffers(cam, cam->nreadbuffers, + IO_READ)) { + DBG(1, "poll() failed, not enough memory"); + goto error; + } + cam->io = IO_READ; + cam->stream = STREAM_ON; + } + + if (cam->io == IO_READ) { + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_for_each_entry(f, &cam->outqueue, frame) + f->state = F_UNUSED; + INIT_LIST_HEAD(&cam->outqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + sn9c102_queue_unusedframes(cam); + } + + poll_wait(filp, &cam->wait_frame, wait); + + if (!list_empty(&cam->outqueue)) + mask |= POLLIN | POLLRDNORM; + + mutex_unlock(&cam->fileop_mutex); + + return mask; + +error: + mutex_unlock(&cam->fileop_mutex); + return POLLERR; +} + + +static void sn9c102_vm_open(struct vm_area_struct* vma) +{ + struct sn9c102_frame_t* f = vma->vm_private_data; + f->vma_use_count++; +} + + +static void sn9c102_vm_close(struct vm_area_struct* vma) +{ + /* NOTE: buffers are not freed here */ + struct sn9c102_frame_t* f = vma->vm_private_data; + f->vma_use_count--; +} + + +static struct vm_operations_struct sn9c102_vm_ops = { + .open = sn9c102_vm_open, + .close = sn9c102_vm_close, +}; + + +static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) +{ + struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + unsigned long size = vma->vm_end - vma->vm_start, + start = vma->vm_start; + void *pos; + u32 i; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || + size != PAGE_ALIGN(cam->frame[0].buf.length)) { + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + for (i = 0; i < cam->nbuffers; i++) { + if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff) + break; + } + if (i == cam->nbuffers) { + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_RESERVED; + + pos = cam->frame[i].bufmem; + while (size > 0) { /* size is page-aligned */ + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { + mutex_unlock(&cam->fileop_mutex); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + size -= PAGE_SIZE; + } + + vma->vm_ops = &sn9c102_vm_ops; + vma->vm_private_data = &cam->frame[i]; + + sn9c102_vm_open(vma); + + mutex_unlock(&cam->fileop_mutex); + + return 0; +} + +/*****************************************************************************/ + +static int +sn9c102_vidioc_querycap(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_capability cap = { + .driver = "sn9c102", + .version = SN9C102_MODULE_VERSION_CODE, + .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING, + }; + + strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); + if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) + strlcpy(cap.bus_info, cam->usbdev->dev.bus_id, + sizeof(cap.bus_info)); + + if (copy_to_user(arg, &cap, sizeof(cap))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_enuminput(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_input i; + + if (copy_from_user(&i, arg, sizeof(i))) + return -EFAULT; + + if (i.index) + return -EINVAL; + + memset(&i, 0, sizeof(i)); + strcpy(i.name, "Camera"); + i.type = V4L2_INPUT_TYPE_CAMERA; + + if (copy_to_user(arg, &i, sizeof(i))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_g_input(struct sn9c102_device* cam, void __user * arg) +{ + int index = 0; + + if (copy_to_user(arg, &index, sizeof(index))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_input(struct sn9c102_device* cam, void __user * arg) +{ + int index; + + if (copy_from_user(&index, arg, sizeof(index))) + return -EFAULT; + + if (index != 0) + return -EINVAL; + + return 0; +} + + +static int +sn9c102_vidioc_query_ctrl(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_queryctrl qc; + u8 i; + + if (copy_from_user(&qc, arg, sizeof(qc))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (qc.id && qc.id == s->qctrl[i].id) { + memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); + if (copy_to_user(arg, &qc, sizeof(qc))) + return -EFAULT; + return 0; + } + + return -EINVAL; +} + + +static int +sn9c102_vidioc_g_ctrl(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + int err = 0; + u8 i; + + if (!s->get_ctrl && !s->set_ctrl) + return -EINVAL; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + + if (!s->get_ctrl) { + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (ctrl.id && ctrl.id == s->qctrl[i].id) { + ctrl.value = s->_qctrl[i].default_value; + goto exit; + } + return -EINVAL; + } else + err = s->get_ctrl(cam, &ctrl); + +exit: + if (copy_to_user(arg, &ctrl, sizeof(ctrl))) + return -EFAULT; + + return err; +} + + +static int +sn9c102_vidioc_s_ctrl(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + u8 i; + int err = 0; + + if (!s->set_ctrl) + return -EINVAL; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (ctrl.id == s->qctrl[i].id) { + if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) + return -EINVAL; + if (ctrl.value < s->qctrl[i].minimum || + ctrl.value > s->qctrl[i].maximum) + return -ERANGE; + ctrl.value -= ctrl.value % s->qctrl[i].step; + break; + } + + if ((err = s->set_ctrl(cam, &ctrl))) + return err; + + s->_qctrl[i].default_value = ctrl.value; + + PDBGG("VIDIOC_S_CTRL: id %lu, value %lu", + (unsigned long)ctrl.id, (unsigned long)ctrl.value); + + return 0; +} + + +static int +sn9c102_vidioc_cropcap(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_cropcap* cc = &(cam->sensor.cropcap); + + cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cc->pixelaspect.numerator = 1; + cc->pixelaspect.denominator = 1; + + if (copy_to_user(arg, cc, sizeof(*cc))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_g_crop(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_crop crop = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + }; + + memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect)); + + if (copy_to_user(arg, &crop, sizeof(crop))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_crop crop; + struct v4l2_rect* rect; + struct v4l2_rect* bounds = &(s->cropcap.bounds); + struct v4l2_pix_format* pix_format = &(s->pix_format); + u8 scale; + const enum sn9c102_stream_state stream = cam->stream; + const u32 nbuffers = cam->nbuffers; + u32 i; + int err = 0; + + if (copy_from_user(&crop, arg, sizeof(crop))) + return -EFAULT; + + rect = &(crop.c); + + if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (cam->module_param.force_munmap) + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_S_CROP failed. " + "Unmap the buffers first."); + return -EINVAL; + } + + /* Preserve R,G or B origin */ + rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L; + rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L; + + if (rect->width < 16) + rect->width = 16; + if (rect->height < 16) + rect->height = 16; + if (rect->width > bounds->width) + rect->width = bounds->width; + if (rect->height > bounds->height) + rect->height = bounds->height; + if (rect->left < bounds->left) + rect->left = bounds->left; + if (rect->top < bounds->top) + rect->top = bounds->top; + if (rect->left + rect->width > bounds->left + bounds->width) + rect->left = bounds->left+bounds->width - rect->width; + if (rect->top + rect->height > bounds->top + bounds->height) + rect->top = bounds->top+bounds->height - rect->height; + + rect->width &= ~15L; + rect->height &= ~15L; + + if (SN9C102_PRESERVE_IMGSCALE) { + /* Calculate the actual scaling factor */ + u32 a, b; + a = rect->width * rect->height; + b = pix_format->width * pix_format->height; + scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; + } else + scale = 1; + + if (cam->stream == STREAM_ON) + if ((err = sn9c102_stream_interrupt(cam))) + return err; + + if (copy_to_user(arg, &crop, sizeof(crop))) { + cam->stream = stream; + return -EFAULT; + } + + if (cam->module_param.force_munmap || cam->io == IO_READ) + sn9c102_release_buffers(cam); + + err = sn9c102_set_crop(cam, rect); + if (s->set_crop) + err += s->set_crop(cam, rect); + err += sn9c102_set_scale(cam, scale); + + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + s->pix_format.width = rect->width/scale; + s->pix_format.height = rect->height/scale; + memcpy(&(s->_rect), rect, sizeof(*rect)); + + if ((cam->module_param.force_munmap || cam->io == IO_READ) && + nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -ENOMEM; + } + + if (cam->io == IO_READ) + sn9c102_empty_framequeues(cam); + else if (cam->module_param.force_munmap) + sn9c102_requeue_outqueue(cam); + + cam->stream = stream; + + return 0; +} + + +static int +sn9c102_vidioc_enum_fmt(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_fmtdesc fmtd; + + if (copy_from_user(&fmtd, arg, sizeof(fmtd))) + return -EFAULT; + + if (fmtd.index == 0) { + strcpy(fmtd.description, "bayer rgb"); + fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8; + } else if (fmtd.index == 1) { + strcpy(fmtd.description, "compressed"); + fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X; + fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; + } else + return -EINVAL; + + fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + memset(&fmtd.reserved, 0, sizeof(fmtd.reserved)); + + if (copy_to_user(arg, &fmtd, sizeof(fmtd))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_format format; + struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format); + + if (copy_from_user(&format, arg, sizeof(format))) + return -EFAULT; + + if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_SN9C10X) + ? 0 : (pfmt->width * pfmt->priv) / 8; + pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); + pfmt->field = V4L2_FIELD_NONE; + memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); + + if (copy_to_user(arg, &format, sizeof(format))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd, + void __user * arg) +{ + struct sn9c102_sensor* s = &cam->sensor; + struct v4l2_format format; + struct v4l2_pix_format* pix; + struct v4l2_pix_format* pfmt = &(s->pix_format); + struct v4l2_rect* bounds = &(s->cropcap.bounds); + struct v4l2_rect rect; + u8 scale; + const enum sn9c102_stream_state stream = cam->stream; + const u32 nbuffers = cam->nbuffers; + u32 i; + int err = 0; + + if (copy_from_user(&format, arg, sizeof(format))) + return -EFAULT; + + pix = &(format.fmt.pix); + + if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memcpy(&rect, &(s->_rect), sizeof(rect)); + + { /* calculate the actual scaling factor */ + u32 a, b; + a = rect.width * rect.height; + b = pix->width * pix->height; + scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; + } + + rect.width = scale * pix->width; + rect.height = scale * pix->height; + + if (rect.width < 16) + rect.width = 16; + if (rect.height < 16) + rect.height = 16; + if (rect.width > bounds->left + bounds->width - rect.left) + rect.width = bounds->left + bounds->width - rect.left; + if (rect.height > bounds->top + bounds->height - rect.top) + rect.height = bounds->top + bounds->height - rect.top; + + rect.width &= ~15L; + rect.height &= ~15L; + + { /* adjust the scaling factor */ + u32 a, b; + a = rect.width * rect.height; + b = pix->width * pix->height; + scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; + } + + pix->width = rect.width / scale; + pix->height = rect.height / scale; + + if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X && + pix->pixelformat != V4L2_PIX_FMT_SBGGR8) + pix->pixelformat = pfmt->pixelformat; + pix->priv = pfmt->priv; /* bpp */ + pix->colorspace = pfmt->colorspace; + pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + ? 0 : (pix->width * pix->priv) / 8; + pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); + pix->field = V4L2_FIELD_NONE; + + if (cmd == VIDIOC_TRY_FMT) { + if (copy_to_user(arg, &format, sizeof(format))) + return -EFAULT; + return 0; + } + + if (cam->module_param.force_munmap) + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_S_FMT failed. Unmap the " + "buffers first."); + return -EINVAL; + } + + if (cam->stream == STREAM_ON) + if ((err = sn9c102_stream_interrupt(cam))) + return err; + + if (copy_to_user(arg, &format, sizeof(format))) { + cam->stream = stream; + return -EFAULT; + } + + if (cam->module_param.force_munmap || cam->io == IO_READ) + sn9c102_release_buffers(cam); + + err += sn9c102_set_pix_format(cam, pix); + err += sn9c102_set_crop(cam, &rect); + if (s->set_pix_format) + err += s->set_pix_format(cam, pix); + if (s->set_crop) + err += s->set_crop(cam, &rect); + err += sn9c102_set_scale(cam, scale); + + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + memcpy(pfmt, pix, sizeof(*pix)); + memcpy(&(s->_rect), &rect, sizeof(rect)); + + if ((cam->module_param.force_munmap || cam->io == IO_READ) && + nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -ENOMEM; + } + + if (cam->io == IO_READ) + sn9c102_empty_framequeues(cam); + else if (cam->module_param.force_munmap) + sn9c102_requeue_outqueue(cam); + + cam->stream = stream; + + return 0; +} + + +static int +sn9c102_vidioc_g_jpegcomp(struct sn9c102_device* cam, void __user * arg) +{ + if (copy_to_user(arg, &cam->compression, + sizeof(cam->compression))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_jpegcomp(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_jpegcompression jc; + const enum sn9c102_stream_state stream = cam->stream; + int err = 0; + + if (copy_from_user(&jc, arg, sizeof(jc))) + return -EFAULT; + + if (jc.quality != 0 && jc.quality != 1) + return -EINVAL; + + if (cam->stream == STREAM_ON) + if ((err = sn9c102_stream_interrupt(cam))) + return err; + + err += sn9c102_set_compression(cam, &jc); + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware " + "problems. To use the camera, close and open " + "/dev/video%d again.", cam->v4ldev->minor); + return -EIO; + } + + cam->compression.quality = jc.quality; + + cam->stream = stream; + + return 0; +} + + +static int +sn9c102_vidioc_reqbufs(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_requestbuffers rb; + u32 i; + int err; + + if (copy_from_user(&rb, arg, sizeof(rb))) + return -EFAULT; + + if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + rb.memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (cam->io == IO_READ) { + DBG(3, "Close and open the device again to choose the mmap " + "I/O method"); + return -EINVAL; + } + + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_REQBUFS failed. Previous buffers are " + "still mapped."); + return -EINVAL; + } + + if (cam->stream == STREAM_ON) + if ((err = sn9c102_stream_interrupt(cam))) + return err; + + sn9c102_empty_framequeues(cam); + + sn9c102_release_buffers(cam); + if (rb.count) + rb.count = sn9c102_request_buffers(cam, rb.count, IO_MMAP); + + if (copy_to_user(arg, &rb, sizeof(rb))) { + sn9c102_release_buffers(cam); + cam->io = IO_NONE; + return -EFAULT; + } + + cam->io = rb.count ? IO_MMAP : IO_NONE; + + return 0; +} + + +static int +sn9c102_vidioc_querybuf(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_buffer b; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + b.index >= cam->nbuffers || cam->io != IO_MMAP) + return -EINVAL; + + memcpy(&b, &cam->frame[b.index].buf, sizeof(b)); + + if (cam->frame[b.index].vma_use_count) + b.flags |= V4L2_BUF_FLAG_MAPPED; + + if (cam->frame[b.index].state == F_DONE) + b.flags |= V4L2_BUF_FLAG_DONE; + else if (cam->frame[b.index].state != F_UNUSED) + b.flags |= V4L2_BUF_FLAG_QUEUED; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_qbuf(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_buffer b; + unsigned long lock_flags; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + b.index >= cam->nbuffers || cam->io != IO_MMAP) + return -EINVAL; + + if (cam->frame[b.index].state != F_UNUSED) + return -EINVAL; + + cam->frame[b.index].state = F_QUEUED; + + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_add_tail(&cam->frame[b.index].frame, &cam->inqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + PDBGG("Frame #%lu queued", (unsigned long)b.index); + + return 0; +} + + +static int +sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp, + void __user * arg) +{ + struct v4l2_buffer b; + struct sn9c102_frame_t *f; + unsigned long lock_flags; + long timeout; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + if (list_empty(&cam->outqueue)) { + if (cam->stream == STREAM_OFF) + return -EINVAL; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) + return timeout; + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + if (!timeout || (cam->state & DEV_MISCONFIGURED)) + return -EIO; + } + + spin_lock_irqsave(&cam->queue_lock, lock_flags); + f = list_entry(cam->outqueue.next, struct sn9c102_frame_t, frame); + list_del(cam->outqueue.next); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + f->state = F_UNUSED; + + memcpy(&b, &f->buf, sizeof(b)); + if (f->vma_use_count) + b.flags |= V4L2_BUF_FLAG_MAPPED; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index); + + return 0; +} + + +static int +sn9c102_vidioc_streamon(struct sn9c102_device* cam, void __user * arg) +{ + int type; + + if (copy_from_user(&type, arg, sizeof(type))) + return -EFAULT; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + if (list_empty(&cam->inqueue)) + return -EINVAL; + + cam->stream = STREAM_ON; + + DBG(3, "Stream on"); + + return 0; +} + + +static int +sn9c102_vidioc_streamoff(struct sn9c102_device* cam, void __user * arg) +{ + int type, err; + + if (copy_from_user(&type, arg, sizeof(type))) + return -EFAULT; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + if (cam->stream == STREAM_ON) + if ((err = sn9c102_stream_interrupt(cam))) + return err; + + sn9c102_empty_framequeues(cam); + + DBG(3, "Stream off"); + + return 0; +} + + +static int +sn9c102_vidioc_g_parm(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_streamparm sp; + + if (copy_from_user(&sp, arg, sizeof(sp))) + return -EFAULT; + + if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + sp.parm.capture.extendedmode = 0; + sp.parm.capture.readbuffers = cam->nreadbuffers; + + if (copy_to_user(arg, &sp, sizeof(sp))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_parm(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_streamparm sp; + + if (copy_from_user(&sp, arg, sizeof(sp))) + return -EFAULT; + + if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + sp.parm.capture.extendedmode = 0; + + if (sp.parm.capture.readbuffers == 0) + sp.parm.capture.readbuffers = cam->nreadbuffers; + + if (sp.parm.capture.readbuffers > SN9C102_MAX_FRAMES) + sp.parm.capture.readbuffers = SN9C102_MAX_FRAMES; + + if (copy_to_user(arg, &sp, sizeof(sp))) + return -EFAULT; + + cam->nreadbuffers = sp.parm.capture.readbuffers; + + return 0; +} + + +static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, + unsigned int cmd, void __user * arg) +{ + struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + + switch (cmd) { + + case VIDIOC_QUERYCAP: + return sn9c102_vidioc_querycap(cam, arg); + + case VIDIOC_ENUMINPUT: + return sn9c102_vidioc_enuminput(cam, arg); + + case VIDIOC_G_INPUT: + return sn9c102_vidioc_g_input(cam, arg); + + case VIDIOC_S_INPUT: + return sn9c102_vidioc_s_input(cam, arg); + + case VIDIOC_QUERYCTRL: + return sn9c102_vidioc_query_ctrl(cam, arg); + + case VIDIOC_G_CTRL: + return sn9c102_vidioc_g_ctrl(cam, arg); + + case VIDIOC_S_CTRL_OLD: + case VIDIOC_S_CTRL: + return sn9c102_vidioc_s_ctrl(cam, arg); + + case VIDIOC_CROPCAP_OLD: + case VIDIOC_CROPCAP: + return sn9c102_vidioc_cropcap(cam, arg); + + case VIDIOC_G_CROP: + return sn9c102_vidioc_g_crop(cam, arg); + + case VIDIOC_S_CROP: + return sn9c102_vidioc_s_crop(cam, arg); + + case VIDIOC_ENUM_FMT: + return sn9c102_vidioc_enum_fmt(cam, arg); + + case VIDIOC_G_FMT: + return sn9c102_vidioc_g_fmt(cam, arg); + + case VIDIOC_TRY_FMT: + case VIDIOC_S_FMT: + return sn9c102_vidioc_try_s_fmt(cam, cmd, arg); + + case VIDIOC_G_JPEGCOMP: + return sn9c102_vidioc_g_jpegcomp(cam, arg); + + case VIDIOC_S_JPEGCOMP: + return sn9c102_vidioc_s_jpegcomp(cam, arg); + + case VIDIOC_REQBUFS: + return sn9c102_vidioc_reqbufs(cam, arg); + + case VIDIOC_QUERYBUF: + return sn9c102_vidioc_querybuf(cam, arg); + + case VIDIOC_QBUF: + return sn9c102_vidioc_qbuf(cam, arg); + + case VIDIOC_DQBUF: + return sn9c102_vidioc_dqbuf(cam, filp, arg); + + case VIDIOC_STREAMON: + return sn9c102_vidioc_streamon(cam, arg); + + case VIDIOC_STREAMOFF: + return sn9c102_vidioc_streamoff(cam, arg); + + case VIDIOC_G_PARM: + return sn9c102_vidioc_g_parm(cam, arg); + + case VIDIOC_S_PARM_OLD: + case VIDIOC_S_PARM: + return sn9c102_vidioc_s_parm(cam, arg); + + case VIDIOC_G_STD: + case VIDIOC_S_STD: + case VIDIOC_QUERYSTD: + case VIDIOC_ENUMSTD: + case VIDIOC_QUERYMENU: + return -EINVAL; + + default: + return -EINVAL; + + } +} + + +static int sn9c102_ioctl(struct inode* inode, struct file* filp, + unsigned int cmd, unsigned long arg) +{ + struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + int err = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + V4LDBG(3, "sn9c102", cmd); + + err = sn9c102_ioctl_v4l2(inode, filp, cmd, (void __user *)arg); + + mutex_unlock(&cam->fileop_mutex); + + return err; +} + +/*****************************************************************************/ + +static struct file_operations sn9c102_fops = { + .owner = THIS_MODULE, + .open = sn9c102_open, + .release = sn9c102_release, + .ioctl = sn9c102_ioctl, + .read = sn9c102_read, + .poll = sn9c102_poll, + .mmap = sn9c102_mmap, + .llseek = no_llseek, +}; + +/*****************************************************************************/ + +/* It exists a single interface only. We do not need to validate anything. */ +static int +sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct sn9c102_device* cam; + static unsigned int dev_nr = 0; + unsigned int i; + int err = 0, r; + + if (!(cam = kzalloc(sizeof(struct sn9c102_device), GFP_KERNEL))) + return -ENOMEM; + + cam->usbdev = udev; + + if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) { + DBG(1, "kmalloc() failed"); + err = -ENOMEM; + goto fail; + } + + if (!(cam->v4ldev = video_device_alloc())) { + DBG(1, "video_device_alloc() failed"); + err = -ENOMEM; + goto fail; + } + + mutex_init(&cam->dev_mutex); + + r = sn9c102_read_reg(cam, 0x00); + if (r < 0 || r != 0x10) { + DBG(1, "Sorry, this is not a SN9C10x based camera " + "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct); + err = -ENODEV; + goto fail; + } + + cam->bridge = (id->idProduct & 0xffc0) == 0x6080 ? + BRIDGE_SN9C103 : BRIDGE_SN9C102; + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + DBG(2, "SN9C10[12] PC Camera Controller detected " + "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct); + break; + case BRIDGE_SN9C103: + DBG(2, "SN9C103 PC Camera Controller detected " + "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct); + break; + } + + for (i = 0; sn9c102_sensor_table[i]; i++) { + err = sn9c102_sensor_table[i](cam); + if (!err) + break; + } + + if (!err) { + DBG(2, "%s image sensor detected", cam->sensor.name); + DBG(3, "Support for %s maintained by %s", + cam->sensor.name, cam->sensor.maintainer); + } else { + DBG(1, "No supported image sensor detected"); + err = -ENODEV; + goto fail; + } + + if (sn9c102_init(cam)) { + DBG(1, "Initialization failed. I will retry on open()."); + cam->state |= DEV_MISCONFIGURED; + } + + strcpy(cam->v4ldev->name, "SN9C10x PC Camera"); + cam->v4ldev->owner = THIS_MODULE; + cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; + cam->v4ldev->hardware = 0; + cam->v4ldev->fops = &sn9c102_fops; + cam->v4ldev->minor = video_nr[dev_nr]; + cam->v4ldev->release = video_device_release; + video_set_drvdata(cam->v4ldev, cam); + + mutex_lock(&cam->dev_mutex); + + err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, + video_nr[dev_nr]); + if (err) { + DBG(1, "V4L2 device registration failed"); + if (err == -ENFILE && video_nr[dev_nr] == -1) + DBG(1, "Free /dev/videoX node not found"); + video_nr[dev_nr] = -1; + dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; + mutex_unlock(&cam->dev_mutex); + goto fail; + } + + DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor); + + cam->module_param.force_munmap = force_munmap[dev_nr]; + cam->module_param.frame_timeout = frame_timeout[dev_nr]; + + dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + sn9c102_create_sysfs(cam); + DBG(2, "Optional device control through 'sysfs' interface ready"); +#endif + + usb_set_intfdata(intf, cam); + + mutex_unlock(&cam->dev_mutex); + + return 0; + +fail: + if (cam) { + kfree(cam->control_buffer); + if (cam->v4ldev) + video_device_release(cam->v4ldev); + kfree(cam); + } + return err; +} + + +static void sn9c102_usb_disconnect(struct usb_interface* intf) +{ + struct sn9c102_device* cam = usb_get_intfdata(intf); + + if (!cam) + return; + + down_write(&sn9c102_disconnect); + + mutex_lock(&cam->dev_mutex); + + DBG(2, "Disconnecting %s...", cam->v4ldev->name); + + wake_up_interruptible_all(&cam->open); + + if (cam->users) { + DBG(2, "Device /dev/video%d is open! Deregistration and " + "memory deallocation are deferred on close.", + cam->v4ldev->minor); + cam->state |= DEV_MISCONFIGURED; + sn9c102_stop_transfer(cam); + cam->state |= DEV_DISCONNECTED; + wake_up_interruptible(&cam->wait_frame); + wake_up(&cam->wait_stream); + usb_get_dev(cam->usbdev); + } else { + cam->state |= DEV_DISCONNECTED; + sn9c102_release_resources(cam); + } + + mutex_unlock(&cam->dev_mutex); + + if (!cam->users) + kfree(cam); + + up_write(&sn9c102_disconnect); +} + + +static struct usb_driver sn9c102_usb_driver = { + .name = "sn9c102", + .id_table = sn9c102_id_table, + .probe = sn9c102_usb_probe, + .disconnect = sn9c102_usb_disconnect, +}; + +/*****************************************************************************/ + +static int __init sn9c102_module_init(void) +{ + int err = 0; + + KDBG(2, SN9C102_MODULE_NAME " v" SN9C102_MODULE_VERSION); + KDBG(3, SN9C102_MODULE_AUTHOR); + + if ((err = usb_register(&sn9c102_usb_driver))) + KDBG(1, "usb_register() failed"); + + return err; +} + + +static void __exit sn9c102_module_exit(void) +{ + usb_deregister(&sn9c102_usb_driver); +} + + +module_init(sn9c102_module_init); +module_exit(sn9c102_module_exit); diff --git a/linux/drivers/media/video/sn9c102/sn9c102_hv7131d.c b/linux/drivers/media/video/sn9c102/sn9c102_hv7131d.c new file mode 100644 index 000000000..c4117bf64 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_hv7131d.c @@ -0,0 +1,271 @@ +/*************************************************************************** + * Plug-in for HV7131D image sensor connected to the SN9C10x PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor hv7131d; + + +static int hv7131d_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x60, 0x17); + err += sn9c102_write_reg(cam, 0x0e, 0x18); + err += sn9c102_write_reg(cam, 0xf2, 0x19); + + err += sn9c102_i2c_write(cam, 0x01, 0x04); + err += sn9c102_i2c_write(cam, 0x02, 0x00); + err += sn9c102_i2c_write(cam, 0x28, 0x00); + + return err; +} + + +static int hv7131d_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + { + int r1 = sn9c102_i2c_read(cam, 0x26), + r2 = sn9c102_i2c_read(cam, 0x27); + if (r1 < 0 || r2 < 0) + return -EIO; + ctrl->value = (r1 << 8) | (r2 & 0xff); + } + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0) + return -EIO; + ctrl->value = 0x3f - (ctrl->value & 0x3f); + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0) + return -EIO; + ctrl->value = 0x3f - (ctrl->value & 0x3f); + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0) + return -EIO; + ctrl->value = 0x3f - (ctrl->value & 0x3f); + return 0; + case SN9C102_V4L2_CID_RESET_LEVEL: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0) + return -EIO; + ctrl->value &= 0x3f; + return 0; + case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x34)) < 0) + return -EIO; + ctrl->value &= 0x07; + return 0; + default: + return -EINVAL; + } +} + + +static int hv7131d_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x26, ctrl->value >> 8); + err += sn9c102_i2c_write(cam, 0x27, ctrl->value & 0xff); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x31, 0x3f - ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x33, 0x3f - ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x32, 0x3f - ctrl->value); + break; + case SN9C102_V4L2_CID_RESET_LEVEL: + err += sn9c102_i2c_write(cam, 0x30, ctrl->value); + break; + case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE: + err += sn9c102_i2c_write(cam, 0x34, ctrl->value); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int hv7131d_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &hv7131d; + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 2, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int hv7131d_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x42, 0x19); + else + err += sn9c102_write_reg(cam, 0xf2, 0x19); + + return err; +} + + +static struct sn9c102_sensor hv7131d = { + .name = "HV7131D", + .maintainer = "Luca Risolia ", + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x11, + .init = &hv7131d_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x0250, + .maximum = 0xffff, + .step = 0x0001, + .default_value = 0x0250, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x1e, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_RESET_LEVEL, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "reset level", + .minimum = 0x19, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x30, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "pixel bias voltage", + .minimum = 0x00, + .maximum = 0x07, + .step = 0x01, + .default_value = 0x02, + .flags = 0, + }, + }, + .get_ctrl = &hv7131d_get_ctrl, + .set_ctrl = &hv7131d_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &hv7131d_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &hv7131d_set_pix_format +}; + + +int sn9c102_probe_hv7131d(struct sn9c102_device* cam) +{ + int r0 = 0, r1 = 0, err = 0; + + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x00, 0x01); + err += sn9c102_write_reg(cam, 0x28, 0x17); + if (err) + return -EIO; + + r0 = sn9c102_i2c_try_read(cam, &hv7131d, 0x00); + r1 = sn9c102_i2c_try_read(cam, &hv7131d, 0x01); + if (r0 < 0 || r1 < 0) + return -EIO; + + if (r0 != 0x00 && r1 != 0x04) + return -ENODEV; + + sn9c102_attach_sensor(cam, &hv7131d); + + return 0; +} diff --git a/linux/drivers/media/video/sn9c102/sn9c102_mi0343.c b/linux/drivers/media/video/sn9c102/sn9c102_mi0343.c new file mode 100644 index 000000000..4169ea4a2 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_mi0343.c @@ -0,0 +1,363 @@ +/*************************************************************************** + * Plug-in for MI-0343 image sensor connected to the SN9C10x PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor mi0343; +static u8 mi0343_i2c_data[5+1]; + + +static int mi0343_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x0a, 0x14); + err += sn9c102_write_reg(cam, 0x40, 0x01); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x07, 0x18); + err += sn9c102_write_reg(cam, 0xa0, 0x19); + + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, + 0x0d, 0x00, 0x01, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, + 0x0d, 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, + 0x03, 0x01, 0xe1, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, + 0x04, 0x02, 0x81, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, + 0x05, 0x00, 0x17, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, + 0x06, 0x00, 0x11, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, + 0x62, 0x04, 0x9a, 0, 0); + + return err; +} + + +static int mi0343_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, + 0x09, 2+1, mi0343_i2c_data) < 0) + return -EIO; + ctrl->value = mi0343_i2c_data[2]; + return 0; + case V4L2_CID_GAIN: + if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, + 0x35, 2+1, mi0343_i2c_data) < 0) + return -EIO; + break; + case V4L2_CID_HFLIP: + if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, + 0x20, 2+1, mi0343_i2c_data) < 0) + return -EIO; + ctrl->value = mi0343_i2c_data[3] & 0x20 ? 1 : 0; + return 0; + case V4L2_CID_VFLIP: + if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, + 0x20, 2+1, mi0343_i2c_data) < 0) + return -EIO; + ctrl->value = mi0343_i2c_data[3] & 0x80 ? 1 : 0; + return 0; + case V4L2_CID_RED_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, + 0x2d, 2+1, mi0343_i2c_data) < 0) + return -EIO; + break; + case V4L2_CID_BLUE_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, + 0x2c, 2+1, mi0343_i2c_data) < 0) + return -EIO; + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, + 0x2e, 2+1, mi0343_i2c_data) < 0) + return -EIO; + break; + default: + return -EINVAL; + } + + switch (ctrl->id) { + case V4L2_CID_GAIN: + case V4L2_CID_RED_BALANCE: + case V4L2_CID_BLUE_BALANCE: + case SN9C102_V4L2_CID_GREEN_BALANCE: + ctrl->value = mi0343_i2c_data[3] | (mi0343_i2c_data[2] << 8); + if (ctrl->value >= 0x10 && ctrl->value <= 0x3f) + ctrl->value -= 0x10; + else if (ctrl->value >= 0x60 && ctrl->value <= 0x7f) + ctrl->value -= 0x60; + else if (ctrl->value >= 0xe0 && ctrl->value <= 0xff) + ctrl->value -= 0xe0; + } + + return 0; +} + + +static int mi0343_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + u16 reg = 0; + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + case V4L2_CID_RED_BALANCE: + case V4L2_CID_BLUE_BALANCE: + case SN9C102_V4L2_CID_GREEN_BALANCE: + if (ctrl->value <= (0x3f-0x10)) + reg = 0x10 + ctrl->value; + else if (ctrl->value <= ((0x3f-0x10) + (0x7f-0x60))) + reg = 0x60 + (ctrl->value - (0x3f-0x10)); + else + reg = 0xe0 + (ctrl->value - (0x3f-0x10) - (0x7f-0x60)); + break; + } + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x09, ctrl->value, 0x00, + 0, 0); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x35, reg >> 8, reg & 0xff, + 0, 0); + break; + case V4L2_CID_HFLIP: + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x20, ctrl->value ? 0x40:0x00, + ctrl->value ? 0x20:0x00, + 0, 0); + break; + case V4L2_CID_VFLIP: + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x20, ctrl->value ? 0x80:0x00, + ctrl->value ? 0x80:0x00, + 0, 0); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x2d, reg >> 8, reg & 0xff, + 0, 0); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x2c, reg >> 8, reg & 0xff, + 0, 0); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x2b, reg >> 8, reg & 0xff, + 0, 0); + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x2e, reg >> 8, reg & 0xff, + 0, 0); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int mi0343_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &mi0343; + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int mi0343_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) { + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x0a, 0x00, 0x03, 0, 0); + err += sn9c102_write_reg(cam, 0x20, 0x19); + } else { + err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, + mi0343.i2c_slave_id, + 0x0a, 0x00, 0x05, 0, 0); + err += sn9c102_write_reg(cam, 0xa0, 0x19); + } + + return err; +} + + +static struct sn9c102_sensor mi0343 = { + .name = "MI-0343", + .maintainer = "Luca Risolia ", + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x5d, + .init = &mi0343_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x06, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),/*0x6d*/ + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0), + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0), + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = ((0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0)), + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .get_ctrl = &mi0343_get_ctrl, + .set_ctrl = &mi0343_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &mi0343_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &mi0343_set_pix_format +}; + + +int sn9c102_probe_mi0343(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x00, 0x01); + err += sn9c102_write_reg(cam, 0x28, 0x17); + if (err) + return -EIO; + + if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, 0x00, + 2, mi0343_i2c_data) < 0) + return -EIO; + + if (mi0343_i2c_data[4] != 0x32 && mi0343_i2c_data[3] != 0xe3) + return -ENODEV; + + sn9c102_attach_sensor(cam, &mi0343); + + return 0; +} diff --git a/linux/drivers/media/video/sn9c102/sn9c102_ov7630.c b/linux/drivers/media/video/sn9c102/sn9c102_ov7630.c new file mode 100644 index 000000000..3da042021 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_ov7630.c @@ -0,0 +1,401 @@ +/*************************************************************************** + * Plug-in for OV7630 image sensor connected to the SN9C10x PC Camera * + * Controllers * + * * + * Copyright (C) 2005-2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor ov7630; + + +static int ov7630_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x60, 0x17); + err += sn9c102_write_reg(cam, 0x0f, 0x18); + err += sn9c102_write_reg(cam, 0x50, 0x19); + + err += sn9c102_i2c_write(cam, 0x12, 0x80); + err += sn9c102_i2c_write(cam, 0x11, 0x01); + err += sn9c102_i2c_write(cam, 0x15, 0x34); + err += sn9c102_i2c_write(cam, 0x16, 0x03); + err += sn9c102_i2c_write(cam, 0x17, 0x1c); + err += sn9c102_i2c_write(cam, 0x18, 0xbd); + err += sn9c102_i2c_write(cam, 0x19, 0x06); + err += sn9c102_i2c_write(cam, 0x1a, 0xf6); + err += sn9c102_i2c_write(cam, 0x1b, 0x04); + err += sn9c102_i2c_write(cam, 0x20, 0xf6); + err += sn9c102_i2c_write(cam, 0x23, 0xee); + err += sn9c102_i2c_write(cam, 0x26, 0xa0); + err += sn9c102_i2c_write(cam, 0x27, 0x9a); + err += sn9c102_i2c_write(cam, 0x28, 0xa0); + err += sn9c102_i2c_write(cam, 0x29, 0x30); + err += sn9c102_i2c_write(cam, 0x2a, 0xa0); + err += sn9c102_i2c_write(cam, 0x2b, 0x1f); + err += sn9c102_i2c_write(cam, 0x2f, 0x3d); + err += sn9c102_i2c_write(cam, 0x30, 0x24); + err += sn9c102_i2c_write(cam, 0x32, 0x86); + err += sn9c102_i2c_write(cam, 0x60, 0xa9); + err += sn9c102_i2c_write(cam, 0x61, 0x42); + err += sn9c102_i2c_write(cam, 0x65, 0x00); + err += sn9c102_i2c_write(cam, 0x69, 0x38); + err += sn9c102_i2c_write(cam, 0x6f, 0x88); + err += sn9c102_i2c_write(cam, 0x70, 0x0b); + err += sn9c102_i2c_write(cam, 0x71, 0x00); + err += sn9c102_i2c_write(cam, 0x74, 0x21); + err += sn9c102_i2c_write(cam, 0x7d, 0xf7); + + return err; +} + + +static int ov7630_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x10, ctrl->value >> 2); + err += sn9c102_i2c_write(cam, 0x76, ctrl->value & 0x03); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x02, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x01, ctrl->value); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x00, ctrl->value); + break; + case V4L2_CID_CONTRAST: + err += ctrl->value ? sn9c102_i2c_write(cam, 0x05, + (ctrl->value-1) | 0x20) + : sn9c102_i2c_write(cam, 0x05, 0x00); + break; + case V4L2_CID_BRIGHTNESS: + err += sn9c102_i2c_write(cam, 0x06, ctrl->value); + break; + case V4L2_CID_SATURATION: + err += sn9c102_i2c_write(cam, 0x03, ctrl->value << 4); + break; + case V4L2_CID_HUE: + err += ctrl->value ? sn9c102_i2c_write(cam, 0x04, + (ctrl->value-1) | 0x20) + : sn9c102_i2c_write(cam, 0x04, 0x00); + break; + case V4L2_CID_DO_WHITE_BALANCE: + err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); + break; + case V4L2_CID_WHITENESS: + err += sn9c102_i2c_write(cam, 0x0d, ctrl->value); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + err += sn9c102_i2c_write(cam, 0x12, (ctrl->value << 2) | 0x78); + break; + case V4L2_CID_AUTOGAIN: + err += sn9c102_i2c_write(cam, 0x13, ctrl->value); + break; + case V4L2_CID_VFLIP: + err += sn9c102_i2c_write(cam, 0x75, 0x0e | (ctrl->value << 7)); + break; + case V4L2_CID_BLACK_LEVEL: + err += sn9c102_i2c_write(cam, 0x25, ctrl->value); + break; + case SN9C102_V4L2_CID_BRIGHT_LEVEL: + err += sn9c102_i2c_write(cam, 0x24, ctrl->value); + break; + case SN9C102_V4L2_CID_GAMMA: + err += sn9c102_i2c_write(cam, 0x14, (ctrl->value << 2) | 0x80); + break; + case SN9C102_V4L2_CID_BAND_FILTER: + err += sn9c102_i2c_write(cam, 0x2d, ctrl->value << 2); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int ov7630_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &ov7630; + int err = 0; + u8 v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int ov7630_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x20, 0x19); + else + err += sn9c102_write_reg(cam, 0x50, 0x19); + + return err; +} + + +static struct sn9c102_sensor ov7630 = { + .name = "OV7630", + .maintainer = "Luca Risolia ", + .sysfs_ops = SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x21, + .init = &ov7630_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x14, + .flags = 0, + }, + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "hue", + .minimum = 0x00, + .maximum = 0x1f+1, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "saturation", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x08, + .flags = 0, + }, + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "contrast", + .minimum = 0x00, + .maximum = 0x1f+1, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x000, + .maximum = 0x3ff, + .step = 0x001, + .default_value = 0x83<<2, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x3a, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x77, + .flags = 0, + }, + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "brightness", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0xa0, + .flags = 0, + }, + { + .id = V4L2_CID_DO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "white balance background: blue", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + { + .id = V4L2_CID_WHITENESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "white balance background: red", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto white balance", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain & exposure mode", + .minimum = 0x00, + .maximum = 0x03, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + { + .id = V4L2_CID_BLACK_LEVEL, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "black pixel ratio", + .minimum = 0x01, + .maximum = 0x9a, + .step = 0x01, + .default_value = 0x8a, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_BRIGHT_LEVEL, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "bright pixel ratio", + .minimum = 0x01, + .maximum = 0x9a, + .step = 0x01, + .default_value = 0x10, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_BAND_FILTER, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "band filter", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "rgb gamma", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .set_ctrl = &ov7630_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &ov7630_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &ov7630_set_pix_format +}; + + +int sn9c102_probe_ov7630(struct sn9c102_device* cam) +{ + const struct usb_device_id ov7630_id_table[] = { + { USB_DEVICE(0x0c45, 0x602c), }, + { USB_DEVICE(0x0c45, 0x602d), }, + { USB_DEVICE(0x0c45, 0x608f), }, + { USB_DEVICE(0x0c45, 0x60b0), }, + { } + }; + int err = 0; + + if (!sn9c102_match_id(cam, ov7630_id_table)) + return -ENODEV; + + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x00, 0x01); + err += sn9c102_write_reg(cam, 0x28, 0x17); + if (err) + return -EIO; + + err += sn9c102_i2c_try_write(cam, &ov7630, 0x0b, 0); + if (err) + return -ENODEV; + + sn9c102_attach_sensor(cam, &ov7630); + + return 0; +} diff --git a/linux/drivers/media/video/sn9c102/sn9c102_pas106b.c b/linux/drivers/media/video/sn9c102/sn9c102_pas106b.c new file mode 100644 index 000000000..991594423 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_pas106b.c @@ -0,0 +1,307 @@ +/*************************************************************************** + * Plug-in for PAS106B image sensor connected to the SN9C10x PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor pas106b; + + +static int pas106b_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x20, 0x19); + err += sn9c102_write_reg(cam, 0x09, 0x18); + + err += sn9c102_i2c_write(cam, 0x02, 0x0c); + err += sn9c102_i2c_write(cam, 0x05, 0x5a); + err += sn9c102_i2c_write(cam, 0x06, 0x88); + err += sn9c102_i2c_write(cam, 0x07, 0x80); + err += sn9c102_i2c_write(cam, 0x10, 0x06); + err += sn9c102_i2c_write(cam, 0x11, 0x06); + err += sn9c102_i2c_write(cam, 0x12, 0x00); + err += sn9c102_i2c_write(cam, 0x14, 0x02); + err += sn9c102_i2c_write(cam, 0x13, 0x01); + + msleep(400); + + return err; +} + + +static int pas106b_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + { + int r1 = sn9c102_i2c_read(cam, 0x03), + r2 = sn9c102_i2c_read(cam, 0x04); + if (r1 < 0 || r2 < 0) + return -EIO; + ctrl->value = (r1 << 4) | (r2 & 0x0f); + } + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0e)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case V4L2_CID_CONTRAST: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0f)) < 0) + return -EIO; + ctrl->value &= 0x07; + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0a)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x1f) << 1; + return 0; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0) + return -EIO; + ctrl->value &= 0xf8; + return 0; + default: + return -EINVAL; + } +} + + +static int pas106b_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x03, ctrl->value >> 4); + err += sn9c102_i2c_write(cam, 0x04, ctrl->value & 0x0f); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x09, ctrl->value); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x0e, ctrl->value); + break; + case V4L2_CID_CONTRAST: + err += sn9c102_i2c_write(cam, 0x0f, ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x0a, ctrl->value >> 1); + err += sn9c102_i2c_write(cam, 0x0b, ctrl->value >> 1); + break; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + err += sn9c102_i2c_write(cam, 0x08, ctrl->value << 3); + break; + default: + return -EINVAL; + } + err += sn9c102_i2c_write(cam, 0x13, 0x01); + + return err ? -EIO : 0; +} + + +static int pas106b_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &pas106b; + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int pas106b_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x2c, 0x17); + else + err += sn9c102_write_reg(cam, 0x20, 0x17); + + return err; +} + + +static struct sn9c102_sensor pas106b = { + .name = "PAS106B", + .maintainer = "Luca Risolia ", + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x40, + .init = &pas106b_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x125, + .maximum = 0xfff, + .step = 0x001, + .default_value = 0x140, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x0d, + .flags = 0, + }, + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "contrast", + .minimum = 0x00, + .maximum = 0x07, + .step = 0x01, + .default_value = 0x00, /* 0x00~0x03 have same effect */ + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x04, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x06, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x3e, + .step = 0x02, + .default_value = 0x02, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "DAC magnitude", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + }, + .get_ctrl = &pas106b_get_ctrl, + .set_ctrl = &pas106b_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + }, + .set_crop = &pas106b_set_crop, + .pix_format = { + .width = 352, + .height = 288, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, /* we use this field as 'bits per pixel' */ + }, + .set_pix_format = &pas106b_set_pix_format +}; + + +int sn9c102_probe_pas106b(struct sn9c102_device* cam) +{ + int r0 = 0, r1 = 0, err = 0; + unsigned int pid = 0; + + /* + Minimal initialization to enable the I2C communication + NOTE: do NOT change the values! + */ + err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */ + err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */ + err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */ + if (err) + return -EIO; + + r0 = sn9c102_i2c_try_read(cam, &pas106b, 0x00); + r1 = sn9c102_i2c_try_read(cam, &pas106b, 0x01); + + if (r0 < 0 || r1 < 0) + return -EIO; + + pid = (r0 << 11) | ((r1 & 0xf0) >> 4); + if (pid != 0x007) + return -ENODEV; + + sn9c102_attach_sensor(cam, &pas106b); + + return 0; +} diff --git a/linux/drivers/media/video/sn9c102/sn9c102_pas202bca.c b/linux/drivers/media/video/sn9c102/sn9c102_pas202bca.c new file mode 100644 index 000000000..c8f1ae215 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_pas202bca.c @@ -0,0 +1,238 @@ +/*************************************************************************** + * Plug-in for PAS202BCA image sensor connected to the SN9C10x PC Camera * + * Controllers * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor pas202bca; + + +static int pas202bca_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x30, 0x19); + err += sn9c102_write_reg(cam, 0x09, 0x18); + + err += sn9c102_i2c_write(cam, 0x02, 0x14); + err += sn9c102_i2c_write(cam, 0x03, 0x40); + err += sn9c102_i2c_write(cam, 0x0d, 0x2c); + err += sn9c102_i2c_write(cam, 0x0e, 0x01); + err += sn9c102_i2c_write(cam, 0x0f, 0xa9); + err += sn9c102_i2c_write(cam, 0x10, 0x08); + err += sn9c102_i2c_write(cam, 0x13, 0x63); + err += sn9c102_i2c_write(cam, 0x15, 0x70); + err += sn9c102_i2c_write(cam, 0x11, 0x01); + + msleep(400); + + return err; +} + + +static int pas202bca_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x24, 0x17); + else + err += sn9c102_write_reg(cam, 0x20, 0x17); + + return err; +} + + +static int pas202bca_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6); + err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x09, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x07, ctrl->value); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x10, ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x08, ctrl->value); + break; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); + break; + default: + return -EINVAL; + } + err += sn9c102_i2c_write(cam, 0x11, 0x01); + + return err ? -EIO : 0; +} + + +static int pas202bca_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &pas202bca; + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 3, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static struct sn9c102_sensor pas202bca = { + .name = "PAS202BCA", + .maintainer = "Luca Risolia ", + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x40, + .init = &pas202bca_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x01e5, + .maximum = 0x3fff, + .step = 0x0001, + .default_value = 0x01e5, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x0c, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x05, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "DAC magnitude", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x04, + .flags = 0, + }, + }, + .set_ctrl = &pas202bca_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &pas202bca_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &pas202bca_set_pix_format +}; + + +int sn9c102_probe_pas202bca(struct sn9c102_device* cam) +{ + const struct usb_device_id pas202bca_id_table[] = { + { USB_DEVICE(0x0c45, 0x60af), }, + { } + }; + int err = 0; + + if (!sn9c102_match_id(cam,pas202bca_id_table)) + return -ENODEV; + + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x40, 0x01); + err += sn9c102_write_reg(cam, 0x28, 0x17); + if (err) + return -EIO; + + if (sn9c102_i2c_try_write(cam, &pas202bca, 0x10, 0)) /* try to write */ + return -ENODEV; + + sn9c102_attach_sensor(cam, &pas202bca); + + return 0; +} diff --git a/linux/drivers/media/video/sn9c102/sn9c102_pas202bcb.c b/linux/drivers/media/video/sn9c102/sn9c102_pas202bcb.c new file mode 100644 index 000000000..e3c1178e3 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_pas202bcb.c @@ -0,0 +1,293 @@ +/*************************************************************************** + * Plug-in for PAS202BCB image sensor connected to the SN9C10x PC Camera * + * Controllers * + * * + * Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio * + * * + * http://cadu.homelinux.com:8080/ * + * * + * DAC Magnitude, exposure and green gain controls added by * + * Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor pas202bcb; + + +static int pas202bcb_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x30, 0x19); + err += sn9c102_write_reg(cam, 0x09, 0x18); + + err += sn9c102_i2c_write(cam, 0x02, 0x14); + err += sn9c102_i2c_write(cam, 0x03, 0x40); + err += sn9c102_i2c_write(cam, 0x0d, 0x2c); + err += sn9c102_i2c_write(cam, 0x0e, 0x01); + err += sn9c102_i2c_write(cam, 0x0f, 0xa9); + err += sn9c102_i2c_write(cam, 0x10, 0x08); + err += sn9c102_i2c_write(cam, 0x13, 0x63); + err += sn9c102_i2c_write(cam, 0x15, 0x70); + err += sn9c102_i2c_write(cam, 0x11, 0x01); + + msleep(400); + + return err; +} + + +static int pas202bcb_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + { + int r1 = sn9c102_i2c_read(cam, 0x04), + r2 = sn9c102_i2c_read(cam, 0x05); + if (r1 < 0 || r2 < 0) + return -EIO; + ctrl->value = (r1 << 6) | (r2 & 0x3f); + } + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x07)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x08)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) + return -EIO; + return 0; + default: + return -EINVAL; + } +} + + +static int pas202bcb_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x24, 0x17); + else + err += sn9c102_write_reg(cam, 0x20, 0x17); + + return err; +} + + +static int pas202bcb_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6); + err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x09, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x07, ctrl->value); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x10, ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x08, ctrl->value); + break; + case SN9C102_V4L2_CID_DAC_MAGNITUDE: + err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); + break; + default: + return -EINVAL; + } + err += sn9c102_i2c_write(cam, 0x11, 0x01); + + return err ? -EIO : 0; +} + + +static int pas202bcb_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &pas202bcb; + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static struct sn9c102_sensor pas202bcb = { + .name = "PAS202BCB", + .maintainer = "Carlos Eduardo Medaglia Dyonisio " + "", + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x40, + .init = &pas202bcb_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x01e5, + .maximum = 0x3fff, + .step = 0x0001, + .default_value = 0x01e5, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x0c, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x05, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "DAC magnitude", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x04, + .flags = 0, + }, + }, + .get_ctrl = &pas202bcb_get_ctrl, + .set_ctrl = &pas202bcb_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &pas202bcb_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &pas202bcb_set_pix_format +}; + + +int sn9c102_probe_pas202bcb(struct sn9c102_device* cam) +{ + int r0 = 0, r1 = 0, err = 0; + unsigned int pid = 0; + + /* + * Minimal initialization to enable the I2C communication + * NOTE: do NOT change the values! + */ + err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */ + err += sn9c102_write_reg(cam, 0x40, 0x01); /* sensor power on */ + err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */ + if (err) + return -EIO; + + r0 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x00); + r1 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x01); + + if (r0 < 0 || r1 < 0) + return -EIO; + + pid = (r0 << 4) | ((r1 & 0xf0) >> 4); + if (pid != 0x017) + return -ENODEV; + + sn9c102_attach_sensor(cam, &pas202bcb); + + return 0; +} diff --git a/linux/drivers/media/video/sn9c102/sn9c102_sensor.h b/linux/drivers/media/video/sn9c102/sn9c102_sensor.h new file mode 100644 index 000000000..88cd05d06 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_sensor.h @@ -0,0 +1,390 @@ +/*************************************************************************** + * API for image sensors connected to the SN9C10x PC Camera Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _SN9C102_SENSOR_H_ +#define _SN9C102_SENSOR_H_ + +#include +#include "compat.h" +#include +#include +#include +#include +#include + +struct sn9c102_device; +struct sn9c102_sensor; + +/*****************************************************************************/ + +/* + OVERVIEW. + This is a small interface that allows you to add support for any CCD/CMOS + image sensors connected to the SN9C10X bridges. The entire API is documented + below. In the most general case, to support a sensor there are three steps + you have to follow: + 1) define the main "sn9c102_sensor" structure by setting the basic fields; + 2) write a probing function to be called by the core module when the USB + camera is recognized, then add both the USB ids and the name of that + function to the two corresponding tables SENSOR_TABLE and ID_TABLE (see + below); + 3) implement the methods that you want/need (and fill the rest of the main + structure accordingly). + "sn9c102_pas106b.c" is an example of all this stuff. Remember that you do + NOT need to touch the source code of the core module for the things to work + properly, unless you find bugs or flaws in it. Finally, do not forget to + read the V4L2 API for completeness. +*/ + +/*****************************************************************************/ + +/* + Probing functions: on success, you must attach the sensor to the camera + by calling sn9c102_attach_sensor() provided below. + To enable the I2C communication, you might need to perform a really basic + initialization of the SN9C10X chip by using the write function declared + ahead. + Functions must return 0 on success, the appropriate error otherwise. +*/ +extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam); +extern int sn9c102_probe_mi0343(struct sn9c102_device* cam); +extern int sn9c102_probe_ov7630(struct sn9c102_device* cam); +extern int sn9c102_probe_pas106b(struct sn9c102_device* cam); +extern int sn9c102_probe_pas202bca(struct sn9c102_device* cam); +extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); + +/* + Add the above entries to this table. Be sure to add the entry in the right + place, since, on failure, the next probing routine is called according to + the order of the list below, from top to bottom. +*/ +#define SN9C102_SENSOR_TABLE \ +static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = { \ + &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ \ + &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ \ + &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ \ + &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ \ + &sn9c102_probe_pas202bca, /* detection mostly based on USB pid/vid */ \ + &sn9c102_probe_ov7630, /* detection mostly based on USB pid/vid */ \ + &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ \ + &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ \ + NULL, \ +}; + +/* Device identification */ +extern struct sn9c102_device* +sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id); + +/* Attach a probed sensor to the camera. */ +extern void +sn9c102_attach_sensor(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor); + +/* + Each SN9C10x camera has proper PID/VID identifiers. + SN9C103 supports multiple interfaces, but we only handle the video class + interface. +*/ +#define SN9C102_USB_DEVICE(vend, prod, intclass) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_CLASS, \ + .idVendor = (vend), \ + .idProduct = (prod), \ + .bInterfaceClass = (intclass) + +#define SN9C102_ID_TABLE \ +static const struct usb_device_id sn9c102_id_table[] = { \ + { USB_DEVICE(0x0c45, 0x6001), }, /* TAS5110C1B */ \ + { USB_DEVICE(0x0c45, 0x6005), }, /* TAS5110C1B */ \ + { USB_DEVICE(0x0c45, 0x6007), }, \ + { USB_DEVICE(0x0c45, 0x6009), }, /* PAS106B */ \ + { USB_DEVICE(0x0c45, 0x600d), }, /* PAS106B */ \ + { USB_DEVICE(0x0c45, 0x6024), }, \ + { USB_DEVICE(0x0c45, 0x6025), }, /* TAS5130D1B and TAS5110C1B */ \ + { USB_DEVICE(0x0c45, 0x6028), }, /* PAS202BCB */ \ + { USB_DEVICE(0x0c45, 0x6029), }, /* PAS106B */ \ + { USB_DEVICE(0x0c45, 0x602a), }, /* HV7131D */ \ + { USB_DEVICE(0x0c45, 0x602b), }, /* MI-0343 */ \ + { USB_DEVICE(0x0c45, 0x602c), }, /* OV7630 */ \ + { USB_DEVICE(0x0c45, 0x602d), }, \ + { USB_DEVICE(0x0c45, 0x602e), }, /* OV7630 */ \ + { USB_DEVICE(0x0c45, 0x6030), }, /* MI03x */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x6080, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x6082, 0xff), }, /* MI0343 & MI0360 */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x6083, 0xff), }, /* HV7131[D|E1] */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x6088, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x608a, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x608b, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x608c, 0xff), }, /* HV7131/R */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x608e, 0xff), }, /* CIS-VF10 */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x608f, 0xff), }, /* OV7630 */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x60a0, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60a2, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60a3, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60a8, 0xff), }, /* PAS106B */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x60aa, 0xff), }, /* TAS5130D1B */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x60ab, 0xff), }, /* TAS5110C1B */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x60ac, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60ae, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60af, 0xff), }, /* PAS202BCB */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x60b0, 0xff), }, /* OV7630 (?) */ \ + { SN9C102_USB_DEVICE(0x0c45, 0x60b2, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60b3, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60b8, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60ba, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60bb, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60bc, 0xff), }, \ + { SN9C102_USB_DEVICE(0x0c45, 0x60be, 0xff), }, \ + { } \ +}; + +/*****************************************************************************/ + +/* + Read/write routines: they always return -1 on error, 0 or the read value + otherwise. NOTE that a real read operation is not supported by the SN9C10X + chip for some of its registers. To work around this problem, a pseudo-read + call is provided instead: it returns the last successfully written value + on the register (0 if it has never been written), the usual -1 on error. +*/ + +/* The "try" I2C I/O versions are used when probing the sensor */ +extern int sn9c102_i2c_try_write(struct sn9c102_device*,struct sn9c102_sensor*, + u8 address, u8 value); +extern int sn9c102_i2c_try_read(struct sn9c102_device*,struct sn9c102_sensor*, + u8 address); + +/* + These must be used if and only if the sensor doesn't implement the standard + I2C protocol. There are a number of good reasons why you must use the + single-byte versions of these functions: do not abuse. The first function + writes n bytes, from data0 to datan, to registers 0x09 - 0x09+n of SN9C10X + chip. The second one programs the registers 0x09 and 0x10 with data0 and + data1, and places the n bytes read from the sensor register table in the + buffer pointed by 'buffer'. Both the functions return -1 on error; the write + version returns 0 on success, while the read version returns the first read + byte. +*/ +extern int sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor, u8 n, + u8 data0, u8 data1, u8 data2, u8 data3, + u8 data4, u8 data5); +extern int sn9c102_i2c_try_raw_read(struct sn9c102_device* cam, + struct sn9c102_sensor* sensor, u8 data0, + u8 data1, u8 n, u8 buffer[]); + +/* To be used after the sensor struct has been attached to the camera struct */ +extern int sn9c102_i2c_write(struct sn9c102_device*, u8 address, u8 value); +extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address); + +/* I/O on registers in the bridge. Could be used by the sensor methods too */ +extern int sn9c102_write_regs(struct sn9c102_device*, u8* buff, u16 index); +extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index); +extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index); + +/* + NOTE: there are no exported debugging functions. To uniform the output you + must use the dev_info()/dev_warn()/dev_err() macros defined in device.h, + already included here, the argument being the struct device '&usbdev->dev' + of the sensor structure. Do NOT use these macros before the sensor is + attached or the kernel will crash! However, you should not need to notify + the user about common errors or other messages, since this is done by the + master module. +*/ + +/*****************************************************************************/ + +enum sn9c102_i2c_sysfs_ops { + SN9C102_I2C_READ = 0x01, + SN9C102_I2C_WRITE = 0x02, +}; + +enum sn9c102_i2c_frequency { /* sensors may support both the frequencies */ + SN9C102_I2C_100KHZ = 0x01, + SN9C102_I2C_400KHZ = 0x02, +}; + +enum sn9c102_i2c_interface { + SN9C102_I2C_2WIRES, + SN9C102_I2C_3WIRES, +}; + +#define SN9C102_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10 + +struct sn9c102_sensor { + char name[32], /* sensor name */ + maintainer[64]; /* name of the mantainer */ + + /* Supported operations through the 'sysfs' interface */ + enum sn9c102_i2c_sysfs_ops sysfs_ops; + + /* + These sensor capabilities must be provided if the SN9C10X controller + needs to communicate through the sensor serial interface by using + at least one of the i2c functions available. + */ + enum sn9c102_i2c_frequency frequency; + enum sn9c102_i2c_interface interface; + + /* + This identifier must be provided if the image sensor implements + the standard I2C protocol. + */ + u8 i2c_slave_id; /* reg. 0x09 */ + + /* + NOTE: Where not noted,most of the functions below are not mandatory. + Set to null if you do not implement them. If implemented, + they must return 0 on success, the proper error otherwise. + */ + + int (*init)(struct sn9c102_device* cam); + /* + This function will be called after the sensor has been attached. + It should be used to initialize the sensor only, but may also + configure part of the SN9C10X chip if necessary. You don't need to + setup picture settings like brightness, contrast, etc.. here, if + the corrisponding controls are implemented (see below), since + they are adjusted in the core driver by calling the set_ctrl() + method after init(), where the arguments are the default values + specified in the v4l2_queryctrl list of supported controls; + Same suggestions apply for other settings, _if_ the corresponding + methods are present; if not, the initialization must configure the + sensor according to the default configuration structures below. + */ + + struct v4l2_queryctrl qctrl[SN9C102_MAX_CTRLS]; + /* + Optional list of default controls, defined as indicated in the + V4L2 API. Menu type controls are not handled by this interface. + */ + + int (*get_ctrl)(struct sn9c102_device* cam, struct v4l2_control* ctrl); + int (*set_ctrl)(struct sn9c102_device* cam, + const struct v4l2_control* ctrl); + /* + You must implement at least the set_ctrl method if you have defined + the list above. The returned value must follow the V4L2 + specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER + are not supported by this driver, so do not implement them. Also, + you don't have to check whether the passed values are out of bounds, + given that this is done by the core module. + */ + + struct v4l2_cropcap cropcap; + /* + Think the image sensor as a grid of R,G,B monochromatic pixels + disposed according to a particular Bayer pattern, which describes + the complete array of pixels, from (0,0) to (xmax, ymax). We will + use this coordinate system from now on. It is assumed the sensor + chip can be programmed to capture/transmit a subsection of that + array of pixels: we will call this subsection "active window". + It is not always true that the largest achievable active window can + cover the whole array of pixels. The V4L2 API defines another + area called "source rectangle", which, in turn, is a subrectangle of + the active window. The SN9C10X chip is always programmed to read the + source rectangle. + The bounds of both the active window and the source rectangle are + specified in the cropcap substructures 'bounds' and 'defrect'. + By default, the source rectangle should cover the largest possible + area. Again, it is not always true that the largest source rectangle + can cover the entire active window, although it is a rare case for + the hardware we have. The bounds of the source rectangle _must_ be + multiple of 16 and must use the same coordinate system as indicated + before; their centers shall align initially. + If necessary, the sensor chip must be initialized during init() to + set the bounds of the active sensor window; however, by default, it + usually covers the largest achievable area (maxwidth x maxheight) + of pixels, so no particular initialization is needed, if you have + defined the correct default bounds in the structures. + See the V4L2 API for further details. + NOTE: once you have defined the bounds of the active window + (struct cropcap.bounds) you must not change them.anymore. + Only 'bounds' and 'defrect' fields are mandatory, other fields + will be ignored. + */ + + int (*set_crop)(struct sn9c102_device* cam, + const struct v4l2_rect* rect); + /* + To be called on VIDIOC_C_SETCROP. The core module always calls a + default routine which configures the appropriate SN9C10X regs (also + scaling), but you may need to override/adjust specific stuff. + 'rect' contains width and height values that are multiple of 16: in + case you override the default function, you always have to program + the chip to match those values; on error return the corresponding + error code without rolling back. + NOTE: in case, you must program the SN9C10X chip to get rid of + blank pixels or blank lines at the _start_ of each line or + frame after each HSYNC or VSYNC, so that the image starts with + real RGB data (see regs 0x12, 0x13) (having set H_SIZE and, + V_SIZE you don't have to care about blank pixels or blank + lines at the end of each line or frame). + */ + + struct v4l2_pix_format pix_format; + /* + What you have to define here are: 1) initial 'width' and 'height' of + the target rectangle 2) the initial 'pixelformat', which can be + either V4L2_PIX_FMT_SN9C10X (for compressed video) or + V4L2_PIX_FMT_SBGGR8 3) 'priv', which we'll be used to indicate the + number of bits per pixel for uncompressed video, 8 or 9 (despite the + current value of 'pixelformat'). + NOTE 1: both 'width' and 'height' _must_ be either 1/1 or 1/2 or 1/4 + of cropcap.defrect.width and cropcap.defrect.height. I + suggest 1/1. + NOTE 2: The initial compression quality is defined by the first bit + of reg 0x17 during the initialization of the image sensor. + NOTE 3: as said above, you have to program the SN9C10X chip to get + rid of any blank pixels, so that the output of the sensor + matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR). + */ + + int (*set_pix_format)(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix); + /* + To be called on VIDIOC_S_FMT, when switching from the SBGGR8 to + SN9C10X pixel format or viceversa. On error return the corresponding + error code without rolling back. + */ + + /* + Do NOT write to the data below, it's READ ONLY. It is used by the + core module to store successfully updated values of the above + settings, for rollbacks..etc..in case of errors during atomic I/O + */ + struct v4l2_queryctrl _qctrl[SN9C102_MAX_CTRLS]; + struct v4l2_rect _rect; +}; + +/*****************************************************************************/ + +/* Private ioctl's for control settings supported by some image sensors */ +#define SN9C102_V4L2_CID_DAC_MAGNITUDE V4L2_CID_PRIVATE_BASE +#define SN9C102_V4L2_CID_GREEN_BALANCE V4L2_CID_PRIVATE_BASE + 1 +#define SN9C102_V4L2_CID_RESET_LEVEL V4L2_CID_PRIVATE_BASE + 2 +#define SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE V4L2_CID_PRIVATE_BASE + 3 +#define SN9C102_V4L2_CID_GAMMA V4L2_CID_PRIVATE_BASE + 4 +#define SN9C102_V4L2_CID_BAND_FILTER V4L2_CID_PRIVATE_BASE + 5 +#define SN9C102_V4L2_CID_BRIGHT_LEVEL V4L2_CID_PRIVATE_BASE + 6 + +#endif /* _SN9C102_SENSOR_H_ */ diff --git a/linux/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c b/linux/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c new file mode 100644 index 000000000..294eb02fb --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c @@ -0,0 +1,159 @@ +/*************************************************************************** + * Plug-in for TAS5110C1B image sensor connected to the SN9C10x PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor tas5110c1b; + + +static int tas5110c1b_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x44, 0x01); + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x0a, 0x14); + err += sn9c102_write_reg(cam, 0x60, 0x17); + err += sn9c102_write_reg(cam, 0x06, 0x18); + err += sn9c102_write_reg(cam, 0xfb, 0x19); + + err += sn9c102_i2c_write(cam, 0xc0, 0x80); + + return err; +} + + +static int tas5110c1b_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int tas5110c1b_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &tas5110c1b; + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + /* Don't change ! */ + err += sn9c102_write_reg(cam, 0x14, 0x1a); + err += sn9c102_write_reg(cam, 0x0a, 0x1b); + err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19); + + return err; +} + + +static int tas5110c1b_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x2b, 0x19); + else + err += sn9c102_write_reg(cam, 0xfb, 0x19); + + return err; +} + + +static struct sn9c102_sensor tas5110c1b = { + .name = "TAS5110C1B", + .maintainer = "Luca Risolia ", + .sysfs_ops = SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_3WIRES, + .init = &tas5110c1b_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xf6, + .step = 0x01, + .default_value = 0x40, + .flags = 0, + }, + }, + .set_ctrl = &tas5110c1b_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + }, + .set_crop = &tas5110c1b_set_crop, + .pix_format = { + .width = 352, + .height = 288, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &tas5110c1b_set_pix_format +}; + + +int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam) +{ + const struct usb_device_id tas5110c1b_id_table[] = { + { USB_DEVICE(0x0c45, 0x6001), }, + { USB_DEVICE(0x0c45, 0x6005), }, + { USB_DEVICE(0x0c45, 0x60ab), }, + { } + }; + + /* Sensor detection is based on USB pid/vid */ + if (!sn9c102_match_id(cam, tas5110c1b_id_table)) + return -ENODEV; + + sn9c102_attach_sensor(cam, &tas5110c1b); + + return 0; +} diff --git a/linux/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c b/linux/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c new file mode 100644 index 000000000..9ecb09032 --- /dev/null +++ b/linux/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c @@ -0,0 +1,169 @@ +/*************************************************************************** + * Plug-in for TAS5130D1B image sensor connected to the SN9C10x PC Camera * + * Controllers * + * * + * Copyright (C) 2004-2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor tas5130d1b; + + +static int tas5130d1b_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x04, 0x01); + err += sn9c102_write_reg(cam, 0x01, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x60, 0x17); + err += sn9c102_write_reg(cam, 0x07, 0x18); + + return err; +} + + +static int tas5130d1b_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x20, 0xf6 - ctrl->value); + break; + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x40, 0x47 - ctrl->value); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int tas5130d1b_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &tas5130d1b; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12; + int err = 0; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + /* Do NOT change! */ + err += sn9c102_write_reg(cam, 0x1f, 0x1a); + err += sn9c102_write_reg(cam, 0x1a, 0x1b); + err += sn9c102_write_reg(cam, sn9c102_pread_reg(cam, 0x19), 0x19); + + return err; +} + + +static int tas5130d1b_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x63, 0x19); + else + err += sn9c102_write_reg(cam, 0xf3, 0x19); + + return err; +} + + +static struct sn9c102_sensor tas5130d1b = { + .name = "TAS5130D1B", + .maintainer = "Luca Risolia ", + .sysfs_ops = SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_3WIRES, + .init = &tas5130d1b_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xf6, + .step = 0x02, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x47, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .set_ctrl = &tas5130d1b_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &tas5130d1b_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &tas5130d1b_set_pix_format +}; + + +int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam) +{ + const struct usb_device_id tas5130d1b_id_table[] = { + { USB_DEVICE(0x0c45, 0x6025), }, + { USB_DEVICE(0x0c45, 0x60aa), }, + { } + }; + + /* Sensor detection is based on USB pid/vid */ + if (!sn9c102_match_id(cam, tas5130d1b_id_table)) + return -ENODEV; + + sn9c102_attach_sensor(cam, &tas5130d1b); + + return 0; +} diff --git a/linux/drivers/media/video/usbvideo/Kconfig b/linux/drivers/media/video/usbvideo/Kconfig new file mode 100644 index 000000000..39269a2c5 --- /dev/null +++ b/linux/drivers/media/video/usbvideo/Kconfig @@ -0,0 +1,38 @@ +config VIDEO_USBVIDEO + tristate + +config USB_VICAM + tristate "USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)" + depends on USB && VIDEO_V4L1 && EXPERIMENTAL + select VIDEO_USBVIDEO + ---help--- + Say Y here if you have 3com homeconnect camera (vicam). + + To compile this driver as a module, choose M here: the + module will be called vicam. + +config USB_IBMCAM + tristate "USB IBM (Xirlink) C-it Camera support" + depends on USB && VIDEO_V4L1 + select VIDEO_USBVIDEO + ---help--- + Say Y here if you want to connect a IBM "C-It" camera, also known as + "Xirlink PC Camera" to your computer's USB port. + + To compile this driver as a module, choose M here: the + module will be called ibmcam. + + This camera has several configuration options which + can be specified when you load the module. Read + to learn more. + +config USB_KONICAWC + tristate "USB Konica Webcam support" + depends on USB && VIDEO_V4L1 + select VIDEO_USBVIDEO + ---help--- + Say Y here if you want support for webcams based on a Konica + chipset. This is known to work with the Intel YC76 webcam. + + To compile this driver as a module, choose M here: the + module will be called konicawc. diff --git a/linux/drivers/media/video/usbvideo/Makefile b/linux/drivers/media/video/usbvideo/Makefile new file mode 100644 index 000000000..bb52eb8dc --- /dev/null +++ b/linux/drivers/media/video/usbvideo/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_VIDEO_USBVIDEO) += usbvideo.o +obj-$(CONFIG_USB_IBMCAM) += ibmcam.o ultracam.o +obj-$(CONFIG_USB_KONICAWC) += konicawc.o +obj-$(CONFIG_USB_VICAM) += vicam.o diff --git a/linux/drivers/media/video/usbvideo/ibmcam.c b/linux/drivers/media/video/usbvideo/ibmcam.c new file mode 100644 index 000000000..76f771b6a --- /dev/null +++ b/linux/drivers/media/video/usbvideo/ibmcam.c @@ -0,0 +1,3932 @@ +/* + * USB IBM C-It Video Camera driver + * + * Supports Xirlink C-It Video Camera, IBM PC Camera, + * IBM NetCamera and Veo Stingray. + * + * This driver is based on earlier work of: + * + * (C) Copyright 1999 Johannes Erdfelt + * (C) Copyright 1999 Randy Dunlap + * + * 5/24/00 Removed optional (and unnecessary) locking of the driver while + * the device remains plugged in. Corrected race conditions in ibmcam_open + * and ibmcam_probe() routines using this as a guideline: + */ + +#include +#include +#include +#include + +#include "usbvideo.h" + +#define IBMCAM_VENDOR_ID 0x0545 +#define IBMCAM_PRODUCT_ID 0x8080 +#define NETCAM_PRODUCT_ID 0x8002 /* IBM NetCamera, close to model 2 */ +#define VEO_800C_PRODUCT_ID 0x800C /* Veo Stingray, repackaged Model 2 */ +#define VEO_800D_PRODUCT_ID 0x800D /* Veo Stingray, repackaged Model 4 */ + +#define MAX_IBMCAM 4 /* How many devices we allow to connect */ +#define USES_IBMCAM_PUTPIXEL 0 /* 0=Fast/oops 1=Slow/secure */ + +/* Header signatures */ + +/* Model 1 header: 00 FF 00 xx */ +#define HDRSIG_MODEL1_128x96 0x06 /* U Y V Y ... */ +#define HDRSIG_MODEL1_176x144 0x0e /* U Y V Y ... */ +#define HDRSIG_MODEL1_352x288 0x00 /* V Y U Y ... */ + +#define IBMCAM_MODEL_1 1 /* XVP-501, 3 interfaces, rev. 0.02 */ +#define IBMCAM_MODEL_2 2 /* KSX-X9903, 2 interfaces, rev. 3.0a */ +#define IBMCAM_MODEL_3 3 /* KSX-X9902, 2 interfaces, rev. 3.01 */ +#define IBMCAM_MODEL_4 4 /* IBM NetCamera, 0545/8002/3.0a */ + +/* Video sizes supported */ +#define VIDEOSIZE_128x96 VIDEOSIZE(128, 96) +#define VIDEOSIZE_176x144 VIDEOSIZE(176,144) +#define VIDEOSIZE_352x288 VIDEOSIZE(352,288) +#define VIDEOSIZE_320x240 VIDEOSIZE(320,240) +#define VIDEOSIZE_352x240 VIDEOSIZE(352,240) +#define VIDEOSIZE_640x480 VIDEOSIZE(640,480) +#define VIDEOSIZE_160x120 VIDEOSIZE(160,120) + +/* Video sizes supported */ +enum { + SIZE_128x96 = 0, + SIZE_160x120, + SIZE_176x144, + SIZE_320x240, + SIZE_352x240, + SIZE_352x288, + SIZE_640x480, + /* Add/remove/rearrange items before this line */ + SIZE_LastItem +}; + +/* + * This structure lives in uvd->user field. + */ +typedef struct { + int initialized; /* Had we already sent init sequence? */ + int camera_model; /* What type of IBM camera we got? */ + int has_hdr; +} ibmcam_t; +#define IBMCAM_T(uvd) ((ibmcam_t *)((uvd)->user_data)) + +static struct usbvideo *cams; + +static int debug; + +static int flags; /* = FLAGS_DISPLAY_HINTS | FLAGS_OVERLAY_STATS; */ + +static const int min_canvasWidth = 8; +static const int min_canvasHeight = 4; + +static int lighting = 1; /* Medium */ + +#define SHARPNESS_MIN 0 +#define SHARPNESS_MAX 6 +static int sharpness = 4; /* Low noise, good details */ + +#define FRAMERATE_MIN 0 +#define FRAMERATE_MAX 6 +static int framerate = -1; + +static int size = SIZE_352x288; + +/* + * Here we define several initialization variables. They may + * be used to automatically set color, hue, brightness and + * contrast to desired values. This is particularly useful in + * case of webcams (which have no controls and no on-screen + * output) and also when a client V4L software is used that + * does not have some of those controls. In any case it's + * good to have startup values as options. + * + * These values are all in [0..255] range. This simplifies + * operation. Note that actual values of V4L variables may + * be scaled up (as much as << 8). User can see that only + * on overlay output, however, or through a V4L client. + */ +static int init_brightness = 128; +static int init_contrast = 192; +static int init_color = 128; +static int init_hue = 128; +static int hue_correction = 128; + +/* Settings for camera model 2 */ +static int init_model2_rg2 = -1; +static int init_model2_sat = -1; +static int init_model2_yb = -1; + +/* 01.01.08 - Added for RCA video in support -LO */ +/* Settings for camera model 3 */ +static int init_model3_input = 0; + +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)"); +module_param(flags, int, 0); +MODULE_PARM_DESC(flags, "Bitfield: 0=VIDIOCSYNC, 1=B/W, 2=show hints, 3=show stats, 4=test pattern, 5=separate frames, 6=clean frames"); +module_param(framerate, int, 0); +MODULE_PARM_DESC(framerate, "Framerate setting: 0=slowest, 6=fastest (default=2)"); +module_param(lighting, int, 0); +MODULE_PARM_DESC(lighting, "Photosensitivity: 0=bright, 1=medium (default), 2=low light"); +module_param(sharpness, int, 0); +MODULE_PARM_DESC(sharpness, "Model1 noise reduction: 0=smooth, 6=sharp (default=4)"); +module_param(size, int, 0); +MODULE_PARM_DESC(size, "Image size: 0=128x96 1=160x120 2=176x144 3=320x240 4=352x240 5=352x288 6=640x480 (default=5)"); +module_param(init_brightness, int, 0); +MODULE_PARM_DESC(init_brightness, "Brightness preconfiguration: 0-255 (default=128)"); +module_param(init_contrast, int, 0); +MODULE_PARM_DESC(init_contrast, "Contrast preconfiguration: 0-255 (default=192)"); +module_param(init_color, int, 0); +MODULE_PARM_DESC(init_color, "Color preconfiguration: 0-255 (default=128)"); +module_param(init_hue, int, 0); +MODULE_PARM_DESC(init_hue, "Hue preconfiguration: 0-255 (default=128)"); +module_param(hue_correction, int, 0); +MODULE_PARM_DESC(hue_correction, "YUV colorspace regulation: 0-255 (default=128)"); + +module_param(init_model2_rg2, int, 0); +MODULE_PARM_DESC(init_model2_rg2, "Model2 preconfiguration: 0-255 (default=47)"); +module_param(init_model2_sat, int, 0); +MODULE_PARM_DESC(init_model2_sat, "Model2 preconfiguration: 0-255 (default=52)"); +module_param(init_model2_yb, int, 0); +MODULE_PARM_DESC(init_model2_yb, "Model2 preconfiguration: 0-255 (default=160)"); + +/* 01.01.08 - Added for RCA video in support -LO */ +module_param(init_model3_input, int, 0); +MODULE_PARM_DESC(init_model3_input, "Model3 input: 0=CCD 1=RCA"); + +MODULE_AUTHOR ("Dmitri"); +MODULE_DESCRIPTION ("IBM/Xirlink C-it USB Camera Driver for Linux (c) 2000"); +MODULE_LICENSE("GPL"); + +/* Still mysterious i2c commands */ +static const unsigned short unknown_88 = 0x0088; +static const unsigned short unknown_89 = 0x0089; +static const unsigned short bright_3x[3] = { 0x0031, 0x0032, 0x0033 }; +static const unsigned short contrast_14 = 0x0014; +static const unsigned short light_27 = 0x0027; +static const unsigned short sharp_13 = 0x0013; + +/* i2c commands for Model 2 cameras */ +static const unsigned short mod2_brightness = 0x001a; /* $5b .. $ee; default=$5a */ +static const unsigned short mod2_set_framerate = 0x001c; /* 0 (fast).. $1F (slow) */ +static const unsigned short mod2_color_balance_rg2 = 0x001e; /* 0 (red) .. $7F (green) */ +static const unsigned short mod2_saturation = 0x0020; /* 0 (b/w) - $7F (full color) */ +static const unsigned short mod2_color_balance_yb = 0x0022; /* 0..$7F, $50 is about right */ +static const unsigned short mod2_hue = 0x0024; /* 0..$7F, $70 is about right */ +static const unsigned short mod2_sensitivity = 0x0028; /* 0 (min) .. $1F (max) */ + +struct struct_initData { + unsigned char req; + unsigned short value; + unsigned short index; +}; + +/* + * ibmcam_size_to_videosize() + * + * This procedure converts module option 'size' into the actual + * videosize_t that defines the image size in pixels. We need + * simplified 'size' because user wants a simple enumerated list + * of choices, not an infinite set of possibilities. + */ +static videosize_t ibmcam_size_to_videosize(int size) +{ + videosize_t vs = VIDEOSIZE_352x288; + RESTRICT_TO_RANGE(size, 0, (SIZE_LastItem-1)); + switch (size) { + case SIZE_128x96: + vs = VIDEOSIZE_128x96; + break; + case SIZE_160x120: + vs = VIDEOSIZE_160x120; + break; + case SIZE_176x144: + vs = VIDEOSIZE_176x144; + break; + case SIZE_320x240: + vs = VIDEOSIZE_320x240; + break; + case SIZE_352x240: + vs = VIDEOSIZE_352x240; + break; + case SIZE_352x288: + vs = VIDEOSIZE_352x288; + break; + case SIZE_640x480: + vs = VIDEOSIZE_640x480; + break; + default: + err("size=%d. is not valid", size); + break; + } + return vs; +} + +/* + * ibmcam_find_header() + * + * Locate one of supported header markers in the queue. + * Once found, remove all preceding bytes AND the marker (4 bytes) + * from the data pump queue. Whatever follows must be video lines. + * + * History: + * 1/21/00 Created. + */ +static enum ParseState ibmcam_find_header(struct uvd *uvd) /* FIXME: Add frame here */ +{ + struct usbvideo_frame *frame; + ibmcam_t *icam; + + if ((uvd->curframe) < 0 || (uvd->curframe >= USBVIDEO_NUMFRAMES)) { + err("ibmcam_find_header: Illegal frame %d.", uvd->curframe); + return scan_EndParse; + } + icam = IBMCAM_T(uvd); + assert(icam != NULL); + frame = &uvd->frame[uvd->curframe]; + icam->has_hdr = 0; + switch (icam->camera_model) { + case IBMCAM_MODEL_1: + { + const int marker_len = 4; + while (RingQueue_GetLength(&uvd->dp) >= marker_len) { + if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) && + (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xFF) && + (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00)) + { +#if 0 /* This code helps to detect new frame markers */ + info("Header sig: 00 FF 00 %02X", RING_QUEUE_PEEK(&uvd->dp, 3)); +#endif + frame->header = RING_QUEUE_PEEK(&uvd->dp, 3); + if ((frame->header == HDRSIG_MODEL1_128x96) || + (frame->header == HDRSIG_MODEL1_176x144) || + (frame->header == HDRSIG_MODEL1_352x288)) + { +#if 0 + info("Header found."); +#endif + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len); + icam->has_hdr = 1; + break; + } + } + /* If we are still here then this doesn't look like a header */ + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1); + } + break; + } + case IBMCAM_MODEL_2: +case IBMCAM_MODEL_4: + { + int marker_len = 0; + switch (uvd->videosize) { + case VIDEOSIZE_176x144: + marker_len = 10; + break; + default: + marker_len = 2; + break; + } + while (RingQueue_GetLength(&uvd->dp) >= marker_len) { + if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) && + (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xFF)) + { +#if 0 + info("Header found."); +#endif + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len); + icam->has_hdr = 1; + frame->header = HDRSIG_MODEL1_176x144; + break; + } + /* If we are still here then this doesn't look like a header */ + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1); + } + break; + } + case IBMCAM_MODEL_3: + { /* + * Headers: (one precedes every frame). nc=no compression, + * bq=best quality bf=best frame rate. + * + * 176x144: 00 FF 02 { 0A=nc CA=bq EA=bf } + * 320x240: 00 FF 02 { 08=nc 28=bq 68=bf } + * 640x480: 00 FF 03 { 08=nc 28=bq 68=bf } + * + * Bytes '00 FF' seem to indicate header. Other two bytes + * encode the frame type. This is a set of bit fields that + * encode image size, compression type etc. These fields + * do NOT contain frame number because all frames carry + * the same header. + */ + const int marker_len = 4; + while (RingQueue_GetLength(&uvd->dp) >= marker_len) { + if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) && + (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xFF) && + (RING_QUEUE_PEEK(&uvd->dp, 2) != 0xFF)) + { + /* + * Combine 2 bytes of frame type into one + * easy to use value + */ + unsigned long byte3, byte4; + + byte3 = RING_QUEUE_PEEK(&uvd->dp, 2); + byte4 = RING_QUEUE_PEEK(&uvd->dp, 3); + frame->header = (byte3 << 8) | byte4; +#if 0 + info("Header found."); +#endif + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len); + icam->has_hdr = 1; + break; + } + /* If we are still here then this doesn't look like a header */ + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1); + } + break; + } + default: + break; + } + if (!icam->has_hdr) { + if (uvd->debug > 2) + info("Skipping frame, no header"); + return scan_EndParse; + } + + /* Header found */ + icam->has_hdr = 1; + uvd->stats.header_count++; + frame->scanstate = ScanState_Lines; + frame->curline = 0; + + if (flags & FLAGS_FORCE_TESTPATTERN) { + usbvideo_TestPattern(uvd, 1, 1); + return scan_NextFrame; + } + return scan_Continue; +} + +/* + * ibmcam_parse_lines() + * + * Parse one line (interlaced) from the buffer, put + * decoded RGB value into the current frame buffer + * and add the written number of bytes (RGB) to + * the *pcopylen. + * + * History: + * 21-Jan-2000 Created. + * 12-Oct-2000 Reworked to reflect interlaced nature of the data. + */ +static enum ParseState ibmcam_parse_lines( + struct uvd *uvd, + struct usbvideo_frame *frame, + long *pcopylen) +{ + unsigned char *f; + ibmcam_t *icam; + unsigned int len, scanLength, scanHeight, order_uv, order_yc; + int v4l_linesize; /* V4L line offset */ + const int hue_corr = (uvd->vpic.hue - 0x8000) >> 10; /* -32..+31 */ + const int hue2_corr = (hue_correction - 128) / 4; /* -32..+31 */ + const int ccm = 128; /* Color correction median - see below */ + int y, u, v, i, frame_done=0, color_corr; + static unsigned char lineBuffer[640*3]; + unsigned const char *chromaLine, *lumaLine; + + assert(uvd != NULL); + assert(frame != NULL); + icam = IBMCAM_T(uvd); + assert(icam != NULL); + color_corr = (uvd->vpic.colour - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ + RESTRICT_TO_RANGE(color_corr, -ccm, ccm+1); + + v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; + + if (IBMCAM_T(uvd)->camera_model == IBMCAM_MODEL_4) { + /* Model 4 frame markers do not carry image size identification */ + switch (uvd->videosize) { + case VIDEOSIZE_128x96: + case VIDEOSIZE_160x120: + case VIDEOSIZE_176x144: + scanLength = VIDEOSIZE_X(uvd->videosize); + scanHeight = VIDEOSIZE_Y(uvd->videosize); + break; + default: + err("ibmcam_parse_lines: Wrong mode."); + return scan_Out; + } + order_yc = 1; /* order_yc: true=Yc false=cY ('c'=either U or V) */ + order_uv = 1; /* Always true in this algorithm */ + } else { + switch (frame->header) { + case HDRSIG_MODEL1_128x96: + scanLength = 128; + scanHeight = 96; + order_uv = 1; /* U Y V Y ... */ + break; + case HDRSIG_MODEL1_176x144: + scanLength = 176; + scanHeight = 144; + order_uv = 1; /* U Y V Y ... */ + break; + case HDRSIG_MODEL1_352x288: + scanLength = 352; + scanHeight = 288; + order_uv = 0; /* Y V Y V ... */ + break; + default: + err("Unknown header signature 00 FF 00 %02lX", frame->header); + return scan_NextFrame; + } + /* order_yc: true=Yc false=cY ('c'=either U or V) */ + order_yc = (IBMCAM_T(uvd)->camera_model == IBMCAM_MODEL_2); + } + + len = scanLength * 3; + assert(len <= sizeof(lineBuffer)); + + /* + * Lines are organized this way: + * + * I420: + * ~~~~ + * + * ___________________________________ + * |-----Y-----|---UVUVUV...UVUV-----| \ + * |-----------+---------------------| \ + * |<-- 176 -->|<------ 176*2 ------>| Total 72. lines (interlaced) + * |... ... | ... | / + * |<-- 352 -->|<------ 352*2 ------>| Total 144. lines (interlaced) + * |___________|_____________________| / + * \ \ + * lumaLine chromaLine + */ + + /* Make sure there's enough data for the entire line */ + if (RingQueue_GetLength(&uvd->dp) < len) + return scan_Out; + + /* Suck one line out of the ring queue */ + RingQueue_Dequeue(&uvd->dp, lineBuffer, len); + + /* + * Make sure that our writing into output buffer + * will not exceed the buffer. Mind that we may write + * not into current output scanline but in several after + * it as well (if we enlarge image vertically.) + */ + if ((frame->curline + 2) >= VIDEOSIZE_Y(frame->request)) + return scan_NextFrame; + + /* + * Now we are sure that entire line (representing all 'scanLength' + * pixels from the camera) is available in the buffer. We + * start copying the line left-aligned to the V4L buffer. + * If the camera line is shorter then we should pad the V4L + * buffer with something (black) to complete the line. + */ + assert(frame->data != NULL); + f = frame->data + (v4l_linesize * frame->curline); + + /* + * To obtain chrominance data from the 'chromaLine' use this: + * v = chromaLine[0]; // 0-1:[0], 2-3:[4], 4-5:[8]... + * u = chromaLine[2]; // 0-1:[2], 2-3:[6], 4-5:[10]... + * + * Indices must be calculated this way: + * v_index = (i >> 1) << 2; + * u_index = (i >> 1) << 2 + 2; + * + * where 'i' is the column number [0..VIDEOSIZE_X(frame->request)-1] + */ + lumaLine = lineBuffer; + chromaLine = lineBuffer + scanLength; + for (i = 0; i < VIDEOSIZE_X(frame->request); i++) + { + unsigned char rv, gv, bv; /* RGB components */ + + /* Check for various visual debugging hints (colorized pixels) */ + if ((flags & FLAGS_DISPLAY_HINTS) && (icam->has_hdr)) { + /* + * This is bad and should not happen. This means that + * we somehow overshoot the line and encountered new + * frame! Obviously our camera/V4L frame size is out + * of whack. This cyan dot will help you to figure + * out where exactly the new frame arrived. + */ + if (icam->has_hdr == 1) { + bv = 0; /* Yellow marker */ + gv = 0xFF; + rv = 0xFF; + } else { + bv = 0xFF; /* Cyan marker */ + gv = 0xFF; + rv = 0; + } + icam->has_hdr = 0; + goto make_pixel; + } + + /* + * Check if we are still in range. We may be out of range if our + * V4L canvas is wider or taller than the camera "native" image. + * Then we quickly fill the remainder of the line with zeros to + * make black color and quit the horizontal scanning loop. + */ + if (((frame->curline + 2) >= scanHeight) || (i >= scanLength)) { + const int j = i * V4L_BYTES_PER_PIXEL; +#if USES_IBMCAM_PUTPIXEL + /* Refresh 'f' because we don't use it much with PUTPIXEL */ + f = frame->data + (v4l_linesize * frame->curline) + j; +#endif + memset(f, 0, v4l_linesize - j); + break; + } + + y = lumaLine[i]; + if (flags & FLAGS_MONOCHROME) /* Use monochrome for debugging */ + rv = gv = bv = y; + else { + int off_0, off_2; + + off_0 = (i >> 1) << 2; + off_2 = off_0 + 2; + + if (order_yc) { + off_0++; + off_2++; + } + if (!order_uv) { + off_0 += 2; + off_2 -= 2; + } + u = chromaLine[off_0] + hue_corr; + v = chromaLine[off_2] + hue2_corr; + + /* Apply color correction */ + if (color_corr != 0) { + /* Magnify up to 2 times, reduce down to zero saturation */ + u = 128 + ((ccm + color_corr) * (u - 128)) / ccm; + v = 128 + ((ccm + color_corr) * (v - 128)) / ccm; + } + YUV_TO_RGB_BY_THE_BOOK(y, u, v, rv, gv, bv); + } + + make_pixel: + /* + * The purpose of creating the pixel here, in one, + * dedicated place is that we may need to make the + * pixel wider and taller than it actually is. This + * may be used if camera generates small frames for + * sake of frame rate (or any other reason.) + * + * The output data consists of B, G, R bytes + * (in this order). + */ +#if USES_IBMCAM_PUTPIXEL + RGB24_PUTPIXEL(frame, i, frame->curline, rv, gv, bv); +#else + *f++ = bv; + *f++ = gv; + *f++ = rv; +#endif + /* + * Typically we do not decide within a legitimate frame + * that we want to end the frame. However debugging code + * may detect marker of new frame within the data. Then + * this condition activates. The 'data' pointer is already + * pointing at the new marker, so we'd better leave it as is. + */ + if (frame_done) + break; /* End scanning of lines */ + } + /* + * Account for number of bytes that we wrote into output V4L frame. + * We do it here, after we are done with the scanline, because we + * may fill more than one output scanline if we do vertical + * enlargement. + */ + frame->curline += 2; + if (pcopylen != NULL) + *pcopylen += 2 * v4l_linesize; + frame->deinterlace = Deinterlace_FillOddLines; + + if (frame_done || (frame->curline >= VIDEOSIZE_Y(frame->request))) + return scan_NextFrame; + else + return scan_Continue; +} + +/* + * ibmcam_model2_320x240_parse_lines() + * + * This procedure deals with a weird RGB format that is produced by IBM + * camera model 2 in modes 320x240 and above; 'x' below is 159 or 175, + * depending on horizontal size of the picture: + * + * <--- 160 or 176 pairs of RA,RB bytes -----> + * *-----------------------------------------* \ + * | RA0 | RB0 | RA1 | RB1 | ... | RAx | RBx | \ This is pair of horizontal lines, + * |-----+-----+-----+-----+ ... +-----+-----| *- or one interlaced line, total + * | B0 | G0 | B1 | G1 | ... | Bx | Gx | / 120 or 144 such pairs which yield + * |=====+=====+=====+=====+ ... +=====+=====| / 240 or 288 lines after deinterlacing. + * + * Each group of FOUR bytes (RAi, RBi, Bi, Gi) where i=0..frame_width/2-1 + * defines ONE pixel. Therefore this format yields 176x144 "decoded" + * resolution at best. I do not know why camera sends such format - the + * previous model (1) just used interlaced I420 and everyone was happy. + * + * I do not know what is the difference between RAi and RBi bytes. Both + * seemingly represent R component, but slightly vary in value (so that + * the picture looks a bit colored if one or another is used). I use + * them both as R component in attempt to at least partially recover the + * lost resolution. + */ +static enum ParseState ibmcam_model2_320x240_parse_lines( + struct uvd *uvd, + struct usbvideo_frame *frame, + long *pcopylen) +{ + unsigned char *f, *la, *lb; + unsigned int len; + int v4l_linesize; /* V4L line offset */ + int i, j, frame_done=0, color_corr; + int scanLength, scanHeight; + static unsigned char lineBuffer[352*2]; + + switch (uvd->videosize) { + case VIDEOSIZE_320x240: + case VIDEOSIZE_352x240: + case VIDEOSIZE_352x288: + scanLength = VIDEOSIZE_X(uvd->videosize); + scanHeight = VIDEOSIZE_Y(uvd->videosize); + break; + default: + err("ibmcam_model2_320x240_parse_lines: Wrong mode."); + return scan_Out; + } + + color_corr = (uvd->vpic.colour) >> 8; /* 0..+255 */ + v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; + + len = scanLength * 2; /* See explanation above */ + assert(len <= sizeof(lineBuffer)); + + /* Make sure there's enough data for the entire line */ + if (RingQueue_GetLength(&uvd->dp) < len) + return scan_Out; + + /* Suck one line out of the ring queue */ + RingQueue_Dequeue(&uvd->dp, lineBuffer, len); + + /* + * Make sure that our writing into output buffer + * will not exceed the buffer. Mind that we may write + * not into current output scanline but in several after + * it as well (if we enlarge image vertically.) + */ + if ((frame->curline + 2) >= VIDEOSIZE_Y(frame->request)) + return scan_NextFrame; + + la = lineBuffer; + lb = lineBuffer + scanLength; + + /* + * Now we are sure that entire line (representing all + * VIDEOSIZE_X(frame->request) + * pixels from the camera) is available in the scratch buffer. We + * start copying the line left-aligned to the V4L buffer (which + * might be larger - not smaller, hopefully). If the camera + * line is shorter then we should pad the V4L buffer with something + * (black in this case) to complete the line. + */ + f = frame->data + (v4l_linesize * frame->curline); + + /* Fill the 2-line strip */ + for (i = 0; i < VIDEOSIZE_X(frame->request); i++) { + int y, rv, gv, bv; /* RGB components */ + + j = i & (~1); + + /* Check for various visual debugging hints (colorized pixels) */ + if ((flags & FLAGS_DISPLAY_HINTS) && (IBMCAM_T(uvd)->has_hdr)) { + if (IBMCAM_T(uvd)->has_hdr == 1) { + bv = 0; /* Yellow marker */ + gv = 0xFF; + rv = 0xFF; + } else { + bv = 0xFF; /* Cyan marker */ + gv = 0xFF; + rv = 0; + } + IBMCAM_T(uvd)->has_hdr = 0; + goto make_pixel; + } + + /* + * Check if we are still in range. We may be out of range if our + * V4L canvas is wider or taller than the camera "native" image. + * Then we quickly fill the remainder of the line with zeros to + * make black color and quit the horizontal scanning loop. + */ + if (((frame->curline + 2) >= scanHeight) || (i >= scanLength)) { + const int j = i * V4L_BYTES_PER_PIXEL; +#if USES_IBMCAM_PUTPIXEL + /* Refresh 'f' because we don't use it much with PUTPIXEL */ + f = frame->data + (v4l_linesize * frame->curline) + j; +#endif + memset(f, 0, v4l_linesize - j); + break; + } + + /* + * Here I use RA and RB components, one per physical pixel. + * This causes fine vertical grid on the picture but may improve + * horizontal resolution. If you prefer replicating, use this: + * rv = la[j + 0]; ... or ... rv = la[j + 1]; + * then the pixel will be replicated. + */ + rv = la[i]; + gv = lb[j + 1]; + bv = lb[j + 0]; + + y = (rv + gv + bv) / 3; /* Brightness (badly calculated) */ + + if (flags & FLAGS_MONOCHROME) /* Use monochrome for debugging */ + rv = gv = bv = y; + else if (color_corr != 128) { + + /* Calculate difference between color and brightness */ + rv -= y; + gv -= y; + bv -= y; + + /* Scale differences */ + rv = (rv * color_corr) / 128; + gv = (gv * color_corr) / 128; + bv = (bv * color_corr) / 128; + + /* Reapply brightness */ + rv += y; + gv += y; + bv += y; + + /* Watch for overflows */ + RESTRICT_TO_RANGE(rv, 0, 255); + RESTRICT_TO_RANGE(gv, 0, 255); + RESTRICT_TO_RANGE(bv, 0, 255); + } + + make_pixel: + RGB24_PUTPIXEL(frame, i, frame->curline, rv, gv, bv); + } + /* + * Account for number of bytes that we wrote into output V4L frame. + * We do it here, after we are done with the scanline, because we + * may fill more than one output scanline if we do vertical + * enlargement. + */ + frame->curline += 2; + *pcopylen += v4l_linesize * 2; + frame->deinterlace = Deinterlace_FillOddLines; + + if (frame_done || (frame->curline >= VIDEOSIZE_Y(frame->request))) + return scan_NextFrame; + else + return scan_Continue; +} + +static enum ParseState ibmcam_model3_parse_lines( + struct uvd *uvd, + struct usbvideo_frame *frame, + long *pcopylen) +{ + unsigned char *data; + const unsigned char *color; + unsigned int len; + int v4l_linesize; /* V4L line offset */ + const int hue_corr = (uvd->vpic.hue - 0x8000) >> 10; /* -32..+31 */ + const int hue2_corr = (hue_correction - 128) / 4; /* -32..+31 */ + const int ccm = 128; /* Color correction median - see below */ + int i, u, v, rw, data_w=0, data_h=0, color_corr; + static unsigned char lineBuffer[640*3]; + + color_corr = (uvd->vpic.colour - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ + RESTRICT_TO_RANGE(color_corr, -ccm, ccm+1); + + v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; + + /* The header tells us what sort of data is in this frame */ + switch (frame->header) { + /* + * Uncompressed modes (that are easy to decode). + */ + case 0x0308: + data_w = 640; + data_h = 480; + break; + case 0x0208: + data_w = 320; + data_h = 240; + break; + case 0x020A: + data_w = 160; + data_h = 120; + break; + /* + * Compressed modes (ViCE - that I don't know how to decode). + */ + case 0x0328: /* 640x480, best quality compression */ + case 0x0368: /* 640x480, best frame rate compression */ + case 0x0228: /* 320x240, best quality compression */ + case 0x0268: /* 320x240, best frame rate compression */ + case 0x02CA: /* 160x120, best quality compression */ + case 0x02EA: /* 160x120, best frame rate compression */ + /* Do nothing with this - not supported */ + err("Unsupported mode $%04lx", frame->header); + return scan_NextFrame; + default: + /* Catch unknown headers, may help in learning new headers */ + err("Strange frame->header=$%08lx", frame->header); + return scan_NextFrame; + } + + /* + * Make sure that our writing into output buffer + * will not exceed the buffer. Note that we may write + * not into current output scanline but in several after + * it as well (if we enlarge image vertically.) + */ + if ((frame->curline + 1) >= data_h) { + if (uvd->debug >= 3) + info("Reached line %d. (frame is done)", frame->curline); + return scan_NextFrame; + } + + /* Make sure there's enough data for the entire line */ + len = 3 * data_w; /* */ + assert(len <= sizeof(lineBuffer)); + + /* Make sure there's enough data for the entire line */ + if (RingQueue_GetLength(&uvd->dp) < len) + return scan_Out; + + /* Suck one line out of the ring queue */ + RingQueue_Dequeue(&uvd->dp, lineBuffer, len); + + data = lineBuffer; + color = data + data_w; /* Point to where color planes begin */ + + /* Bottom-to-top scanning */ + rw = (int)VIDEOSIZE_Y(frame->request) - (int)(frame->curline) - 1; + RESTRICT_TO_RANGE(rw, 0, VIDEOSIZE_Y(frame->request)-1); + + for (i = 0; i < VIDEOSIZE_X(frame->request); i++) { + int y, rv, gv, bv; /* RGB components */ + + if (i < data_w) { + y = data[i]; /* Luminosity is the first line */ + + /* Apply static color correction */ + u = color[i*2] + hue_corr; + v = color[i*2 + 1] + hue2_corr; + + /* Apply color correction */ + if (color_corr != 0) { + /* Magnify up to 2 times, reduce down to zero saturation */ + u = 128 + ((ccm + color_corr) * (u - 128)) / ccm; + v = 128 + ((ccm + color_corr) * (v - 128)) / ccm; + } + } else + y = 0, u = v = 128; + + YUV_TO_RGB_BY_THE_BOOK(y, u, v, rv, gv, bv); + RGB24_PUTPIXEL(frame, i, rw, rv, gv, bv); /* Done by deinterlacing now */ + } + frame->deinterlace = Deinterlace_FillEvenLines; + + /* + * Account for number of bytes that we wrote into output V4L frame. + * We do it here, after we are done with the scanline, because we + * may fill more than one output scanline if we do vertical + * enlargement. + */ + frame->curline += 2; + *pcopylen += 2 * v4l_linesize; + + if (frame->curline >= VIDEOSIZE_Y(frame->request)) { + if (uvd->debug >= 3) { + info("All requested lines (%ld.) done.", + VIDEOSIZE_Y(frame->request)); + } + return scan_NextFrame; + } else + return scan_Continue; +} + +/* + * ibmcam_model4_128x96_parse_lines() + * + * This decoder is for one strange data format that is produced by Model 4 + * camera only in 128x96 mode. This is RGB format and here is its description. + * First of all, this is non-interlaced stream, meaning that all scan lines + * are present in the datastream. There are 96 consecutive blocks of data + * that describe all 96 lines of the image. Each block is 5*128 bytes long + * and carries R, G, B components. The format of the block is shown in the + * code below. First 128*2 bytes are interleaved R and G components. Then + * we have a gap (junk data) 64 bytes long. Then follow B and something + * else, also interleaved (this makes another 128*2 bytes). After that + * probably another 64 bytes of junk follow. + * + * History: + * 10-Feb-2001 Created. + */ +static enum ParseState ibmcam_model4_128x96_parse_lines( + struct uvd *uvd, + struct usbvideo_frame *frame, + long *pcopylen) +{ + const unsigned char *data_rv, *data_gv, *data_bv; + unsigned int len; + int i, v4l_linesize; /* V4L line offset */ + const int data_w=128, data_h=96; + static unsigned char lineBuffer[128*5]; + + v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; + + /* + * Make sure that our writing into output buffer + * will not exceed the buffer. Note that we may write + * not into current output scanline but in several after + * it as well (if we enlarge image vertically.) + */ + if ((frame->curline + 1) >= data_h) { + if (uvd->debug >= 3) + info("Reached line %d. (frame is done)", frame->curline); + return scan_NextFrame; + } + + /* + * RGRGRG .... RGRG_____________B?B?B? ... B?B?____________ + * <---- 128*2 ---><---- 64 ---><--- 128*2 ---><--- 64 ---> + */ + + /* Make sure there's enough data for the entire line */ + len = 5 * data_w; + assert(len <= sizeof(lineBuffer)); + + /* Make sure there's enough data for the entire line */ + if (RingQueue_GetLength(&uvd->dp) < len) + return scan_Out; + + /* Suck one line out of the ring queue */ + RingQueue_Dequeue(&uvd->dp, lineBuffer, len); + + data_rv = lineBuffer; + data_gv = lineBuffer + 1; + data_bv = lineBuffer + data_w*2 + data_w/2; + for (i = 0; i < VIDEOSIZE_X(frame->request); i++) { + int rv, gv, bv; /* RGB components */ + if (i < data_w) { + const int j = i * 2; + gv = data_rv[j]; + rv = data_gv[j]; + bv = data_bv[j]; + if (flags & FLAGS_MONOCHROME) { + unsigned long y; + y = rv + gv + bv; + y /= 3; + if (y > 0xFF) + y = 0xFF; + rv = gv = bv = (unsigned char) y; + } + } else { + rv = gv = bv = 0; + } + RGB24_PUTPIXEL(frame, i, frame->curline, rv, gv, bv); + } + frame->deinterlace = Deinterlace_None; + frame->curline++; + *pcopylen += v4l_linesize; + + if (frame->curline >= VIDEOSIZE_Y(frame->request)) { + if (uvd->debug >= 3) { + info("All requested lines (%ld.) done.", + VIDEOSIZE_Y(frame->request)); + } + return scan_NextFrame; + } else + return scan_Continue; +} + +/* + * ibmcam_ProcessIsocData() + * + * Generic routine to parse the ring queue data. It employs either + * ibmcam_find_header() or ibmcam_parse_lines() to do most + * of work. + * + * History: + * 1/21/00 Created. + */ +static void ibmcam_ProcessIsocData(struct uvd *uvd, + struct usbvideo_frame *frame) +{ + enum ParseState newstate; + long copylen = 0; + int mod = IBMCAM_T(uvd)->camera_model; + + while (1) { + newstate = scan_Out; + if (RingQueue_GetLength(&uvd->dp) > 0) { + if (frame->scanstate == ScanState_Scanning) { + newstate = ibmcam_find_header(uvd); + } else if (frame->scanstate == ScanState_Lines) { + if ((mod == IBMCAM_MODEL_2) && + ((uvd->videosize == VIDEOSIZE_352x288) || + (uvd->videosize == VIDEOSIZE_320x240) || + (uvd->videosize == VIDEOSIZE_352x240))) + { + newstate = ibmcam_model2_320x240_parse_lines( + uvd, frame, ©len); + } else if (mod == IBMCAM_MODEL_4) { + /* + * Model 4 cameras (IBM NetCamera) use Model 2 decoder (RGB) + * for 320x240 and above; 160x120 and 176x144 uses Model 1 + * decoder (YUV), and 128x96 mode uses ??? + */ + if ((uvd->videosize == VIDEOSIZE_352x288) || + (uvd->videosize == VIDEOSIZE_320x240) || + (uvd->videosize == VIDEOSIZE_352x240)) + { + newstate = ibmcam_model2_320x240_parse_lines(uvd, frame, ©len); + } else if (uvd->videosize == VIDEOSIZE_128x96) { + newstate = ibmcam_model4_128x96_parse_lines(uvd, frame, ©len); + } else { + newstate = ibmcam_parse_lines(uvd, frame, ©len); + } + } else if (mod == IBMCAM_MODEL_3) { + newstate = ibmcam_model3_parse_lines(uvd, frame, ©len); + } else { + newstate = ibmcam_parse_lines(uvd, frame, ©len); + } + } + } + if (newstate == scan_Continue) + continue; + else if ((newstate == scan_NextFrame) || (newstate == scan_Out)) + break; + else + return; /* scan_EndParse */ + } + + if (newstate == scan_NextFrame) { + frame->frameState = FrameState_Done; + uvd->curframe = -1; + uvd->stats.frame_num++; + if ((mod == IBMCAM_MODEL_2) || (mod == IBMCAM_MODEL_4)) { + /* Need software contrast adjustment for those cameras */ + frame->flags |= USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST; + } + } + + /* Update the frame's uncompressed length. */ + frame->seqRead_Length += copylen; + +#if 0 + { + static unsigned char j=0; + memset(frame->data, j++, uvd->max_frame_size); + frame->frameState = FrameState_Ready; + } +#endif +} + +/* + * ibmcam_veio() + * + * History: + * 1/27/00 Added check for dev == NULL; this happens if camera is unplugged. + */ +static int ibmcam_veio( + struct uvd *uvd, + unsigned char req, + unsigned short value, + unsigned short index) +{ + static const char proc[] = "ibmcam_veio"; + unsigned char cp[8] /* = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } */; + int i; + + if (!CAMERA_IS_OPERATIONAL(uvd)) + return 0; + + if (req == 1) { + i = usb_control_msg( + uvd->dev, + usb_rcvctrlpipe(uvd->dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + value, + index, + cp, + sizeof(cp), + 1000); +#if 0 + info("USB => %02x%02x%02x%02x%02x%02x%02x%02x " + "(req=$%02x val=$%04x ind=$%04x)", + cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7], + req, value, index); +#endif + } else { + i = usb_control_msg( + uvd->dev, + usb_sndctrlpipe(uvd->dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + value, + index, + NULL, + 0, + 1000); + } + if (i < 0) { + err("%s: ERROR=%d. Camera stopped; Reconnect or reload driver.", + proc, i); + uvd->last_error = i; + } + return i; +} + +/* + * ibmcam_calculate_fps() + * + * This procedure roughly calculates the real frame rate based + * on FPS code (framerate=NNN option). Actual FPS differs + * slightly depending on lighting conditions, so that actual frame + * rate is determined by the camera. Since I don't know how to ask + * the camera what FPS is now I have to use the FPS code instead. + * + * The FPS code is in range [0..6], 0 is slowest, 6 is fastest. + * Corresponding real FPS should be in range [3..30] frames per second. + * The conversion formula is obvious: + * + * real_fps = 3 + (fps_code * 4.5) + * + * History: + * 1/18/00 Created. + */ +static int ibmcam_calculate_fps(struct uvd *uvd) +{ + return 3 + framerate*4 + framerate/2; +} + +/* + * ibmcam_send_FF_04_02() + * + * This procedure sends magic 3-command prefix to the camera. + * The purpose of this prefix is not known. + * + * History: + * 1/2/00 Created. + */ +static void ibmcam_send_FF_04_02(struct uvd *uvd) +{ + ibmcam_veio(uvd, 0, 0x00FF, 0x0127); + ibmcam_veio(uvd, 0, 0x0004, 0x0124); + ibmcam_veio(uvd, 0, 0x0002, 0x0124); +} + +static void ibmcam_send_00_04_06(struct uvd *uvd) +{ + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x0004, 0x0124); + ibmcam_veio(uvd, 0, 0x0006, 0x0124); +} + +static void ibmcam_send_x_00(struct uvd *uvd, unsigned short x) +{ + ibmcam_veio(uvd, 0, x, 0x0127); + ibmcam_veio(uvd, 0, 0x0000, 0x0124); +} + +static void ibmcam_send_x_00_05(struct uvd *uvd, unsigned short x) +{ + ibmcam_send_x_00(uvd, x); + ibmcam_veio(uvd, 0, 0x0005, 0x0124); +} + +static void ibmcam_send_x_00_05_02(struct uvd *uvd, unsigned short x) +{ + ibmcam_veio(uvd, 0, x, 0x0127); + ibmcam_veio(uvd, 0, 0x0000, 0x0124); + ibmcam_veio(uvd, 0, 0x0005, 0x0124); + ibmcam_veio(uvd, 0, 0x0002, 0x0124); +} + +static void ibmcam_send_x_01_00_05(struct uvd *uvd, unsigned short x) +{ + ibmcam_veio(uvd, 0, x, 0x0127); + ibmcam_veio(uvd, 0, 0x0001, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0124); + ibmcam_veio(uvd, 0, 0x0005, 0x0124); +} + +static void ibmcam_send_x_00_05_02_01(struct uvd *uvd, unsigned short x) +{ + ibmcam_veio(uvd, 0, x, 0x0127); + ibmcam_veio(uvd, 0, 0x0000, 0x0124); + ibmcam_veio(uvd, 0, 0x0005, 0x0124); + ibmcam_veio(uvd, 0, 0x0002, 0x0124); + ibmcam_veio(uvd, 0, 0x0001, 0x0124); +} + +static void ibmcam_send_x_00_05_02_08_01(struct uvd *uvd, unsigned short x) +{ + ibmcam_veio(uvd, 0, x, 0x0127); + ibmcam_veio(uvd, 0, 0x0000, 0x0124); + ibmcam_veio(uvd, 0, 0x0005, 0x0124); + ibmcam_veio(uvd, 0, 0x0002, 0x0124); + ibmcam_veio(uvd, 0, 0x0008, 0x0124); + ibmcam_veio(uvd, 0, 0x0001, 0x0124); +} + +static void ibmcam_Packet_Format1(struct uvd *uvd, unsigned char fkey, unsigned char val) +{ + ibmcam_send_x_01_00_05(uvd, unknown_88); + ibmcam_send_x_00_05(uvd, fkey); + ibmcam_send_x_00_05_02_08_01(uvd, val); + ibmcam_send_x_00_05(uvd, unknown_88); + ibmcam_send_x_00_05_02_01(uvd, fkey); + ibmcam_send_x_00_05(uvd, unknown_89); + ibmcam_send_x_00(uvd, fkey); + ibmcam_send_00_04_06(uvd); + ibmcam_veio(uvd, 1, 0x0000, 0x0126); + ibmcam_send_FF_04_02(uvd); +} + +static void ibmcam_PacketFormat2(struct uvd *uvd, unsigned char fkey, unsigned char val) +{ + ibmcam_send_x_01_00_05 (uvd, unknown_88); + ibmcam_send_x_00_05 (uvd, fkey); + ibmcam_send_x_00_05_02 (uvd, val); +} + +static void ibmcam_model2_Packet2(struct uvd *uvd) +{ + ibmcam_veio(uvd, 0, 0x00ff, 0x012d); + ibmcam_veio(uvd, 0, 0xfea3, 0x0124); +} + +static void ibmcam_model2_Packet1(struct uvd *uvd, unsigned short v1, unsigned short v2) +{ + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x00ff, 0x012e); + ibmcam_veio(uvd, 0, v1, 0x012f); + ibmcam_veio(uvd, 0, 0x00ff, 0x0130); + ibmcam_veio(uvd, 0, 0xc719, 0x0124); + ibmcam_veio(uvd, 0, v2, 0x0127); + + ibmcam_model2_Packet2(uvd); +} + +/* + * ibmcam_model3_Packet1() + * + * 00_0078_012d + * 00_0097_012f + * 00_d141_0124 + * 00_0096_0127 + * 00_fea8_0124 +*/ +static void ibmcam_model3_Packet1(struct uvd *uvd, unsigned short v1, unsigned short v2) +{ + ibmcam_veio(uvd, 0, 0x0078, 0x012d); + ibmcam_veio(uvd, 0, v1, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, v2, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); +} + +static void ibmcam_model4_BrightnessPacket(struct uvd *uvd, int i) +{ + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0026, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, i, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0038, 0x012d); + ibmcam_veio(uvd, 0, 0x0004, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); +} + +/* + * ibmcam_adjust_contrast() + * + * The contrast value changes from 0 (high contrast) to 15 (low contrast). + * This is in reverse to usual order of things (such as TV controls), so + * we reverse it again here. + * + * TODO: we probably don't need to send the setup 5 times... + * + * History: + * 1/2/00 Created. + */ +static void ibmcam_adjust_contrast(struct uvd *uvd) +{ + unsigned char a_contrast = uvd->vpic.contrast >> 12; + unsigned char new_contrast; + + if (a_contrast >= 16) + a_contrast = 15; + new_contrast = 15 - a_contrast; + if (new_contrast == uvd->vpic_old.contrast) + return; + uvd->vpic_old.contrast = new_contrast; + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + { + const int ntries = 5; + int i; + for (i=0; i < ntries; i++) { + ibmcam_Packet_Format1(uvd, contrast_14, new_contrast); + ibmcam_send_FF_04_02(uvd); + } + break; + } + case IBMCAM_MODEL_2: + case IBMCAM_MODEL_4: + /* Models 2, 4 do not have this control; implemented in software. */ + break; + case IBMCAM_MODEL_3: + { /* Preset hardware values */ + static const struct { + unsigned short cv1; + unsigned short cv2; + unsigned short cv3; + } cv[7] = { + { 0x05, 0x05, 0x0f }, /* Minimum */ + { 0x04, 0x04, 0x16 }, + { 0x02, 0x03, 0x16 }, + { 0x02, 0x08, 0x16 }, + { 0x01, 0x0c, 0x16 }, + { 0x01, 0x0e, 0x16 }, + { 0x01, 0x10, 0x16 } /* Maximum */ + }; + int i = a_contrast / 2; + RESTRICT_TO_RANGE(i, 0, 6); + ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop */ + ibmcam_model3_Packet1(uvd, 0x0067, cv[i].cv1); + ibmcam_model3_Packet1(uvd, 0x005b, cv[i].cv2); + ibmcam_model3_Packet1(uvd, 0x005c, cv[i].cv3); + ibmcam_veio(uvd, 0, 0x0001, 0x0114); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go! */ + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); + break; + } + default: + break; + } +} + +/* + * ibmcam_change_lighting_conditions() + * + * Camera model 1: + * We have 3 levels of lighting conditions: 0=Bright, 1=Medium, 2=Low. + * + * Camera model 2: + * We have 16 levels of lighting, 0 for bright light and up to 15 for + * low light. But values above 5 or so are useless because camera is + * not really capable to produce anything worth viewing at such light. + * This setting may be altered only in certain camera state. + * + * Low lighting forces slower FPS. Lighting is set as a module parameter. + * + * History: + * 1/5/00 Created. + * 2/20/00 Added support for Model 2 cameras. + */ +static void ibmcam_change_lighting_conditions(struct uvd *uvd) +{ + static const char proc[] = "ibmcam_change_lighting_conditions"; + + if (debug > 0) + info("%s: Set lighting to %hu.", proc, lighting); + + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + { + const int ntries = 5; + int i; + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, light_27, (unsigned short) lighting); + break; + } + case IBMCAM_MODEL_2: +#if 0 + /* + * This command apparently requires camera to be stopped. My + * experiments showed that it -is- possible to alter the lighting + * conditions setting "on the fly", but why bother? This setting does + * not work reliably in all cases, so I decided simply to leave the + * setting where Xirlink put it - in the camera setup phase. This code + * is commented out because it does not work at -any- moment, so its + * presence makes no sense. You may use it for experiments. + */ + ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop camera */ + ibmcam_model2_Packet1(uvd, mod2_sensitivity, lighting); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Start camera */ +#endif + break; + case IBMCAM_MODEL_3: + case IBMCAM_MODEL_4: + default: + break; + } +} + +/* + * ibmcam_set_sharpness() + * + * Cameras model 1 have internal smoothing feature. It is controlled by value in + * range [0..6], where 0 is most smooth and 6 is most sharp (raw image, I guess). + * Recommended value is 4. Cameras model 2 do not have this feature at all. + */ +static void ibmcam_set_sharpness(struct uvd *uvd) +{ + static const char proc[] = "ibmcam_set_sharpness"; + + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + { + static const unsigned short sa[] = { 0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a }; + unsigned short i, sv; + + RESTRICT_TO_RANGE(sharpness, SHARPNESS_MIN, SHARPNESS_MAX); + if (debug > 0) + info("%s: Set sharpness to %hu.", proc, sharpness); + + sv = sa[sharpness - SHARPNESS_MIN]; + for (i=0; i < 2; i++) { + ibmcam_send_x_01_00_05 (uvd, unknown_88); + ibmcam_send_x_00_05 (uvd, sharp_13); + ibmcam_send_x_00_05_02 (uvd, sv); + } + break; + } + case IBMCAM_MODEL_2: + case IBMCAM_MODEL_4: + /* Models 2, 4 do not have this control */ + break; + case IBMCAM_MODEL_3: + { /* + * "Use a table of magic numbers. + * This setting doesn't really change much. + * But that's how Windows does it." + */ + static const struct { + unsigned short sv1; + unsigned short sv2; + unsigned short sv3; + unsigned short sv4; + } sv[7] = { + { 0x00, 0x00, 0x05, 0x14 }, /* Smoothest */ + { 0x01, 0x04, 0x05, 0x14 }, + { 0x02, 0x04, 0x05, 0x14 }, + { 0x03, 0x04, 0x05, 0x14 }, + { 0x03, 0x05, 0x05, 0x14 }, + { 0x03, 0x06, 0x05, 0x14 }, + { 0x03, 0x07, 0x05, 0x14 } /* Sharpest */ + }; + RESTRICT_TO_RANGE(sharpness, SHARPNESS_MIN, SHARPNESS_MAX); + RESTRICT_TO_RANGE(sharpness, 0, 6); + ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop */ + ibmcam_model3_Packet1(uvd, 0x0060, sv[sharpness].sv1); + ibmcam_model3_Packet1(uvd, 0x0061, sv[sharpness].sv2); + ibmcam_model3_Packet1(uvd, 0x0062, sv[sharpness].sv3); + ibmcam_model3_Packet1(uvd, 0x0063, sv[sharpness].sv4); + ibmcam_veio(uvd, 0, 0x0001, 0x0114); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go! */ + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); + ibmcam_veio(uvd, 0, 0x0001, 0x0113); + break; + } + default: + break; + } +} + +/* + * ibmcam_set_brightness() + * + * This procedure changes brightness of the picture. + */ +static void ibmcam_set_brightness(struct uvd *uvd) +{ + static const char proc[] = "ibmcam_set_brightness"; + static const unsigned short n = 1; + + if (debug > 0) + info("%s: Set brightness to %hu.", proc, uvd->vpic.brightness); + + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + { + unsigned short i, j, bv[3]; + bv[0] = bv[1] = bv[2] = uvd->vpic.brightness >> 10; + if (bv[0] == (uvd->vpic_old.brightness >> 10)) + return; + uvd->vpic_old.brightness = bv[0]; + for (j=0; j < 3; j++) + for (i=0; i < n; i++) + ibmcam_Packet_Format1(uvd, bright_3x[j], bv[j]); + break; + } + case IBMCAM_MODEL_2: + { + unsigned short i, j; + i = uvd->vpic.brightness >> 12; /* 0 .. 15 */ + j = 0x60 + i * ((0xee - 0x60) / 16); /* 0x60 .. 0xee or so */ + if (uvd->vpic_old.brightness == j) + break; + uvd->vpic_old.brightness = j; + ibmcam_model2_Packet1(uvd, mod2_brightness, j); + break; + } + case IBMCAM_MODEL_3: + { + /* Model 3: Brightness range 'i' in [0x0C..0x3F] */ + unsigned short i = + 0x0C + (uvd->vpic.brightness / (0xFFFF / (0x3F - 0x0C + 1))); + RESTRICT_TO_RANGE(i, 0x0C, 0x3F); + if (uvd->vpic_old.brightness == i) + break; + uvd->vpic_old.brightness = i; + ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop */ + ibmcam_model3_Packet1(uvd, 0x0036, i); + ibmcam_veio(uvd, 0, 0x0001, 0x0114); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go! */ + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); + ibmcam_veio(uvd, 0, 0x0001, 0x0113); + break; + } + case IBMCAM_MODEL_4: + { + /* Model 4: Brightness range 'i' in [0x04..0xb4] */ + unsigned short i = 0x04 + (uvd->vpic.brightness / (0xFFFF / (0xb4 - 0x04 + 1))); + RESTRICT_TO_RANGE(i, 0x04, 0xb4); + if (uvd->vpic_old.brightness == i) + break; + uvd->vpic_old.brightness = i; + ibmcam_model4_BrightnessPacket(uvd, i); + break; + } + default: + break; + } +} + +static void ibmcam_set_hue(struct uvd *uvd) +{ + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_2: + { + unsigned short hue = uvd->vpic.hue >> 9; /* 0 .. 7F */ + if (uvd->vpic_old.hue == hue) + return; + uvd->vpic_old.hue = hue; + ibmcam_model2_Packet1(uvd, mod2_hue, hue); + /* ibmcam_model2_Packet1(uvd, mod2_saturation, sat); */ + break; + } + case IBMCAM_MODEL_3: + { +#if 0 /* This seems not to work. No problem, will fix programmatically */ + unsigned short hue = 0x05 + (uvd->vpic.hue / (0xFFFF / (0x37 - 0x05 + 1))); + RESTRICT_TO_RANGE(hue, 0x05, 0x37); + if (uvd->vpic_old.hue == hue) + return; + uvd->vpic_old.hue = hue; + ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop */ + ibmcam_model3_Packet1(uvd, 0x007e, hue); + ibmcam_veio(uvd, 0, 0x0001, 0x0114); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go! */ + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); + ibmcam_veio(uvd, 0, 0x0001, 0x0113); +#endif + break; + } + case IBMCAM_MODEL_4: + { + unsigned short r_gain, g_gain, b_gain, hue; + + /* + * I am not sure r/g/b_gain variables exactly control gain + * of those channels. Most likely they subtly change some + * very internal image processing settings in the camera. + * In any case, here is what they do, and feel free to tweak: + * + * r_gain: seriously affects red gain + * g_gain: seriously affects green gain + * b_gain: seriously affects blue gain + * hue: changes average color from violet (0) to red (0xFF) + * + * These settings are preset for a decent white balance in + * 320x240, 352x288 modes. Low-res modes exhibit higher contrast + * and therefore may need different values here. + */ + hue = 20 + (uvd->vpic.hue >> 9); + switch (uvd->videosize) { + case VIDEOSIZE_128x96: + r_gain = 90; + g_gain = 166; + b_gain = 175; + break; + case VIDEOSIZE_160x120: + r_gain = 70; + g_gain = 166; + b_gain = 185; + break; + case VIDEOSIZE_176x144: + r_gain = 160; + g_gain = 175; + b_gain = 185; + break; + default: + r_gain = 120; + g_gain = 166; + b_gain = 175; + break; + } + RESTRICT_TO_RANGE(hue, 1, 0x7f); + + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x001e, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, g_gain, 0x0127); /* Green gain */ + ibmcam_veio(uvd, 0, r_gain, 0x012e); /* Red gain */ + ibmcam_veio(uvd, 0, b_gain, 0x0130); /* Blue gain */ + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, hue, 0x012d); /* Hue */ + ibmcam_veio(uvd, 0, 0xf545, 0x0124); + break; + } + default: + break; + } +} + +/* + * ibmcam_adjust_picture() + * + * This procedure gets called from V4L interface to update picture settings. + * Here we change brightness and contrast. + */ +static void ibmcam_adjust_picture(struct uvd *uvd) +{ + ibmcam_adjust_contrast(uvd); + ibmcam_set_brightness(uvd); + ibmcam_set_hue(uvd); +} + +static int ibmcam_model1_setup(struct uvd *uvd) +{ + const int ntries = 5; + int i; + + ibmcam_veio(uvd, 1, 0x00, 0x0128); + ibmcam_veio(uvd, 1, 0x00, 0x0100); + ibmcam_veio(uvd, 0, 0x01, 0x0100); /* LED On */ + ibmcam_veio(uvd, 1, 0x00, 0x0100); + ibmcam_veio(uvd, 0, 0x81, 0x0100); /* LED Off */ + ibmcam_veio(uvd, 1, 0x00, 0x0100); + ibmcam_veio(uvd, 0, 0x01, 0x0100); /* LED On */ + ibmcam_veio(uvd, 0, 0x01, 0x0108); + + ibmcam_veio(uvd, 0, 0x03, 0x0112); + ibmcam_veio(uvd, 1, 0x00, 0x0115); + ibmcam_veio(uvd, 0, 0x06, 0x0115); + ibmcam_veio(uvd, 1, 0x00, 0x0116); + ibmcam_veio(uvd, 0, 0x44, 0x0116); + ibmcam_veio(uvd, 1, 0x00, 0x0116); + ibmcam_veio(uvd, 0, 0x40, 0x0116); + ibmcam_veio(uvd, 1, 0x00, 0x0115); + ibmcam_veio(uvd, 0, 0x0e, 0x0115); + ibmcam_veio(uvd, 0, 0x19, 0x012c); + + ibmcam_Packet_Format1(uvd, 0x00, 0x1e); + ibmcam_Packet_Format1(uvd, 0x39, 0x0d); + ibmcam_Packet_Format1(uvd, 0x39, 0x09); + ibmcam_Packet_Format1(uvd, 0x3b, 0x00); + ibmcam_Packet_Format1(uvd, 0x28, 0x22); + ibmcam_Packet_Format1(uvd, light_27, 0); + ibmcam_Packet_Format1(uvd, 0x2b, 0x1f); + ibmcam_Packet_Format1(uvd, 0x39, 0x08); + + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x2c, 0x00); + + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x30, 0x14); + + ibmcam_PacketFormat2(uvd, 0x39, 0x02); + ibmcam_PacketFormat2(uvd, 0x01, 0xe1); + ibmcam_PacketFormat2(uvd, 0x02, 0xcd); + ibmcam_PacketFormat2(uvd, 0x03, 0xcd); + ibmcam_PacketFormat2(uvd, 0x04, 0xfa); + ibmcam_PacketFormat2(uvd, 0x3f, 0xff); + ibmcam_PacketFormat2(uvd, 0x39, 0x00); + + ibmcam_PacketFormat2(uvd, 0x39, 0x02); + ibmcam_PacketFormat2(uvd, 0x0a, 0x37); + ibmcam_PacketFormat2(uvd, 0x0b, 0xb8); + ibmcam_PacketFormat2(uvd, 0x0c, 0xf3); + ibmcam_PacketFormat2(uvd, 0x0d, 0xe3); + ibmcam_PacketFormat2(uvd, 0x0e, 0x0d); + ibmcam_PacketFormat2(uvd, 0x0f, 0xf2); + ibmcam_PacketFormat2(uvd, 0x10, 0xd5); + ibmcam_PacketFormat2(uvd, 0x11, 0xba); + ibmcam_PacketFormat2(uvd, 0x12, 0x53); + ibmcam_PacketFormat2(uvd, 0x3f, 0xff); + ibmcam_PacketFormat2(uvd, 0x39, 0x00); + + ibmcam_PacketFormat2(uvd, 0x39, 0x02); + ibmcam_PacketFormat2(uvd, 0x16, 0x00); + ibmcam_PacketFormat2(uvd, 0x17, 0x28); + ibmcam_PacketFormat2(uvd, 0x18, 0x7d); + ibmcam_PacketFormat2(uvd, 0x19, 0xbe); + ibmcam_PacketFormat2(uvd, 0x3f, 0xff); + ibmcam_PacketFormat2(uvd, 0x39, 0x00); + + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x00, 0x18); + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x13, 0x18); + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x14, 0x06); + + /* This is default brightness */ + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x31, 0x37); + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x32, 0x46); + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x33, 0x55); + + ibmcam_Packet_Format1(uvd, 0x2e, 0x04); + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x2d, 0x04); + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x29, 0x80); + ibmcam_Packet_Format1(uvd, 0x2c, 0x01); + ibmcam_Packet_Format1(uvd, 0x30, 0x17); + ibmcam_Packet_Format1(uvd, 0x39, 0x08); + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x34, 0x00); + + ibmcam_veio(uvd, 0, 0x00, 0x0101); + ibmcam_veio(uvd, 0, 0x00, 0x010a); + + switch (uvd->videosize) { + case VIDEOSIZE_128x96: + ibmcam_veio(uvd, 0, 0x80, 0x0103); + ibmcam_veio(uvd, 0, 0x60, 0x0105); + ibmcam_veio(uvd, 0, 0x0c, 0x010b); + ibmcam_veio(uvd, 0, 0x04, 0x011b); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x0b, 0x011d); + ibmcam_veio(uvd, 0, 0x00, 0x011e); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x00, 0x0129); + break; + case VIDEOSIZE_176x144: + ibmcam_veio(uvd, 0, 0xb0, 0x0103); + ibmcam_veio(uvd, 0, 0x8f, 0x0105); + ibmcam_veio(uvd, 0, 0x06, 0x010b); + ibmcam_veio(uvd, 0, 0x04, 0x011b); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x0d, 0x011d); + ibmcam_veio(uvd, 0, 0x00, 0x011e); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x03, 0x0129); + break; + case VIDEOSIZE_352x288: + ibmcam_veio(uvd, 0, 0xb0, 0x0103); + ibmcam_veio(uvd, 0, 0x90, 0x0105); + ibmcam_veio(uvd, 0, 0x02, 0x010b); + ibmcam_veio(uvd, 0, 0x04, 0x011b); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x05, 0x011d); + ibmcam_veio(uvd, 0, 0x00, 0x011e); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x00, 0x0129); + break; + } + + ibmcam_veio(uvd, 0, 0xff, 0x012b); + + /* This is another brightness - don't know why */ + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x31, 0xc3); + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x32, 0xd2); + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, 0x33, 0xe1); + + /* Default contrast */ + for (i=0; i < ntries; i++) + ibmcam_Packet_Format1(uvd, contrast_14, 0x0a); + + /* Default sharpness */ + for (i=0; i < 2; i++) + ibmcam_PacketFormat2(uvd, sharp_13, 0x1a); /* Level 4 FIXME */ + + /* Default lighting conditions */ + ibmcam_Packet_Format1(uvd, light_27, lighting); /* 0=Bright 2=Low */ + + /* Assorted init */ + + switch (uvd->videosize) { + case VIDEOSIZE_128x96: + ibmcam_Packet_Format1(uvd, 0x2b, 0x1e); + ibmcam_veio(uvd, 0, 0xc9, 0x0119); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x80, 0x0109); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x36, 0x0102); + ibmcam_veio(uvd, 0, 0x1a, 0x0104); + ibmcam_veio(uvd, 0, 0x04, 0x011a); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x2b, 0x011c); + ibmcam_veio(uvd, 0, 0x23, 0x012a); /* Same everywhere */ +#if 0 + ibmcam_veio(uvd, 0, 0x00, 0x0106); + ibmcam_veio(uvd, 0, 0x38, 0x0107); +#else + ibmcam_veio(uvd, 0, 0x02, 0x0106); + ibmcam_veio(uvd, 0, 0x2a, 0x0107); +#endif + break; + case VIDEOSIZE_176x144: + ibmcam_Packet_Format1(uvd, 0x2b, 0x1e); + ibmcam_veio(uvd, 0, 0xc9, 0x0119); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x80, 0x0109); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x04, 0x0102); + ibmcam_veio(uvd, 0, 0x02, 0x0104); + ibmcam_veio(uvd, 0, 0x04, 0x011a); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x2b, 0x011c); + ibmcam_veio(uvd, 0, 0x23, 0x012a); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x01, 0x0106); + ibmcam_veio(uvd, 0, 0xca, 0x0107); + break; + case VIDEOSIZE_352x288: + ibmcam_Packet_Format1(uvd, 0x2b, 0x1f); + ibmcam_veio(uvd, 0, 0xc9, 0x0119); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x80, 0x0109); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x08, 0x0102); + ibmcam_veio(uvd, 0, 0x01, 0x0104); + ibmcam_veio(uvd, 0, 0x04, 0x011a); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x2f, 0x011c); + ibmcam_veio(uvd, 0, 0x23, 0x012a); /* Same everywhere */ + ibmcam_veio(uvd, 0, 0x03, 0x0106); + ibmcam_veio(uvd, 0, 0xf6, 0x0107); + break; + } + return (CAMERA_IS_OPERATIONAL(uvd) ? 0 : -EFAULT); +} + +static int ibmcam_model2_setup(struct uvd *uvd) +{ + ibmcam_veio(uvd, 0, 0x0000, 0x0100); /* LED on */ + ibmcam_veio(uvd, 1, 0x0000, 0x0116); + ibmcam_veio(uvd, 0, 0x0060, 0x0116); + ibmcam_veio(uvd, 0, 0x0002, 0x0112); + ibmcam_veio(uvd, 0, 0x00bc, 0x012c); + ibmcam_veio(uvd, 0, 0x0008, 0x012b); + ibmcam_veio(uvd, 0, 0x0000, 0x0108); + ibmcam_veio(uvd, 0, 0x0001, 0x0133); + ibmcam_veio(uvd, 0, 0x0001, 0x0102); + switch (uvd->videosize) { + case VIDEOSIZE_176x144: + ibmcam_veio(uvd, 0, 0x002c, 0x0103); /* All except 320x240 */ + ibmcam_veio(uvd, 0, 0x0000, 0x0104); /* Same */ + ibmcam_veio(uvd, 0, 0x0024, 0x0105); /* 176x144, 352x288 */ + ibmcam_veio(uvd, 0, 0x00b9, 0x010a); /* Unique to this mode */ + ibmcam_veio(uvd, 0, 0x0038, 0x0119); /* Unique to this mode */ + ibmcam_veio(uvd, 0, 0x0003, 0x0106); /* Same */ + ibmcam_veio(uvd, 0, 0x0090, 0x0107); /* Unique to every mode*/ + break; + case VIDEOSIZE_320x240: + ibmcam_veio(uvd, 0, 0x0028, 0x0103); /* Unique to this mode */ + ibmcam_veio(uvd, 0, 0x0000, 0x0104); /* Same */ + ibmcam_veio(uvd, 0, 0x001e, 0x0105); /* 320x240, 352x240 */ + ibmcam_veio(uvd, 0, 0x0039, 0x010a); /* All except 176x144 */ + ibmcam_veio(uvd, 0, 0x0070, 0x0119); /* All except 176x144 */ + ibmcam_veio(uvd, 0, 0x0003, 0x0106); /* Same */ + ibmcam_veio(uvd, 0, 0x0098, 0x0107); /* Unique to every mode*/ + break; + case VIDEOSIZE_352x240: + ibmcam_veio(uvd, 0, 0x002c, 0x0103); /* All except 320x240 */ + ibmcam_veio(uvd, 0, 0x0000, 0x0104); /* Same */ + ibmcam_veio(uvd, 0, 0x001e, 0x0105); /* 320x240, 352x240 */ + ibmcam_veio(uvd, 0, 0x0039, 0x010a); /* All except 176x144 */ + ibmcam_veio(uvd, 0, 0x0070, 0x0119); /* All except 176x144 */ + ibmcam_veio(uvd, 0, 0x0003, 0x0106); /* Same */ + ibmcam_veio(uvd, 0, 0x00da, 0x0107); /* Unique to every mode*/ + break; + case VIDEOSIZE_352x288: + ibmcam_veio(uvd, 0, 0x002c, 0x0103); /* All except 320x240 */ + ibmcam_veio(uvd, 0, 0x0000, 0x0104); /* Same */ + ibmcam_veio(uvd, 0, 0x0024, 0x0105); /* 176x144, 352x288 */ + ibmcam_veio(uvd, 0, 0x0039, 0x010a); /* All except 176x144 */ + ibmcam_veio(uvd, 0, 0x0070, 0x0119); /* All except 176x144 */ + ibmcam_veio(uvd, 0, 0x0003, 0x0106); /* Same */ + ibmcam_veio(uvd, 0, 0x00fe, 0x0107); /* Unique to every mode*/ + break; + } + return (CAMERA_IS_OPERATIONAL(uvd) ? 0 : -EFAULT); +} + +/* + * ibmcam_model1_setup_after_video_if() + * + * This code adds finishing touches to the video data interface. + * Here we configure the frame rate and turn on the LED. + */ +static void ibmcam_model1_setup_after_video_if(struct uvd *uvd) +{ + unsigned short internal_frame_rate; + + RESTRICT_TO_RANGE(framerate, FRAMERATE_MIN, FRAMERATE_MAX); + internal_frame_rate = FRAMERATE_MAX - framerate; /* 0=Fast 6=Slow */ + ibmcam_veio(uvd, 0, 0x01, 0x0100); /* LED On */ + ibmcam_veio(uvd, 0, internal_frame_rate, 0x0111); + ibmcam_veio(uvd, 0, 0x01, 0x0114); + ibmcam_veio(uvd, 0, 0xc0, 0x010c); +} + +static void ibmcam_model2_setup_after_video_if(struct uvd *uvd) +{ + unsigned short setup_model2_rg2, setup_model2_sat, setup_model2_yb; + + ibmcam_veio(uvd, 0, 0x0000, 0x0100); /* LED on */ + + switch (uvd->videosize) { + case VIDEOSIZE_176x144: + ibmcam_veio(uvd, 0, 0x0050, 0x0111); + ibmcam_veio(uvd, 0, 0x00d0, 0x0111); + break; + case VIDEOSIZE_320x240: + case VIDEOSIZE_352x240: + case VIDEOSIZE_352x288: + ibmcam_veio(uvd, 0, 0x0040, 0x0111); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + break; + } + ibmcam_veio(uvd, 0, 0x009b, 0x010f); + ibmcam_veio(uvd, 0, 0x00bb, 0x010f); + + /* + * Hardware settings, may affect CMOS sensor; not user controls! + * ------------------------------------------------------------- + * 0x0004: no effect + * 0x0006: hardware effect + * 0x0008: no effect + * 0x000a: stops video stream, probably important h/w setting + * 0x000c: changes color in hardware manner (not user setting) + * 0x0012: changes number of colors (does not affect speed) + * 0x002a: no effect + * 0x002c: hardware setting (related to scan lines) + * 0x002e: stops video stream, probably important h/w setting + */ + ibmcam_model2_Packet1(uvd, 0x000a, 0x005c); + ibmcam_model2_Packet1(uvd, 0x0004, 0x0000); + ibmcam_model2_Packet1(uvd, 0x0006, 0x00fb); + ibmcam_model2_Packet1(uvd, 0x0008, 0x0000); + ibmcam_model2_Packet1(uvd, 0x000c, 0x0009); + ibmcam_model2_Packet1(uvd, 0x0012, 0x000a); + ibmcam_model2_Packet1(uvd, 0x002a, 0x0000); + ibmcam_model2_Packet1(uvd, 0x002c, 0x0000); + ibmcam_model2_Packet1(uvd, 0x002e, 0x0008); + + /* + * Function 0x0030 pops up all over the place. Apparently + * it is a hardware control register, with every bit assigned to + * do something. + */ + ibmcam_model2_Packet1(uvd, 0x0030, 0x0000); + + /* + * Magic control of CMOS sensor. Only lower values like + * 0-3 work, and picture shifts left or right. Don't change. + */ + switch (uvd->videosize) { + case VIDEOSIZE_176x144: + ibmcam_model2_Packet1(uvd, 0x0014, 0x0002); + ibmcam_model2_Packet1(uvd, 0x0016, 0x0002); /* Horizontal shift */ + ibmcam_model2_Packet1(uvd, 0x0018, 0x004a); /* Another hardware setting */ + break; + case VIDEOSIZE_320x240: + ibmcam_model2_Packet1(uvd, 0x0014, 0x0009); + ibmcam_model2_Packet1(uvd, 0x0016, 0x0005); /* Horizontal shift */ + ibmcam_model2_Packet1(uvd, 0x0018, 0x0044); /* Another hardware setting */ + break; + case VIDEOSIZE_352x240: + /* This mode doesn't work as Windows programs it; changed to work */ + ibmcam_model2_Packet1(uvd, 0x0014, 0x0009); /* Windows sets this to 8 */ + ibmcam_model2_Packet1(uvd, 0x0016, 0x0003); /* Horizontal shift */ + ibmcam_model2_Packet1(uvd, 0x0018, 0x0044); /* Windows sets this to 0x0045 */ + break; + case VIDEOSIZE_352x288: + ibmcam_model2_Packet1(uvd, 0x0014, 0x0003); + ibmcam_model2_Packet1(uvd, 0x0016, 0x0002); /* Horizontal shift */ + ibmcam_model2_Packet1(uvd, 0x0018, 0x004a); /* Another hardware setting */ + break; + } + + ibmcam_model2_Packet1(uvd, mod2_brightness, 0x005a); + + /* + * We have our own frame rate setting varying from 0 (slowest) to 6 (fastest). + * The camera model 2 allows frame rate in range [0..0x1F] where 0 is also the + * slowest setting. However for all practical reasons high settings make no + * sense because USB is not fast enough to support high FPS. Be aware that + * the picture datastream will be severely disrupted if you ask for + * frame rate faster than allowed for the video size - see below: + * + * Allowable ranges (obtained experimentally on OHCI, K6-3, 450 MHz): + * ----------------------------------------------------------------- + * 176x144: [6..31] + * 320x240: [8..31] + * 352x240: [10..31] + * 352x288: [16..31] I have to raise lower threshold for stability... + * + * As usual, slower FPS provides better sensitivity. + */ + { + short hw_fps=31, i_framerate; + + RESTRICT_TO_RANGE(framerate, FRAMERATE_MIN, FRAMERATE_MAX); + i_framerate = FRAMERATE_MAX - framerate + FRAMERATE_MIN; + switch (uvd->videosize) { + case VIDEOSIZE_176x144: + hw_fps = 6 + i_framerate*4; + break; + case VIDEOSIZE_320x240: + hw_fps = 8 + i_framerate*3; + break; + case VIDEOSIZE_352x240: + hw_fps = 10 + i_framerate*2; + break; + case VIDEOSIZE_352x288: + hw_fps = 28 + i_framerate/2; + break; + } + if (uvd->debug > 0) + info("Framerate (hardware): %hd.", hw_fps); + RESTRICT_TO_RANGE(hw_fps, 0, 31); + ibmcam_model2_Packet1(uvd, mod2_set_framerate, hw_fps); + } + + /* + * This setting does not visibly affect pictures; left it here + * because it was present in Windows USB data stream. This function + * does not allow arbitrary values and apparently is a bit mask, to + * be activated only at appropriate time. Don't change it randomly! + */ + switch (uvd->videosize) { + case VIDEOSIZE_176x144: + ibmcam_model2_Packet1(uvd, 0x0026, 0x00c2); + break; + case VIDEOSIZE_320x240: + ibmcam_model2_Packet1(uvd, 0x0026, 0x0044); + break; + case VIDEOSIZE_352x240: + ibmcam_model2_Packet1(uvd, 0x0026, 0x0046); + break; + case VIDEOSIZE_352x288: + ibmcam_model2_Packet1(uvd, 0x0026, 0x0048); + break; + } + + ibmcam_model2_Packet1(uvd, mod2_sensitivity, lighting); + + if (init_model2_rg2 >= 0) { + RESTRICT_TO_RANGE(init_model2_rg2, 0, 255); + setup_model2_rg2 = init_model2_rg2; + } else + setup_model2_rg2 = 0x002f; + + if (init_model2_sat >= 0) { + RESTRICT_TO_RANGE(init_model2_sat, 0, 255); + setup_model2_sat = init_model2_sat; + } else + setup_model2_sat = 0x0034; + + if (init_model2_yb >= 0) { + RESTRICT_TO_RANGE(init_model2_yb, 0, 255); + setup_model2_yb = init_model2_yb; + } else + setup_model2_yb = 0x00a0; + + ibmcam_model2_Packet1(uvd, mod2_color_balance_rg2, setup_model2_rg2); + ibmcam_model2_Packet1(uvd, mod2_saturation, setup_model2_sat); + ibmcam_model2_Packet1(uvd, mod2_color_balance_yb, setup_model2_yb); + ibmcam_model2_Packet1(uvd, mod2_hue, uvd->vpic.hue >> 9); /* 0 .. 7F */; + + /* Hardware control command */ + ibmcam_model2_Packet1(uvd, 0x0030, 0x0004); + + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); /* Go camera, go! */ + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); +} + +static void ibmcam_model4_setup_after_video_if(struct uvd *uvd) +{ + switch (uvd->videosize) { + case VIDEOSIZE_128x96: + ibmcam_veio(uvd, 0, 0x0000, 0x0100); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x00bc, 0x012c); + ibmcam_veio(uvd, 0, 0x0080, 0x012b); + ibmcam_veio(uvd, 0, 0x0000, 0x0108); + ibmcam_veio(uvd, 0, 0x0001, 0x0133); + ibmcam_veio(uvd, 0, 0x009b, 0x010f); + ibmcam_veio(uvd, 0, 0x00bb, 0x010f); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x000a, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x005c, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0004, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00fb, 0x012e); + ibmcam_veio(uvd, 0, 0x0000, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x000c, 0x0127); + ibmcam_veio(uvd, 0, 0x0009, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0012, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0008, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x002a, 0x012d); + ibmcam_veio(uvd, 0, 0x0000, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0034, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x0070, 0x0119); + ibmcam_veio(uvd, 0, 0x00d2, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x005e, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x00d0, 0x0111); + ibmcam_veio(uvd, 0, 0x0039, 0x010a); + ibmcam_veio(uvd, 0, 0x0001, 0x0102); + ibmcam_veio(uvd, 0, 0x0028, 0x0103); + ibmcam_veio(uvd, 0, 0x0000, 0x0104); + ibmcam_veio(uvd, 0, 0x001e, 0x0105); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0016, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x000a, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0014, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012e); + ibmcam_veio(uvd, 0, 0x001a, 0x0130); + ibmcam_veio(uvd, 0, 0x8a0a, 0x0124); + ibmcam_veio(uvd, 0, 0x005a, 0x012d); + ibmcam_veio(uvd, 0, 0x9545, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x0127); + ibmcam_veio(uvd, 0, 0x0018, 0x012e); + ibmcam_veio(uvd, 0, 0x0043, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x001c, 0x0127); + ibmcam_veio(uvd, 0, 0x00eb, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0032, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0036, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x001e, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0017, 0x0127); + ibmcam_veio(uvd, 0, 0x0013, 0x012e); + ibmcam_veio(uvd, 0, 0x0031, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x0017, 0x012d); + ibmcam_veio(uvd, 0, 0x0078, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0004, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + break; + case VIDEOSIZE_160x120: + ibmcam_veio(uvd, 0, 0x0000, 0x0100); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x00bc, 0x012c); + ibmcam_veio(uvd, 0, 0x0080, 0x012b); + ibmcam_veio(uvd, 0, 0x0000, 0x0108); + ibmcam_veio(uvd, 0, 0x0001, 0x0133); + ibmcam_veio(uvd, 0, 0x009b, 0x010f); + ibmcam_veio(uvd, 0, 0x00bb, 0x010f); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x000a, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x005c, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0004, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00fb, 0x012e); + ibmcam_veio(uvd, 0, 0x0000, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x000c, 0x0127); + ibmcam_veio(uvd, 0, 0x0009, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0012, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0008, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x002a, 0x012d); + ibmcam_veio(uvd, 0, 0x0000, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0034, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x0038, 0x0119); + ibmcam_veio(uvd, 0, 0x00d8, 0x0107); + ibmcam_veio(uvd, 0, 0x0002, 0x0106); + ibmcam_veio(uvd, 0, 0x00d0, 0x0111); + ibmcam_veio(uvd, 0, 0x00b9, 0x010a); + ibmcam_veio(uvd, 0, 0x0001, 0x0102); + ibmcam_veio(uvd, 0, 0x0028, 0x0103); + ibmcam_veio(uvd, 0, 0x0000, 0x0104); + ibmcam_veio(uvd, 0, 0x001e, 0x0105); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0016, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x000b, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0014, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012e); + ibmcam_veio(uvd, 0, 0x001a, 0x0130); + ibmcam_veio(uvd, 0, 0x8a0a, 0x0124); + ibmcam_veio(uvd, 0, 0x005a, 0x012d); + ibmcam_veio(uvd, 0, 0x9545, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x0127); + ibmcam_veio(uvd, 0, 0x0018, 0x012e); + ibmcam_veio(uvd, 0, 0x0043, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x001c, 0x0127); + ibmcam_veio(uvd, 0, 0x00c7, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0032, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0025, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0036, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x001e, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0048, 0x0127); + ibmcam_veio(uvd, 0, 0x0035, 0x012e); + ibmcam_veio(uvd, 0, 0x00d0, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x0048, 0x012d); + ibmcam_veio(uvd, 0, 0x0090, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x0001, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0004, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + break; + case VIDEOSIZE_176x144: + ibmcam_veio(uvd, 0, 0x0000, 0x0100); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x00bc, 0x012c); + ibmcam_veio(uvd, 0, 0x0080, 0x012b); + ibmcam_veio(uvd, 0, 0x0000, 0x0108); + ibmcam_veio(uvd, 0, 0x0001, 0x0133); + ibmcam_veio(uvd, 0, 0x009b, 0x010f); + ibmcam_veio(uvd, 0, 0x00bb, 0x010f); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x000a, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x005c, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0004, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00fb, 0x012e); + ibmcam_veio(uvd, 0, 0x0000, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x000c, 0x0127); + ibmcam_veio(uvd, 0, 0x0009, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0012, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0008, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x002a, 0x012d); + ibmcam_veio(uvd, 0, 0x0000, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0034, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x0038, 0x0119); + ibmcam_veio(uvd, 0, 0x00d6, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x0018, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x00d0, 0x0111); + ibmcam_veio(uvd, 0, 0x00b9, 0x010a); + ibmcam_veio(uvd, 0, 0x0001, 0x0102); + ibmcam_veio(uvd, 0, 0x002c, 0x0103); + ibmcam_veio(uvd, 0, 0x0000, 0x0104); + ibmcam_veio(uvd, 0, 0x0024, 0x0105); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0016, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0007, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0014, 0x012d); + ibmcam_veio(uvd, 0, 0x0001, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012e); + ibmcam_veio(uvd, 0, 0x001a, 0x0130); + ibmcam_veio(uvd, 0, 0x8a0a, 0x0124); + ibmcam_veio(uvd, 0, 0x005e, 0x012d); + ibmcam_veio(uvd, 0, 0x9545, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x0127); + ibmcam_veio(uvd, 0, 0x0018, 0x012e); + ibmcam_veio(uvd, 0, 0x0049, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x001c, 0x0127); + ibmcam_veio(uvd, 0, 0x00c7, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0032, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0028, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0036, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x001e, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0010, 0x0127); + ibmcam_veio(uvd, 0, 0x0013, 0x012e); + ibmcam_veio(uvd, 0, 0x002a, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x0010, 0x012d); + ibmcam_veio(uvd, 0, 0x006d, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x0001, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0004, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + break; + case VIDEOSIZE_320x240: + ibmcam_veio(uvd, 0, 0x0000, 0x0100); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x00bc, 0x012c); + ibmcam_veio(uvd, 0, 0x0080, 0x012b); + ibmcam_veio(uvd, 0, 0x0000, 0x0108); + ibmcam_veio(uvd, 0, 0x0001, 0x0133); + ibmcam_veio(uvd, 0, 0x009b, 0x010f); + ibmcam_veio(uvd, 0, 0x00bb, 0x010f); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x000a, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x005c, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0004, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00fb, 0x012e); + ibmcam_veio(uvd, 0, 0x0000, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x000c, 0x0127); + ibmcam_veio(uvd, 0, 0x0009, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0012, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0008, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x002a, 0x012d); + ibmcam_veio(uvd, 0, 0x0000, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0034, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x0070, 0x0119); + ibmcam_veio(uvd, 0, 0x00d2, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x005e, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x00d0, 0x0111); + ibmcam_veio(uvd, 0, 0x0039, 0x010a); + ibmcam_veio(uvd, 0, 0x0001, 0x0102); + ibmcam_veio(uvd, 0, 0x0028, 0x0103); + ibmcam_veio(uvd, 0, 0x0000, 0x0104); + ibmcam_veio(uvd, 0, 0x001e, 0x0105); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0016, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x000a, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0014, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012e); + ibmcam_veio(uvd, 0, 0x001a, 0x0130); + ibmcam_veio(uvd, 0, 0x8a0a, 0x0124); + ibmcam_veio(uvd, 0, 0x005a, 0x012d); + ibmcam_veio(uvd, 0, 0x9545, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x0127); + ibmcam_veio(uvd, 0, 0x0018, 0x012e); + ibmcam_veio(uvd, 0, 0x0043, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x001c, 0x0127); + ibmcam_veio(uvd, 0, 0x00eb, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0032, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0036, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x001e, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0017, 0x0127); + ibmcam_veio(uvd, 0, 0x0013, 0x012e); + ibmcam_veio(uvd, 0, 0x0031, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x0017, 0x012d); + ibmcam_veio(uvd, 0, 0x0078, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0004, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + break; + case VIDEOSIZE_352x288: + ibmcam_veio(uvd, 0, 0x0000, 0x0100); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x00bc, 0x012c); + ibmcam_veio(uvd, 0, 0x0080, 0x012b); + ibmcam_veio(uvd, 0, 0x0000, 0x0108); + ibmcam_veio(uvd, 0, 0x0001, 0x0133); + ibmcam_veio(uvd, 0, 0x009b, 0x010f); + ibmcam_veio(uvd, 0, 0x00bb, 0x010f); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x000a, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x005c, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0004, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00fb, 0x012e); + ibmcam_veio(uvd, 0, 0x0000, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x000c, 0x0127); + ibmcam_veio(uvd, 0, 0x0009, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0012, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0008, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x002a, 0x012d); + ibmcam_veio(uvd, 0, 0x0000, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0034, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x0070, 0x0119); + ibmcam_veio(uvd, 0, 0x00f2, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x008c, 0x0107); + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x00c0, 0x0111); + ibmcam_veio(uvd, 0, 0x0039, 0x010a); + ibmcam_veio(uvd, 0, 0x0001, 0x0102); + ibmcam_veio(uvd, 0, 0x002c, 0x0103); + ibmcam_veio(uvd, 0, 0x0000, 0x0104); + ibmcam_veio(uvd, 0, 0x0024, 0x0105); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0016, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0006, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0014, 0x012d); + ibmcam_veio(uvd, 0, 0x0002, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012e); + ibmcam_veio(uvd, 0, 0x001a, 0x0130); + ibmcam_veio(uvd, 0, 0x8a0a, 0x0124); + ibmcam_veio(uvd, 0, 0x005e, 0x012d); + ibmcam_veio(uvd, 0, 0x9545, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x0127); + ibmcam_veio(uvd, 0, 0x0018, 0x012e); + ibmcam_veio(uvd, 0, 0x0049, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012f); + ibmcam_veio(uvd, 0, 0xd055, 0x0124); + ibmcam_veio(uvd, 0, 0x001c, 0x0127); + ibmcam_veio(uvd, 0, 0x00cf, 0x012e); + ibmcam_veio(uvd, 0, 0xaa28, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0032, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0x00aa, 0x0130); + ibmcam_veio(uvd, 0, 0x82a8, 0x0124); + ibmcam_veio(uvd, 0, 0x0036, 0x012d); + ibmcam_veio(uvd, 0, 0x0008, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0xfffa, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x001e, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0010, 0x0127); + ibmcam_veio(uvd, 0, 0x0013, 0x012e); + ibmcam_veio(uvd, 0, 0x0025, 0x0130); + ibmcam_veio(uvd, 0, 0x8a28, 0x0124); + ibmcam_veio(uvd, 0, 0x0010, 0x012d); + ibmcam_veio(uvd, 0, 0x0048, 0x012f); + ibmcam_veio(uvd, 0, 0xd145, 0x0124); + ibmcam_veio(uvd, 0, 0x0000, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00aa, 0x012d); + ibmcam_veio(uvd, 0, 0x0038, 0x012f); + ibmcam_veio(uvd, 0, 0xd141, 0x0124); + ibmcam_veio(uvd, 0, 0x0004, 0x0127); + ibmcam_veio(uvd, 0, 0xfea8, 0x0124); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + break; + } + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); +} + +static void ibmcam_model3_setup_after_video_if(struct uvd *uvd) +{ + int i; + /* + * 01.01.08 - Added for RCA video in support -LO + * This struct is used to init the Model3 cam to use the RCA video in port + * instead of the CCD sensor. + */ + static const struct struct_initData initData[] = { + {0, 0x0000, 0x010c}, + {0, 0x0006, 0x012c}, + {0, 0x0078, 0x012d}, + {0, 0x0046, 0x012f}, + {0, 0xd141, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfea8, 0x0124}, + {1, 0x0000, 0x0116}, + {0, 0x0064, 0x0116}, + {1, 0x0000, 0x0115}, + {0, 0x0003, 0x0115}, + {0, 0x0008, 0x0123}, + {0, 0x0000, 0x0117}, + {0, 0x0000, 0x0112}, + {0, 0x0080, 0x0100}, + {0, 0x0000, 0x0100}, + {1, 0x0000, 0x0116}, + {0, 0x0060, 0x0116}, + {0, 0x0002, 0x0112}, + {0, 0x0000, 0x0123}, + {0, 0x0001, 0x0117}, + {0, 0x0040, 0x0108}, + {0, 0x0019, 0x012c}, + {0, 0x0040, 0x0116}, + {0, 0x000a, 0x0115}, + {0, 0x000b, 0x0115}, + {0, 0x0078, 0x012d}, + {0, 0x0046, 0x012f}, + {0, 0xd141, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfea8, 0x0124}, + {0, 0x0064, 0x0116}, + {0, 0x0000, 0x0115}, + {0, 0x0001, 0x0115}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00aa, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00f2, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x000f, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00f8, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00fc, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00f9, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x003c, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xffff, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0027, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0019, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0021, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0006, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0045, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002a, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x000e, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002b, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00f4, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002c, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0004, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002d, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0014, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002e, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0003, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x002f, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0003, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0014, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0053, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0x0000, 0x0101}, + {0, 0x00a0, 0x0103}, + {0, 0x0078, 0x0105}, + {0, 0x0000, 0x010a}, + {0, 0x0024, 0x010b}, + {0, 0x0028, 0x0119}, + {0, 0x0088, 0x011b}, + {0, 0x0002, 0x011d}, + {0, 0x0003, 0x011e}, + {0, 0x0000, 0x0129}, + {0, 0x00fc, 0x012b}, + {0, 0x0008, 0x0102}, + {0, 0x0000, 0x0104}, + {0, 0x0008, 0x011a}, + {0, 0x0028, 0x011c}, + {0, 0x0021, 0x012a}, + {0, 0x0000, 0x0118}, + {0, 0x0000, 0x0132}, + {0, 0x0000, 0x0109}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0031, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x00dc, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0032, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0020, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0001, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0040, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0037, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0030, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0xfff9, 0x0124}, + {0, 0x0086, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0038, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0008, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0x0000, 0x0127}, + {0, 0xfff8, 0x0124}, + {0, 0xfffd, 0x0124}, + {0, 0xfffa, 0x0124}, + {0, 0x0003, 0x0106}, + {0, 0x0062, 0x0107}, + {0, 0x0003, 0x0111}, + }; +#define NUM_INIT_DATA + + unsigned short compression = 0; /* 0=none, 7=best frame rate */ + int f_rate; /* 0=Fastest 7=slowest */ + + if (IBMCAM_T(uvd)->initialized) + return; + + /* Internal frame rate is controlled by f_rate value */ + f_rate = 7 - framerate; + RESTRICT_TO_RANGE(f_rate, 0, 7); + + ibmcam_veio(uvd, 0, 0x0000, 0x0100); + ibmcam_veio(uvd, 1, 0x0000, 0x0116); + ibmcam_veio(uvd, 0, 0x0060, 0x0116); + ibmcam_veio(uvd, 0, 0x0002, 0x0112); + ibmcam_veio(uvd, 0, 0x0000, 0x0123); + ibmcam_veio(uvd, 0, 0x0001, 0x0117); + ibmcam_veio(uvd, 0, 0x0040, 0x0108); + ibmcam_veio(uvd, 0, 0x0019, 0x012c); + ibmcam_veio(uvd, 0, 0x0060, 0x0116); + ibmcam_veio(uvd, 0, 0x0002, 0x0115); + ibmcam_veio(uvd, 0, 0x0003, 0x0115); + ibmcam_veio(uvd, 1, 0x0000, 0x0115); + ibmcam_veio(uvd, 0, 0x000b, 0x0115); + ibmcam_model3_Packet1(uvd, 0x000a, 0x0040); + ibmcam_model3_Packet1(uvd, 0x000b, 0x00f6); + ibmcam_model3_Packet1(uvd, 0x000c, 0x0002); + ibmcam_model3_Packet1(uvd, 0x000d, 0x0020); + ibmcam_model3_Packet1(uvd, 0x000e, 0x0033); + ibmcam_model3_Packet1(uvd, 0x000f, 0x0007); + ibmcam_model3_Packet1(uvd, 0x0010, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0011, 0x0070); + ibmcam_model3_Packet1(uvd, 0x0012, 0x0030); + ibmcam_model3_Packet1(uvd, 0x0013, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0014, 0x0001); + ibmcam_model3_Packet1(uvd, 0x0015, 0x0001); + ibmcam_model3_Packet1(uvd, 0x0016, 0x0001); + ibmcam_model3_Packet1(uvd, 0x0017, 0x0001); + ibmcam_model3_Packet1(uvd, 0x0018, 0x0000); + ibmcam_model3_Packet1(uvd, 0x001e, 0x00c3); + ibmcam_model3_Packet1(uvd, 0x0020, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0028, 0x0010); + ibmcam_model3_Packet1(uvd, 0x0029, 0x0054); + ibmcam_model3_Packet1(uvd, 0x002a, 0x0013); + ibmcam_model3_Packet1(uvd, 0x002b, 0x0007); + ibmcam_model3_Packet1(uvd, 0x002d, 0x0028); + ibmcam_model3_Packet1(uvd, 0x002e, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0031, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0032, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0033, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0034, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0035, 0x0038); + ibmcam_model3_Packet1(uvd, 0x003a, 0x0001); + ibmcam_model3_Packet1(uvd, 0x003c, 0x001e); + ibmcam_model3_Packet1(uvd, 0x003f, 0x000a); + ibmcam_model3_Packet1(uvd, 0x0041, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0046, 0x003f); + ibmcam_model3_Packet1(uvd, 0x0047, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0050, 0x0005); + ibmcam_model3_Packet1(uvd, 0x0052, 0x001a); + ibmcam_model3_Packet1(uvd, 0x0053, 0x0003); + ibmcam_model3_Packet1(uvd, 0x005a, 0x006b); + ibmcam_model3_Packet1(uvd, 0x005d, 0x001e); + ibmcam_model3_Packet1(uvd, 0x005e, 0x0030); + ibmcam_model3_Packet1(uvd, 0x005f, 0x0041); + ibmcam_model3_Packet1(uvd, 0x0064, 0x0008); + ibmcam_model3_Packet1(uvd, 0x0065, 0x0015); + ibmcam_model3_Packet1(uvd, 0x0068, 0x000f); + ibmcam_model3_Packet1(uvd, 0x0079, 0x0000); + ibmcam_model3_Packet1(uvd, 0x007a, 0x0000); + ibmcam_model3_Packet1(uvd, 0x007c, 0x003f); + ibmcam_model3_Packet1(uvd, 0x0082, 0x000f); + ibmcam_model3_Packet1(uvd, 0x0085, 0x0000); + ibmcam_model3_Packet1(uvd, 0x0099, 0x0000); + ibmcam_model3_Packet1(uvd, 0x009b, 0x0023); + ibmcam_model3_Packet1(uvd, 0x009c, 0x0022); + ibmcam_model3_Packet1(uvd, 0x009d, 0x0096); + ibmcam_model3_Packet1(uvd, 0x009e, 0x0096); + ibmcam_model3_Packet1(uvd, 0x009f, 0x000a); + + switch (uvd->videosize) { + case VIDEOSIZE_160x120: + ibmcam_veio(uvd, 0, 0x0000, 0x0101); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x00a0, 0x0103); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x0078, 0x0105); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x0000, 0x010a); /* Same */ + ibmcam_veio(uvd, 0, 0x0024, 0x010b); /* Differs everywhere */ + ibmcam_veio(uvd, 0, 0x00a9, 0x0119); + ibmcam_veio(uvd, 0, 0x0016, 0x011b); + ibmcam_veio(uvd, 0, 0x0002, 0x011d); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x0003, 0x011e); /* Same on 176x144, 640x480 */ + ibmcam_veio(uvd, 0, 0x0000, 0x0129); /* Same */ + ibmcam_veio(uvd, 0, 0x00fc, 0x012b); /* Same */ + ibmcam_veio(uvd, 0, 0x0018, 0x0102); + ibmcam_veio(uvd, 0, 0x0004, 0x0104); + ibmcam_veio(uvd, 0, 0x0004, 0x011a); + ibmcam_veio(uvd, 0, 0x0028, 0x011c); + ibmcam_veio(uvd, 0, 0x0022, 0x012a); /* Same */ + ibmcam_veio(uvd, 0, 0x0000, 0x0118); + ibmcam_veio(uvd, 0, 0x0000, 0x0132); + ibmcam_model3_Packet1(uvd, 0x0021, 0x0001); /* Same */ + ibmcam_veio(uvd, 0, compression, 0x0109); + break; + case VIDEOSIZE_320x240: + ibmcam_veio(uvd, 0, 0x0000, 0x0101); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x00a0, 0x0103); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x0078, 0x0105); /* Same on 176x144, 320x240 */ + ibmcam_veio(uvd, 0, 0x0000, 0x010a); /* Same */ + ibmcam_veio(uvd, 0, 0x0028, 0x010b); /* Differs everywhere */ + ibmcam_veio(uvd, 0, 0x0002, 0x011d); /* Same */ + ibmcam_veio(uvd, 0, 0x0000, 0x011e); + ibmcam_veio(uvd, 0, 0x0000, 0x0129); /* Same */ + ibmcam_veio(uvd, 0, 0x00fc, 0x012b); /* Same */ + /* 4 commands from 160x120 skipped */ + ibmcam_veio(uvd, 0, 0x0022, 0x012a); /* Same */ + ibmcam_model3_Packet1(uvd, 0x0021, 0x0001); /* Same */ + ibmcam_veio(uvd, 0, compression, 0x0109); + ibmcam_veio(uvd, 0, 0x00d9, 0x0119); + ibmcam_veio(uvd, 0, 0x0006, 0x011b); + ibmcam_veio(uvd, 0, 0x0021, 0x0102); /* Same on 320x240, 640x480 */ + ibmcam_veio(uvd, 0, 0x0010, 0x0104); + ibmcam_veio(uvd, 0, 0x0004, 0x011a); + ibmcam_veio(uvd, 0, 0x003f, 0x011c); + ibmcam_veio(uvd, 0, 0x001c, 0x0118); + ibmcam_veio(uvd, 0, 0x0000, 0x0132); + break; + case VIDEOSIZE_640x480: + ibmcam_veio(uvd, 0, 0x00f0, 0x0105); + ibmcam_veio(uvd, 0, 0x0000, 0x010a); /* Same */ + ibmcam_veio(uvd, 0, 0x0038, 0x010b); /* Differs everywhere */ + ibmcam_veio(uvd, 0, 0x00d9, 0x0119); /* Same on 320x240, 640x480 */ + ibmcam_veio(uvd, 0, 0x0006, 0x011b); /* Same on 320x240, 640x480 */ + ibmcam_veio(uvd, 0, 0x0004, 0x011d); /* NC */ + ibmcam_veio(uvd, 0, 0x0003, 0x011e); /* Same on 176x144, 640x480 */ + ibmcam_veio(uvd, 0, 0x0000, 0x0129); /* Same */ + ibmcam_veio(uvd, 0, 0x00fc, 0x012b); /* Same */ + ibmcam_veio(uvd, 0, 0x0021, 0x0102); /* Same on 320x240, 640x480 */ + ibmcam_veio(uvd, 0, 0x0016, 0x0104); /* NC */ + ibmcam_veio(uvd, 0, 0x0004, 0x011a); /* Same on 320x240, 640x480 */ + ibmcam_veio(uvd, 0, 0x003f, 0x011c); /* Same on 320x240, 640x480 */ + ibmcam_veio(uvd, 0, 0x0022, 0x012a); /* Same */ + ibmcam_veio(uvd, 0, 0x001c, 0x0118); /* Same on 320x240, 640x480 */ + ibmcam_model3_Packet1(uvd, 0x0021, 0x0001); /* Same */ + ibmcam_veio(uvd, 0, compression, 0x0109); + ibmcam_veio(uvd, 0, 0x0040, 0x0101); + ibmcam_veio(uvd, 0, 0x0040, 0x0103); + ibmcam_veio(uvd, 0, 0x0000, 0x0132); /* Same on 320x240, 640x480 */ + break; + } + ibmcam_model3_Packet1(uvd, 0x007e, 0x000e); /* Hue */ + ibmcam_model3_Packet1(uvd, 0x0036, 0x0011); /* Brightness */ + ibmcam_model3_Packet1(uvd, 0x0060, 0x0002); /* Sharpness */ + ibmcam_model3_Packet1(uvd, 0x0061, 0x0004); /* Sharpness */ + ibmcam_model3_Packet1(uvd, 0x0062, 0x0005); /* Sharpness */ + ibmcam_model3_Packet1(uvd, 0x0063, 0x0014); /* Sharpness */ + ibmcam_model3_Packet1(uvd, 0x0096, 0x00a0); /* Red gain */ + ibmcam_model3_Packet1(uvd, 0x0097, 0x0096); /* Blue gain */ + ibmcam_model3_Packet1(uvd, 0x0067, 0x0001); /* Contrast */ + ibmcam_model3_Packet1(uvd, 0x005b, 0x000c); /* Contrast */ + ibmcam_model3_Packet1(uvd, 0x005c, 0x0016); /* Contrast */ + ibmcam_model3_Packet1(uvd, 0x0098, 0x000b); + ibmcam_model3_Packet1(uvd, 0x002c, 0x0003); /* Was 1, broke 640x480 */ + ibmcam_model3_Packet1(uvd, 0x002f, 0x002a); + ibmcam_model3_Packet1(uvd, 0x0030, 0x0029); + ibmcam_model3_Packet1(uvd, 0x0037, 0x0002); + ibmcam_model3_Packet1(uvd, 0x0038, 0x0059); + ibmcam_model3_Packet1(uvd, 0x003d, 0x002e); + ibmcam_model3_Packet1(uvd, 0x003e, 0x0028); + ibmcam_model3_Packet1(uvd, 0x0078, 0x0005); + ibmcam_model3_Packet1(uvd, 0x007b, 0x0011); + ibmcam_model3_Packet1(uvd, 0x007d, 0x004b); + ibmcam_model3_Packet1(uvd, 0x007f, 0x0022); + ibmcam_model3_Packet1(uvd, 0x0080, 0x000c); + ibmcam_model3_Packet1(uvd, 0x0081, 0x000b); + ibmcam_model3_Packet1(uvd, 0x0083, 0x00fd); + ibmcam_model3_Packet1(uvd, 0x0086, 0x000b); + ibmcam_model3_Packet1(uvd, 0x0087, 0x000b); + ibmcam_model3_Packet1(uvd, 0x007e, 0x000e); + ibmcam_model3_Packet1(uvd, 0x0096, 0x00a0); /* Red gain */ + ibmcam_model3_Packet1(uvd, 0x0097, 0x0096); /* Blue gain */ + ibmcam_model3_Packet1(uvd, 0x0098, 0x000b); + + switch (uvd->videosize) { + case VIDEOSIZE_160x120: + ibmcam_veio(uvd, 0, 0x0002, 0x0106); + ibmcam_veio(uvd, 0, 0x0008, 0x0107); + ibmcam_veio(uvd, 0, f_rate, 0x0111); /* Frame rate */ + ibmcam_model3_Packet1(uvd, 0x001f, 0x0000); /* Same */ + ibmcam_model3_Packet1(uvd, 0x0039, 0x001f); /* Same */ + ibmcam_model3_Packet1(uvd, 0x003b, 0x003c); /* Same */ + ibmcam_model3_Packet1(uvd, 0x0040, 0x000a); + ibmcam_model3_Packet1(uvd, 0x0051, 0x000a); + break; + case VIDEOSIZE_320x240: + ibmcam_veio(uvd, 0, 0x0003, 0x0106); + ibmcam_veio(uvd, 0, 0x0062, 0x0107); + ibmcam_veio(uvd, 0, f_rate, 0x0111); /* Frame rate */ + ibmcam_model3_Packet1(uvd, 0x001f, 0x0000); /* Same */ + ibmcam_model3_Packet1(uvd, 0x0039, 0x001f); /* Same */ + ibmcam_model3_Packet1(uvd, 0x003b, 0x003c); /* Same */ + ibmcam_model3_Packet1(uvd, 0x0040, 0x0008); + ibmcam_model3_Packet1(uvd, 0x0051, 0x000b); + break; + case VIDEOSIZE_640x480: + ibmcam_veio(uvd, 0, 0x0002, 0x0106); /* Adjustments */ + ibmcam_veio(uvd, 0, 0x00b4, 0x0107); /* Adjustments */ + ibmcam_veio(uvd, 0, f_rate, 0x0111); /* Frame rate */ + ibmcam_model3_Packet1(uvd, 0x001f, 0x0002); /* !Same */ + ibmcam_model3_Packet1(uvd, 0x0039, 0x003e); /* !Same */ + ibmcam_model3_Packet1(uvd, 0x0040, 0x0008); + ibmcam_model3_Packet1(uvd, 0x0051, 0x000a); + break; + } + + /* 01.01.08 - Added for RCA video in support -LO */ + if(init_model3_input) { + if (debug > 0) + info("Setting input to RCA."); + for (i=0; i < ARRAY_SIZE(initData); i++) { + ibmcam_veio(uvd, initData[i].req, initData[i].value, initData[i].index); + } + } + + ibmcam_veio(uvd, 0, 0x0001, 0x0114); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); +} + +/* + * ibmcam_video_stop() + * + * This code tells camera to stop streaming. The interface remains + * configured and bandwidth - claimed. + */ +static void ibmcam_video_stop(struct uvd *uvd) +{ + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + ibmcam_veio(uvd, 0, 0x00, 0x010c); + ibmcam_veio(uvd, 0, 0x00, 0x010c); + ibmcam_veio(uvd, 0, 0x01, 0x0114); + ibmcam_veio(uvd, 0, 0xc0, 0x010c); + ibmcam_veio(uvd, 0, 0x00, 0x010c); + ibmcam_send_FF_04_02(uvd); + ibmcam_veio(uvd, 1, 0x00, 0x0100); + ibmcam_veio(uvd, 0, 0x81, 0x0100); /* LED Off */ + break; + case IBMCAM_MODEL_2: +case IBMCAM_MODEL_4: + ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Stop the camera */ + + ibmcam_model2_Packet1(uvd, 0x0030, 0x0004); + + ibmcam_veio(uvd, 0, 0x0080, 0x0100); /* LED Off */ + ibmcam_veio(uvd, 0, 0x0020, 0x0111); + ibmcam_veio(uvd, 0, 0x00a0, 0x0111); + + ibmcam_model2_Packet1(uvd, 0x0030, 0x0002); + + ibmcam_veio(uvd, 0, 0x0020, 0x0111); + ibmcam_veio(uvd, 0, 0x0000, 0x0112); + break; + case IBMCAM_MODEL_3: +#if 1 + ibmcam_veio(uvd, 0, 0x0000, 0x010c); + + /* Here we are supposed to select video interface alt. setting 0 */ + ibmcam_veio(uvd, 0, 0x0006, 0x012c); + + ibmcam_model3_Packet1(uvd, 0x0046, 0x0000); + + ibmcam_veio(uvd, 1, 0x0000, 0x0116); + ibmcam_veio(uvd, 0, 0x0064, 0x0116); + ibmcam_veio(uvd, 1, 0x0000, 0x0115); + ibmcam_veio(uvd, 0, 0x0003, 0x0115); + ibmcam_veio(uvd, 0, 0x0008, 0x0123); + ibmcam_veio(uvd, 0, 0x0000, 0x0117); + ibmcam_veio(uvd, 0, 0x0000, 0x0112); + ibmcam_veio(uvd, 0, 0x0080, 0x0100); + IBMCAM_T(uvd)->initialized = 0; +#endif + break; + } /* switch */ +} + +/* + * ibmcam_reinit_iso() + * + * This procedure sends couple of commands to the camera and then + * resets the video pipe. This sequence was observed to reinit the + * camera or, at least, to initiate ISO data stream. + * + * History: + * 1/2/00 Created. + */ +static void ibmcam_reinit_iso(struct uvd *uvd, int do_stop) +{ + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + if (do_stop) + ibmcam_video_stop(uvd); + ibmcam_veio(uvd, 0, 0x0001, 0x0114); + ibmcam_veio(uvd, 0, 0x00c0, 0x010c); + usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp)); + ibmcam_model1_setup_after_video_if(uvd); + break; + case IBMCAM_MODEL_2: + ibmcam_model2_setup_after_video_if(uvd); + break; + case IBMCAM_MODEL_3: + ibmcam_video_stop(uvd); + ibmcam_model3_setup_after_video_if(uvd); + break; + case IBMCAM_MODEL_4: + ibmcam_model4_setup_after_video_if(uvd); + break; + } +} + +static void ibmcam_video_start(struct uvd *uvd) +{ + ibmcam_change_lighting_conditions(uvd); + ibmcam_set_sharpness(uvd); + ibmcam_reinit_iso(uvd, 0); +} + +/* + * Return negative code on failure, 0 on success. + */ +static int ibmcam_setup_on_open(struct uvd *uvd) +{ + int setup_ok = 0; /* Success by default */ + /* Send init sequence only once, it's large! */ + if (!IBMCAM_T(uvd)->initialized) { /* FIXME rename */ + switch (IBMCAM_T(uvd)->camera_model) { + case IBMCAM_MODEL_1: + setup_ok = ibmcam_model1_setup(uvd); + break; + case IBMCAM_MODEL_2: + setup_ok = ibmcam_model2_setup(uvd); + break; + case IBMCAM_MODEL_3: + case IBMCAM_MODEL_4: + /* We do all setup when Isoc stream is requested */ + break; + } + IBMCAM_T(uvd)->initialized = (setup_ok != 0); + } + return setup_ok; +} + +static void ibmcam_configure_video(struct uvd *uvd) +{ + if (uvd == NULL) + return; + + RESTRICT_TO_RANGE(init_brightness, 0, 255); + RESTRICT_TO_RANGE(init_contrast, 0, 255); + RESTRICT_TO_RANGE(init_color, 0, 255); + RESTRICT_TO_RANGE(init_hue, 0, 255); + RESTRICT_TO_RANGE(hue_correction, 0, 255); + + memset(&uvd->vpic, 0, sizeof(uvd->vpic)); + memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old)); + + uvd->vpic.colour = init_color << 8; + uvd->vpic.hue = init_hue << 8; + uvd->vpic.brightness = init_brightness << 8; + uvd->vpic.contrast = init_contrast << 8; + uvd->vpic.whiteness = 105 << 8; /* This one isn't used */ + uvd->vpic.depth = 24; + uvd->vpic.palette = VIDEO_PALETTE_RGB24; + + memset(&uvd->vcap, 0, sizeof(uvd->vcap)); + strcpy(uvd->vcap.name, "IBM USB Camera"); + uvd->vcap.type = VID_TYPE_CAPTURE; + uvd->vcap.channels = 1; + uvd->vcap.audios = 0; + uvd->vcap.maxwidth = VIDEOSIZE_X(uvd->canvas); + uvd->vcap.maxheight = VIDEOSIZE_Y(uvd->canvas); + uvd->vcap.minwidth = min_canvasWidth; + uvd->vcap.minheight = min_canvasHeight; + + memset(&uvd->vchan, 0, sizeof(uvd->vchan)); + uvd->vchan.flags = 0; + uvd->vchan.tuners = 0; + uvd->vchan.channel = 0; + uvd->vchan.type = VIDEO_TYPE_CAMERA; + strcpy(uvd->vchan.name, "Camera"); +} + +/* + * ibmcam_probe() + * + * This procedure queries device descriptor and accepts the interface + * if it looks like IBM C-it camera. + * + * History: + * 22-Jan-2000 Moved camera init code to ibmcam_open() + * 27=Jan-2000 Changed to use static structures, added locking. + * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). + * 03-Jul-2000 Fixed endianness bug. + * 12-Nov-2000 Reworked to comply with new probe() signature. + * 23-Jan-2001 Added compatibility with 2.2.x kernels. + */ +static int ibmcam_probe(struct usb_interface *intf, const struct usb_device_id *devid) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct uvd *uvd = NULL; + int ix, i, nas, model=0, canvasX=0, canvasY=0; + int actInterface=-1, inactInterface=-1, maxPS=0; + __u8 ifnum = intf->altsetting->desc.bInterfaceNumber; + unsigned char video_ep = 0; + + if (debug >= 1) + info("ibmcam_probe(%p,%u.)", intf, ifnum); + + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return -ENODEV; + + /* Check the version/revision */ + switch (le16_to_cpu(dev->descriptor.bcdDevice)) { + case 0x0002: + if (ifnum != 2) + return -ENODEV; + model = IBMCAM_MODEL_1; + break; + case 0x030A: + if (ifnum != 0) + return -ENODEV; + if ((le16_to_cpu(dev->descriptor.idProduct) == NETCAM_PRODUCT_ID) || + (le16_to_cpu(dev->descriptor.idProduct) == VEO_800D_PRODUCT_ID)) + model = IBMCAM_MODEL_4; + else + model = IBMCAM_MODEL_2; + break; + case 0x0301: + if (ifnum != 0) + return -ENODEV; + model = IBMCAM_MODEL_3; + break; + default: + err("IBM camera with revision 0x%04x is not supported.", + le16_to_cpu(dev->descriptor.bcdDevice)); + return -ENODEV; + } + + /* Print detailed info on what we found so far */ + do { + char *brand = NULL; + switch (le16_to_cpu(dev->descriptor.idProduct)) { + case NETCAM_PRODUCT_ID: + brand = "IBM NetCamera"; + break; + case VEO_800C_PRODUCT_ID: + brand = "Veo Stingray [800C]"; + break; + case VEO_800D_PRODUCT_ID: + brand = "Veo Stingray [800D]"; + break; + case IBMCAM_PRODUCT_ID: + default: + brand = "IBM PC Camera"; /* a.k.a. Xirlink C-It */ + break; + } + info("%s USB camera found (model %d, rev. 0x%04x)", + brand, model, le16_to_cpu(dev->descriptor.bcdDevice)); + } while (0); + + /* Validate found interface: must have one ISO endpoint */ + nas = intf->num_altsetting; + if (debug > 0) + info("Number of alternate settings=%d.", nas); + if (nas < 2) { + err("Too few alternate settings for this camera!"); + return -ENODEV; + } + /* Validate all alternate settings */ + for (ix=0; ix < nas; ix++) { + const struct usb_host_interface *interface; + const struct usb_endpoint_descriptor *endpoint; + + interface = &intf->altsetting[ix]; + i = interface->desc.bAlternateSetting; + if (interface->desc.bNumEndpoints != 1) { + err("Interface %d. has %u. endpoints!", + ifnum, (unsigned)(interface->desc.bNumEndpoints)); + return -ENODEV; + } + endpoint = &interface->endpoint[0].desc; + if (video_ep == 0) + video_ep = endpoint->bEndpointAddress; + else if (video_ep != endpoint->bEndpointAddress) { + err("Alternate settings have different endpoint addresses!"); + return -ENODEV; + } + if ((endpoint->bmAttributes & 0x03) != 0x01) { + err("Interface %d. has non-ISO endpoint!", ifnum); + return -ENODEV; + } + if ((endpoint->bEndpointAddress & 0x80) == 0) { + err("Interface %d. has ISO OUT endpoint!", ifnum); + return -ENODEV; + } + if (le16_to_cpu(endpoint->wMaxPacketSize) == 0) { + if (inactInterface < 0) + inactInterface = i; + else { + err("More than one inactive alt. setting!"); + return -ENODEV; + } + } else { + if (actInterface < 0) { + actInterface = i; + maxPS = le16_to_cpu(endpoint->wMaxPacketSize); + if (debug > 0) + info("Active setting=%d. maxPS=%d.", i, maxPS); + } else + err("More than one active alt. setting! Ignoring #%d.", i); + } + } + if ((maxPS <= 0) || (actInterface < 0) || (inactInterface < 0)) { + err("Failed to recognize the camera!"); + return -ENODEV; + } + + /* Validate options */ + switch (model) { + case IBMCAM_MODEL_1: + RESTRICT_TO_RANGE(lighting, 0, 2); + RESTRICT_TO_RANGE(size, SIZE_128x96, SIZE_352x288); + if (framerate < 0) + framerate = 2; + canvasX = 352; + canvasY = 288; + break; + case IBMCAM_MODEL_2: + RESTRICT_TO_RANGE(lighting, 0, 15); + RESTRICT_TO_RANGE(size, SIZE_176x144, SIZE_352x240); + if (framerate < 0) + framerate = 2; + canvasX = 352; + canvasY = 240; + break; + case IBMCAM_MODEL_3: + RESTRICT_TO_RANGE(lighting, 0, 15); /* FIXME */ + switch (size) { + case SIZE_160x120: + canvasX = 160; + canvasY = 120; + if (framerate < 0) + framerate = 2; + RESTRICT_TO_RANGE(framerate, 0, 5); + break; + default: + info("IBM camera: using 320x240"); + size = SIZE_320x240; + /* No break here */ + case SIZE_320x240: + canvasX = 320; + canvasY = 240; + if (framerate < 0) + framerate = 3; + RESTRICT_TO_RANGE(framerate, 0, 5); + break; + case SIZE_640x480: + canvasX = 640; + canvasY = 480; + framerate = 0; /* Slowest, and maybe even that is too fast */ + break; + } + break; + case IBMCAM_MODEL_4: + RESTRICT_TO_RANGE(lighting, 0, 2); + switch (size) { + case SIZE_128x96: + canvasX = 128; + canvasY = 96; + break; + case SIZE_160x120: + canvasX = 160; + canvasY = 120; + break; + default: + info("IBM NetCamera: using 176x144"); + size = SIZE_176x144; + /* No break here */ + case SIZE_176x144: + canvasX = 176; + canvasY = 144; + break; + case SIZE_320x240: + canvasX = 320; + canvasY = 240; + break; + case SIZE_352x288: + canvasX = 352; + canvasY = 288; + break; + } + break; + default: + err("IBM camera: Model %d. not supported!", model); + return -ENODEV; + } + + uvd = usbvideo_AllocateDevice(cams); + if (uvd != NULL) { + /* Here uvd is a fully allocated uvd object */ + uvd->flags = flags; + uvd->debug = debug; + uvd->dev = dev; + uvd->iface = ifnum; + uvd->ifaceAltInactive = inactInterface; + uvd->ifaceAltActive = actInterface; + uvd->video_endp = video_ep; + uvd->iso_packet_len = maxPS; + uvd->paletteBits = 1L << VIDEO_PALETTE_RGB24; + uvd->defaultPalette = VIDEO_PALETTE_RGB24; + uvd->canvas = VIDEOSIZE(canvasX, canvasY); + uvd->videosize = ibmcam_size_to_videosize(size); + + /* Initialize ibmcam-specific data */ + assert(IBMCAM_T(uvd) != NULL); + IBMCAM_T(uvd)->camera_model = model; + IBMCAM_T(uvd)->initialized = 0; + + ibmcam_configure_video(uvd); + + i = usbvideo_RegisterVideoDevice(uvd); + if (i != 0) { + err("usbvideo_RegisterVideoDevice() failed."); + uvd = NULL; + } + } + usb_set_intfdata (intf, uvd); + return 0; +} + + +static struct usb_device_id id_table[] = { + { USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x0002, 0x0002) }, /* Model 1 */ + { USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x030a, 0x030a) }, /* Model 2 */ + { USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x0301, 0x0301) }, /* Model 3 */ + { USB_DEVICE_VER(IBMCAM_VENDOR_ID, NETCAM_PRODUCT_ID, 0x030a, 0x030a) }, /* Model 4 */ + { USB_DEVICE_VER(IBMCAM_VENDOR_ID, VEO_800C_PRODUCT_ID, 0x030a, 0x030a) }, /* Model 2 */ + { USB_DEVICE_VER(IBMCAM_VENDOR_ID, VEO_800D_PRODUCT_ID, 0x030a, 0x030a) }, /* Model 4 */ + { } /* Terminating entry */ +}; + +/* + * ibmcam_init() + * + * This code is run to initialize the driver. + * + * History: + * 1/27/00 Reworked to use statically allocated ibmcam structures. + * 21/10/00 Completely redesigned to use usbvideo services. + */ +static int __init ibmcam_init(void) +{ + struct usbvideo_cb cbTbl; + memset(&cbTbl, 0, sizeof(cbTbl)); + cbTbl.probe = ibmcam_probe; + cbTbl.setupOnOpen = ibmcam_setup_on_open; + cbTbl.videoStart = ibmcam_video_start; + cbTbl.videoStop = ibmcam_video_stop; + cbTbl.processData = ibmcam_ProcessIsocData; + cbTbl.postProcess = usbvideo_DeinterlaceFrame; + cbTbl.adjustPicture = ibmcam_adjust_picture; + cbTbl.getFPS = ibmcam_calculate_fps; + return usbvideo_register( + &cams, + MAX_IBMCAM, + sizeof(ibmcam_t), + "ibmcam", + &cbTbl, + THIS_MODULE, + id_table); +} + +static void __exit ibmcam_cleanup(void) +{ + usbvideo_Deregister(&cams); +} + +MODULE_DEVICE_TABLE(usb, id_table); + +module_init(ibmcam_init); +module_exit(ibmcam_cleanup); diff --git a/linux/drivers/media/video/usbvideo/konicawc.c b/linux/drivers/media/video/usbvideo/konicawc.c new file mode 100644 index 000000000..c11f5d46b --- /dev/null +++ b/linux/drivers/media/video/usbvideo/konicawc.c @@ -0,0 +1,978 @@ +/* + * konicawc.c - konica webcam driver + * + * Author: Simon Evans + * + * Copyright (C) 2002 Simon Evans + * + * Licence: GPL + * + * Driver for USB webcams based on Konica chipset. This + * chipset is used in Intel YC76 camera. + * + */ + +#include +#include +#include +#include +#include + +#include "usbvideo.h" + +#define MAX_BRIGHTNESS 108 +#define MAX_CONTRAST 108 +#define MAX_SATURATION 108 +#define MAX_SHARPNESS 108 +#define MAX_WHITEBAL 372 +#define MAX_SPEED 6 + + +#define MAX_CAMERAS 1 + +#define DRIVER_VERSION "v1.4" +#define DRIVER_DESC "Konica Webcam driver" + +enum ctrl_req { + SetWhitebal = 0x01, + SetBrightness = 0x02, + SetSharpness = 0x03, + SetContrast = 0x04, + SetSaturation = 0x05, +}; + + +enum frame_sizes { + SIZE_160X120 = 0, + SIZE_160X136 = 1, + SIZE_176X144 = 2, + SIZE_320X240 = 3, + +}; + +#define MAX_FRAME_SIZE SIZE_320X240 + +static struct usbvideo *cams; + +#ifdef CONFIG_USB_DEBUG +static int debug; +#define DEBUG(n, format, arg...) \ + if (n <= debug) { \ + printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __FUNCTION__ , ## arg); \ + } +#else +#define DEBUG(n, arg...) +static const int debug = 0; +#endif + + +/* Some default values for initial camera settings, + can be set by modprobe */ + +static int size; +static int speed = 6; /* Speed (fps) 0 (slowest) to 6 (fastest) */ +static int brightness = MAX_BRIGHTNESS/2; +static int contrast = MAX_CONTRAST/2; +static int saturation = MAX_SATURATION/2; +static int sharpness = MAX_SHARPNESS/2; +static int whitebal = 3*(MAX_WHITEBAL/4); + +static const int spd_to_iface[] = { 1, 0, 3, 2, 4, 5, 6 }; + +/* These FPS speeds are from the windows config box. They are + * indexed on size (0-2) and speed (0-6). Divide by 3 to get the + * real fps. + */ + +static const int spd_to_fps[][7] = { { 24, 40, 48, 60, 72, 80, 100 }, + { 24, 40, 48, 60, 72, 80, 100 }, + { 18, 30, 36, 45, 54, 60, 75 }, + { 6, 10, 12, 15, 18, 21, 25 } }; + +struct cam_size { + u16 width; + u16 height; + u8 cmd; +}; + +static const struct cam_size camera_sizes[] = { { 160, 120, 0x7 }, + { 160, 136, 0xa }, + { 176, 144, 0x4 }, + { 320, 240, 0x5 } }; + +struct konicawc { + u8 brightness; /* camera uses 0 - 9, x11 for real value */ + u8 contrast; /* as above */ + u8 saturation; /* as above */ + u8 sharpness; /* as above */ + u8 white_bal; /* 0 - 33, x11 for real value */ + u8 speed; /* Stored as 0 - 6, used as index in spd_to_* (above) */ + u8 size; /* Frame Size */ + int height; + int width; + struct urb *sts_urb[USBVIDEO_NUMSBUF]; + u8 sts_buf[USBVIDEO_NUMSBUF][FRAMES_PER_DESC]; + struct urb *last_data_urb; + int lastframe; + int cur_frame_size; /* number of bytes in current frame size */ + int maxline; /* number of lines per frame */ + int yplanesz; /* Number of bytes in the Y plane */ + unsigned int buttonsts:1; +#ifdef CONFIG_INPUT + struct input_dev *input; + char input_physname[64]; +#endif +}; + + +#define konicawc_set_misc(uvd, req, value, index) konicawc_ctrl_msg(uvd, USB_DIR_OUT, req, value, index, NULL, 0) +#define konicawc_get_misc(uvd, req, value, index, buf, sz) konicawc_ctrl_msg(uvd, USB_DIR_IN, req, value, index, buf, sz) +#define konicawc_set_value(uvd, value, index) konicawc_ctrl_msg(uvd, USB_DIR_OUT, 2, value, index, NULL, 0) + + +static int konicawc_ctrl_msg(struct uvd *uvd, u8 dir, u8 request, u16 value, u16 index, void *buf, int len) +{ + int retval = usb_control_msg(uvd->dev, + dir ? usb_rcvctrlpipe(uvd->dev, 0) : usb_sndctrlpipe(uvd->dev, 0), + request, 0x40 | dir, value, index, buf, len, 1000); + return retval < 0 ? retval : 0; +} + + +static inline void konicawc_camera_on(struct uvd *uvd) +{ + DEBUG(0, "camera on"); + konicawc_set_misc(uvd, 0x2, 1, 0x0b); +} + + +static inline void konicawc_camera_off(struct uvd *uvd) +{ + DEBUG(0, "camera off"); + konicawc_set_misc(uvd, 0x2, 0, 0x0b); +} + + +static void konicawc_set_camera_size(struct uvd *uvd) +{ + struct konicawc *cam = (struct konicawc *)uvd->user_data; + + konicawc_set_misc(uvd, 0x2, camera_sizes[cam->size].cmd, 0x08); + cam->width = camera_sizes[cam->size].width; + cam->height = camera_sizes[cam->size].height; + cam->yplanesz = cam->height * cam->width; + cam->cur_frame_size = (cam->yplanesz * 3) / 2; + cam->maxline = cam->yplanesz / 256; + uvd->videosize = VIDEOSIZE(cam->width, cam->height); +} + + +static int konicawc_setup_on_open(struct uvd *uvd) +{ + struct konicawc *cam = (struct konicawc *)uvd->user_data; + + DEBUG(1, "setting brightness to %d (%d)", cam->brightness, + cam->brightness * 11); + konicawc_set_value(uvd, cam->brightness, SetBrightness); + DEBUG(1, "setting white balance to %d (%d)", cam->white_bal, + cam->white_bal * 11); + konicawc_set_value(uvd, cam->white_bal, SetWhitebal); + DEBUG(1, "setting contrast to %d (%d)", cam->contrast, + cam->contrast * 11); + konicawc_set_value(uvd, cam->contrast, SetContrast); + DEBUG(1, "setting saturation to %d (%d)", cam->saturation, + cam->saturation * 11); + konicawc_set_value(uvd, cam->saturation, SetSaturation); + DEBUG(1, "setting sharpness to %d (%d)", cam->sharpness, + cam->sharpness * 11); + konicawc_set_value(uvd, cam->sharpness, SetSharpness); + konicawc_set_camera_size(uvd); + cam->lastframe = -2; + cam->buttonsts = 0; + return 0; +} + + +static void konicawc_adjust_picture(struct uvd *uvd) +{ + struct konicawc *cam = (struct konicawc *)uvd->user_data; + + konicawc_camera_off(uvd); + DEBUG(1, "new brightness: %d", uvd->vpic.brightness); + uvd->vpic.brightness = (uvd->vpic.brightness > MAX_BRIGHTNESS) ? MAX_BRIGHTNESS : uvd->vpic.brightness; + if(cam->brightness != uvd->vpic.brightness / 11) { + cam->brightness = uvd->vpic.brightness / 11; + DEBUG(1, "setting brightness to %d (%d)", cam->brightness, + cam->brightness * 11); + konicawc_set_value(uvd, cam->brightness, SetBrightness); + } + + DEBUG(1, "new contrast: %d", uvd->vpic.contrast); + uvd->vpic.contrast = (uvd->vpic.contrast > MAX_CONTRAST) ? MAX_CONTRAST : uvd->vpic.contrast; + if(cam->contrast != uvd->vpic.contrast / 11) { + cam->contrast = uvd->vpic.contrast / 11; + DEBUG(1, "setting contrast to %d (%d)", cam->contrast, + cam->contrast * 11); + konicawc_set_value(uvd, cam->contrast, SetContrast); + } + konicawc_camera_on(uvd); +} + +#ifdef CONFIG_INPUT + +static void konicawc_register_input(struct konicawc *cam, struct usb_device *dev) +{ + struct input_dev *input_dev; + + usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname)); + strncat(cam->input_physname, "/input0", sizeof(cam->input_physname)); + + cam->input = input_dev = input_allocate_device(); + if (!input_dev) { + warn("Not enough memory for camera's input device\n"); + return; + } + + input_dev->name = "Konicawc snapshot button"; + input_dev->phys = cam->input_physname; + usb_to_input_id(dev, &input_dev->id); + input_dev->cdev.dev = &dev->dev; + + input_dev->evbit[0] = BIT(EV_KEY); + input_dev->keybit[LONG(BTN_0)] = BIT(BTN_0); + + input_dev->private = cam; + + input_register_device(cam->input); +} + +static void konicawc_unregister_input(struct konicawc *cam) +{ + if (cam->input) { + input_unregister_device(cam->input); + cam->input = NULL; + } +} + +static void konicawc_report_buttonstat(struct konicawc *cam) +{ + if (cam->input) { + input_report_key(cam->input, BTN_0, cam->buttonsts); + input_sync(cam->input); + } +} + +#else + +static inline void konicawc_register_input(struct konicawc *cam, struct usb_device *dev) { } +static inline void konicawc_unregister_input(struct konicawc *cam) { } +static inline void konicawc_report_buttonstat(struct konicawc *cam) { } + +#endif /* CONFIG_INPUT */ + +static int konicawc_compress_iso(struct uvd *uvd, struct urb *dataurb, struct urb *stsurb) +{ + char *cdata; + int i, totlen = 0; + unsigned char *status = stsurb->transfer_buffer; + int keep = 0, discard = 0, bad = 0; + struct konicawc *cam = (struct konicawc *)uvd->user_data; + + for (i = 0; i < dataurb->number_of_packets; i++) { + int button = cam->buttonsts; + unsigned char sts; + int n = dataurb->iso_frame_desc[i].actual_length; + int st = dataurb->iso_frame_desc[i].status; + cdata = dataurb->transfer_buffer + + dataurb->iso_frame_desc[i].offset; + + /* Detect and ignore errored packets */ + if (st < 0) { + DEBUG(1, "Data error: packet=%d. len=%d. status=%d.", + i, n, st); + uvd->stats.iso_err_count++; + continue; + } + + /* Detect and ignore empty packets */ + if (n <= 0) { + uvd->stats.iso_skip_count++; + continue; + } + + /* See what the status data said about the packet */ + sts = *(status+stsurb->iso_frame_desc[i].offset); + + /* sts: 0x80-0xff: frame start with frame number (ie 0-7f) + * otherwise: + * bit 0 0: keep packet + * 1: drop packet (padding data) + * + * bit 4 0 button not clicked + * 1 button clicked + * button is used to `take a picture' (in software) + */ + + if(sts < 0x80) { + button = !!(sts & 0x40); + sts &= ~0x40; + } + + /* work out the button status, but don't do + anything with it for now */ + + if(button != cam->buttonsts) { + DEBUG(2, "button: %sclicked", button ? "" : "un"); + cam->buttonsts = button; + konicawc_report_buttonstat(cam); + } + + if(sts == 0x01) { /* drop frame */ + discard++; + continue; + } + + if((sts > 0x01) && (sts < 0x80)) { + info("unknown status %2.2x", sts); + bad++; + continue; + } + if(!sts && cam->lastframe == -2) { + DEBUG(2, "dropping frame looking for image start"); + continue; + } + + keep++; + if(sts & 0x80) { /* frame start */ + unsigned char marker[] = { 0, 0xff, 0, 0x00 }; + + if(cam->lastframe == -2) { + DEBUG(2, "found initial image"); + cam->lastframe = -1; + } + + marker[3] = sts & 0x7F; + RingQueue_Enqueue(&uvd->dp, marker, 4); + totlen += 4; + } + + totlen += n; /* Little local accounting */ + RingQueue_Enqueue(&uvd->dp, cdata, n); + } + DEBUG(8, "finished: keep = %d discard = %d bad = %d added %d bytes", + keep, discard, bad, totlen); + return totlen; +} + + +static void resubmit_urb(struct uvd *uvd, struct urb *urb) +{ + int i, ret; + for (i = 0; i < FRAMES_PER_DESC; i++) { + urb->iso_frame_desc[i].status = 0; + } + urb->dev = uvd->dev; + urb->status = 0; + ret = usb_submit_urb(urb, GFP_ATOMIC); + DEBUG(3, "submitting urb of length %d", urb->transfer_buffer_length); + if(ret) + err("usb_submit_urb error (%d)", ret); + +} + + +static void konicawc_isoc_irq(struct urb *urb, struct pt_regs *regs) +{ + struct uvd *uvd = urb->context; + struct konicawc *cam = (struct konicawc *)uvd->user_data; + + /* We don't want to do anything if we are about to be removed! */ + if (!CAMERA_IS_OPERATIONAL(uvd)) + return; + + if (!uvd->streaming) { + DEBUG(1, "Not streaming, but interrupt!"); + return; + } + + DEBUG(3, "got frame %d len = %d buflen =%d", urb->start_frame, urb->actual_length, urb->transfer_buffer_length); + + uvd->stats.urb_count++; + + if (urb->transfer_buffer_length > 32) { + cam->last_data_urb = urb; + return; + } + /* Copy the data received into ring queue */ + if(cam->last_data_urb) { + int len = 0; + if(urb->start_frame != cam->last_data_urb->start_frame) + err("Lost sync on frames"); + else if (!urb->status && !cam->last_data_urb->status) + len = konicawc_compress_iso(uvd, cam->last_data_urb, urb); + + resubmit_urb(uvd, cam->last_data_urb); + resubmit_urb(uvd, urb); + cam->last_data_urb = NULL; + uvd->stats.urb_length = len; + uvd->stats.data_count += len; + if(len) + RingQueue_WakeUpInterruptible(&uvd->dp); + return; + } + return; +} + + +static int konicawc_start_data(struct uvd *uvd) +{ + struct usb_device *dev = uvd->dev; + int i, errFlag; + struct konicawc *cam = (struct konicawc *)uvd->user_data; + int pktsz; + struct usb_interface *intf; + struct usb_host_interface *interface = NULL; + + intf = usb_ifnum_to_if(dev, uvd->iface); + if (intf) + interface = usb_altnum_to_altsetting(intf, + spd_to_iface[cam->speed]); + if (!interface) + return -ENXIO; + pktsz = le16_to_cpu(interface->endpoint[1].desc.wMaxPacketSize); + DEBUG(1, "pktsz = %d", pktsz); + if (!CAMERA_IS_OPERATIONAL(uvd)) { + err("Camera is not operational"); + return -EFAULT; + } + uvd->curframe = -1; + konicawc_camera_on(uvd); + /* Alternate interface 1 is is the biggest frame size */ + i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive); + if (i < 0) { + err("usb_set_interface error"); + uvd->last_error = i; + return -EBUSY; + } + + /* We double buffer the Iso lists */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + int j, k; + struct urb *urb = uvd->sbuf[i].urb; + urb->dev = dev; + urb->context = uvd; + urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp); + urb->interval = 1; + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = uvd->sbuf[i].data; + urb->complete = konicawc_isoc_irq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = pktsz * FRAMES_PER_DESC; + for (j=k=0; j < FRAMES_PER_DESC; j++, k += pktsz) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = pktsz; + } + + urb = cam->sts_urb[i]; + urb->dev = dev; + urb->context = uvd; + urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp-1); + urb->interval = 1; + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = cam->sts_buf[i]; + urb->complete = konicawc_isoc_irq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = FRAMES_PER_DESC; + for (j=0; j < FRAMES_PER_DESC; j++) { + urb->iso_frame_desc[j].offset = j; + urb->iso_frame_desc[j].length = 1; + } + } + + cam->last_data_urb = NULL; + + /* Submit all URBs */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + errFlag = usb_submit_urb(cam->sts_urb[i], GFP_KERNEL); + if (errFlag) + err("usb_submit_isoc(%d) ret %d", i, errFlag); + + errFlag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL); + if (errFlag) + err ("usb_submit_isoc(%d) ret %d", i, errFlag); + } + + uvd->streaming = 1; + DEBUG(1, "streaming=1 video_endp=$%02x", uvd->video_endp); + return 0; +} + + +static void konicawc_stop_data(struct uvd *uvd) +{ + int i, j; + struct konicawc *cam; + + if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL)) + return; + + konicawc_camera_off(uvd); + uvd->streaming = 0; + cam = (struct konicawc *)uvd->user_data; + cam->last_data_urb = NULL; + + /* Unschedule all of the iso td's */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + usb_kill_urb(uvd->sbuf[i].urb); + usb_kill_urb(cam->sts_urb[i]); + } + + if (!uvd->remove_pending) { + /* Set packet size to 0 */ + j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive); + if (j < 0) { + err("usb_set_interface() error %d.", j); + uvd->last_error = j; + } + } +} + + +static void konicawc_process_isoc(struct uvd *uvd, struct usbvideo_frame *frame) +{ + struct konicawc *cam = (struct konicawc *)uvd->user_data; + int maxline = cam->maxline; + int yplanesz = cam->yplanesz; + + assert(frame != NULL); + + DEBUG(5, "maxline = %d yplanesz = %d", maxline, yplanesz); + DEBUG(3, "Frame state = %d", frame->scanstate); + + if(frame->scanstate == ScanState_Scanning) { + int drop = 0; + int curframe; + int fdrops = 0; + DEBUG(3, "Searching for marker, queue len = %d", RingQueue_GetLength(&uvd->dp)); + while(RingQueue_GetLength(&uvd->dp) >= 4) { + if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) && + (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xff) && + (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00) && + (RING_QUEUE_PEEK(&uvd->dp, 3) < 0x80)) { + curframe = RING_QUEUE_PEEK(&uvd->dp, 3); + if(cam->lastframe >= 0) { + fdrops = (0x80 + curframe - cam->lastframe) & 0x7F; + fdrops--; + if(fdrops) { + info("Dropped %d frames (%d -> %d)", fdrops, + cam->lastframe, curframe); + } + } + cam->lastframe = curframe; + frame->curline = 0; + frame->scanstate = ScanState_Lines; + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 4); + break; + } + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1); + drop++; + } + if(drop) + DEBUG(2, "dropped %d bytes looking for new frame", drop); + } + + if(frame->scanstate == ScanState_Scanning) + return; + + /* Try to move data from queue into frame buffer + * We get data in blocks of 384 bytes made up of: + * 256 Y, 64 U, 64 V. + * This needs to be written out as a Y plane, a U plane and a V plane. + */ + + while ( frame->curline < maxline && (RingQueue_GetLength(&uvd->dp) >= 384)) { + /* Y */ + RingQueue_Dequeue(&uvd->dp, frame->data + (frame->curline * 256), 256); + /* U */ + RingQueue_Dequeue(&uvd->dp, frame->data + yplanesz + (frame->curline * 64), 64); + /* V */ + RingQueue_Dequeue(&uvd->dp, frame->data + (5 * yplanesz)/4 + (frame->curline * 64), 64); + frame->seqRead_Length += 384; + frame->curline++; + } + /* See if we filled the frame */ + if (frame->curline == maxline) { + DEBUG(5, "got whole frame"); + + frame->frameState = FrameState_Done_Hold; + frame->curline = 0; + uvd->curframe = -1; + uvd->stats.frame_num++; + } +} + + +static int konicawc_find_fps(int size, int fps) +{ + int i; + + fps *= 3; + DEBUG(1, "konica_find_fps: size = %d fps = %d", size, fps); + if(fps <= spd_to_fps[size][0]) + return 0; + + if(fps >= spd_to_fps[size][MAX_SPEED]) + return MAX_SPEED; + + for(i = 0; i < MAX_SPEED; i++) { + if((fps >= spd_to_fps[size][i]) && (fps <= spd_to_fps[size][i+1])) { + DEBUG(2, "fps %d between %d and %d", fps, i, i+1); + if( (fps - spd_to_fps[size][i]) < (spd_to_fps[size][i+1] - fps)) + return i; + else + return i+1; + } + } + return MAX_SPEED+1; +} + + +static int konicawc_set_video_mode(struct uvd *uvd, struct video_window *vw) +{ + struct konicawc *cam = (struct konicawc *)uvd->user_data; + int newspeed = cam->speed; + int newsize; + int x = vw->width; + int y = vw->height; + int fps = vw->flags; + + if(x > 0 && y > 0) { + DEBUG(2, "trying to find size %d,%d", x, y); + for(newsize = 0; newsize <= MAX_FRAME_SIZE; newsize++) { + if((camera_sizes[newsize].width == x) && (camera_sizes[newsize].height == y)) + break; + } + } else { + newsize = cam->size; + } + + if(newsize > MAX_FRAME_SIZE) { + DEBUG(1, "couldn't find size %d,%d", x, y); + return -EINVAL; + } + + if(fps > 0) { + DEBUG(1, "trying to set fps to %d", fps); + newspeed = konicawc_find_fps(newsize, fps); + DEBUG(1, "find_fps returned %d (%d)", newspeed, spd_to_fps[newsize][newspeed]); + } + + if(newspeed > MAX_SPEED) + return -EINVAL; + + DEBUG(1, "setting size to %d speed to %d", newsize, newspeed); + if((newsize == cam->size) && (newspeed == cam->speed)) { + DEBUG(1, "Nothing to do"); + return 0; + } + DEBUG(0, "setting to %dx%d @ %d fps", camera_sizes[newsize].width, + camera_sizes[newsize].height, spd_to_fps[newsize][newspeed]/3); + + konicawc_stop_data(uvd); + uvd->ifaceAltActive = spd_to_iface[newspeed]; + DEBUG(1, "new interface = %d", uvd->ifaceAltActive); + cam->speed = newspeed; + + if(cam->size != newsize) { + cam->size = newsize; + konicawc_set_camera_size(uvd); + } + + /* Flush the input queue and clear any current frame in progress */ + + RingQueue_Flush(&uvd->dp); + cam->lastframe = -2; + if(uvd->curframe != -1) { + uvd->frame[uvd->curframe].curline = 0; + uvd->frame[uvd->curframe].seqRead_Length = 0; + uvd->frame[uvd->curframe].seqRead_Index = 0; + } + + konicawc_start_data(uvd); + return 0; +} + + +static int konicawc_calculate_fps(struct uvd *uvd) +{ + struct konicawc *cam = uvd->user_data; + return spd_to_fps[cam->size][cam->speed]/3; +} + + +static void konicawc_configure_video(struct uvd *uvd) +{ + struct konicawc *cam = (struct konicawc *)uvd->user_data; + u8 buf[2]; + + memset(&uvd->vpic, 0, sizeof(uvd->vpic)); + memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old)); + + RESTRICT_TO_RANGE(brightness, 0, MAX_BRIGHTNESS); + RESTRICT_TO_RANGE(contrast, 0, MAX_CONTRAST); + RESTRICT_TO_RANGE(saturation, 0, MAX_SATURATION); + RESTRICT_TO_RANGE(sharpness, 0, MAX_SHARPNESS); + RESTRICT_TO_RANGE(whitebal, 0, MAX_WHITEBAL); + + cam->brightness = brightness / 11; + cam->contrast = contrast / 11; + cam->saturation = saturation / 11; + cam->sharpness = sharpness / 11; + cam->white_bal = whitebal / 11; + + uvd->vpic.colour = 108; + uvd->vpic.hue = 108; + uvd->vpic.brightness = brightness; + uvd->vpic.contrast = contrast; + uvd->vpic.whiteness = whitebal; + uvd->vpic.depth = 6; + uvd->vpic.palette = VIDEO_PALETTE_YUV420P; + + memset(&uvd->vcap, 0, sizeof(uvd->vcap)); + strcpy(uvd->vcap.name, "Konica Webcam"); + uvd->vcap.type = VID_TYPE_CAPTURE; + uvd->vcap.channels = 1; + uvd->vcap.audios = 0; + uvd->vcap.minwidth = camera_sizes[SIZE_160X120].width; + uvd->vcap.minheight = camera_sizes[SIZE_160X120].height; + uvd->vcap.maxwidth = camera_sizes[SIZE_320X240].width; + uvd->vcap.maxheight = camera_sizes[SIZE_320X240].height; + + memset(&uvd->vchan, 0, sizeof(uvd->vchan)); + uvd->vchan.flags = 0 ; + uvd->vchan.tuners = 0; + uvd->vchan.channel = 0; + uvd->vchan.type = VIDEO_TYPE_CAMERA; + strcpy(uvd->vchan.name, "Camera"); + + /* Talk to device */ + DEBUG(1, "device init"); + if(!konicawc_get_misc(uvd, 0x3, 0, 0x10, buf, 2)) + DEBUG(2, "3,10 -> %2.2x %2.2x", buf[0], buf[1]); + if(!konicawc_get_misc(uvd, 0x3, 0, 0x10, buf, 2)) + DEBUG(2, "3,10 -> %2.2x %2.2x", buf[0], buf[1]); + if(konicawc_set_misc(uvd, 0x2, 0, 0xd)) + DEBUG(2, "2,0,d failed"); + DEBUG(1, "setting initial values"); +} + +static int konicawc_probe(struct usb_interface *intf, const struct usb_device_id *devid) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct uvd *uvd = NULL; + int ix, i, nas; + int actInterface=-1, inactInterface=-1, maxPS=0; + unsigned char video_ep = 0; + + DEBUG(1, "konicawc_probe(%p)", intf); + + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return -ENODEV; + + info("Konica Webcam (rev. 0x%04x)", le16_to_cpu(dev->descriptor.bcdDevice)); + RESTRICT_TO_RANGE(speed, 0, MAX_SPEED); + + /* Validate found interface: must have one ISO endpoint */ + nas = intf->num_altsetting; + if (nas != 8) { + err("Incorrect number of alternate settings (%d) for this camera!", nas); + return -ENODEV; + } + /* Validate all alternate settings */ + for (ix=0; ix < nas; ix++) { + const struct usb_host_interface *interface; + const struct usb_endpoint_descriptor *endpoint; + + interface = &intf->altsetting[ix]; + i = interface->desc.bAlternateSetting; + if (interface->desc.bNumEndpoints != 2) { + err("Interface %d. has %u. endpoints!", + interface->desc.bInterfaceNumber, + (unsigned)(interface->desc.bNumEndpoints)); + return -ENODEV; + } + endpoint = &interface->endpoint[1].desc; + DEBUG(1, "found endpoint: addr: 0x%2.2x maxps = 0x%4.4x", + endpoint->bEndpointAddress, le16_to_cpu(endpoint->wMaxPacketSize)); + if (video_ep == 0) + video_ep = endpoint->bEndpointAddress; + else if (video_ep != endpoint->bEndpointAddress) { + err("Alternate settings have different endpoint addresses!"); + return -ENODEV; + } + if ((endpoint->bmAttributes & 0x03) != 0x01) { + err("Interface %d. has non-ISO endpoint!", + interface->desc.bInterfaceNumber); + return -ENODEV; + } + if ((endpoint->bEndpointAddress & 0x80) == 0) { + err("Interface %d. has ISO OUT endpoint!", + interface->desc.bInterfaceNumber); + return -ENODEV; + } + if (le16_to_cpu(endpoint->wMaxPacketSize) == 0) { + if (inactInterface < 0) + inactInterface = i; + else { + err("More than one inactive alt. setting!"); + return -ENODEV; + } + } else { + if (i == spd_to_iface[speed]) { + /* This one is the requested one */ + actInterface = i; + } + } + if (le16_to_cpu(endpoint->wMaxPacketSize) > maxPS) + maxPS = le16_to_cpu(endpoint->wMaxPacketSize); + } + if(actInterface == -1) { + err("Cant find required endpoint"); + return -ENODEV; + } + + DEBUG(1, "Selecting requested active setting=%d. maxPS=%d.", actInterface, maxPS); + + uvd = usbvideo_AllocateDevice(cams); + if (uvd != NULL) { + struct konicawc *cam = (struct konicawc *)(uvd->user_data); + /* Here uvd is a fully allocated uvd object */ + for(i = 0; i < USBVIDEO_NUMSBUF; i++) { + cam->sts_urb[i] = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); + if(cam->sts_urb[i] == NULL) { + while(i--) { + usb_free_urb(cam->sts_urb[i]); + } + err("can't allocate urbs"); + return -ENOMEM; + } + } + cam->speed = speed; + RESTRICT_TO_RANGE(size, SIZE_160X120, SIZE_320X240); + cam->width = camera_sizes[size].width; + cam->height = camera_sizes[size].height; + cam->size = size; + + uvd->flags = 0; + uvd->debug = debug; + uvd->dev = dev; + uvd->iface = intf->altsetting->desc.bInterfaceNumber; + uvd->ifaceAltInactive = inactInterface; + uvd->ifaceAltActive = actInterface; + uvd->video_endp = video_ep; + uvd->iso_packet_len = maxPS; + uvd->paletteBits = 1L << VIDEO_PALETTE_YUV420P; + uvd->defaultPalette = VIDEO_PALETTE_YUV420P; + uvd->canvas = VIDEOSIZE(320, 240); + uvd->videosize = VIDEOSIZE(cam->width, cam->height); + + /* Initialize konicawc specific data */ + konicawc_configure_video(uvd); + + i = usbvideo_RegisterVideoDevice(uvd); + uvd->max_frame_size = (320 * 240 * 3)/2; + if (i != 0) { + err("usbvideo_RegisterVideoDevice() failed."); + uvd = NULL; + } + + konicawc_register_input(cam, dev); + } + + if (uvd) { + usb_set_intfdata (intf, uvd); + return 0; + } + return -EIO; +} + + +static void konicawc_free_uvd(struct uvd *uvd) +{ + int i; + struct konicawc *cam = (struct konicawc *)uvd->user_data; + + konicawc_unregister_input(cam); + + for (i = 0; i < USBVIDEO_NUMSBUF; i++) { + usb_free_urb(cam->sts_urb[i]); + cam->sts_urb[i] = NULL; + } +} + + +static struct usb_device_id id_table[] = { + { USB_DEVICE(0x04c8, 0x0720) }, /* Intel YC 76 */ + { } /* Terminating entry */ +}; + + +static int __init konicawc_init(void) +{ + struct usbvideo_cb cbTbl; + info(DRIVER_DESC " " DRIVER_VERSION); + memset(&cbTbl, 0, sizeof(cbTbl)); + cbTbl.probe = konicawc_probe; + cbTbl.setupOnOpen = konicawc_setup_on_open; + cbTbl.processData = konicawc_process_isoc; + cbTbl.getFPS = konicawc_calculate_fps; + cbTbl.setVideoMode = konicawc_set_video_mode; + cbTbl.startDataPump = konicawc_start_data; + cbTbl.stopDataPump = konicawc_stop_data; + cbTbl.adjustPicture = konicawc_adjust_picture; + cbTbl.userFree = konicawc_free_uvd; + return usbvideo_register( + &cams, + MAX_CAMERAS, + sizeof(struct konicawc), + "konicawc", + &cbTbl, + THIS_MODULE, + id_table); +} + + +static void __exit konicawc_cleanup(void) +{ + usbvideo_Deregister(&cams); +} + + +MODULE_DEVICE_TABLE(usb, id_table); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Simon Evans "); +MODULE_DESCRIPTION(DRIVER_DESC); +module_param(speed, int, 0); +MODULE_PARM_DESC(speed, "Initial speed: 0 (slowest) - 6 (fastest)"); +module_param(size, int, 0); +MODULE_PARM_DESC(size, "Initial Size 0: 160x120 1: 160x136 2: 176x144 3: 320x240"); +module_param(brightness, int, 0); +MODULE_PARM_DESC(brightness, "Initial brightness 0 - 108"); +module_param(contrast, int, 0); +MODULE_PARM_DESC(contrast, "Initial contrast 0 - 108"); +module_param(saturation, int, 0); +MODULE_PARM_DESC(saturation, "Initial saturation 0 - 108"); +module_param(sharpness, int, 0); +MODULE_PARM_DESC(sharpness, "Initial brightness 0 - 108"); +module_param(whitebal, int, 0); +MODULE_PARM_DESC(whitebal, "Initial white balance 0 - 363"); + +#ifdef CONFIG_USB_DEBUG +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)"); +#endif + +module_init(konicawc_init); +module_exit(konicawc_cleanup); diff --git a/linux/drivers/media/video/usbvideo/ultracam.c b/linux/drivers/media/video/usbvideo/ultracam.c new file mode 100644 index 000000000..10c58b4a2 --- /dev/null +++ b/linux/drivers/media/video/usbvideo/ultracam.c @@ -0,0 +1,679 @@ +/* + * USB NB Camera driver + * + * HISTORY: + * 25-Dec-2002 Dmitri Removed lighting, sharpness parameters, methods. + */ + +#include +#include +#include +#include + +#include "usbvideo.h" + +#define ULTRACAM_VENDOR_ID 0x0461 +#define ULTRACAM_PRODUCT_ID 0x0813 + +#define MAX_CAMERAS 4 /* How many devices we allow to connect */ + +/* + * This structure lives in uvd_t->user field. + */ +typedef struct { + int initialized; /* Had we already sent init sequence? */ + int camera_model; /* What type of IBM camera we got? */ + int has_hdr; +} ultracam_t; +#define ULTRACAM_T(uvd) ((ultracam_t *)((uvd)->user_data)) + +static struct usbvideo *cams = NULL; + +static int debug = 0; + +static int flags = 0; /* FLAGS_DISPLAY_HINTS | FLAGS_OVERLAY_STATS; */ + +static const int min_canvasWidth = 8; +static const int min_canvasHeight = 4; + +#define FRAMERATE_MIN 0 +#define FRAMERATE_MAX 6 +static int framerate = -1; + +/* + * Here we define several initialization variables. They may + * be used to automatically set color, hue, brightness and + * contrast to desired values. This is particularly useful in + * case of webcams (which have no controls and no on-screen + * output) and also when a client V4L software is used that + * does not have some of those controls. In any case it's + * good to have startup values as options. + * + * These values are all in [0..255] range. This simplifies + * operation. Note that actual values of V4L variables may + * be scaled up (as much as << 8). User can see that only + * on overlay output, however, or through a V4L client. + */ +static int init_brightness = 128; +static int init_contrast = 192; +static int init_color = 128; +static int init_hue = 128; +static int hue_correction = 128; + +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)"); +module_param(flags, int, 0); +MODULE_PARM_DESC(flags, + "Bitfield: 0=VIDIOCSYNC, " + "1=B/W, " + "2=show hints, " + "3=show stats, " + "4=test pattern, " + "5=separate frames, " + "6=clean frames"); +module_param(framerate, int, 0); +MODULE_PARM_DESC(framerate, "Framerate setting: 0=slowest, 6=fastest (default=2)"); + +module_param(init_brightness, int, 0); +MODULE_PARM_DESC(init_brightness, "Brightness preconfiguration: 0-255 (default=128)"); +module_param(init_contrast, int, 0); +MODULE_PARM_DESC(init_contrast, "Contrast preconfiguration: 0-255 (default=192)"); +module_param(init_color, int, 0); +MODULE_PARM_DESC(init_color, "Color preconfiguration: 0-255 (default=128)"); +module_param(init_hue, int, 0); +MODULE_PARM_DESC(init_hue, "Hue preconfiguration: 0-255 (default=128)"); +module_param(hue_correction, int, 0); +MODULE_PARM_DESC(hue_correction, "YUV colorspace regulation: 0-255 (default=128)"); + +/* + * ultracam_ProcessIsocData() + * + * Generic routine to parse the ring queue data. It employs either + * ultracam_find_header() or ultracam_parse_lines() to do most + * of work. + * + * 02-Nov-2000 First (mostly dummy) version. + * 06-Nov-2000 Rewrote to dump all data into frame. + */ +static void ultracam_ProcessIsocData(struct uvd *uvd, struct usbvideo_frame *frame) +{ + int n; + + assert(uvd != NULL); + assert(frame != NULL); + + /* Try to move data from queue into frame buffer */ + n = RingQueue_GetLength(&uvd->dp); + if (n > 0) { + int m; + /* See how much spare we have left */ + m = uvd->max_frame_size - frame->seqRead_Length; + if (n > m) + n = m; + /* Now move that much data into frame buffer */ + RingQueue_Dequeue( + &uvd->dp, + frame->data + frame->seqRead_Length, + m); + frame->seqRead_Length += m; + } + /* See if we filled the frame */ + if (frame->seqRead_Length >= uvd->max_frame_size) { + frame->frameState = FrameState_Done; + uvd->curframe = -1; + uvd->stats.frame_num++; + } +} + +/* + * ultracam_veio() + * + * History: + * 1/27/00 Added check for dev == NULL; this happens if camera is unplugged. + */ +static int ultracam_veio( + struct uvd *uvd, + unsigned char req, + unsigned short value, + unsigned short index, + int is_out) +{ + static const char proc[] = "ultracam_veio"; + unsigned char cp[8] /* = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } */; + int i; + + if (!CAMERA_IS_OPERATIONAL(uvd)) + return 0; + + if (!is_out) { + i = usb_control_msg( + uvd->dev, + usb_rcvctrlpipe(uvd->dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + cp, + sizeof(cp), + 1000); +#if 1 + info("USB => %02x%02x%02x%02x%02x%02x%02x%02x " + "(req=$%02x val=$%04x ind=$%04x)", + cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7], + req, value, index); +#endif + } else { + i = usb_control_msg( + uvd->dev, + usb_sndctrlpipe(uvd->dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + NULL, + 0, + 1000); + } + if (i < 0) { + err("%s: ERROR=%d. Camera stopped; Reconnect or reload driver.", + proc, i); + uvd->last_error = i; + } + return i; +} + +/* + * ultracam_calculate_fps() + */ +static int ultracam_calculate_fps(struct uvd *uvd) +{ + return 3 + framerate*4 + framerate/2; +} + +/* + * ultracam_adjust_contrast() + */ +static void ultracam_adjust_contrast(struct uvd *uvd) +{ +} + +/* + * ultracam_set_brightness() + * + * This procedure changes brightness of the picture. + */ +static void ultracam_set_brightness(struct uvd *uvd) +{ +} + +static void ultracam_set_hue(struct uvd *uvd) +{ +} + +/* + * ultracam_adjust_picture() + * + * This procedure gets called from V4L interface to update picture settings. + * Here we change brightness and contrast. + */ +static void ultracam_adjust_picture(struct uvd *uvd) +{ + ultracam_adjust_contrast(uvd); + ultracam_set_brightness(uvd); + ultracam_set_hue(uvd); +} + +/* + * ultracam_video_stop() + * + * This code tells camera to stop streaming. The interface remains + * configured and bandwidth - claimed. + */ +static void ultracam_video_stop(struct uvd *uvd) +{ +} + +/* + * ultracam_reinit_iso() + * + * This procedure sends couple of commands to the camera and then + * resets the video pipe. This sequence was observed to reinit the + * camera or, at least, to initiate ISO data stream. + */ +static void ultracam_reinit_iso(struct uvd *uvd, int do_stop) +{ +} + +static void ultracam_video_start(struct uvd *uvd) +{ + ultracam_reinit_iso(uvd, 0); +} + +static int ultracam_resetPipe(struct uvd *uvd) +{ + usb_clear_halt(uvd->dev, uvd->video_endp); + return 0; +} + +static int ultracam_alternateSetting(struct uvd *uvd, int setting) +{ + static const char proc[] = "ultracam_alternateSetting"; + int i; + i = usb_set_interface(uvd->dev, uvd->iface, setting); + if (i < 0) { + err("%s: usb_set_interface error", proc); + uvd->last_error = i; + return -EBUSY; + } + return 0; +} + +/* + * Return negative code on failure, 0 on success. + */ +static int ultracam_setup_on_open(struct uvd *uvd) +{ + int setup_ok = 0; /* Success by default */ + /* Send init sequence only once, it's large! */ + if (!ULTRACAM_T(uvd)->initialized) { + ultracam_alternateSetting(uvd, 0x04); + ultracam_alternateSetting(uvd, 0x00); + ultracam_veio(uvd, 0x02, 0x0004, 0x000b, 1); + ultracam_veio(uvd, 0x02, 0x0001, 0x0005, 1); + ultracam_veio(uvd, 0x02, 0x8000, 0x0000, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0000, 1); + ultracam_veio(uvd, 0x00, 0x00b0, 0x0001, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0002, 1); + ultracam_veio(uvd, 0x00, 0x000c, 0x0003, 1); + ultracam_veio(uvd, 0x00, 0x000b, 0x0004, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0005, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0006, 1); + ultracam_veio(uvd, 0x00, 0x0079, 0x0007, 1); + ultracam_veio(uvd, 0x00, 0x003b, 0x0008, 1); + ultracam_veio(uvd, 0x00, 0x0002, 0x000f, 1); + ultracam_veio(uvd, 0x00, 0x0001, 0x0010, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0011, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00bf, 1); + ultracam_veio(uvd, 0x00, 0x0001, 0x00c0, 1); + ultracam_veio(uvd, 0x00, 0x0010, 0x00cb, 1); + ultracam_veio(uvd, 0x01, 0x00a4, 0x0001, 1); + ultracam_veio(uvd, 0x01, 0x0010, 0x0002, 1); + ultracam_veio(uvd, 0x01, 0x0066, 0x0007, 1); + ultracam_veio(uvd, 0x01, 0x000b, 0x0008, 1); + ultracam_veio(uvd, 0x01, 0x0034, 0x0009, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x000a, 1); + ultracam_veio(uvd, 0x01, 0x002e, 0x000b, 1); + ultracam_veio(uvd, 0x01, 0x00d6, 0x000c, 1); + ultracam_veio(uvd, 0x01, 0x00fc, 0x000d, 1); + ultracam_veio(uvd, 0x01, 0x00f1, 0x000e, 1); + ultracam_veio(uvd, 0x01, 0x00da, 0x000f, 1); + ultracam_veio(uvd, 0x01, 0x0036, 0x0010, 1); + ultracam_veio(uvd, 0x01, 0x000b, 0x0011, 1); + ultracam_veio(uvd, 0x01, 0x0001, 0x0012, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x0013, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x0014, 1); + ultracam_veio(uvd, 0x01, 0x0087, 0x0051, 1); + ultracam_veio(uvd, 0x01, 0x0040, 0x0052, 1); + ultracam_veio(uvd, 0x01, 0x0058, 0x0053, 1); + ultracam_veio(uvd, 0x01, 0x0040, 0x0054, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x0040, 1); + ultracam_veio(uvd, 0x01, 0x0010, 0x0041, 1); + ultracam_veio(uvd, 0x01, 0x0020, 0x0042, 1); + ultracam_veio(uvd, 0x01, 0x0030, 0x0043, 1); + ultracam_veio(uvd, 0x01, 0x0040, 0x0044, 1); + ultracam_veio(uvd, 0x01, 0x0050, 0x0045, 1); + ultracam_veio(uvd, 0x01, 0x0060, 0x0046, 1); + ultracam_veio(uvd, 0x01, 0x0070, 0x0047, 1); + ultracam_veio(uvd, 0x01, 0x0080, 0x0048, 1); + ultracam_veio(uvd, 0x01, 0x0090, 0x0049, 1); + ultracam_veio(uvd, 0x01, 0x00a0, 0x004a, 1); + ultracam_veio(uvd, 0x01, 0x00b0, 0x004b, 1); + ultracam_veio(uvd, 0x01, 0x00c0, 0x004c, 1); + ultracam_veio(uvd, 0x01, 0x00d0, 0x004d, 1); + ultracam_veio(uvd, 0x01, 0x00e0, 0x004e, 1); + ultracam_veio(uvd, 0x01, 0x00f0, 0x004f, 1); + ultracam_veio(uvd, 0x01, 0x00ff, 0x0050, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x0056, 1); + ultracam_veio(uvd, 0x00, 0x0080, 0x00c1, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c2, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0080, 0x00c1, 1); + ultracam_veio(uvd, 0x00, 0x0004, 0x00c2, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0002, 0x00c1, 1); + ultracam_veio(uvd, 0x00, 0x0020, 0x00c2, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0040, 0x00c1, 1); + ultracam_veio(uvd, 0x00, 0x0017, 0x00c2, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1); + ultracam_veio(uvd, 0x00, 0x00c0, 0x00c1, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00c2, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1); + ultracam_veio(uvd, 0x02, 0xc040, 0x0001, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x0008, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x0009, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x000a, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x000b, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x000c, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x000d, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x000e, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x000f, 0); + ultracam_veio(uvd, 0x01, 0x0000, 0x0010, 0); + ultracam_veio(uvd, 0x01, 0x000b, 0x0008, 1); + ultracam_veio(uvd, 0x01, 0x0034, 0x0009, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x000a, 1); + ultracam_veio(uvd, 0x01, 0x002e, 0x000b, 1); + ultracam_veio(uvd, 0x01, 0x00d6, 0x000c, 1); + ultracam_veio(uvd, 0x01, 0x00fc, 0x000d, 1); + ultracam_veio(uvd, 0x01, 0x00f1, 0x000e, 1); + ultracam_veio(uvd, 0x01, 0x00da, 0x000f, 1); + ultracam_veio(uvd, 0x01, 0x0036, 0x0010, 1); + ultracam_veio(uvd, 0x01, 0x0000, 0x0001, 0); + ultracam_veio(uvd, 0x01, 0x0064, 0x0001, 1); + ultracam_veio(uvd, 0x01, 0x0059, 0x0051, 1); + ultracam_veio(uvd, 0x01, 0x003f, 0x0052, 1); + ultracam_veio(uvd, 0x01, 0x0094, 0x0053, 1); + ultracam_veio(uvd, 0x01, 0x00ff, 0x0011, 1); + ultracam_veio(uvd, 0x01, 0x0003, 0x0012, 1); + ultracam_veio(uvd, 0x01, 0x00f7, 0x0013, 1); + ultracam_veio(uvd, 0x00, 0x0009, 0x0011, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0001, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x0000, 1); + ultracam_veio(uvd, 0x00, 0x0020, 0x00c1, 1); + ultracam_veio(uvd, 0x00, 0x0010, 0x00c2, 1); + ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1); + ultracam_alternateSetting(uvd, 0x04); + ultracam_veio(uvd, 0x02, 0x0000, 0x0001, 1); + ultracam_veio(uvd, 0x02, 0x0000, 0x0001, 1); + ultracam_veio(uvd, 0x02, 0x0000, 0x0006, 1); + ultracam_veio(uvd, 0x02, 0x9000, 0x0007, 1); + ultracam_veio(uvd, 0x02, 0x0042, 0x0001, 1); + ultracam_veio(uvd, 0x02, 0x0000, 0x000b, 0); + ultracam_resetPipe(uvd); + ULTRACAM_T(uvd)->initialized = (setup_ok != 0); + } + return setup_ok; +} + +static void ultracam_configure_video(struct uvd *uvd) +{ + if (uvd == NULL) + return; + + RESTRICT_TO_RANGE(init_brightness, 0, 255); + RESTRICT_TO_RANGE(init_contrast, 0, 255); + RESTRICT_TO_RANGE(init_color, 0, 255); + RESTRICT_TO_RANGE(init_hue, 0, 255); + RESTRICT_TO_RANGE(hue_correction, 0, 255); + + memset(&uvd->vpic, 0, sizeof(uvd->vpic)); + memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old)); + + uvd->vpic.colour = init_color << 8; + uvd->vpic.hue = init_hue << 8; + uvd->vpic.brightness = init_brightness << 8; + uvd->vpic.contrast = init_contrast << 8; + uvd->vpic.whiteness = 105 << 8; /* This one isn't used */ + uvd->vpic.depth = 24; + uvd->vpic.palette = VIDEO_PALETTE_RGB24; + + memset(&uvd->vcap, 0, sizeof(uvd->vcap)); + strcpy(uvd->vcap.name, "IBM Ultra Camera"); + uvd->vcap.type = VID_TYPE_CAPTURE; + uvd->vcap.channels = 1; + uvd->vcap.audios = 0; + uvd->vcap.maxwidth = VIDEOSIZE_X(uvd->canvas); + uvd->vcap.maxheight = VIDEOSIZE_Y(uvd->canvas); + uvd->vcap.minwidth = min_canvasWidth; + uvd->vcap.minheight = min_canvasHeight; + + memset(&uvd->vchan, 0, sizeof(uvd->vchan)); + uvd->vchan.flags = 0; + uvd->vchan.tuners = 0; + uvd->vchan.channel = 0; + uvd->vchan.type = VIDEO_TYPE_CAMERA; + strcpy(uvd->vchan.name, "Camera"); +} + +/* + * ultracam_probe() + * + * This procedure queries device descriptor and accepts the interface + * if it looks like our camera. + * + * History: + * 12-Nov-2000 Reworked to comply with new probe() signature. + * 23-Jan-2001 Added compatibility with 2.2.x kernels. + */ +static int ultracam_probe(struct usb_interface *intf, const struct usb_device_id *devid) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct uvd *uvd = NULL; + int ix, i, nas; + int actInterface=-1, inactInterface=-1, maxPS=0; + unsigned char video_ep = 0; + + if (debug >= 1) + info("ultracam_probe(%p)", intf); + + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return -ENODEV; + + info("IBM Ultra camera found (rev. 0x%04x)", + le16_to_cpu(dev->descriptor.bcdDevice)); + + /* Validate found interface: must have one ISO endpoint */ + nas = intf->num_altsetting; + if (debug > 0) + info("Number of alternate settings=%d.", nas); + if (nas < 8) { + err("Too few alternate settings for this camera!"); + return -ENODEV; + } + /* Validate all alternate settings */ + for (ix=0; ix < nas; ix++) { + const struct usb_host_interface *interface; + const struct usb_endpoint_descriptor *endpoint; + + interface = &intf->altsetting[ix]; + i = interface->desc.bAlternateSetting; + if (interface->desc.bNumEndpoints != 1) { + err("Interface %d. has %u. endpoints!", + interface->desc.bInterfaceNumber, + (unsigned)(interface->desc.bNumEndpoints)); + return -ENODEV; + } + endpoint = &interface->endpoint[0].desc; + if (video_ep == 0) + video_ep = endpoint->bEndpointAddress; + else if (video_ep != endpoint->bEndpointAddress) { + err("Alternate settings have different endpoint addresses!"); + return -ENODEV; + } + if ((endpoint->bmAttributes & 0x03) != 0x01) { + err("Interface %d. has non-ISO endpoint!", + interface->desc.bInterfaceNumber); + return -ENODEV; + } + if ((endpoint->bEndpointAddress & 0x80) == 0) { + err("Interface %d. has ISO OUT endpoint!", + interface->desc.bInterfaceNumber); + return -ENODEV; + } + if (le16_to_cpu(endpoint->wMaxPacketSize) == 0) { + if (inactInterface < 0) + inactInterface = i; + else { + err("More than one inactive alt. setting!"); + return -ENODEV; + } + } else { + if (actInterface < 0) { + actInterface = i; + maxPS = le16_to_cpu(endpoint->wMaxPacketSize); + if (debug > 0) + info("Active setting=%d. maxPS=%d.", i, maxPS); + } else { + /* Got another active alt. setting */ + if (maxPS < le16_to_cpu(endpoint->wMaxPacketSize)) { + /* This one is better! */ + actInterface = i; + maxPS = le16_to_cpu(endpoint->wMaxPacketSize); + if (debug > 0) { + info("Even better ctive setting=%d. maxPS=%d.", + i, maxPS); + } + } + } + } + } + if ((maxPS <= 0) || (actInterface < 0) || (inactInterface < 0)) { + err("Failed to recognize the camera!"); + return -ENODEV; + } + + uvd = usbvideo_AllocateDevice(cams); + if (uvd != NULL) { + /* Here uvd is a fully allocated uvd object */ + uvd->flags = flags; + uvd->debug = debug; + uvd->dev = dev; + uvd->iface = intf->altsetting->desc.bInterfaceNumber; + uvd->ifaceAltInactive = inactInterface; + uvd->ifaceAltActive = actInterface; + uvd->video_endp = video_ep; + uvd->iso_packet_len = maxPS; + uvd->paletteBits = 1L << VIDEO_PALETTE_RGB24; + uvd->defaultPalette = VIDEO_PALETTE_RGB24; + uvd->canvas = VIDEOSIZE(640, 480); /* FIXME */ + uvd->videosize = uvd->canvas; /* ultracam_size_to_videosize(size);*/ + + /* Initialize ibmcam-specific data */ + assert(ULTRACAM_T(uvd) != NULL); + ULTRACAM_T(uvd)->camera_model = 0; /* Not used yet */ + ULTRACAM_T(uvd)->initialized = 0; + + ultracam_configure_video(uvd); + + i = usbvideo_RegisterVideoDevice(uvd); + if (i != 0) { + err("usbvideo_RegisterVideoDevice() failed."); + uvd = NULL; + } + } + + if (uvd) { + usb_set_intfdata (intf, uvd); + return 0; + } + return -EIO; +} + + +static struct usb_device_id id_table[] = { + { USB_DEVICE(ULTRACAM_VENDOR_ID, ULTRACAM_PRODUCT_ID) }, + { } /* Terminating entry */ +}; + +/* + * ultracam_init() + * + * This code is run to initialize the driver. + */ +static int __init ultracam_init(void) +{ + struct usbvideo_cb cbTbl; + memset(&cbTbl, 0, sizeof(cbTbl)); + cbTbl.probe = ultracam_probe; + cbTbl.setupOnOpen = ultracam_setup_on_open; + cbTbl.videoStart = ultracam_video_start; + cbTbl.videoStop = ultracam_video_stop; + cbTbl.processData = ultracam_ProcessIsocData; + cbTbl.postProcess = usbvideo_DeinterlaceFrame; + cbTbl.adjustPicture = ultracam_adjust_picture; + cbTbl.getFPS = ultracam_calculate_fps; + return usbvideo_register( + &cams, + MAX_CAMERAS, + sizeof(ultracam_t), + "ultracam", + &cbTbl, + THIS_MODULE, + id_table); +} + +static void __exit ultracam_cleanup(void) +{ + usbvideo_Deregister(&cams); +} + +MODULE_DEVICE_TABLE(usb, id_table); +MODULE_LICENSE("GPL"); + +module_init(ultracam_init); +module_exit(ultracam_cleanup); diff --git a/linux/drivers/media/video/usbvideo/usbvideo.c b/linux/drivers/media/video/usbvideo/usbvideo.c new file mode 100644 index 000000000..13b37c8c0 --- /dev/null +++ b/linux/drivers/media/video/usbvideo/usbvideo.c @@ -0,0 +1,2190 @@ +/* + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "usbvideo.h" + +#if defined(MAP_NR) +#define virt_to_page(v) MAP_NR(v) /* Kernels 2.2.x */ +#endif + +static int video_nr = -1; +module_param(video_nr, int, 0); + +/* + * Local prototypes. + */ +static void usbvideo_Disconnect(struct usb_interface *intf); +static void usbvideo_CameraRelease(struct uvd *uvd); + +static int usbvideo_v4l_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma); +static int usbvideo_v4l_open(struct inode *inode, struct file *file); +static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos); +static int usbvideo_v4l_close(struct inode *inode, struct file *file); + +static int usbvideo_StartDataPump(struct uvd *uvd); +static void usbvideo_StopDataPump(struct uvd *uvd); +static int usbvideo_GetFrame(struct uvd *uvd, int frameNum); +static int usbvideo_NewFrame(struct uvd *uvd, int framenum); +static void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd, + struct usbvideo_frame *frame); + +/*******************************/ +/* Memory management functions */ +/*******************************/ +static void *usbvideo_rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + +static void usbvideo_rvfree(void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + +static void RingQueue_Initialize(struct RingQueue *rq) +{ + assert(rq != NULL); + init_waitqueue_head(&rq->wqh); +} + +static void RingQueue_Allocate(struct RingQueue *rq, int rqLen) +{ + /* Make sure the requested size is a power of 2 and + round up if necessary. This allows index wrapping + using masks rather than modulo */ + + int i = 1; + assert(rq != NULL); + assert(rqLen > 0); + + while(rqLen >> i) + i++; + if(rqLen != 1 << (i-1)) + rqLen = 1 << i; + + rq->length = rqLen; + rq->ri = rq->wi = 0; + rq->queue = usbvideo_rvmalloc(rq->length); + assert(rq->queue != NULL); +} + +static int RingQueue_IsAllocated(const struct RingQueue *rq) +{ + if (rq == NULL) + return 0; + return (rq->queue != NULL) && (rq->length > 0); +} + +static void RingQueue_Free(struct RingQueue *rq) +{ + assert(rq != NULL); + if (RingQueue_IsAllocated(rq)) { + usbvideo_rvfree(rq->queue, rq->length); + rq->queue = NULL; + rq->length = 0; + } +} + +int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len) +{ + int rql, toread; + + assert(rq != NULL); + assert(dst != NULL); + + rql = RingQueue_GetLength(rq); + if(!rql) + return 0; + + /* Clip requested length to available data */ + if(len > rql) + len = rql; + + toread = len; + if(rq->ri > rq->wi) { + /* Read data from tail */ + int read = (toread < (rq->length - rq->ri)) ? toread : rq->length - rq->ri; + memcpy(dst, rq->queue + rq->ri, read); + toread -= read; + dst += read; + rq->ri = (rq->ri + read) & (rq->length-1); + } + if(toread) { + /* Read data from head */ + memcpy(dst, rq->queue + rq->ri, toread); + rq->ri = (rq->ri + toread) & (rq->length-1); + } + return len; +} + +EXPORT_SYMBOL(RingQueue_Dequeue); + +int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n) +{ + int enqueued = 0; + + assert(rq != NULL); + assert(cdata != NULL); + assert(rq->length > 0); + while (n > 0) { + int m, q_avail; + + /* Calculate the largest chunk that fits the tail of the ring */ + q_avail = rq->length - rq->wi; + if (q_avail <= 0) { + rq->wi = 0; + q_avail = rq->length; + } + m = n; + assert(q_avail > 0); + if (m > q_avail) + m = q_avail; + + memcpy(rq->queue + rq->wi, cdata, m); + RING_QUEUE_ADVANCE_INDEX(rq, wi, m); + cdata += m; + enqueued += m; + n -= m; + } + return enqueued; +} + +EXPORT_SYMBOL(RingQueue_Enqueue); + +static void RingQueue_InterruptibleSleepOn(struct RingQueue *rq) +{ + assert(rq != NULL); + interruptible_sleep_on(&rq->wqh); +} + +void RingQueue_WakeUpInterruptible(struct RingQueue *rq) +{ + assert(rq != NULL); + if (waitqueue_active(&rq->wqh)) + wake_up_interruptible(&rq->wqh); +} + +EXPORT_SYMBOL(RingQueue_WakeUpInterruptible); + +void RingQueue_Flush(struct RingQueue *rq) +{ + assert(rq != NULL); + rq->ri = 0; + rq->wi = 0; +} + +EXPORT_SYMBOL(RingQueue_Flush); + + +/* + * usbvideo_VideosizeToString() + * + * This procedure converts given videosize value to readable string. + * + * History: + * 07-Aug-2000 Created. + * 19-Oct-2000 Reworked for usbvideo module. + */ +static void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs) +{ + char tmp[40]; + int n; + + n = 1 + sprintf(tmp, "%ldx%ld", VIDEOSIZE_X(vs), VIDEOSIZE_Y(vs)); + assert(n < sizeof(tmp)); + if ((buf == NULL) || (bufLen < n)) + err("usbvideo_VideosizeToString: buffer is too small."); + else + memmove(buf, tmp, n); +} + +/* + * usbvideo_OverlayChar() + * + * History: + * 01-Feb-2000 Created. + */ +static void usbvideo_OverlayChar(struct uvd *uvd, struct usbvideo_frame *frame, + int x, int y, int ch) +{ + static const unsigned short digits[16] = { + 0xF6DE, /* 0 */ + 0x2492, /* 1 */ + 0xE7CE, /* 2 */ + 0xE79E, /* 3 */ + 0xB792, /* 4 */ + 0xF39E, /* 5 */ + 0xF3DE, /* 6 */ + 0xF492, /* 7 */ + 0xF7DE, /* 8 */ + 0xF79E, /* 9 */ + 0x77DA, /* a */ + 0xD75C, /* b */ + 0xF24E, /* c */ + 0xD6DC, /* d */ + 0xF34E, /* e */ + 0xF348 /* f */ + }; + unsigned short digit; + int ix, iy; + + if ((uvd == NULL) || (frame == NULL)) + return; + + if (ch >= '0' && ch <= '9') + ch -= '0'; + else if (ch >= 'A' && ch <= 'F') + ch = 10 + (ch - 'A'); + else if (ch >= 'a' && ch <= 'f') + ch = 10 + (ch - 'a'); + else + return; + digit = digits[ch]; + + for (iy=0; iy < 5; iy++) { + for (ix=0; ix < 3; ix++) { + if (digit & 0x8000) { + if (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24)) { +/* TODO */ RGB24_PUTPIXEL(frame, x+ix, y+iy, 0xFF, 0xFF, 0xFF); + } + } + digit = digit << 1; + } + } +} + +/* + * usbvideo_OverlayString() + * + * History: + * 01-Feb-2000 Created. + */ +static void usbvideo_OverlayString(struct uvd *uvd, struct usbvideo_frame *frame, + int x, int y, const char *str) +{ + while (*str) { + usbvideo_OverlayChar(uvd, frame, x, y, *str); + str++; + x += 4; /* 3 pixels character + 1 space */ + } +} + +/* + * usbvideo_OverlayStats() + * + * Overlays important debugging information. + * + * History: + * 01-Feb-2000 Created. + */ +static void usbvideo_OverlayStats(struct uvd *uvd, struct usbvideo_frame *frame) +{ + const int y_diff = 8; + char tmp[16]; + int x = 10, y=10; + long i, j, barLength; + const int qi_x1 = 60, qi_y1 = 10; + const int qi_x2 = VIDEOSIZE_X(frame->request) - 10, qi_h = 10; + + /* Call the user callback, see if we may proceed after that */ + if (VALID_CALLBACK(uvd, overlayHook)) { + if (GET_CALLBACK(uvd, overlayHook)(uvd, frame) < 0) + return; + } + + /* + * We draw a (mostly) hollow rectangle with qi_xxx coordinates. + * Left edge symbolizes the queue index 0; right edge symbolizes + * the full capacity of the queue. + */ + barLength = qi_x2 - qi_x1 - 2; + if ((barLength > 10) && (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24))) { +/* TODO */ long u_lo, u_hi, q_used; + long m_ri, m_wi, m_lo, m_hi; + + /* + * Determine fill zones (used areas of the queue): + * 0 xxxxxxx u_lo ...... uvd->dp.ri xxxxxxxx u_hi ..... uvd->dp.length + * + * if u_lo < 0 then there is no first filler. + */ + + q_used = RingQueue_GetLength(&uvd->dp); + if ((uvd->dp.ri + q_used) >= uvd->dp.length) { + u_hi = uvd->dp.length; + u_lo = (q_used + uvd->dp.ri) & (uvd->dp.length-1); + } else { + u_hi = (q_used + uvd->dp.ri); + u_lo = -1; + } + + /* Convert byte indices into screen units */ + m_ri = qi_x1 + ((barLength * uvd->dp.ri) / uvd->dp.length); + m_wi = qi_x1 + ((barLength * uvd->dp.wi) / uvd->dp.length); + m_lo = (u_lo > 0) ? (qi_x1 + ((barLength * u_lo) / uvd->dp.length)) : -1; + m_hi = qi_x1 + ((barLength * u_hi) / uvd->dp.length); + + for (j=qi_y1; j < (qi_y1 + qi_h); j++) { + for (i=qi_x1; i < qi_x2; i++) { + /* Draw border lines */ + if ((j == qi_y1) || (j == (qi_y1 + qi_h - 1)) || + (i == qi_x1) || (i == (qi_x2 - 1))) { + RGB24_PUTPIXEL(frame, i, j, 0xFF, 0xFF, 0xFF); + continue; + } + /* For all other points the Y coordinate does not matter */ + if ((i >= m_ri) && (i <= (m_ri + 3))) { + RGB24_PUTPIXEL(frame, i, j, 0x00, 0xFF, 0x00); + } else if ((i >= m_wi) && (i <= (m_wi + 3))) { + RGB24_PUTPIXEL(frame, i, j, 0xFF, 0x00, 0x00); + } else if ((i < m_lo) || ((i > m_ri) && (i < m_hi))) + RGB24_PUTPIXEL(frame, i, j, 0x00, 0x00, 0xFF); + } + } + } + + sprintf(tmp, "%8lx", uvd->stats.frame_num); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.urb_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.urb_length); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.data_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.header_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.iso_skip_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.iso_err_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", uvd->vpic.colour); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", uvd->vpic.hue); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", uvd->vpic.brightness >> 8); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", uvd->vpic.contrast >> 12); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8d", uvd->vpic.whiteness >> 8); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; +} + +/* + * usbvideo_ReportStatistics() + * + * This procedure prints packet and transfer statistics. + * + * History: + * 14-Jan-2000 Corrected default multiplier. + */ +static void usbvideo_ReportStatistics(const struct uvd *uvd) +{ + if ((uvd != NULL) && (uvd->stats.urb_count > 0)) { + unsigned long allPackets, badPackets, goodPackets, percent; + allPackets = uvd->stats.urb_count * CAMERA_URB_FRAMES; + badPackets = uvd->stats.iso_skip_count + uvd->stats.iso_err_count; + goodPackets = allPackets - badPackets; + /* Calculate percentage wisely, remember integer limits */ + assert(allPackets != 0); + if (goodPackets < (((unsigned long)-1)/100)) + percent = (100 * goodPackets) / allPackets; + else + percent = goodPackets / (allPackets / 100); + info("Packet Statistics: Total=%lu. Empty=%lu. Usage=%lu%%", + allPackets, badPackets, percent); + if (uvd->iso_packet_len > 0) { + unsigned long allBytes, xferBytes; + char multiplier = ' '; + allBytes = allPackets * uvd->iso_packet_len; + xferBytes = uvd->stats.data_count; + assert(allBytes != 0); + if (xferBytes < (((unsigned long)-1)/100)) + percent = (100 * xferBytes) / allBytes; + else + percent = xferBytes / (allBytes / 100); + /* Scale xferBytes for easy reading */ + if (xferBytes > 10*1024) { + xferBytes /= 1024; + multiplier = 'K'; + if (xferBytes > 10*1024) { + xferBytes /= 1024; + multiplier = 'M'; + if (xferBytes > 10*1024) { + xferBytes /= 1024; + multiplier = 'G'; + if (xferBytes > 10*1024) { + xferBytes /= 1024; + multiplier = 'T'; + } + } + } + } + info("Transfer Statistics: Transferred=%lu%cB Usage=%lu%%", + xferBytes, multiplier, percent); + } + } +} + +/* + * usbvideo_TestPattern() + * + * Procedure forms a test pattern (yellow grid on blue background). + * + * Parameters: + * fullframe: if TRUE then entire frame is filled, otherwise the procedure + * continues from the current scanline. + * pmode 0: fill the frame with solid blue color (like on VCR or TV) + * 1: Draw a colored grid + * + * History: + * 01-Feb-2000 Created. + */ +void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode) +{ + struct usbvideo_frame *frame; + int num_cell = 0; + int scan_length = 0; + static int num_pass = 0; + + if (uvd == NULL) { + err("%s: uvd == NULL", __FUNCTION__); + return; + } + if ((uvd->curframe < 0) || (uvd->curframe >= USBVIDEO_NUMFRAMES)) { + err("%s: uvd->curframe=%d.", __FUNCTION__, uvd->curframe); + return; + } + + /* Grab the current frame */ + frame = &uvd->frame[uvd->curframe]; + + /* Optionally start at the beginning */ + if (fullframe) { + frame->curline = 0; + frame->seqRead_Length = 0; + } +#if 0 + { /* For debugging purposes only */ + char tmp[20]; + usbvideo_VideosizeToString(tmp, sizeof(tmp), frame->request); + info("testpattern: frame=%s", tmp); + } +#endif + /* Form every scan line */ + for (; frame->curline < VIDEOSIZE_Y(frame->request); frame->curline++) { + int i; + unsigned char *f = frame->data + + (VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL * frame->curline); + for (i=0; i < VIDEOSIZE_X(frame->request); i++) { + unsigned char cb=0x80; + unsigned char cg = 0; + unsigned char cr = 0; + + if (pmode == 1) { + if (frame->curline % 32 == 0) + cb = 0, cg = cr = 0xFF; + else if (i % 32 == 0) { + if (frame->curline % 32 == 1) + num_cell++; + cb = 0, cg = cr = 0xFF; + } else { + cb = ((num_cell*7) + num_pass) & 0xFF; + cg = ((num_cell*5) + num_pass*2) & 0xFF; + cr = ((num_cell*3) + num_pass*3) & 0xFF; + } + } else { + /* Just the blue screen */ + } + + *f++ = cb; + *f++ = cg; + *f++ = cr; + scan_length += 3; + } + } + + frame->frameState = FrameState_Done; + frame->seqRead_Length += scan_length; + ++num_pass; + + /* We do this unconditionally, regardless of FLAGS_OVERLAY_STATS */ + usbvideo_OverlayStats(uvd, frame); +} + +EXPORT_SYMBOL(usbvideo_TestPattern); + + +#ifdef DEBUG +/* + * usbvideo_HexDump() + * + * A debugging tool. Prints hex dumps. + * + * History: + * 29-Jul-2000 Added printing of offsets. + */ +void usbvideo_HexDump(const unsigned char *data, int len) +{ + const int bytes_per_line = 32; + char tmp[128]; /* 32*3 + 5 */ + int i, k; + + for (i=k=0; len > 0; i++, len--) { + if (i > 0 && ((i % bytes_per_line) == 0)) { + printk("%s\n", tmp); + k=0; + } + if ((i % bytes_per_line) == 0) + k += sprintf(&tmp[k], "%04x: ", i); + k += sprintf(&tmp[k], "%02x ", data[i]); + } + if (k > 0) + printk("%s\n", tmp); +} + +EXPORT_SYMBOL(usbvideo_HexDump); + +#endif + +/* ******************************************************************** */ + +/* XXX: this piece of crap really wants some error handling.. */ +static void usbvideo_ClientIncModCount(struct uvd *uvd) +{ + if (uvd == NULL) { + err("%s: uvd == NULL", __FUNCTION__); + return; + } + if (uvd->handle == NULL) { + err("%s: uvd->handle == NULL", __FUNCTION__); + return; + } + if (uvd->handle->md_module == NULL) { + err("%s: uvd->handle->md_module == NULL", __FUNCTION__); + return; + } + if (!try_module_get(uvd->handle->md_module)) { + err("%s: try_module_get() == 0", __FUNCTION__); + return; + } +} + +static void usbvideo_ClientDecModCount(struct uvd *uvd) +{ + if (uvd == NULL) { + err("%s: uvd == NULL", __FUNCTION__); + return; + } + if (uvd->handle == NULL) { + err("%s: uvd->handle == NULL", __FUNCTION__); + return; + } + if (uvd->handle->md_module == NULL) { + err("%s: uvd->handle->md_module == NULL", __FUNCTION__); + return; + } + module_put(uvd->handle->md_module); +} + +int usbvideo_register( + struct usbvideo **pCams, + const int num_cams, + const int num_extra, + const char *driverName, + const struct usbvideo_cb *cbTbl, + struct module *md, + const struct usb_device_id *id_table) +{ + struct usbvideo *cams; + int i, base_size, result; + + /* Check parameters for sanity */ + if ((num_cams <= 0) || (pCams == NULL) || (cbTbl == NULL)) { + err("%s: Illegal call", __FUNCTION__); + return -EINVAL; + } + + /* Check registration callback - must be set! */ + if (cbTbl->probe == NULL) { + err("%s: probe() is required!", __FUNCTION__); + return -EINVAL; + } + + base_size = num_cams * sizeof(struct uvd) + sizeof(struct usbvideo); + cams = (struct usbvideo *) kzalloc(base_size, GFP_KERNEL); + if (cams == NULL) { + err("Failed to allocate %d. bytes for usbvideo struct", base_size); + return -ENOMEM; + } + dbg("%s: Allocated $%p (%d. bytes) for %d. cameras", + __FUNCTION__, cams, base_size, num_cams); + + /* Copy callbacks, apply defaults for those that are not set */ + memmove(&cams->cb, cbTbl, sizeof(cams->cb)); + if (cams->cb.getFrame == NULL) + cams->cb.getFrame = usbvideo_GetFrame; + if (cams->cb.disconnect == NULL) + cams->cb.disconnect = usbvideo_Disconnect; + if (cams->cb.startDataPump == NULL) + cams->cb.startDataPump = usbvideo_StartDataPump; + if (cams->cb.stopDataPump == NULL) + cams->cb.stopDataPump = usbvideo_StopDataPump; + + cams->num_cameras = num_cams; + cams->cam = (struct uvd *) &cams[1]; + cams->md_module = md; + if (cams->md_module == NULL) + warn("%s: module == NULL!", __FUNCTION__); + mutex_init(&cams->lock); /* to 1 == available */ + + for (i = 0; i < num_cams; i++) { + struct uvd *up = &cams->cam[i]; + + up->handle = cams; + + /* Allocate user_data separately because of kmalloc's limits */ + if (num_extra > 0) { + up->user_size = num_cams * num_extra; + up->user_data = kmalloc(up->user_size, GFP_KERNEL); + if (up->user_data == NULL) { + err("%s: Failed to allocate user_data (%d. bytes)", + __FUNCTION__, up->user_size); + while (i) { + up = &cams->cam[--i]; + kfree(up->user_data); + } + kfree(cams); + return -ENOMEM; + } + dbg("%s: Allocated cams[%d].user_data=$%p (%d. bytes)", + __FUNCTION__, i, up->user_data, up->user_size); + } + } + + /* + * Register ourselves with USB stack. + */ + strcpy(cams->drvName, (driverName != NULL) ? driverName : "Unknown"); + cams->usbdrv.name = cams->drvName; + cams->usbdrv.probe = cams->cb.probe; + cams->usbdrv.disconnect = cams->cb.disconnect; + cams->usbdrv.id_table = id_table; + + /* + * Update global handle to usbvideo. This is very important + * because probe() can be called before usb_register() returns. + * If the handle is not yet updated then the probe() will fail. + */ + *pCams = cams; + result = usb_register(&cams->usbdrv); + if (result) { + for (i = 0; i < num_cams; i++) { + struct uvd *up = &cams->cam[i]; + kfree(up->user_data); + } + kfree(cams); + } + + return result; +} + +EXPORT_SYMBOL(usbvideo_register); + +/* + * usbvideo_Deregister() + * + * Procedure frees all usbvideo and user data structures. Be warned that + * if you had some dynamically allocated components in ->user field then + * you should free them before calling here. + */ +void usbvideo_Deregister(struct usbvideo **pCams) +{ + struct usbvideo *cams; + int i; + + if (pCams == NULL) { + err("%s: pCams == NULL", __FUNCTION__); + return; + } + cams = *pCams; + if (cams == NULL) { + err("%s: cams == NULL", __FUNCTION__); + return; + } + + dbg("%s: Deregistering %s driver.", __FUNCTION__, cams->drvName); + usb_deregister(&cams->usbdrv); + + dbg("%s: Deallocating cams=$%p (%d. cameras)", __FUNCTION__, cams, cams->num_cameras); + for (i=0; i < cams->num_cameras; i++) { + struct uvd *up = &cams->cam[i]; + int warning = 0; + + if (up->user_data != NULL) { + if (up->user_size <= 0) + ++warning; + } else { + if (up->user_size > 0) + ++warning; + } + if (warning) { + err("%s: Warning: user_data=$%p user_size=%d.", + __FUNCTION__, up->user_data, up->user_size); + } else { + dbg("%s: Freeing %d. $%p->user_data=$%p", + __FUNCTION__, i, up, up->user_data); + kfree(up->user_data); + } + } + /* Whole array was allocated in one chunk */ + dbg("%s: Freed %d uvd structures", + __FUNCTION__, cams->num_cameras); + kfree(cams); + *pCams = NULL; +} + +EXPORT_SYMBOL(usbvideo_Deregister); + +/* + * usbvideo_Disconnect() + * + * This procedure stops all driver activity. Deallocation of + * the interface-private structure (pointed by 'ptr') is done now + * (if we don't have any open files) or later, when those files + * are closed. After that driver should be removable. + * + * This code handles surprise removal. The uvd->user is a counter which + * increments on open() and decrements on close(). If we see here that + * this counter is not 0 then we have a client who still has us opened. + * We set uvd->remove_pending flag as early as possible, and after that + * all access to the camera will gracefully fail. These failures should + * prompt client to (eventually) close the video device, and then - in + * usbvideo_v4l_close() - we decrement uvd->uvd_used and usage counter. + * + * History: + * 22-Jan-2000 Added polling of MOD_IN_USE to delay removal until all users gone. + * 27-Jan-2000 Reworked to allow pending disconnects; see xxx_close() + * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). + * 19-Oct-2000 Moved to usbvideo module. + */ +static void usbvideo_Disconnect(struct usb_interface *intf) +{ + struct uvd *uvd = usb_get_intfdata (intf); + int i; + + if (uvd == NULL) { + err("%s($%p): Illegal call.", __FUNCTION__, intf); + return; + } + + usb_set_intfdata (intf, NULL); + + usbvideo_ClientIncModCount(uvd); + if (uvd->debug > 0) + info("%s(%p.)", __FUNCTION__, intf); + + mutex_lock(&uvd->lock); + uvd->remove_pending = 1; /* Now all ISO data will be ignored */ + + /* At this time we ask to cancel outstanding URBs */ + GET_CALLBACK(uvd, stopDataPump)(uvd); + + for (i=0; i < USBVIDEO_NUMSBUF; i++) + usb_free_urb(uvd->sbuf[i].urb); + + usb_put_dev(uvd->dev); + uvd->dev = NULL; /* USB device is no more */ + + video_unregister_device(&uvd->vdev); + if (uvd->debug > 0) + info("%s: Video unregistered.", __FUNCTION__); + + if (uvd->user) + info("%s: In use, disconnect pending.", __FUNCTION__); + else + usbvideo_CameraRelease(uvd); + mutex_unlock(&uvd->lock); + info("USB camera disconnected."); + + usbvideo_ClientDecModCount(uvd); +} + +/* + * usbvideo_CameraRelease() + * + * This code does final release of uvd. This happens + * after the device is disconnected -and- all clients + * closed their files. + * + * History: + * 27-Jan-2000 Created. + */ +static void usbvideo_CameraRelease(struct uvd *uvd) +{ + if (uvd == NULL) { + err("%s: Illegal call", __FUNCTION__); + return; + } + + RingQueue_Free(&uvd->dp); + if (VALID_CALLBACK(uvd, userFree)) + GET_CALLBACK(uvd, userFree)(uvd); + uvd->uvd_used = 0; /* This is atomic, no need to take mutex */ +} + +/* + * usbvideo_find_struct() + * + * This code searches the array of preallocated (static) structures + * and returns index of the first one that isn't in use. Returns -1 + * if there are no free structures. + * + * History: + * 27-Jan-2000 Created. + */ +static int usbvideo_find_struct(struct usbvideo *cams) +{ + int u, rv = -1; + + if (cams == NULL) { + err("No usbvideo handle?"); + return -1; + } + mutex_lock(&cams->lock); + for (u = 0; u < cams->num_cameras; u++) { + struct uvd *uvd = &cams->cam[u]; + if (!uvd->uvd_used) /* This one is free */ + { + uvd->uvd_used = 1; /* In use now */ + mutex_init(&uvd->lock); /* to 1 == available */ + uvd->dev = NULL; + rv = u; + break; + } + } + mutex_unlock(&cams->lock); + return rv; +} + +static struct file_operations usbvideo_fops = { + .owner = THIS_MODULE, + .open = usbvideo_v4l_open, + .release =usbvideo_v4l_close, + .read = usbvideo_v4l_read, + .mmap = usbvideo_v4l_mmap, + .ioctl = usbvideo_v4l_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; +static const struct video_device usbvideo_template = { + .owner = THIS_MODULE, + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_CPIA, + .fops = &usbvideo_fops, +}; + +struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams) +{ + int i, devnum; + struct uvd *uvd = NULL; + + if (cams == NULL) { + err("No usbvideo handle?"); + return NULL; + } + + devnum = usbvideo_find_struct(cams); + if (devnum == -1) { + err("IBM USB camera driver: Too many devices!"); + return NULL; + } + uvd = &cams->cam[devnum]; + dbg("Device entry #%d. at $%p", devnum, uvd); + + /* Not relying upon caller we increase module counter ourselves */ + usbvideo_ClientIncModCount(uvd); + + mutex_lock(&uvd->lock); + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + uvd->sbuf[i].urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); + if (uvd->sbuf[i].urb == NULL) { + err("usb_alloc_urb(%d.) failed.", FRAMES_PER_DESC); + uvd->uvd_used = 0; + uvd = NULL; + goto allocate_done; + } + } + uvd->user=0; + uvd->remove_pending = 0; + uvd->last_error = 0; + RingQueue_Initialize(&uvd->dp); + + /* Initialize video device structure */ + uvd->vdev = usbvideo_template; + sprintf(uvd->vdev.name, "%.20s USB Camera", cams->drvName); + /* + * The client is free to overwrite those because we + * return control to the client's probe function right now. + */ +allocate_done: + mutex_unlock(&uvd->lock); + usbvideo_ClientDecModCount(uvd); + return uvd; +} + +EXPORT_SYMBOL(usbvideo_AllocateDevice); + +int usbvideo_RegisterVideoDevice(struct uvd *uvd) +{ + char tmp1[20], tmp2[20]; /* Buffers for printing */ + + if (uvd == NULL) { + err("%s: Illegal call.", __FUNCTION__); + return -EINVAL; + } + if (uvd->video_endp == 0) { + info("%s: No video endpoint specified; data pump disabled.", __FUNCTION__); + } + if (uvd->paletteBits == 0) { + err("%s: No palettes specified!", __FUNCTION__); + return -EINVAL; + } + if (uvd->defaultPalette == 0) { + info("%s: No default palette!", __FUNCTION__); + } + + uvd->max_frame_size = VIDEOSIZE_X(uvd->canvas) * + VIDEOSIZE_Y(uvd->canvas) * V4L_BYTES_PER_PIXEL; + usbvideo_VideosizeToString(tmp1, sizeof(tmp1), uvd->videosize); + usbvideo_VideosizeToString(tmp2, sizeof(tmp2), uvd->canvas); + + if (uvd->debug > 0) { + info("%s: iface=%d. endpoint=$%02x paletteBits=$%08lx", + __FUNCTION__, uvd->iface, uvd->video_endp, uvd->paletteBits); + } + if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { + err("%s: video_register_device failed", __FUNCTION__); + return -EPIPE; + } + if (uvd->debug > 1) { + info("%s: video_register_device() successful", __FUNCTION__); + } + if (uvd->dev == NULL) { + err("%s: uvd->dev == NULL", __FUNCTION__); + return -EINVAL; + } + + info("%s on /dev/video%d: canvas=%s videosize=%s", + (uvd->handle != NULL) ? uvd->handle->drvName : "???", + uvd->vdev.minor, tmp2, tmp1); + + usb_get_dev(uvd->dev); + return 0; +} + +EXPORT_SYMBOL(usbvideo_RegisterVideoDevice); + +/* ******************************************************************** */ + +static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct uvd *uvd = file->private_data; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end-vma->vm_start; + unsigned long page, pos; + + if (!CAMERA_IS_OPERATIONAL(uvd)) + return -EFAULT; + + if (size > (((USBVIDEO_NUMFRAMES * uvd->max_frame_size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) + return -EINVAL; + + pos = (unsigned long) uvd->fbuf; + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + +/* + * usbvideo_v4l_open() + * + * This is part of Video 4 Linux API. The driver can be opened by one + * client only (checks internal counter 'uvdser'). The procedure + * then allocates buffers needed for video processing. + * + * History: + * 22-Jan-2000 Rewrote, moved scratch buffer allocation here. Now the + * camera is also initialized here (once per connect), at + * expense of V4L client (it waits on open() call). + * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers. + * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). + */ +static int usbvideo_v4l_open(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct uvd *uvd = (struct uvd *) dev; + const int sb_size = FRAMES_PER_DESC * uvd->iso_packet_len; + int i, errCode = 0; + + if (uvd->debug > 1) + info("%s($%p)", __FUNCTION__, dev); + + usbvideo_ClientIncModCount(uvd); + mutex_lock(&uvd->lock); + + if (uvd->user) { + err("%s: Someone tried to open an already opened device!", __FUNCTION__); + errCode = -EBUSY; + } else { + /* Clear statistics */ + memset(&uvd->stats, 0, sizeof(uvd->stats)); + + /* Clean pointers so we know if we allocated something */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) + uvd->sbuf[i].data = NULL; + + /* Allocate memory for the frame buffers */ + uvd->fbuf_size = USBVIDEO_NUMFRAMES * uvd->max_frame_size; + uvd->fbuf = usbvideo_rvmalloc(uvd->fbuf_size); + RingQueue_Allocate(&uvd->dp, RING_QUEUE_SIZE); + if ((uvd->fbuf == NULL) || + (!RingQueue_IsAllocated(&uvd->dp))) { + err("%s: Failed to allocate fbuf or dp", __FUNCTION__); + errCode = -ENOMEM; + } else { + /* Allocate all buffers */ + for (i=0; i < USBVIDEO_NUMFRAMES; i++) { + uvd->frame[i].frameState = FrameState_Unused; + uvd->frame[i].data = uvd->fbuf + i*(uvd->max_frame_size); + /* + * Set default sizes in case IOCTL (VIDIOCMCAPTURE) + * is not used (using read() instead). + */ + uvd->frame[i].canvas = uvd->canvas; + uvd->frame[i].seqRead_Index = 0; + } + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + uvd->sbuf[i].data = kmalloc(sb_size, GFP_KERNEL); + if (uvd->sbuf[i].data == NULL) { + errCode = -ENOMEM; + break; + } + } + } + if (errCode != 0) { + /* Have to free all that memory */ + if (uvd->fbuf != NULL) { + usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size); + uvd->fbuf = NULL; + } + RingQueue_Free(&uvd->dp); + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + kfree(uvd->sbuf[i].data); + uvd->sbuf[i].data = NULL; + } + } + } + + /* If so far no errors then we shall start the camera */ + if (errCode == 0) { + /* Start data pump if we have valid endpoint */ + if (uvd->video_endp != 0) + errCode = GET_CALLBACK(uvd, startDataPump)(uvd); + if (errCode == 0) { + if (VALID_CALLBACK(uvd, setupOnOpen)) { + if (uvd->debug > 1) + info("%s: setupOnOpen callback", __FUNCTION__); + errCode = GET_CALLBACK(uvd, setupOnOpen)(uvd); + if (errCode < 0) { + err("%s: setupOnOpen callback failed (%d.).", + __FUNCTION__, errCode); + } else if (uvd->debug > 1) { + info("%s: setupOnOpen callback successful", __FUNCTION__); + } + } + if (errCode == 0) { + uvd->settingsAdjusted = 0; + if (uvd->debug > 1) + info("%s: Open succeeded.", __FUNCTION__); + uvd->user++; + file->private_data = uvd; + } + } + } + mutex_unlock(&uvd->lock); + if (errCode != 0) + usbvideo_ClientDecModCount(uvd); + if (uvd->debug > 0) + info("%s: Returning %d.", __FUNCTION__, errCode); + return errCode; +} + +/* + * usbvideo_v4l_close() + * + * This is part of Video 4 Linux API. The procedure + * stops streaming and deallocates all buffers that were earlier + * allocated in usbvideo_v4l_open(). + * + * History: + * 22-Jan-2000 Moved scratch buffer deallocation here. + * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers. + * 24-May-2000 Moved MOD_DEC_USE_COUNT outside of code that can sleep. + */ +static int usbvideo_v4l_close(struct inode *inode, struct file *file) +{ + struct video_device *dev = file->private_data; + struct uvd *uvd = (struct uvd *) dev; + int i; + + if (uvd->debug > 1) + info("%s($%p)", __FUNCTION__, dev); + + mutex_lock(&uvd->lock); + GET_CALLBACK(uvd, stopDataPump)(uvd); + usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size); + uvd->fbuf = NULL; + RingQueue_Free(&uvd->dp); + + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + kfree(uvd->sbuf[i].data); + uvd->sbuf[i].data = NULL; + } + +#if USBVIDEO_REPORT_STATS + usbvideo_ReportStatistics(uvd); +#endif + + uvd->user--; + if (uvd->remove_pending) { + if (uvd->debug > 0) + info("usbvideo_v4l_close: Final disconnect."); + usbvideo_CameraRelease(uvd); + } + mutex_unlock(&uvd->lock); + usbvideo_ClientDecModCount(uvd); + + if (uvd->debug > 1) + info("%s: Completed.", __FUNCTION__); + file->private_data = NULL; + return 0; +} + +/* + * usbvideo_v4l_ioctl() + * + * This is part of Video 4 Linux API. The procedure handles ioctl() calls. + * + * History: + * 22-Jan-2000 Corrected VIDIOCSPICT to reject unsupported settings. + */ +static int usbvideo_v4l_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct uvd *uvd = file->private_data; + + if (!CAMERA_IS_OPERATIONAL(uvd)) + return -EIO; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability *b = arg; + *b = uvd->vcap; + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + *v = uvd->vchan; + return 0; + } + case VIDIOCSCHAN: + { + struct video_channel *v = arg; + if (v->channel != 0) + return -EINVAL; + return 0; + } + case VIDIOCGPICT: + { + struct video_picture *pic = arg; + *pic = uvd->vpic; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture *pic = arg; + /* + * Use temporary 'video_picture' structure to preserve our + * own settings (such as color depth, palette) that we + * aren't allowing everyone (V4L client) to change. + */ + uvd->vpic.brightness = pic->brightness; + uvd->vpic.hue = pic->hue; + uvd->vpic.colour = pic->colour; + uvd->vpic.contrast = pic->contrast; + uvd->settingsAdjusted = 0; /* Will force new settings */ + return 0; + } + case VIDIOCSWIN: + { + struct video_window *vw = arg; + + if(VALID_CALLBACK(uvd, setVideoMode)) { + return GET_CALLBACK(uvd, setVideoMode)(uvd, vw); + } + + if (vw->flags) + return -EINVAL; + if (vw->clipcount) + return -EINVAL; + if (vw->width != VIDEOSIZE_X(uvd->canvas)) + return -EINVAL; + if (vw->height != VIDEOSIZE_Y(uvd->canvas)) + return -EINVAL; + + return 0; + } + case VIDIOCGWIN: + { + struct video_window *vw = arg; + + vw->x = 0; + vw->y = 0; + vw->width = VIDEOSIZE_X(uvd->videosize); + vw->height = VIDEOSIZE_Y(uvd->videosize); + vw->chromakey = 0; + if (VALID_CALLBACK(uvd, getFPS)) + vw->flags = GET_CALLBACK(uvd, getFPS)(uvd); + else + vw->flags = 10; /* FIXME: do better! */ + return 0; + } + case VIDIOCGMBUF: + { + struct video_mbuf *vm = arg; + int i; + + memset(vm, 0, sizeof(*vm)); + vm->size = uvd->max_frame_size * USBVIDEO_NUMFRAMES; + vm->frames = USBVIDEO_NUMFRAMES; + for(i = 0; i < USBVIDEO_NUMFRAMES; i++) + vm->offsets[i] = i * uvd->max_frame_size; + + return 0; + } + case VIDIOCMCAPTURE: + { + struct video_mmap *vm = arg; + + if (uvd->debug >= 1) { + info("VIDIOCMCAPTURE: frame=%d. size=%dx%d, format=%d.", + vm->frame, vm->width, vm->height, vm->format); + } + /* + * Check if the requested size is supported. If the requestor + * requests too big a frame then we may be tricked into accessing + * outside of own preallocated frame buffer (in uvd->frame). + * This will cause oops or a security hole. Theoretically, we + * could only clamp the size down to acceptable bounds, but then + * we'd need to figure out how to insert our smaller buffer into + * larger caller's buffer... this is not an easy question. So we + * here just flatly reject too large requests, assuming that the + * caller will resubmit with smaller size. Callers should know + * what size we support (returned by VIDIOCGCAP). However vidcat, + * for one, does not care and allows to ask for any size. + */ + if ((vm->width > VIDEOSIZE_X(uvd->canvas)) || + (vm->height > VIDEOSIZE_Y(uvd->canvas))) { + if (uvd->debug > 0) { + info("VIDIOCMCAPTURE: Size=%dx%d too large; " + "allowed only up to %ldx%ld", vm->width, vm->height, + VIDEOSIZE_X(uvd->canvas), VIDEOSIZE_Y(uvd->canvas)); + } + return -EINVAL; + } + /* Check if the palette is supported */ + if (((1L << vm->format) & uvd->paletteBits) == 0) { + if (uvd->debug > 0) { + info("VIDIOCMCAPTURE: format=%d. not supported" + " (paletteBits=$%08lx)", + vm->format, uvd->paletteBits); + } + return -EINVAL; + } + if ((vm->frame < 0) || (vm->frame >= USBVIDEO_NUMFRAMES)) { + err("VIDIOCMCAPTURE: vm.frame=%d. !E [0-%d]", vm->frame, USBVIDEO_NUMFRAMES-1); + return -EINVAL; + } + if (uvd->frame[vm->frame].frameState == FrameState_Grabbing) { + /* Not an error - can happen */ + } + uvd->frame[vm->frame].request = VIDEOSIZE(vm->width, vm->height); + uvd->frame[vm->frame].palette = vm->format; + + /* Mark it as ready */ + uvd->frame[vm->frame].frameState = FrameState_Ready; + + return usbvideo_NewFrame(uvd, vm->frame); + } + case VIDIOCSYNC: + { + int *frameNum = arg; + int ret; + + if (*frameNum < 0 || *frameNum >= USBVIDEO_NUMFRAMES) + return -EINVAL; + + if (uvd->debug >= 1) + info("VIDIOCSYNC: syncing to frame %d.", *frameNum); + if (uvd->flags & FLAGS_NO_DECODING) + ret = usbvideo_GetFrame(uvd, *frameNum); + else if (VALID_CALLBACK(uvd, getFrame)) { + ret = GET_CALLBACK(uvd, getFrame)(uvd, *frameNum); + if ((ret < 0) && (uvd->debug >= 1)) { + err("VIDIOCSYNC: getFrame() returned %d.", ret); + } + } else { + err("VIDIOCSYNC: getFrame is not set"); + ret = -EFAULT; + } + + /* + * The frame is in FrameState_Done_Hold state. Release it + * right now because its data is already mapped into + * the user space and it's up to the application to + * make use of it until it asks for another frame. + */ + uvd->frame[*frameNum].frameState = FrameState_Unused; + return ret; + } + case VIDIOCGFBUF: + { + struct video_buffer *vb = arg; + + memset(vb, 0, sizeof(*vb)); + return 0; + } + case VIDIOCKEY: + return 0; + + case VIDIOCCAPTURE: + return -EINVAL; + + case VIDIOCSFBUF: + + case VIDIOCGTUNER: + case VIDIOCSTUNER: + + case VIDIOCGFREQ: + case VIDIOCSFREQ: + + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int usbvideo_v4l_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, usbvideo_v4l_do_ioctl); +} + +/* + * usbvideo_v4l_read() + * + * This is mostly boring stuff. We simply ask for a frame and when it + * arrives copy all the video data from it into user space. There is + * no obvious need to override this method. + * + * History: + * 20-Oct-2000 Created. + * 01-Nov-2000 Added mutex (uvd->lock). + */ +static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct uvd *uvd = file->private_data; + int noblock = file->f_flags & O_NONBLOCK; + int frmx = -1, i; + struct usbvideo_frame *frame; + + if (!CAMERA_IS_OPERATIONAL(uvd) || (buf == NULL)) + return -EFAULT; + + if (uvd->debug >= 1) + info("%s: %Zd. bytes, noblock=%d.", __FUNCTION__, count, noblock); + + mutex_lock(&uvd->lock); + + /* See if a frame is completed, then use it. */ + for(i = 0; i < USBVIDEO_NUMFRAMES; i++) { + if ((uvd->frame[i].frameState == FrameState_Done) || + (uvd->frame[i].frameState == FrameState_Done_Hold) || + (uvd->frame[i].frameState == FrameState_Error)) { + frmx = i; + break; + } + } + + /* FIXME: If we don't start a frame here then who ever does? */ + if (noblock && (frmx == -1)) { + count = -EAGAIN; + goto read_done; + } + + /* + * If no FrameState_Done, look for a FrameState_Grabbing state. + * See if a frame is in process (grabbing), then use it. + * We will need to wait until it becomes cooked, of course. + */ + if (frmx == -1) { + for(i = 0; i < USBVIDEO_NUMFRAMES; i++) { + if (uvd->frame[i].frameState == FrameState_Grabbing) { + frmx = i; + break; + } + } + } + + /* + * If no frame is active, start one. We don't care which one + * it will be, so #0 is as good as any. + * In read access mode we don't have convenience of VIDIOCMCAPTURE + * to specify the requested palette (video format) on per-frame + * basis. This means that we have to return data in -some- format + * and just hope that the client knows what to do with it. + * The default format is configured in uvd->defaultPalette field + * as one of VIDEO_PALETTE_xxx values. We stuff it into the new + * frame and initiate the frame filling process. + */ + if (frmx == -1) { + if (uvd->defaultPalette == 0) { + err("%s: No default palette; don't know what to do!", __FUNCTION__); + count = -EFAULT; + goto read_done; + } + frmx = 0; + /* + * We have no per-frame control over video size. + * Therefore we only can use whatever size was + * specified as default. + */ + uvd->frame[frmx].request = uvd->videosize; + uvd->frame[frmx].palette = uvd->defaultPalette; + uvd->frame[frmx].frameState = FrameState_Ready; + usbvideo_NewFrame(uvd, frmx); + /* Now frame 0 is supposed to start filling... */ + } + + /* + * Get a pointer to the active frame. It is either previously + * completed frame or frame in progress but not completed yet. + */ + frame = &uvd->frame[frmx]; + + /* + * Sit back & wait until the frame gets filled and postprocessed. + * If we fail to get the picture [in time] then return the error. + * In this call we specify that we want the frame to be waited for, + * postprocessed and switched into FrameState_Done_Hold state. This + * state is used to hold the frame as "fully completed" between + * subsequent partial reads of the same frame. + */ + if (frame->frameState != FrameState_Done_Hold) { + long rv = -EFAULT; + if (uvd->flags & FLAGS_NO_DECODING) + rv = usbvideo_GetFrame(uvd, frmx); + else if (VALID_CALLBACK(uvd, getFrame)) + rv = GET_CALLBACK(uvd, getFrame)(uvd, frmx); + else + err("getFrame is not set"); + if ((rv != 0) || (frame->frameState != FrameState_Done_Hold)) { + count = rv; + goto read_done; + } + } + + /* + * Copy bytes to user space. We allow for partial reads, which + * means that the user application can request read less than + * the full frame size. It is up to the application to issue + * subsequent calls until entire frame is read. + * + * First things first, make sure we don't copy more than we + * have - even if the application wants more. That would be + * a big security embarassment! + */ + if ((count + frame->seqRead_Index) > frame->seqRead_Length) + count = frame->seqRead_Length - frame->seqRead_Index; + + /* + * Copy requested amount of data to user space. We start + * copying from the position where we last left it, which + * will be zero for a new frame (not read before). + */ + if (copy_to_user(buf, frame->data + frame->seqRead_Index, count)) { + count = -EFAULT; + goto read_done; + } + + /* Update last read position */ + frame->seqRead_Index += count; + if (uvd->debug >= 1) { + err("%s: {copy} count used=%Zd, new seqRead_Index=%ld", + __FUNCTION__, count, frame->seqRead_Index); + } + + /* Finally check if the frame is done with and "release" it */ + if (frame->seqRead_Index >= frame->seqRead_Length) { + /* All data has been read */ + frame->seqRead_Index = 0; + + /* Mark it as available to be used again. */ + uvd->frame[frmx].frameState = FrameState_Unused; + if (usbvideo_NewFrame(uvd, (frmx + 1) % USBVIDEO_NUMFRAMES)) { + err("%s: usbvideo_NewFrame failed.", __FUNCTION__); + } + } +read_done: + mutex_unlock(&uvd->lock); + return count; +} + +/* + * Make all of the blocks of data contiguous + */ +static int usbvideo_CompressIsochronous(struct uvd *uvd, struct urb *urb) +{ + char *cdata; + int i, totlen = 0; + + for (i = 0; i < urb->number_of_packets; i++) { + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + + cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + /* Detect and ignore errored packets */ + if (st < 0) { + if (uvd->debug >= 1) + err("Data error: packet=%d. len=%d. status=%d.", i, n, st); + uvd->stats.iso_err_count++; + continue; + } + + /* Detect and ignore empty packets */ + if (n <= 0) { + uvd->stats.iso_skip_count++; + continue; + } + totlen += n; /* Little local accounting */ + RingQueue_Enqueue(&uvd->dp, cdata, n); + } + return totlen; +} + +static void usbvideo_IsocIrq(struct urb *urb, struct pt_regs *regs) +{ + int i, ret, len; + struct uvd *uvd = urb->context; + + /* We don't want to do anything if we are about to be removed! */ + if (!CAMERA_IS_OPERATIONAL(uvd)) + return; +#if 0 + if (urb->actual_length > 0) { + info("urb=$%p status=%d. errcount=%d. length=%d.", + urb, urb->status, urb->error_count, urb->actual_length); + } else { + static int c = 0; + if (c++ % 100 == 0) + info("No Isoc data"); + } +#endif + + if (!uvd->streaming) { + if (uvd->debug >= 1) + info("Not streaming, but interrupt!"); + return; + } + + uvd->stats.urb_count++; + if (urb->actual_length <= 0) + goto urb_done_with; + + /* Copy the data received into ring queue */ + len = usbvideo_CompressIsochronous(uvd, urb); + uvd->stats.urb_length = len; + if (len <= 0) + goto urb_done_with; + + /* Here we got some data */ + uvd->stats.data_count += len; + RingQueue_WakeUpInterruptible(&uvd->dp); + +urb_done_with: + for (i = 0; i < FRAMES_PER_DESC; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + urb->status = 0; + urb->dev = uvd->dev; + ret = usb_submit_urb (urb, GFP_KERNEL); + if(ret) + err("usb_submit_urb error (%d)", ret); + return; +} + +/* + * usbvideo_StartDataPump() + * + * History: + * 27-Jan-2000 Used ibmcam->iface, ibmcam->ifaceAltActive instead + * of hardcoded values. Simplified by using for loop, + * allowed any number of URBs. + */ +static int usbvideo_StartDataPump(struct uvd *uvd) +{ + struct usb_device *dev = uvd->dev; + int i, errFlag; + + if (uvd->debug > 1) + info("%s($%p)", __FUNCTION__, uvd); + + if (!CAMERA_IS_OPERATIONAL(uvd)) { + err("%s: Camera is not operational", __FUNCTION__); + return -EFAULT; + } + uvd->curframe = -1; + + /* Alternate interface 1 is is the biggest frame size */ + i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive); + if (i < 0) { + err("%s: usb_set_interface error", __FUNCTION__); + uvd->last_error = i; + return -EBUSY; + } + if (VALID_CALLBACK(uvd, videoStart)) + GET_CALLBACK(uvd, videoStart)(uvd); + else + err("%s: videoStart not set", __FUNCTION__); + + /* We double buffer the Iso lists */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + int j, k; + struct urb *urb = uvd->sbuf[i].urb; + urb->dev = dev; + urb->context = uvd; + urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp); + urb->interval = 1; + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = uvd->sbuf[i].data; + urb->complete = usbvideo_IsocIrq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = uvd->iso_packet_len * FRAMES_PER_DESC; + for (j=k=0; j < FRAMES_PER_DESC; j++, k += uvd->iso_packet_len) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = uvd->iso_packet_len; + } + } + + /* Submit all URBs */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + errFlag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL); + if (errFlag) + err("%s: usb_submit_isoc(%d) ret %d", __FUNCTION__, i, errFlag); + } + + uvd->streaming = 1; + if (uvd->debug > 1) + info("%s: streaming=1 video_endp=$%02x", __FUNCTION__, uvd->video_endp); + return 0; +} + +/* + * usbvideo_StopDataPump() + * + * This procedure stops streaming and deallocates URBs. Then it + * activates zero-bandwidth alt. setting of the video interface. + * + * History: + * 22-Jan-2000 Corrected order of actions to work after surprise removal. + * 27-Jan-2000 Used uvd->iface, uvd->ifaceAltInactive instead of hardcoded values. + */ +static void usbvideo_StopDataPump(struct uvd *uvd) +{ + int i, j; + + if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL)) + return; + + if (uvd->debug > 1) + info("%s($%p)", __FUNCTION__, uvd); + + /* Unschedule all of the iso td's */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + usb_kill_urb(uvd->sbuf[i].urb); + } + if (uvd->debug > 1) + info("%s: streaming=0", __FUNCTION__); + uvd->streaming = 0; + + if (!uvd->remove_pending) { + /* Invoke minidriver's magic to stop the camera */ + if (VALID_CALLBACK(uvd, videoStop)) + GET_CALLBACK(uvd, videoStop)(uvd); + else + err("%s: videoStop not set", __FUNCTION__); + + /* Set packet size to 0 */ + j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive); + if (j < 0) { + err("%s: usb_set_interface() error %d.", __FUNCTION__, j); + uvd->last_error = j; + } + } +} + +/* + * usbvideo_NewFrame() + * + * History: + * 29-Mar-00 Added copying of previous frame into the current one. + * 6-Aug-00 Added model 3 video sizes, removed redundant width, height. + */ +static int usbvideo_NewFrame(struct uvd *uvd, int framenum) +{ + struct usbvideo_frame *frame; + int n; + + if (uvd->debug > 1) + info("usbvideo_NewFrame($%p,%d.)", uvd, framenum); + + /* If we're not grabbing a frame right now and the other frame is */ + /* ready to be grabbed into, then use it instead */ + if (uvd->curframe != -1) + return 0; + + /* If necessary we adjust picture settings between frames */ + if (!uvd->settingsAdjusted) { + if (VALID_CALLBACK(uvd, adjustPicture)) + GET_CALLBACK(uvd, adjustPicture)(uvd); + uvd->settingsAdjusted = 1; + } + + n = (framenum + 1) % USBVIDEO_NUMFRAMES; + if (uvd->frame[n].frameState == FrameState_Ready) + framenum = n; + + frame = &uvd->frame[framenum]; + + frame->frameState = FrameState_Grabbing; + frame->scanstate = ScanState_Scanning; + frame->seqRead_Length = 0; /* Accumulated in xxx_parse_data() */ + frame->deinterlace = Deinterlace_None; + frame->flags = 0; /* No flags yet, up to minidriver (or us) to set them */ + uvd->curframe = framenum; + + /* + * Normally we would want to copy previous frame into the current one + * before we even start filling it with data; this allows us to stop + * filling at any moment; top portion of the frame will be new and + * bottom portion will stay as it was in previous frame. If we don't + * do that then missing chunks of video stream will result in flickering + * portions of old data whatever it was before. + * + * If we choose not to copy previous frame (to, for example, save few + * bus cycles - the frame can be pretty large!) then we have an option + * to clear the frame before using. If we experience losses in this + * mode then missing picture will be black (no flickering). + * + * Finally, if user chooses not to clean the current frame before + * filling it with data then the old data will be visible if we fail + * to refill entire frame with new data. + */ + if (!(uvd->flags & FLAGS_SEPARATE_FRAMES)) { + /* This copies previous frame into this one to mask losses */ + int prev = (framenum - 1 + USBVIDEO_NUMFRAMES) % USBVIDEO_NUMFRAMES; + memmove(frame->data, uvd->frame[prev].data, uvd->max_frame_size); + } else { + if (uvd->flags & FLAGS_CLEAN_FRAMES) { + /* This provides a "clean" frame but slows things down */ + memset(frame->data, 0, uvd->max_frame_size); + } + } + return 0; +} + +/* + * usbvideo_CollectRawData() + * + * This procedure can be used instead of 'processData' callback if you + * only want to dump the raw data from the camera into the output + * device (frame buffer). You can look at it with V4L client, but the + * image will be unwatchable. The main purpose of this code and of the + * mode FLAGS_NO_DECODING is debugging and capturing of datastreams from + * new, unknown cameras. This procedure will be automatically invoked + * instead of the specified callback handler when uvd->flags has bit + * FLAGS_NO_DECODING set. Therefore, any regular build of any driver + * based on usbvideo can use this feature at any time. + */ +static void usbvideo_CollectRawData(struct uvd *uvd, struct usbvideo_frame *frame) +{ + int n; + + assert(uvd != NULL); + assert(frame != NULL); + + /* Try to move data from queue into frame buffer */ + n = RingQueue_GetLength(&uvd->dp); + if (n > 0) { + int m; + /* See how much space we have left */ + m = uvd->max_frame_size - frame->seqRead_Length; + if (n > m) + n = m; + /* Now move that much data into frame buffer */ + RingQueue_Dequeue( + &uvd->dp, + frame->data + frame->seqRead_Length, + m); + frame->seqRead_Length += m; + } + /* See if we filled the frame */ + if (frame->seqRead_Length >= uvd->max_frame_size) { + frame->frameState = FrameState_Done; + uvd->curframe = -1; + uvd->stats.frame_num++; + } +} + +static int usbvideo_GetFrame(struct uvd *uvd, int frameNum) +{ + struct usbvideo_frame *frame = &uvd->frame[frameNum]; + + if (uvd->debug >= 2) + info("%s($%p,%d.)", __FUNCTION__, uvd, frameNum); + + switch (frame->frameState) { + case FrameState_Unused: + if (uvd->debug >= 2) + info("%s: FrameState_Unused", __FUNCTION__); + return -EINVAL; + case FrameState_Ready: + case FrameState_Grabbing: + case FrameState_Error: + { + int ntries, signalPending; + redo: + if (!CAMERA_IS_OPERATIONAL(uvd)) { + if (uvd->debug >= 2) + info("%s: Camera is not operational (1)", __FUNCTION__); + return -EIO; + } + ntries = 0; + do { + RingQueue_InterruptibleSleepOn(&uvd->dp); + signalPending = signal_pending(current); + if (!CAMERA_IS_OPERATIONAL(uvd)) { + if (uvd->debug >= 2) + info("%s: Camera is not operational (2)", __FUNCTION__); + return -EIO; + } + assert(uvd->fbuf != NULL); + if (signalPending) { + if (uvd->debug >= 2) + info("%s: Signal=$%08x", __FUNCTION__, signalPending); + if (uvd->flags & FLAGS_RETRY_VIDIOCSYNC) { + usbvideo_TestPattern(uvd, 1, 0); + uvd->curframe = -1; + uvd->stats.frame_num++; + if (uvd->debug >= 2) + info("%s: Forced test pattern screen", __FUNCTION__); + return 0; + } else { + /* Standard answer: Interrupted! */ + if (uvd->debug >= 2) + info("%s: Interrupted!", __FUNCTION__); + return -EINTR; + } + } else { + /* No signals - we just got new data in dp queue */ + if (uvd->flags & FLAGS_NO_DECODING) + usbvideo_CollectRawData(uvd, frame); + else if (VALID_CALLBACK(uvd, processData)) + GET_CALLBACK(uvd, processData)(uvd, frame); + else + err("%s: processData not set", __FUNCTION__); + } + } while (frame->frameState == FrameState_Grabbing); + if (uvd->debug >= 2) { + info("%s: Grabbing done; state=%d. (%lu. bytes)", + __FUNCTION__, frame->frameState, frame->seqRead_Length); + } + if (frame->frameState == FrameState_Error) { + int ret = usbvideo_NewFrame(uvd, frameNum); + if (ret < 0) { + err("%s: usbvideo_NewFrame() failed (%d.)", __FUNCTION__, ret); + return ret; + } + goto redo; + } + /* Note that we fall through to meet our destiny below */ + } + case FrameState_Done: + /* + * Do all necessary postprocessing of data prepared in + * "interrupt" code and the collecting code above. The + * frame gets marked as FrameState_Done by queue parsing code. + * This status means that we collected enough data and + * most likely processed it as we went through. However + * the data may need postprocessing, such as deinterlacing + * or picture adjustments implemented in software (horror!) + * + * As soon as the frame becomes "final" it gets promoted to + * FrameState_Done_Hold status where it will remain until the + * caller consumed all the video data from the frame. Then + * the empty shell of ex-frame is thrown out for dogs to eat. + * But we, worried about pets, will recycle the frame! + */ + uvd->stats.frame_num++; + if ((uvd->flags & FLAGS_NO_DECODING) == 0) { + if (VALID_CALLBACK(uvd, postProcess)) + GET_CALLBACK(uvd, postProcess)(uvd, frame); + if (frame->flags & USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST) + usbvideo_SoftwareContrastAdjustment(uvd, frame); + } + frame->frameState = FrameState_Done_Hold; + if (uvd->debug >= 2) + info("%s: Entered FrameState_Done_Hold state.", __FUNCTION__); + return 0; + + case FrameState_Done_Hold: + /* + * We stay in this state indefinitely until someone external, + * like ioctl() or read() call finishes digesting the frame + * data. Then it will mark the frame as FrameState_Unused and + * it will be released back into the wild to roam freely. + */ + if (uvd->debug >= 2) + info("%s: FrameState_Done_Hold state.", __FUNCTION__); + return 0; + } + + /* Catch-all for other cases. We shall not be here. */ + err("%s: Invalid state %d.", __FUNCTION__, frame->frameState); + frame->frameState = FrameState_Unused; + return 0; +} + +/* + * usbvideo_DeinterlaceFrame() + * + * This procedure deinterlaces the given frame. Some cameras produce + * only half of scanlines - sometimes only even lines, sometimes only + * odd lines. The deinterlacing method is stored in frame->deinterlace + * variable. + * + * Here we scan the frame vertically and replace missing scanlines with + * average between surrounding ones - before and after. If we have no + * line above then we just copy next line. Similarly, if we need to + * create a last line then preceding line is used. + */ +void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame) +{ + if ((uvd == NULL) || (frame == NULL)) + return; + + if ((frame->deinterlace == Deinterlace_FillEvenLines) || + (frame->deinterlace == Deinterlace_FillOddLines)) + { + const int v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; + int i = (frame->deinterlace == Deinterlace_FillEvenLines) ? 0 : 1; + + for (; i < VIDEOSIZE_Y(frame->request); i += 2) { + const unsigned char *fs1, *fs2; + unsigned char *fd; + int ip, in, j; /* Previous and next lines */ + + /* + * Need to average lines before and after 'i'. + * If we go out of bounds seeking those lines then + * we point back to existing line. + */ + ip = i - 1; /* First, get rough numbers */ + in = i + 1; + + /* Now validate */ + if (ip < 0) + ip = in; + if (in >= VIDEOSIZE_Y(frame->request)) + in = ip; + + /* Sanity check */ + if ((ip < 0) || (in < 0) || + (ip >= VIDEOSIZE_Y(frame->request)) || + (in >= VIDEOSIZE_Y(frame->request))) + { + err("Error: ip=%d. in=%d. req.height=%ld.", + ip, in, VIDEOSIZE_Y(frame->request)); + break; + } + + /* Now we need to average lines 'ip' and 'in' to produce line 'i' */ + fs1 = frame->data + (v4l_linesize * ip); + fs2 = frame->data + (v4l_linesize * in); + fd = frame->data + (v4l_linesize * i); + + /* Average lines around destination */ + for (j=0; j < v4l_linesize; j++) { + fd[j] = (unsigned char)((((unsigned) fs1[j]) + + ((unsigned)fs2[j])) >> 1); + } + } + } + + /* Optionally display statistics on the screen */ + if (uvd->flags & FLAGS_OVERLAY_STATS) + usbvideo_OverlayStats(uvd, frame); +} + +EXPORT_SYMBOL(usbvideo_DeinterlaceFrame); + +/* + * usbvideo_SoftwareContrastAdjustment() + * + * This code adjusts the contrast of the frame, assuming RGB24 format. + * As most software image processing, this job is CPU-intensive. + * Get a camera that supports hardware adjustment! + * + * History: + * 09-Feb-2001 Created. + */ +static void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd, + struct usbvideo_frame *frame) +{ + int i, j, v4l_linesize; + signed long adj; + const int ccm = 128; /* Color correction median - see below */ + + if ((uvd == NULL) || (frame == NULL)) { + err("%s: Illegal call.", __FUNCTION__); + return; + } + adj = (uvd->vpic.contrast - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ + RESTRICT_TO_RANGE(adj, -ccm, ccm+1); + if (adj == 0) { + /* In rare case of no adjustment */ + return; + } + v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; + for (i=0; i < VIDEOSIZE_Y(frame->request); i++) { + unsigned char *fd = frame->data + (v4l_linesize * i); + for (j=0; j < v4l_linesize; j++) { + signed long v = (signed long) fd[j]; + /* Magnify up to 2 times, reduce down to zero */ + v = 128 + ((ccm + adj) * (v - 128)) / ccm; + RESTRICT_TO_RANGE(v, 0, 0xFF); /* Must flatten tails */ + fd[j] = (unsigned char) v; + } + } +} + +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/usbvideo/usbvideo.h b/linux/drivers/media/video/usbvideo/usbvideo.h new file mode 100644 index 000000000..0d2066b7d --- /dev/null +++ b/linux/drivers/media/video/usbvideo/usbvideo.h @@ -0,0 +1,395 @@ +/* + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef usbvideo_h +#define usbvideo_h + +#include +#include "compat.h" +#include +#include +#include + +/* Most helpful debugging aid */ +#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__)))) + +#define USBVIDEO_REPORT_STATS 1 /* Set to 0 to block statistics on close */ + +/* Bit flags (options) */ +#define FLAGS_RETRY_VIDIOCSYNC (1 << 0) +#define FLAGS_MONOCHROME (1 << 1) +#define FLAGS_DISPLAY_HINTS (1 << 2) +#define FLAGS_OVERLAY_STATS (1 << 3) +#define FLAGS_FORCE_TESTPATTERN (1 << 4) +#define FLAGS_SEPARATE_FRAMES (1 << 5) +#define FLAGS_CLEAN_FRAMES (1 << 6) +#define FLAGS_NO_DECODING (1 << 7) + +/* Bit flags for frames (apply to the frame where they are specified) */ +#define USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST (1 << 0) + +/* Camera capabilities (maximum) */ +#define CAMERA_URB_FRAMES 32 +#define CAMERA_MAX_ISO_PACKET 1023 /* 1022 actually sent by camera */ +#define FRAMES_PER_DESC (CAMERA_URB_FRAMES) +#define FRAME_SIZE_PER_DESC (CAMERA_MAX_ISO_PACKET) + +/* This macro restricts an int variable to an inclusive range */ +#define RESTRICT_TO_RANGE(v,mi,ma) { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); } + +#define V4L_BYTES_PER_PIXEL 3 /* Because we produce RGB24 */ + +/* + * Use this macro to construct constants for different video sizes. + * We have to deal with different video sizes that have to be + * configured in the device or compared against when we receive + * a data. Normally one would define a bunch of VIDEOSIZE_x_by_y + * #defines and that's the end of story. However this solution + * does not allow to convert between real pixel sizes and the + * constant (integer) value that may be used to tag a frame or + * whatever. The set of macros below constructs videosize constants + * from the pixel size and allows to reconstruct the pixel size + * from the combined value later. + */ +#define VIDEOSIZE(x,y) (((x) & 0xFFFFL) | (((y) & 0xFFFFL) << 16)) +#define VIDEOSIZE_X(vs) ((vs) & 0xFFFFL) +#define VIDEOSIZE_Y(vs) (((vs) >> 16) & 0xFFFFL) +typedef unsigned long videosize_t; + +/* + * This macro checks if the camera is still operational. The 'uvd' + * pointer must be valid, uvd->dev must be valid, we are not + * removing the device and the device has not erred on us. + */ +#define CAMERA_IS_OPERATIONAL(uvd) (\ + (uvd != NULL) && \ + ((uvd)->dev != NULL) && \ + ((uvd)->last_error == 0) && \ + (!(uvd)->remove_pending)) + +/* + * We use macros to do YUV -> RGB conversion because this is + * very important for speed and totally unimportant for size. + * + * YUV -> RGB Conversion + * --------------------- + * + * B = 1.164*(Y-16) + 2.018*(V-128) + * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128) + * R = 1.164*(Y-16) + 1.596*(U-128) + * + * If you fancy integer arithmetics (as you should), hear this: + * + * 65536*B = 76284*(Y-16) + 132252*(V-128) + * 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128) + * 65536*R = 76284*(Y-16) + 104595*(U-128) + * + * Make sure the output values are within [0..255] range. + */ +#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x))) +#define YUV_TO_RGB_BY_THE_BOOK(my,mu,mv,mr,mg,mb) { \ + int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \ + mm_y = (my) - 16; \ + mm_u = (mu) - 128; \ + mm_v = (mv) - 128; \ + mm_yc= mm_y * 76284; \ + mm_b = (mm_yc + 132252*mm_v ) >> 16; \ + mm_g = (mm_yc - 53281*mm_u - 25625*mm_v ) >> 16; \ + mm_r = (mm_yc + 104595*mm_u ) >> 16; \ + mb = LIMIT_RGB(mm_b); \ + mg = LIMIT_RGB(mm_g); \ + mr = LIMIT_RGB(mm_r); \ +} + +#define RING_QUEUE_SIZE (128*1024) /* Must be a power of 2 */ +#define RING_QUEUE_ADVANCE_INDEX(rq,ind,n) (rq)->ind = ((rq)->ind + (n)) & ((rq)->length-1) +#define RING_QUEUE_DEQUEUE_BYTES(rq,n) RING_QUEUE_ADVANCE_INDEX(rq,ri,n) +#define RING_QUEUE_PEEK(rq,ofs) ((rq)->queue[((ofs) + (rq)->ri) & ((rq)->length-1)]) + +struct RingQueue { + unsigned char *queue; /* Data from the Isoc data pump */ + int length; /* How many bytes allocated for the queue */ + int wi; /* That's where we write */ + int ri; /* Read from here until you hit write index */ + wait_queue_head_t wqh; /* Processes waiting */ +}; + +enum ScanState { + ScanState_Scanning, /* Scanning for header */ + ScanState_Lines /* Parsing lines */ +}; + +/* Completion states of the data parser */ +enum ParseState { + scan_Continue, /* Just parse next item */ + scan_NextFrame, /* Frame done, send it to V4L */ + scan_Out, /* Not enough data for frame */ + scan_EndParse /* End parsing */ +}; + +enum FrameState { + FrameState_Unused, /* Unused (no MCAPTURE) */ + FrameState_Ready, /* Ready to start grabbing */ + FrameState_Grabbing, /* In the process of being grabbed into */ + FrameState_Done, /* Finished grabbing, but not been synced yet */ + FrameState_Done_Hold, /* Are syncing or reading */ + FrameState_Error, /* Something bad happened while processing */ +}; + +/* + * Some frames may contain only even or odd lines. This type + * specifies what type of deinterlacing is required. + */ +enum Deinterlace { + Deinterlace_None=0, + Deinterlace_FillOddLines, + Deinterlace_FillEvenLines +}; + +#define USBVIDEO_NUMFRAMES 2 /* How many frames we work with */ +#define USBVIDEO_NUMSBUF 2 /* How many URBs linked in a ring */ + +/* This structure represents one Isoc request - URB and buffer */ +struct usbvideo_sbuf { + char *data; + struct urb *urb; +}; + +struct usbvideo_frame { + char *data; /* Frame buffer */ + unsigned long header; /* Significant bits from the header */ + + videosize_t canvas; /* The canvas (max. image) allocated */ + videosize_t request; /* That's what the application asked for */ + unsigned short palette; /* The desired format */ + + enum FrameState frameState;/* State of grabbing */ + enum ScanState scanstate; /* State of scanning */ + enum Deinterlace deinterlace; + int flags; /* USBVIDEO_FRAME_FLAG_xxx bit flags */ + + int curline; /* Line of frame we're working on */ + + long seqRead_Length; /* Raw data length of frame */ + long seqRead_Index; /* Amount of data that has been already read */ + + void *user; /* Additional data that user may need */ +}; + +/* Statistics that can be overlaid on screen */ +struct usbvideo_statistics { + unsigned long frame_num; /* Sequential number of the frame */ + unsigned long urb_count; /* How many URBs we received so far */ + unsigned long urb_length; /* Length of last URB */ + unsigned long data_count; /* How many bytes we received */ + unsigned long header_count; /* How many frame headers we found */ + unsigned long iso_skip_count; /* How many empty ISO packets received */ + unsigned long iso_err_count; /* How many bad ISO packets received */ +}; + +struct usbvideo; + +struct uvd { + struct video_device vdev; /* Must be the first field! */ + struct usb_device *dev; + struct usbvideo *handle; /* Points back to the struct usbvideo */ + void *user_data; /* Camera-dependent data */ + int user_size; /* Size of that camera-dependent data */ + int debug; /* Debug level for usbvideo */ + unsigned char iface; /* Video interface number */ + unsigned char video_endp; + unsigned char ifaceAltActive; + unsigned char ifaceAltInactive; /* Alt settings */ + unsigned long flags; /* FLAGS_USBVIDEO_xxx */ + unsigned long paletteBits; /* Which palettes we accept? */ + unsigned short defaultPalette; /* What palette to use for read() */ + struct mutex lock; + int user; /* user count for exclusive use */ + + videosize_t videosize; /* Current setting */ + videosize_t canvas; /* This is the width,height of the V4L canvas */ + int max_frame_size; /* Bytes in one video frame */ + + int uvd_used; /* Is this structure in use? */ + int streaming; /* Are we streaming Isochronous? */ + int grabbing; /* Are we grabbing? */ + int settingsAdjusted; /* Have we adjusted contrast etc.? */ + int last_error; /* What calamity struck us? */ + + char *fbuf; /* Videodev buffer area */ + int fbuf_size; /* Videodev buffer size */ + + int curframe; + int iso_packet_len; /* Videomode-dependent, saves bus bandwidth */ + + struct RingQueue dp; /* Isoc data pump */ + struct usbvideo_frame frame[USBVIDEO_NUMFRAMES]; + struct usbvideo_sbuf sbuf[USBVIDEO_NUMSBUF]; + + volatile int remove_pending; /* If set then about to exit */ + + struct video_picture vpic, vpic_old; /* Picture settings */ + struct video_capability vcap; /* Video capabilities */ + struct video_channel vchan; /* May be used for tuner support */ + struct usbvideo_statistics stats; + char videoName[32]; /* Holds name like "video7" */ +}; + +/* + * usbvideo callbacks (virtual methods). They are set when usbvideo + * services are registered. All of these default to NULL, except those + * that default to usbvideo-provided methods. + */ +struct usbvideo_cb { + int (*probe)(struct usb_interface *, const struct usb_device_id *); + void (*userFree)(struct uvd *); + void (*disconnect)(struct usb_interface *); + int (*setupOnOpen)(struct uvd *); + void (*videoStart)(struct uvd *); + void (*videoStop)(struct uvd *); + void (*processData)(struct uvd *, struct usbvideo_frame *); + void (*postProcess)(struct uvd *, struct usbvideo_frame *); + void (*adjustPicture)(struct uvd *); + int (*getFPS)(struct uvd *); + int (*overlayHook)(struct uvd *, struct usbvideo_frame *); + int (*getFrame)(struct uvd *, int); + int (*startDataPump)(struct uvd *uvd); + void (*stopDataPump)(struct uvd *uvd); + int (*setVideoMode)(struct uvd *uvd, struct video_window *vw); +}; + +struct usbvideo { + int num_cameras; /* As allocated */ + struct usb_driver usbdrv; /* Interface to the USB stack */ + char drvName[80]; /* Driver name */ + struct mutex lock; /* Mutex protecting camera structures */ + struct usbvideo_cb cb; /* Table of callbacks (virtual methods) */ + struct video_device vdt; /* Video device template */ + struct uvd *cam; /* Array of camera structures */ + struct module *md_module; /* Minidriver module */ +}; + + +/* + * This macro retrieves callback address from the struct uvd object. + * No validity checks are done here, so be sure to check the + * callback beforehand with VALID_CALLBACK. + */ +#define GET_CALLBACK(uvd,cbName) ((uvd)->handle->cb.cbName) + +/* + * This macro returns either callback pointer or NULL. This is safe + * macro, meaning that most of components of data structures involved + * may be NULL - this only results in NULL being returned. You may + * wish to use this macro to make sure that the callback is callable. + * However keep in mind that those checks take time. + */ +#define VALID_CALLBACK(uvd,cbName) ((((uvd) != NULL) && \ + ((uvd)->handle != NULL)) ? GET_CALLBACK(uvd,cbName) : NULL) + +int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len); +int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n); +void RingQueue_WakeUpInterruptible(struct RingQueue *rq); +void RingQueue_Flush(struct RingQueue *rq); + +static inline int RingQueue_GetLength(const struct RingQueue *rq) +{ + return (rq->wi - rq->ri + rq->length) & (rq->length-1); +} + +static inline int RingQueue_GetFreeSpace(const struct RingQueue *rq) +{ + return rq->length - RingQueue_GetLength(rq); +} + +void usbvideo_DrawLine( + struct usbvideo_frame *frame, + int x1, int y1, + int x2, int y2, + unsigned char cr, unsigned char cg, unsigned char cb); +void usbvideo_HexDump(const unsigned char *data, int len); +void usbvideo_SayAndWait(const char *what); +void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode); + +/* Memory allocation routines */ +unsigned long usbvideo_kvirt_to_pa(unsigned long adr); + +int usbvideo_register( + struct usbvideo **pCams, + const int num_cams, + const int num_extra, + const char *driverName, + const struct usbvideo_cb *cbTable, + struct module *md, + const struct usb_device_id *id_table); +struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams); +int usbvideo_RegisterVideoDevice(struct uvd *uvd); +void usbvideo_Deregister(struct usbvideo **uvt); + +int usbvideo_v4l_initialize(struct video_device *dev); + +void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame); + +/* + * This code performs bounds checking - use it when working with + * new formats, or else you may get oopses all over the place. + * If pixel falls out of bounds then it gets shoved back (as close + * to place of offence as possible) and is painted bright red. + * + * There are two important concepts: frame width, height and + * V4L canvas width, height. The former is the area requested by + * the application -for this very frame-. The latter is the largest + * possible frame that we can serve (we advertise that via V4L ioctl). + * The frame data is expected to be formatted as lines of length + * VIDEOSIZE_X(fr->request), total VIDEOSIZE_Y(frame->request) lines. + */ +static inline void RGB24_PUTPIXEL( + struct usbvideo_frame *fr, + int ix, int iy, + unsigned char vr, + unsigned char vg, + unsigned char vb) +{ + register unsigned char *pf; + int limiter = 0, mx, my; + mx = ix; + my = iy; + if (mx < 0) { + mx=0; + limiter++; + } else if (mx >= VIDEOSIZE_X((fr)->request)) { + mx= VIDEOSIZE_X((fr)->request) - 1; + limiter++; + } + if (my < 0) { + my = 0; + limiter++; + } else if (my >= VIDEOSIZE_Y((fr)->request)) { + my = VIDEOSIZE_Y((fr)->request) - 1; + limiter++; + } + pf = (fr)->data + V4L_BYTES_PER_PIXEL*((iy)*VIDEOSIZE_X((fr)->request) + (ix)); + if (limiter) { + *pf++ = 0; + *pf++ = 0; + *pf++ = 0xFF; + } else { + *pf++ = (vb); + *pf++ = (vg); + *pf++ = (vr); + } +} + +#endif /* usbvideo_h */ diff --git a/linux/drivers/media/video/usbvideo/vicam.c b/linux/drivers/media/video/usbvideo/vicam.c new file mode 100644 index 000000000..98cb58e89 --- /dev/null +++ b/linux/drivers/media/video/usbvideo/vicam.c @@ -0,0 +1,1412 @@ +/* + * USB ViCam WebCam driver + * Copyright (c) 2002 Joe Burks (jburks@wavicle.org), + * Christopher L Cheney (ccheney@cheney.cx), + * Pavel Machek (pavel@suse.cz), + * John Tyner (jtyner@cs.ucr.edu), + * Monroe Williams (monroe@pobox.com) + * + * Supports 3COM HomeConnect PC Digital WebCam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This source code is based heavily on the CPiA webcam driver which was + * written by Peter Pregler, Scott J. Bertin and Johannes Erdfelt + * + * Portions of this code were also copied from usbvideo.c + * + * Special thanks to the the whole team at Sourceforge for help making + * this driver become a reality. Notably: + * Andy Armstrong who reverse engineered the color encoding and + * Pavel Machek and Chris Cheney who worked on reverse engineering the + * camera controls and wrote the first generation driver. + */ + +#include +#include +#include +#include "compat.h" +#include +#include +#include +#include +#include +#include +#include "usbvideo.h" + +// #define VICAM_DEBUG + +#ifdef VICAM_DEBUG +#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, __FUNCTION__, lineno, ##args) +#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):%s (%d):"fmt,##args) +#else +#define DBG(fmn,args...) do {} while(0) +#endif + +#define DRIVER_AUTHOR "Joe Burks, jburks@wavicle.org" +#define DRIVER_DESC "ViCam WebCam Driver" + +/* Define these values to match your device */ +#define USB_VICAM_VENDOR_ID 0x04c1 +#define USB_VICAM_PRODUCT_ID 0x009d + +#define VICAM_BYTES_PER_PIXEL 3 +#define VICAM_MAX_READ_SIZE (512*242+128) +#define VICAM_MAX_FRAME_SIZE (VICAM_BYTES_PER_PIXEL*320*240) +#define VICAM_FRAMES 2 + +#define VICAM_HEADER_SIZE 64 + +#define clamp( x, l, h ) max_t( __typeof__( x ), \ + ( l ), \ + min_t( __typeof__( x ), \ + ( h ), \ + ( x ) ) ) + +/* Not sure what all the bytes in these char + * arrays do, but they're necessary to make + * the camera work. + */ + +static unsigned char setup1[] = { + 0xB6, 0xC3, 0x1F, 0x00, 0x02, 0x64, 0xE7, 0x67, + 0xFD, 0xFF, 0x0E, 0xC0, 0xE7, 0x09, 0xDE, 0x00, + 0x8E, 0x00, 0xC0, 0x09, 0x40, 0x03, 0xC0, 0x17, + 0x44, 0x03, 0x4B, 0xAF, 0xC0, 0x07, 0x00, 0x00, + 0x4B, 0xAF, 0x97, 0xCF, 0x00, 0x00 +}; + +static unsigned char setup2[] = { + 0xB6, 0xC3, 0x03, 0x00, 0x03, 0x64, 0x18, 0x00, + 0x00, 0x00 +}; + +static unsigned char setup3[] = { + 0xB6, 0xC3, 0x01, 0x00, 0x06, 0x64, 0x00, 0x00 +}; + +static unsigned char setup4[] = { + 0xB6, 0xC3, 0x8F, 0x06, 0x02, 0x64, 0xE7, 0x07, + 0x00, 0x00, 0x08, 0xC0, 0xE7, 0x07, 0x00, 0x00, + 0x3E, 0xC0, 0xE7, 0x07, 0x54, 0x01, 0xAA, 0x00, + 0xE7, 0x07, 0xC8, 0x05, 0xB6, 0x00, 0xE7, 0x07, + 0x42, 0x01, 0xD2, 0x00, 0xE7, 0x07, 0x7C, 0x00, + 0x16, 0x00, 0xE7, 0x07, 0x56, 0x00, 0x18, 0x00, + 0xE7, 0x07, 0x06, 0x00, 0x92, 0xC0, 0xE7, 0x07, + 0x00, 0x00, 0x1E, 0xC0, 0xE7, 0x07, 0xFF, 0xFF, + 0x22, 0xC0, 0xE7, 0x07, 0x04, 0x00, 0x24, 0xC0, + 0xE7, 0x07, 0xEC, 0x27, 0x28, 0xC0, 0xE7, 0x07, + 0x16, 0x01, 0x8E, 0x00, 0xE7, 0x87, 0x01, 0x00, + 0x0E, 0xC0, 0x97, 0xCF, 0xD7, 0x09, 0x00, 0xC0, + 0xE7, 0x77, 0x01, 0x00, 0x92, 0xC0, 0x09, 0xC1, + 0xE7, 0x09, 0xFE, 0x05, 0x24, 0x01, 0xE7, 0x09, + 0x04, 0x06, 0x26, 0x01, 0xE7, 0x07, 0x07, 0x00, + 0x92, 0xC0, 0xE7, 0x05, 0x00, 0xC0, 0xC0, 0xDF, + 0x97, 0xCF, 0x17, 0x00, 0x57, 0x00, 0x17, 0x02, + 0xD7, 0x09, 0x00, 0xC0, 0xE7, 0x77, 0x01, 0x00, + 0x92, 0xC0, 0x0A, 0xC1, 0xE7, 0x57, 0xFF, 0xFF, + 0xFA, 0x05, 0x0D, 0xC0, 0xE7, 0x57, 0x00, 0x00, + 0xFA, 0x05, 0x0F, 0xC0, 0x9F, 0xAF, 0xC6, 0x00, + 0xE7, 0x05, 0x00, 0xC0, 0xC8, 0x05, 0xC1, 0x05, + 0xC0, 0x05, 0xC0, 0xDF, 0x97, 0xCF, 0x27, 0xDA, + 0xFA, 0x05, 0xEF, 0x07, 0x01, 0x00, 0x0B, 0x06, + 0x73, 0xCF, 0x9F, 0xAF, 0x78, 0x01, 0x9F, 0xAF, + 0x1A, 0x03, 0x6E, 0xCF, 0xE7, 0x09, 0xFC, 0x05, + 0x24, 0x01, 0xE7, 0x09, 0x02, 0x06, 0x26, 0x01, + 0xE7, 0x07, 0x07, 0x00, 0x92, 0xC0, 0xE7, 0x09, + 0xFC, 0x05, 0xFE, 0x05, 0xE7, 0x09, 0x02, 0x06, + 0x04, 0x06, 0xE7, 0x09, 0x00, 0x06, 0xFC, 0x05, + 0xE7, 0x09, 0xFE, 0x05, 0x00, 0x06, 0x27, 0xDA, + 0xFA, 0x05, 0xE7, 0x57, 0x01, 0x00, 0xFA, 0x05, + 0x02, 0xCA, 0x04, 0xC0, 0x97, 0xCF, 0x9F, 0xAF, + 0x66, 0x05, 0x97, 0xCF, 0xE7, 0x07, 0x40, 0x00, + 0x02, 0x06, 0xC8, 0x09, 0xFC, 0x05, 0x9F, 0xAF, + 0xDA, 0x02, 0x97, 0xCF, 0xCF, 0x17, 0x02, 0x00, + 0xEF, 0x57, 0x81, 0x00, 0x09, 0x06, 0x9F, 0xA0, + 0xB6, 0x01, 0xEF, 0x57, 0x80, 0x00, 0x09, 0x06, + 0x9F, 0xA0, 0x40, 0x02, 0xEF, 0x57, 0x01, 0x00, + 0x0B, 0x06, 0x9F, 0xA0, 0x46, 0x03, 0xE7, 0x07, + 0x01, 0x00, 0x0A, 0xC0, 0x46, 0xAF, 0x47, 0xAF, + 0x9F, 0xAF, 0x40, 0x02, 0xE7, 0x07, 0x2E, 0x00, + 0x0A, 0xC0, 0xEF, 0x87, 0x80, 0x00, 0x09, 0x06, + 0x97, 0xCF, 0x00, 0x0E, 0x01, 0x00, 0xC0, 0x57, + 0x51, 0x00, 0x9F, 0xC0, 0x9E, 0x02, 0xC0, 0x57, + 0x50, 0x00, 0x20, 0xC0, 0xC0, 0x57, 0x55, 0x00, + 0x12, 0xC0, 0xC0, 0x57, 0x56, 0x00, 0x9F, 0xC0, + 0x72, 0x02, 0x9F, 0xCF, 0xD6, 0x02, 0xC1, 0x0B, + 0x08, 0x06, 0x01, 0xD0, 0x6F, 0x90, 0x08, 0x06, + 0xC0, 0x07, 0x08, 0x00, 0xC1, 0x0B, 0x08, 0x06, + 0x9F, 0xAF, 0x28, 0x05, 0x97, 0xCF, 0x2F, 0x0E, + 0x02, 0x00, 0x08, 0x06, 0xC0, 0x07, 0x08, 0x00, + 0xC1, 0x0B, 0x08, 0x06, 0x9F, 0xAF, 0x28, 0x05, + 0x9F, 0xCF, 0xD6, 0x02, 0x2F, 0x0E, 0x02, 0x00, + 0x09, 0x06, 0xEF, 0x87, 0x80, 0x00, 0x09, 0x06, + 0x9F, 0xCF, 0xD6, 0x02, 0xEF, 0x67, 0x7F, 0xFF, + 0x09, 0x06, 0xE7, 0x67, 0xFF, 0xFD, 0x22, 0xC0, + 0xE7, 0x67, 0xEF, 0xFF, 0x24, 0xC0, 0xE7, 0x87, + 0x10, 0x00, 0x28, 0xC0, 0x9F, 0xAF, 0xB8, 0x05, + 0xE7, 0x87, 0xE0, 0x21, 0x24, 0xC0, 0x9F, 0xAF, + 0xA8, 0x05, 0xE7, 0x87, 0x08, 0x00, 0x24, 0xC0, + 0xE7, 0x67, 0xDF, 0xFF, 0x24, 0xC0, 0xC8, 0x07, + 0x0A, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xC1, 0x07, + 0x01, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0x9F, 0xAF, + 0xB8, 0x05, 0xC0, 0x07, 0x9E, 0x00, 0x9F, 0xAF, + 0x44, 0x05, 0xE7, 0x67, 0xFF, 0xFE, 0x24, 0xC0, + 0xC0, 0x09, 0x20, 0xC0, 0xE7, 0x87, 0x00, 0x01, + 0x24, 0xC0, 0xC0, 0x77, 0x00, 0x02, 0x0F, 0xC1, + 0xE7, 0x67, 0xF7, 0xFF, 0x24, 0xC0, 0xE7, 0x67, + 0xF7, 0xFF, 0x24, 0xC0, 0xE7, 0x87, 0x08, 0x00, + 0x24, 0xC0, 0x08, 0xDA, 0x5E, 0xC1, 0xEF, 0x07, + 0x80, 0x00, 0x09, 0x06, 0x97, 0xCF, 0xEF, 0x07, + 0x01, 0x00, 0x0A, 0x06, 0x97, 0xCF, 0xEF, 0x07, + 0x00, 0x00, 0x0B, 0x06, 0xEF, 0x07, 0x00, 0x00, + 0x0A, 0x06, 0xEF, 0x67, 0x7F, 0xFF, 0x09, 0x06, + 0xEF, 0x07, 0x00, 0x00, 0x0D, 0x06, 0xE7, 0x67, + 0xEF, 0xFF, 0x28, 0xC0, 0xE7, 0x67, 0x17, 0xD8, + 0x24, 0xC0, 0xE7, 0x07, 0x00, 0x00, 0x1E, 0xC0, + 0xE7, 0x07, 0xFF, 0xFF, 0x22, 0xC0, 0x97, 0xCF, + 0xC8, 0x07, 0x0E, 0x06, 0x9F, 0xAF, 0xDA, 0x02, + 0xE7, 0x07, 0x00, 0x00, 0xF2, 0x05, 0xE7, 0x07, + 0x10, 0x00, 0xF6, 0x05, 0xE7, 0x07, 0x0E, 0x06, + 0xF4, 0x05, 0xE7, 0x07, 0xD6, 0x02, 0xF8, 0x05, + 0xC8, 0x07, 0xF2, 0x05, 0xC1, 0x07, 0x00, 0x80, + 0x50, 0xAF, 0x97, 0xCF, 0x2F, 0x0C, 0x02, 0x00, + 0x07, 0x06, 0x2F, 0x0C, 0x04, 0x00, 0x06, 0x06, + 0xE7, 0x07, 0x00, 0x00, 0xF2, 0x05, 0xE7, 0x07, + 0x10, 0x00, 0xF6, 0x05, 0xE7, 0x07, 0xE2, 0x05, + 0xF4, 0x05, 0xE7, 0x07, 0xCE, 0x02, 0xF8, 0x05, + 0xC8, 0x07, 0xF2, 0x05, 0xC1, 0x07, 0x00, 0x80, + 0x51, 0xAF, 0x97, 0xCF, 0x9F, 0xAF, 0x66, 0x04, + 0x9F, 0xAF, 0x1A, 0x03, 0x59, 0xAF, 0x97, 0xCF, + 0xC0, 0x07, 0x0E, 0x00, 0xC1, 0x0B, 0x0C, 0x06, + 0x41, 0xD1, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, + 0x3C, 0x00, 0x9F, 0xAF, 0x44, 0x05, 0x68, 0x00, + 0xC0, 0x07, 0x3B, 0x00, 0x9F, 0xAF, 0x44, 0x05, + 0x6F, 0x00, 0x0C, 0x06, 0x68, 0x00, 0xE0, 0x07, + 0x04, 0x01, 0xE8, 0x0B, 0x0A, 0x06, 0xE8, 0x07, + 0x00, 0x00, 0xE0, 0x07, 0x00, 0x02, 0xE0, 0x07, + 0xEC, 0x01, 0xE0, 0x07, 0xFC, 0xFF, 0x97, 0xCF, + 0xE7, 0x07, 0xFF, 0xFF, 0xFA, 0x05, 0xEF, 0x07, + 0x00, 0x00, 0x0B, 0x06, 0xE7, 0x07, 0x0E, 0x06, + 0x24, 0x01, 0xE7, 0x07, 0x0E, 0x06, 0xFE, 0x05, + 0xE7, 0x07, 0x40, 0x00, 0x26, 0x01, 0xE7, 0x07, + 0x40, 0x00, 0x04, 0x06, 0xE7, 0x07, 0x07, 0x00, + 0x92, 0xC0, 0x97, 0xCF, 0xEF, 0x07, 0x02, 0x00, + 0x0B, 0x06, 0x9F, 0xAF, 0x78, 0x01, 0xEF, 0x77, + 0x80, 0x00, 0x07, 0x06, 0x9F, 0xC0, 0x14, 0x04, + 0xEF, 0x77, 0x01, 0x00, 0x07, 0x06, 0x37, 0xC0, + 0xEF, 0x77, 0x01, 0x00, 0x0D, 0x06, 0x0F, 0xC1, + 0xEF, 0x07, 0x01, 0x00, 0x0D, 0x06, 0xC0, 0x07, + 0x02, 0x00, 0xC1, 0x07, 0x30, 0x00, 0x9F, 0xAF, + 0x28, 0x05, 0xC0, 0x07, 0x01, 0x00, 0xC1, 0x07, + 0x02, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0xC8, 0x07, + 0xFF, 0x4F, 0x9F, 0xAF, 0xA8, 0x05, 0xC0, 0x07, + 0x38, 0x00, 0x9F, 0xAF, 0x44, 0x05, 0xC1, 0x77, + 0x03, 0x00, 0x02, 0xC1, 0x08, 0xDA, 0x75, 0xC1, + 0xC1, 0x77, 0x01, 0x00, 0x0A, 0xC1, 0xC0, 0x07, + 0x01, 0x00, 0xC1, 0x07, 0x02, 0x00, 0x9F, 0xAF, + 0x28, 0x05, 0xEF, 0x07, 0x01, 0x00, 0x06, 0x06, + 0x2C, 0xCF, 0xC0, 0x07, 0x01, 0x00, 0xC1, 0x07, + 0x04, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0xEF, 0x07, + 0x00, 0x00, 0x06, 0x06, 0x22, 0xCF, 0xEF, 0x07, + 0x00, 0x00, 0x0D, 0x06, 0xEF, 0x57, 0x01, 0x00, + 0x06, 0x06, 0x1B, 0xC0, 0xC0, 0x07, 0x01, 0x00, + 0xC1, 0x07, 0x01, 0x00, 0x9F, 0xAF, 0x28, 0x05, + 0xC0, 0x07, 0x02, 0x00, 0xC1, 0x07, 0x30, 0x00, + 0x9F, 0xAF, 0x28, 0x05, 0xC8, 0x07, 0xFF, 0x4F, + 0x9F, 0xAF, 0xA8, 0x05, 0xC0, 0x07, 0x38, 0x00, + 0x9F, 0xAF, 0x44, 0x05, 0xC1, 0x67, 0x03, 0x00, + 0xC1, 0x57, 0x03, 0x00, 0x02, 0xC0, 0x08, 0xDA, + 0x73, 0xC1, 0xC0, 0x07, 0x02, 0x00, 0xC1, 0x07, + 0x12, 0x00, 0xEF, 0x57, 0x00, 0x00, 0x06, 0x06, + 0x02, 0xC0, 0xC1, 0x07, 0x23, 0x00, 0x9F, 0xAF, + 0x28, 0x05, 0xC0, 0x07, 0x14, 0x00, 0xC1, 0x0B, + 0xEA, 0x05, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, + 0x3E, 0x00, 0x9F, 0xAF, 0x0A, 0x05, 0xE7, 0x09, + 0xE4, 0x05, 0xFA, 0x05, 0x27, 0xD8, 0xFA, 0x05, + 0xE7, 0x07, 0x0E, 0x06, 0xFC, 0x05, 0xE7, 0x07, + 0x4E, 0x06, 0x00, 0x06, 0xE7, 0x07, 0x40, 0x00, + 0x02, 0x06, 0x9F, 0xAF, 0x66, 0x05, 0x9F, 0xAF, + 0xC6, 0x00, 0x97, 0xCF, 0xC1, 0x0B, 0xE2, 0x05, + 0x41, 0xD0, 0x01, 0xD2, 0xC1, 0x17, 0x23, 0x00, + 0x9F, 0xAF, 0xDC, 0x04, 0xC0, 0x07, 0x04, 0x00, + 0xC1, 0x0B, 0xE3, 0x05, 0x9F, 0xAF, 0x28, 0x05, + 0xC0, 0x07, 0x06, 0x00, 0xC1, 0x09, 0xE6, 0x05, + 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x07, 0x00, + 0xC1, 0x09, 0xE6, 0x05, 0xC1, 0xD1, 0x9F, 0xAF, + 0x28, 0x05, 0xC0, 0x07, 0x0B, 0x00, 0xC1, 0x09, + 0xE8, 0x05, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, + 0x0C, 0x00, 0xC1, 0x09, 0xE8, 0x05, 0xC1, 0xD1, + 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x0D, 0x00, + 0xC1, 0x07, 0x09, 0x00, 0x9F, 0xAF, 0x28, 0x05, + 0xC0, 0x07, 0x03, 0x00, 0xC1, 0x07, 0x32, 0x00, + 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x0F, 0x00, + 0xC1, 0x07, 0x00, 0x00, 0x9F, 0xAF, 0x28, 0x05, + 0x97, 0xCF, 0xE7, 0x67, 0xFF, 0xD9, 0x24, 0xC0, + 0xC8, 0x07, 0x0A, 0x00, 0x40, 0x00, 0xC0, 0x67, + 0x00, 0x02, 0x27, 0x80, 0x24, 0xC0, 0xE7, 0x87, + 0x00, 0x04, 0x24, 0xC0, 0xE7, 0x67, 0xFF, 0xF9, + 0x24, 0xC0, 0x01, 0xD2, 0x08, 0xDA, 0x72, 0xC1, + 0xE7, 0x87, 0x00, 0x20, 0x24, 0xC0, 0x97, 0xCF, + 0x27, 0x00, 0x1E, 0xC0, 0xE7, 0x87, 0xFF, 0x00, + 0x22, 0xC0, 0xE7, 0x67, 0x7F, 0xFF, 0x24, 0xC0, + 0xE7, 0x87, 0x80, 0x00, 0x24, 0xC0, 0xE7, 0x87, + 0x80, 0x00, 0x24, 0xC0, 0x97, 0xCF, 0x9F, 0xAF, + 0x0A, 0x05, 0x67, 0x00, 0x1E, 0xC0, 0xE7, 0x67, + 0xBF, 0xFF, 0x24, 0xC0, 0xE7, 0x87, 0x40, 0x00, + 0x24, 0xC0, 0xE7, 0x87, 0x40, 0x00, 0x24, 0xC0, + 0x97, 0xCF, 0x9F, 0xAF, 0x0A, 0x05, 0xE7, 0x67, + 0x00, 0xFF, 0x22, 0xC0, 0xE7, 0x67, 0xFF, 0xFE, + 0x24, 0xC0, 0xE7, 0x67, 0xFF, 0xFE, 0x24, 0xC0, + 0xC1, 0x09, 0x20, 0xC0, 0xE7, 0x87, 0x00, 0x01, + 0x24, 0xC0, 0x97, 0xCF, 0xC0, 0x07, 0x40, 0x00, + 0xC8, 0x09, 0xFC, 0x05, 0xE7, 0x67, 0x00, 0xFF, + 0x22, 0xC0, 0xE7, 0x67, 0xFF, 0xFE, 0x24, 0xC0, + 0xE7, 0x67, 0xBF, 0xFF, 0x24, 0xC0, 0xE7, 0x67, + 0xBF, 0xFF, 0x24, 0xC0, 0x00, 0xDA, 0xE8, 0x09, + 0x20, 0xC0, 0xE7, 0x87, 0x40, 0x00, 0x24, 0xC0, + 0xE7, 0x87, 0x40, 0x00, 0x24, 0xC0, 0x00, 0xDA, + 0xE8, 0x09, 0x20, 0xC0, 0x6D, 0xC1, 0xE7, 0x87, + 0x00, 0x01, 0x24, 0xC0, 0x97, 0xCF, 0xE7, 0x07, + 0x32, 0x00, 0x12, 0xC0, 0xE7, 0x77, 0x00, 0x80, + 0x12, 0xC0, 0x7C, 0xC0, 0x97, 0xCF, 0xE7, 0x07, + 0x20, 0x4E, 0x12, 0xC0, 0xE7, 0x77, 0x00, 0x80, + 0x12, 0xC0, 0x7C, 0xC0, 0x97, 0xCF, 0x09, 0x02, + 0x19, 0x00, 0x01, 0x01, 0x00, 0x80, 0x96, 0x09, + 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x05, 0x81, 0x02, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static unsigned char setup5[] = { + 0xB6, 0xC3, 0x2F, 0x01, 0x03, 0x64, 0x0E, 0x00, + 0x14, 0x00, 0x1A, 0x00, 0x20, 0x00, 0x26, 0x00, + 0x4A, 0x00, 0x64, 0x00, 0x6A, 0x00, 0x92, 0x00, + 0x9A, 0x00, 0xA0, 0x00, 0xB2, 0x00, 0xB8, 0x00, + 0xBE, 0x00, 0xC2, 0x00, 0xC8, 0x00, 0xCE, 0x00, + 0xDC, 0x00, 0xDA, 0x00, 0xE2, 0x00, 0xE0, 0x00, + 0xE8, 0x00, 0xE6, 0x00, 0xEE, 0x00, 0xEC, 0x00, + 0xF2, 0x00, 0xF8, 0x00, 0x02, 0x01, 0x0A, 0x01, + 0x0E, 0x01, 0x12, 0x01, 0x1E, 0x01, 0x22, 0x01, + 0x28, 0x01, 0x2C, 0x01, 0x32, 0x01, 0x36, 0x01, + 0x44, 0x01, 0x50, 0x01, 0x5E, 0x01, 0x72, 0x01, + 0x76, 0x01, 0x7A, 0x01, 0x80, 0x01, 0x88, 0x01, + 0x8C, 0x01, 0x94, 0x01, 0x9C, 0x01, 0xA0, 0x01, + 0xA4, 0x01, 0xAA, 0x01, 0xB0, 0x01, 0xB4, 0x01, + 0xBA, 0x01, 0xD0, 0x01, 0xDA, 0x01, 0xF6, 0x01, + 0xFA, 0x01, 0x02, 0x02, 0x34, 0x02, 0x3C, 0x02, + 0x44, 0x02, 0x4A, 0x02, 0x50, 0x02, 0x56, 0x02, + 0x74, 0x02, 0x78, 0x02, 0x7E, 0x02, 0x84, 0x02, + 0x8A, 0x02, 0x88, 0x02, 0x90, 0x02, 0x8E, 0x02, + 0x94, 0x02, 0xA2, 0x02, 0xA8, 0x02, 0xAE, 0x02, + 0xB4, 0x02, 0xBA, 0x02, 0xB8, 0x02, 0xC0, 0x02, + 0xBE, 0x02, 0xC4, 0x02, 0xD0, 0x02, 0xD4, 0x02, + 0xE0, 0x02, 0xE6, 0x02, 0xEE, 0x02, 0xF8, 0x02, + 0xFC, 0x02, 0x06, 0x03, 0x1E, 0x03, 0x24, 0x03, + 0x28, 0x03, 0x30, 0x03, 0x2E, 0x03, 0x3C, 0x03, + 0x4A, 0x03, 0x4E, 0x03, 0x54, 0x03, 0x58, 0x03, + 0x5E, 0x03, 0x66, 0x03, 0x6E, 0x03, 0x7A, 0x03, + 0x86, 0x03, 0x8E, 0x03, 0x96, 0x03, 0xB2, 0x03, + 0xB8, 0x03, 0xC6, 0x03, 0xCC, 0x03, 0xD4, 0x03, + 0xDA, 0x03, 0xE8, 0x03, 0xF4, 0x03, 0xFC, 0x03, + 0x04, 0x04, 0x20, 0x04, 0x2A, 0x04, 0x32, 0x04, + 0x36, 0x04, 0x3E, 0x04, 0x44, 0x04, 0x42, 0x04, + 0x48, 0x04, 0x4E, 0x04, 0x4C, 0x04, 0x54, 0x04, + 0x52, 0x04, 0x5A, 0x04, 0x5E, 0x04, 0x62, 0x04, + 0x68, 0x04, 0x74, 0x04, 0x7C, 0x04, 0x80, 0x04, + 0x88, 0x04, 0x8C, 0x04, 0x94, 0x04, 0x9A, 0x04, + 0xA2, 0x04, 0xA6, 0x04, 0xAE, 0x04, 0xB4, 0x04, + 0xC0, 0x04, 0xCC, 0x04, 0xD8, 0x04, 0x2A, 0x05, + 0x46, 0x05, 0x6C, 0x05, 0x00, 0x00 +}; + +/* rvmalloc / rvfree copied from usbvideo.c + * + * Not sure why these are not yet non-statics which I can reference through + * usbvideo.h the same as it is in 2.4.20. I bet this will get fixed sometime + * in the future. + * +*/ +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + +static void rvfree(void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + +struct vicam_camera { + u16 shutter_speed; // capture shutter speed + u16 gain; // capture gain + + u8 *raw_image; // raw data captured from the camera + u8 *framebuf; // processed data in RGB24 format + u8 *cntrlbuf; // area used to send control msgs + + struct video_device vdev; // v4l video device + struct usb_device *udev; // usb device + + /* guard against simultaneous accesses to the camera */ + struct mutex cam_lock; + + int is_initialized; + u8 open_count; + u8 bulkEndpoint; + int needsDummyRead; + +#if defined(CONFIG_VIDEO_PROC_FS) + struct proc_dir_entry *proc_dir; +#endif + +}; + +static int vicam_probe( struct usb_interface *intf, const struct usb_device_id *id); +static void vicam_disconnect(struct usb_interface *intf); +static void read_frame(struct vicam_camera *cam, int framenum); +static void vicam_decode_color(const u8 *, u8 *); + +static int __send_control_msg(struct vicam_camera *cam, + u8 request, + u16 value, + u16 index, + unsigned char *cp, + u16 size) +{ + int status; + + /* cp must be memory that has been allocated by kmalloc */ + + status = usb_control_msg(cam->udev, + usb_sndctrlpipe(cam->udev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, + cp, size, 1000); + + status = min(status, 0); + + if (status < 0) { + printk(KERN_INFO "Failed sending control message, error %d.\n", + status); + } + + return status; +} + +static int send_control_msg(struct vicam_camera *cam, + u8 request, + u16 value, + u16 index, + unsigned char *cp, + u16 size) +{ + int status = -ENODEV; + mutex_lock(&cam->cam_lock); + if (cam->udev) { + status = __send_control_msg(cam, request, value, + index, cp, size); + } + mutex_unlock(&cam->cam_lock); + return status; +} +static int +initialize_camera(struct vicam_camera *cam) +{ + const struct { + u8 *data; + u32 size; + } firmware[] = { + { .data = setup1, .size = sizeof(setup1) }, + { .data = setup2, .size = sizeof(setup2) }, + { .data = setup3, .size = sizeof(setup3) }, + { .data = setup4, .size = sizeof(setup4) }, + { .data = setup5, .size = sizeof(setup5) }, + { .data = setup3, .size = sizeof(setup3) }, + { .data = NULL, .size = 0 } + }; + + int err, i; + + for (i = 0, err = 0; firmware[i].data && !err; i++) { + memcpy(cam->cntrlbuf, firmware[i].data, firmware[i].size); + + err = send_control_msg(cam, 0xff, 0, 0, + cam->cntrlbuf, firmware[i].size); + } + + return err; +} + +static int +set_camera_power(struct vicam_camera *cam, int state) +{ + int status; + + if ((status = send_control_msg(cam, 0x50, state, 0, NULL, 0)) < 0) + return status; + + if (state) { + send_control_msg(cam, 0x55, 1, 0, NULL, 0); + } + + return 0; +} + +static int +vicam_ioctl(struct inode *inode, struct file *file, unsigned int ioctlnr, unsigned long arg) +{ + void __user *user_arg = (void __user *)arg; + struct vicam_camera *cam = file->private_data; + int retval = 0; + + if (!cam) + return -ENODEV; + + switch (ioctlnr) { + /* query capabilities */ + case VIDIOCGCAP: + { + struct video_capability b; + + DBG("VIDIOCGCAP\n"); + memset(&b, 0, sizeof(b)); + strcpy(b.name, "ViCam-based Camera"); + b.type = VID_TYPE_CAPTURE; + b.channels = 1; + b.audios = 0; + b.maxwidth = 320; /* VIDEOSIZE_CIF */ + b.maxheight = 240; + b.minwidth = 320; /* VIDEOSIZE_48_48 */ + b.minheight = 240; + + if (copy_to_user(user_arg, &b, sizeof(b))) + retval = -EFAULT; + + break; + } + /* get/set video source - we are a camera and nothing else */ + case VIDIOCGCHAN: + { + struct video_channel v; + + DBG("VIDIOCGCHAN\n"); + if (copy_from_user(&v, user_arg, sizeof(v))) { + retval = -EFAULT; + break; + } + if (v.channel != 0) { + retval = -EINVAL; + break; + } + + v.channel = 0; + strcpy(v.name, "Camera"); + v.tuners = 0; + v.flags = 0; + v.type = VIDEO_TYPE_CAMERA; + v.norm = 0; + + if (copy_to_user(user_arg, &v, sizeof(v))) + retval = -EFAULT; + break; + } + + case VIDIOCSCHAN: + { + int v; + + if (copy_from_user(&v, user_arg, sizeof(v))) + retval = -EFAULT; + DBG("VIDIOCSCHAN %d\n", v); + + if (retval == 0 && v != 0) + retval = -EINVAL; + + break; + } + + /* image properties */ + case VIDIOCGPICT: + { + struct video_picture vp; + DBG("VIDIOCGPICT\n"); + memset(&vp, 0, sizeof (struct video_picture)); + vp.brightness = cam->gain << 8; + vp.depth = 24; + vp.palette = VIDEO_PALETTE_RGB24; + if (copy_to_user(user_arg, &vp, sizeof (struct video_picture))) + retval = -EFAULT; + break; + } + + case VIDIOCSPICT: + { + struct video_picture vp; + + if (copy_from_user(&vp, user_arg, sizeof(vp))) { + retval = -EFAULT; + break; + } + + DBG("VIDIOCSPICT depth = %d, pal = %d\n", vp.depth, + vp.palette); + + cam->gain = vp.brightness >> 8; + + if (vp.depth != 24 + || vp.palette != VIDEO_PALETTE_RGB24) + retval = -EINVAL; + + break; + } + + /* get/set capture window */ + case VIDIOCGWIN: + { + struct video_window vw; + vw.x = 0; + vw.y = 0; + vw.width = 320; + vw.height = 240; + vw.chromakey = 0; + vw.flags = 0; + vw.clips = NULL; + vw.clipcount = 0; + + DBG("VIDIOCGWIN\n"); + + if (copy_to_user(user_arg, (void *)&vw, sizeof(vw))) + retval = -EFAULT; + + // I'm not sure what the deal with a capture window is, it is very poorly described + // in the doc. So I won't support it now. + break; + } + + case VIDIOCSWIN: + { + + struct video_window vw; + + if (copy_from_user(&vw, user_arg, sizeof(vw))) { + retval = -EFAULT; + break; + } + + DBG("VIDIOCSWIN %d x %d\n", vw.width, vw.height); + + if ( vw.width != 320 || vw.height != 240 ) + retval = -EFAULT; + + break; + } + + /* mmap interface */ + case VIDIOCGMBUF: + { + struct video_mbuf vm; + int i; + + DBG("VIDIOCGMBUF\n"); + memset(&vm, 0, sizeof (vm)); + vm.size = + VICAM_MAX_FRAME_SIZE * VICAM_FRAMES; + vm.frames = VICAM_FRAMES; + for (i = 0; i < VICAM_FRAMES; i++) + vm.offsets[i] = VICAM_MAX_FRAME_SIZE * i; + + if (copy_to_user(user_arg, (void *)&vm, sizeof(vm))) + retval = -EFAULT; + + break; + } + + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + // int video_size; + + if (copy_from_user((void *)&vm, user_arg, sizeof(vm))) { + retval = -EFAULT; + break; + } + + DBG("VIDIOCMCAPTURE frame=%d, height=%d, width=%d, format=%d.\n",vm.frame,vm.width,vm.height,vm.format); + + if ( vm.frame >= VICAM_FRAMES || vm.format != VIDEO_PALETTE_RGB24 ) + retval = -EINVAL; + + // in theory right here we'd start the image capturing + // (fill in a bulk urb and submit it asynchronously) + // + // Instead we're going to do a total hack job for now and + // retrieve the frame in VIDIOCSYNC + + break; + } + + case VIDIOCSYNC: + { + int frame; + + if (copy_from_user((void *)&frame, user_arg, sizeof(int))) { + retval = -EFAULT; + break; + } + DBG("VIDIOCSYNC: %d\n", frame); + + read_frame(cam, frame); + vicam_decode_color(cam->raw_image, + cam->framebuf + + frame * VICAM_MAX_FRAME_SIZE ); + + break; + } + + /* pointless to implement overlay with this camera */ + case VIDIOCCAPTURE: + case VIDIOCGFBUF: + case VIDIOCSFBUF: + case VIDIOCKEY: + retval = -EINVAL; + break; + + /* tuner interface - we have none */ + case VIDIOCGTUNER: + case VIDIOCSTUNER: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + retval = -EINVAL; + break; + + /* audio interface - we have none */ + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + retval = -EINVAL; + break; + default: + retval = -ENOIOCTLCMD; + break; + } + + return retval; +} + +static int +vicam_open(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct vicam_camera *cam = + (struct vicam_camera *) dev->priv; + DBG("open\n"); + + if (!cam) { + printk(KERN_ERR + "vicam video_device improperly initialized"); + return -EINVAL; + } + + /* the videodev_lock held above us protects us from + * simultaneous opens...for now. we probably shouldn't + * rely on this fact forever. + */ + + if (cam->open_count > 0) { + printk(KERN_INFO + "vicam_open called on already opened camera"); + return -EBUSY; + } + + cam->raw_image = kmalloc(VICAM_MAX_READ_SIZE, GFP_KERNEL); + if (!cam->raw_image) { + return -ENOMEM; + } + + cam->framebuf = rvmalloc(VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); + if (!cam->framebuf) { + kfree(cam->raw_image); + return -ENOMEM; + } + + cam->cntrlbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!cam->cntrlbuf) { + kfree(cam->raw_image); + rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); + return -ENOMEM; + } + + // First upload firmware, then turn the camera on + + if (!cam->is_initialized) { + initialize_camera(cam); + + cam->is_initialized = 1; + } + + set_camera_power(cam, 1); + + cam->needsDummyRead = 1; + cam->open_count++; + + file->private_data = cam; + + return 0; +} + +static int +vicam_close(struct inode *inode, struct file *file) +{ + struct vicam_camera *cam = file->private_data; + int open_count; + struct usb_device *udev; + + DBG("close\n"); + + /* it's not the end of the world if + * we fail to turn the camera off. + */ + + set_camera_power(cam, 0); + + kfree(cam->raw_image); + rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); + kfree(cam->cntrlbuf); + + mutex_lock(&cam->cam_lock); + + cam->open_count--; + open_count = cam->open_count; + udev = cam->udev; + + mutex_unlock(&cam->cam_lock); + + if (!open_count && !udev) { + kfree(cam); + } + + return 0; +} + +static void vicam_decode_color(const u8 *data, u8 *rgb) +{ + /* vicam_decode_color - Convert from Vicam Y-Cr-Cb to RGB + * Copyright (C) 2002 Monroe Williams (monroe@pobox.com) + */ + + int i, prevY, nextY; + + prevY = 512; + nextY = 512; + + data += VICAM_HEADER_SIZE; + + for( i = 0; i < 240; i++, data += 512 ) { + const int y = ( i * 242 ) / 240; + + int j, prevX, nextX; + int Y, Cr, Cb; + + if ( y == 242 - 1 ) { + nextY = -512; + } + + prevX = 1; + nextX = 1; + + for ( j = 0; j < 320; j++, rgb += 3 ) { + const int x = ( j * 512 ) / 320; + const u8 * const src = &data[x]; + + if ( x == 512 - 1 ) { + nextX = -1; + } + + Cr = ( src[prevX] - src[0] ) + + ( src[nextX] - src[0] ); + Cr /= 2; + + Cb = ( src[prevY] - src[prevX + prevY] ) + + ( src[prevY] - src[nextX + prevY] ) + + ( src[nextY] - src[prevX + nextY] ) + + ( src[nextY] - src[nextX + nextY] ); + Cb /= 4; + + Y = 1160 * ( src[0] + ( Cr / 2 ) - 16 ); + + if ( i & 1 ) { + int Ct = Cr; + Cr = Cb; + Cb = Ct; + } + + if ( ( x ^ i ) & 1 ) { + Cr = -Cr; + Cb = -Cb; + } + + rgb[0] = clamp( ( ( Y + ( 2017 * Cb ) ) + + 500 ) / 900, 0, 255 ); + rgb[1] = clamp( ( ( Y - ( 392 * Cb ) - + ( 813 * Cr ) ) + + 500 ) / 1000, 0, 255 ); + rgb[2] = clamp( ( ( Y + ( 1594 * Cr ) ) + + 500 ) / 1300, 0, 255 ); + + prevX = -1; + } + + prevY = -512; + } +} + +static void +read_frame(struct vicam_camera *cam, int framenum) +{ + unsigned char *request = cam->cntrlbuf; + int realShutter; + int n; + int actual_length; + + if (cam->needsDummyRead) { + cam->needsDummyRead = 0; + read_frame(cam, framenum); + } + + memset(request, 0, 16); + request[0] = cam->gain; // 0 = 0% gain, FF = 100% gain + + request[1] = 0; // 512x242 capture + + request[2] = 0x90; // the function of these two bytes + request[3] = 0x07; // is not yet understood + + if (cam->shutter_speed > 60) { + // Short exposure + realShutter = + ((-15631900 / cam->shutter_speed) + 260533) / 1000; + request[4] = realShutter & 0xFF; + request[5] = (realShutter >> 8) & 0xFF; + request[6] = 0x03; + request[7] = 0x01; + } else { + // Long exposure + realShutter = 15600 / cam->shutter_speed - 1; + request[4] = 0; + request[5] = 0; + request[6] = realShutter & 0xFF; + request[7] = realShutter >> 8; + } + + // Per John Markus Bjørndalen, byte at index 8 causes problems if it isn't 0 + request[8] = 0; + // bytes 9-15 do not seem to affect exposure or image quality + + mutex_lock(&cam->cam_lock); + + if (!cam->udev) { + goto done; + } + + n = __send_control_msg(cam, 0x51, 0x80, 0, request, 16); + + if (n < 0) { + printk(KERN_ERR + " Problem sending frame capture control message"); + goto done; + } + + n = usb_bulk_msg(cam->udev, + usb_rcvbulkpipe(cam->udev, cam->bulkEndpoint), + cam->raw_image, + 512 * 242 + 128, &actual_length, 10000); + + if (n < 0) { + printk(KERN_ERR "Problem during bulk read of frame data: %d\n", + n); + } + + done: + mutex_unlock(&cam->cam_lock); +} + +static ssize_t +vicam_read( struct file *file, char __user *buf, size_t count, loff_t *ppos ) +{ + struct vicam_camera *cam = file->private_data; + + DBG("read %d bytes.\n", (int) count); + + if (*ppos >= VICAM_MAX_FRAME_SIZE) { + *ppos = 0; + return 0; + } + + if (*ppos == 0) { + read_frame(cam, 0); + vicam_decode_color(cam->raw_image, + cam->framebuf + + 0 * VICAM_MAX_FRAME_SIZE); + } + + count = min_t(size_t, count, VICAM_MAX_FRAME_SIZE - *ppos); + + if (copy_to_user(buf, &cam->framebuf[*ppos], count)) { + count = -EFAULT; + } else { + *ppos += count; + } + + if (count == VICAM_MAX_FRAME_SIZE) { + *ppos = 0; + } + + return count; +} + + +static int +vicam_mmap(struct file *file, struct vm_area_struct *vma) +{ + // TODO: allocate the raw frame buffer if necessary + unsigned long page, pos; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end-vma->vm_start; + struct vicam_camera *cam = file->private_data; + + if (!cam) + return -ENODEV; + + DBG("vicam_mmap: %ld\n", size); + + /* We let mmap allocate as much as it wants because Linux was adding 2048 bytes + * to the size the application requested for mmap and it was screwing apps up. + if (size > VICAM_FRAMES*VICAM_MAX_FRAME_SIZE) + return -EINVAL; + */ + + pos = (unsigned long)cam->framebuf; + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + +#if defined(CONFIG_VIDEO_PROC_FS) + +static struct proc_dir_entry *vicam_proc_root = NULL; + +static int vicam_read_helper(char *page, char **start, off_t off, + int count, int *eof, int value) +{ + char *out = page; + int len; + + out += sprintf(out, "%d",value); + + len = out - page; + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + + *start = page + off; + return len; +} + +static int vicam_read_proc_shutter(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + return vicam_read_helper(page,start,off,count,eof, + ((struct vicam_camera *)data)->shutter_speed); +} + +static int vicam_read_proc_gain(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + return vicam_read_helper(page,start,off,count,eof, + ((struct vicam_camera *)data)->gain); +} + +static int +vicam_write_proc_shutter(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + u16 stmp; + char kbuf[8]; + struct vicam_camera *cam = (struct vicam_camera *) data; + + if (count > 6) + return -EINVAL; + + if (copy_from_user(kbuf, buffer, count)) + return -EFAULT; + + stmp = (u16) simple_strtoul(kbuf, NULL, 10); + if (stmp < 4 || stmp > 32000) + return -EINVAL; + + cam->shutter_speed = stmp; + + return count; +} + +static int +vicam_write_proc_gain(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + u16 gtmp; + char kbuf[8]; + + struct vicam_camera *cam = (struct vicam_camera *) data; + + if (count > 4) + return -EINVAL; + + if (copy_from_user(kbuf, buffer, count)) + return -EFAULT; + + gtmp = (u16) simple_strtoul(kbuf, NULL, 10); + if (gtmp > 255) + return -EINVAL; + cam->gain = gtmp; + + return count; +} + +static void +vicam_create_proc_root(void) +{ + vicam_proc_root = proc_mkdir("video/vicam", NULL); + + if (vicam_proc_root) + vicam_proc_root->owner = THIS_MODULE; + else + printk(KERN_ERR + "could not create /proc entry for vicam!"); +} + +static void +vicam_destroy_proc_root(void) +{ + if (vicam_proc_root) + remove_proc_entry("video/vicam", 0); +} + +static void +vicam_create_proc_entry(struct vicam_camera *cam) +{ + char name[64]; + struct proc_dir_entry *ent; + + DBG(KERN_INFO "vicam: creating proc entry\n"); + + if (!vicam_proc_root || !cam) { + printk(KERN_INFO + "vicam: could not create proc entry, %s pointer is null.\n", + (!cam ? "camera" : "root")); + return; + } + + sprintf(name, "video%d", cam->vdev.minor); + + cam->proc_dir = proc_mkdir(name, vicam_proc_root); + + if ( !cam->proc_dir ) + return; // FIXME: We should probably return an error here + + ent = create_proc_entry("shutter", S_IFREG | S_IRUGO | S_IWUSR, + cam->proc_dir); + if (ent) { + ent->data = cam; + ent->read_proc = vicam_read_proc_shutter; + ent->write_proc = vicam_write_proc_shutter; + ent->size = 64; + } + + ent = create_proc_entry("gain", S_IFREG | S_IRUGO | S_IWUSR, + cam->proc_dir); + if (ent) { + ent->data = cam; + ent->read_proc = vicam_read_proc_gain; + ent->write_proc = vicam_write_proc_gain; + ent->size = 64; + } +} + +static void +vicam_destroy_proc_entry(void *ptr) +{ + struct vicam_camera *cam = (struct vicam_camera *) ptr; + char name[16]; + + if ( !cam->proc_dir ) + return; + + sprintf(name, "video%d", cam->vdev.minor); + remove_proc_entry("shutter", cam->proc_dir); + remove_proc_entry("gain", cam->proc_dir); + remove_proc_entry(name,vicam_proc_root); + cam->proc_dir = NULL; + +} + +#else +static inline void vicam_create_proc_root(void) { } +static inline void vicam_destroy_proc_root(void) { } +static inline void vicam_create_proc_entry(struct vicam_camera *cam) { } +static inline void vicam_destroy_proc_entry(void *ptr) { } +#endif + +static struct file_operations vicam_fops = { + .owner = THIS_MODULE, + .open = vicam_open, + .release = vicam_close, + .read = vicam_read, + .mmap = vicam_mmap, + .ioctl = vicam_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .llseek = no_llseek, +}; + +static struct video_device vicam_template = { + .owner = THIS_MODULE, + .name = "ViCam-based USB Camera", + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_VICAM, + .fops = &vicam_fops, + .minor = -1, +}; + +/* table of devices that work with this driver */ +static struct usb_device_id vicam_table[] = { + {USB_DEVICE(USB_VICAM_VENDOR_ID, USB_VICAM_PRODUCT_ID)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, vicam_table); + +static struct usb_driver vicam_driver = { + .name = "vicam", + .probe = vicam_probe, + .disconnect = vicam_disconnect, + .id_table = vicam_table +}; + +/** + * vicam_probe + * @intf: the interface + * @id: the device id + * + * Called by the usb core when a new device is connected that it thinks + * this driver might be interested in. + */ +static int +vicam_probe( struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + int bulkEndpoint = 0; + const struct usb_host_interface *interface; + const struct usb_endpoint_descriptor *endpoint; + struct vicam_camera *cam; + + printk(KERN_INFO "ViCam based webcam connected\n"); + + interface = intf->cur_altsetting; + + DBG(KERN_DEBUG "Interface %d. has %u. endpoints!\n", + interface->desc.bInterfaceNumber, (unsigned) (interface->desc.bNumEndpoints)); + endpoint = &interface->endpoint[0].desc; + + if ((endpoint->bEndpointAddress & 0x80) && + ((endpoint->bmAttributes & 3) == 0x02)) { + /* we found a bulk in endpoint */ + bulkEndpoint = endpoint->bEndpointAddress; + } else { + printk(KERN_ERR + "No bulk in endpoint was found ?! (this is bad)\n"); + } + + if ((cam = + kmalloc(sizeof (struct vicam_camera), GFP_KERNEL)) == NULL) { + printk(KERN_WARNING + "could not allocate kernel memory for vicam_camera struct\n"); + return -ENOMEM; + } + + memset(cam, 0, sizeof (struct vicam_camera)); + + cam->shutter_speed = 15; + + mutex_init(&cam->cam_lock); + + memcpy(&cam->vdev, &vicam_template, + sizeof (vicam_template)); + cam->vdev.priv = cam; // sort of a reverse mapping for those functions that get vdev only + + cam->udev = dev; + cam->bulkEndpoint = bulkEndpoint; + + if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1) == -1) { + kfree(cam); + printk(KERN_WARNING "video_register_device failed\n"); + return -EIO; + } + + vicam_create_proc_entry(cam); + + printk(KERN_INFO "ViCam webcam driver now controlling video device %d\n",cam->vdev.minor); + + usb_set_intfdata (intf, cam); + + return 0; +} + +static void +vicam_disconnect(struct usb_interface *intf) +{ + int open_count; + struct vicam_camera *cam = usb_get_intfdata (intf); + usb_set_intfdata (intf, NULL); + + /* we must unregister the device before taking its + * cam_lock. This is because the video open call + * holds the same lock as video unregister. if we + * unregister inside of the cam_lock and open also + * uses the cam_lock, we get deadlock. + */ + + video_unregister_device(&cam->vdev); + + /* stop the camera from being used */ + + mutex_lock(&cam->cam_lock); + + /* mark the camera as gone */ + + cam->udev = NULL; + + vicam_destroy_proc_entry(cam); + + /* the only thing left to do is synchronize with + * our close/release function on who should release + * the camera memory. if there are any users using the + * camera, it's their job. if there are no users, + * it's ours. + */ + + open_count = cam->open_count; + + mutex_unlock(&cam->cam_lock); + + if (!open_count) { + kfree(cam); + } + + printk(KERN_DEBUG "ViCam-based WebCam disconnected\n"); +} + +/* + */ +static int __init +usb_vicam_init(void) +{ + int retval; + DBG(KERN_INFO "ViCam-based WebCam driver startup\n"); + vicam_create_proc_root(); + retval = usb_register(&vicam_driver); + if (retval) + printk(KERN_WARNING "usb_register failed!\n"); + return retval; +} + +static void __exit +usb_vicam_exit(void) +{ + DBG(KERN_INFO + "ViCam-based WebCam driver shutdown\n"); + + usb_deregister(&vicam_driver); + vicam_destroy_proc_root(); +} + +module_init(usb_vicam_init); +module_exit(usb_vicam_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/vivi.c b/linux/drivers/media/video/vivi.c index 70721671c..843fdecbc 100644 --- a/linux/drivers/media/video/vivi.c +++ b/linux/drivers/media/video/vivi.c @@ -26,7 +26,8 @@ #include #include #include -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15) +#ifdef CONFIG_VIDEO_V4L1_COMPAT +/* Include V4L1 specific functions. Should be removed soon */ #include #endif #include diff --git a/linux/drivers/media/video/zc0301/Kconfig b/linux/drivers/media/video/zc0301/Kconfig new file mode 100644 index 000000000..115833e4f --- /dev/null +++ b/linux/drivers/media/video/zc0301/Kconfig @@ -0,0 +1,11 @@ +config USB_ZC0301 + tristate "USB ZC0301 Image Processor and Control Chip support" + depends on USB && VIDEO_V4L1 + ---help--- + Say Y here if you want support for cameras based on the ZC0301 + Image Processor and Control Chip. + + See for more info. + + To compile this driver as a module, choose M here: the + module will be called zc0301. diff --git a/linux/drivers/media/video/zc0301/Makefile b/linux/drivers/media/video/zc0301/Makefile new file mode 100644 index 000000000..d749199d8 --- /dev/null +++ b/linux/drivers/media/video/zc0301/Makefile @@ -0,0 +1,3 @@ +zc0301-objs := zc0301_core.o zc0301_pas202bcb.o + +obj-$(CONFIG_USB_ZC0301) += zc0301.o diff --git a/linux/drivers/media/video/zc0301/zc0301.h b/linux/drivers/media/video/zc0301/zc0301.h new file mode 100644 index 000000000..6959c980a --- /dev/null +++ b/linux/drivers/media/video/zc0301/zc0301.h @@ -0,0 +1,193 @@ +/*************************************************************************** + * V4L2 driver for ZC0301 Image Processor and Control Chip * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _ZC0301_H_ +#define _ZC0301_H_ + +#include +#include +#include "compat.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zc0301_sensor.h" + +/*****************************************************************************/ + +#define ZC0301_DEBUG +#define ZC0301_DEBUG_LEVEL 2 +#define ZC0301_MAX_DEVICES 64 +#define ZC0301_FORCE_MUNMAP 0 +#define ZC0301_MAX_FRAMES 32 +#define ZC0301_COMPRESSION_QUALITY 0 +#define ZC0301_URBS 2 +#define ZC0301_ISO_PACKETS 7 +#define ZC0301_ALTERNATE_SETTING 7 +#define ZC0301_URB_TIMEOUT msecs_to_jiffies(2 * ZC0301_ISO_PACKETS) +#define ZC0301_CTRL_TIMEOUT 100 +#define ZC0301_FRAME_TIMEOUT 2 + +/*****************************************************************************/ + +ZC0301_ID_TABLE +ZC0301_SENSOR_TABLE + +enum zc0301_frame_state { + F_UNUSED, + F_QUEUED, + F_GRABBING, + F_DONE, + F_ERROR, +}; + +struct zc0301_frame_t { + void* bufmem; + struct v4l2_buffer buf; + enum zc0301_frame_state state; + struct list_head frame; + unsigned long vma_use_count; +}; + +enum zc0301_dev_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +enum zc0301_io_method { + IO_NONE, + IO_READ, + IO_MMAP, +}; + +enum zc0301_stream_state { + STREAM_OFF, + STREAM_INTERRUPT, + STREAM_ON, +}; + +struct zc0301_module_param { + u8 force_munmap; + u16 frame_timeout; +}; + +static DECLARE_RWSEM(zc0301_disconnect); + +struct zc0301_device { + struct video_device* v4ldev; + + struct zc0301_sensor sensor; + + struct usb_device* usbdev; + struct urb* urb[ZC0301_URBS]; + void* transfer_buffer[ZC0301_URBS]; + u8* control_buffer; + + struct zc0301_frame_t *frame_current, frame[ZC0301_MAX_FRAMES]; + struct list_head inqueue, outqueue; + u32 frame_count, nbuffers, nreadbuffers; + + enum zc0301_io_method io; + enum zc0301_stream_state stream; + + struct v4l2_jpegcompression compression; + + struct zc0301_module_param module_param; + + enum zc0301_dev_state state; + u8 users; + + struct mutex dev_mutex, fileop_mutex; + spinlock_t queue_lock; + wait_queue_head_t open, wait_frame, wait_stream; +}; + +/*****************************************************************************/ + +struct zc0301_device* +zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id) +{ + return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL; +} + +void +zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor) +{ + memcpy(&cam->sensor, sensor, sizeof(struct zc0301_sensor)); +} + +/*****************************************************************************/ + +#undef DBG +#undef KDBG +#ifdef ZC0301_DEBUG +# define DBG(level, fmt, args...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1) \ + dev_err(&cam->usbdev->dev, fmt "\n", ## args); \ + else if ((level) == 2) \ + dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ + else if ((level) >= 3) \ + dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args); \ + } \ +} while (0) +# define KDBG(level, fmt, args...) \ +do { \ + if (debug >= (level)) { \ + if ((level) == 1 || (level) == 2) \ + pr_info("zc0301: " fmt "\n", ## args); \ + else if ((level) == 3) \ + pr_debug("zc0301: [%s:%d] " fmt "\n", __FUNCTION__, \ + __LINE__ , ## args); \ + } \ +} while (0) +# define V4LDBG(level, name, cmd) \ +do { \ + if (debug >= (level)) \ + v4l_print_ioctl(name, cmd); \ +} while (0) +#else +# define DBG(level, fmt, args...) do {;} while(0) +# define KDBG(level, fmt, args...) do {;} while(0) +# define V4LDBG(level, name, cmd) do {;} while(0) +#endif + +#undef PDBG +#define PDBG(fmt, args...) \ +dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args) + +#undef PDBGG +#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ + +#endif /* _ZC0301_H_ */ diff --git a/linux/drivers/media/video/zc0301/zc0301_core.c b/linux/drivers/media/video/zc0301/zc0301_core.c new file mode 100644 index 000000000..0fad39754 --- /dev/null +++ b/linux/drivers/media/video/zc0301/zc0301_core.c @@ -0,0 +1,2055 @@ +/*************************************************************************** + * Video4Linux2 driver for ZC0301 Image Processor and Control Chip * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * Informations about the chip internals needed to enable the I2C protocol * + * have been taken from the documentation of the ZC030x Video4Linux1 * + * driver written by Andrew Birkett * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zc0301.h" + +/*****************************************************************************/ + +#define ZC0301_MODULE_NAME "V4L2 driver for ZC0301 " \ + "Image Processor and Control Chip" +#define ZC0301_MODULE_AUTHOR "(C) 2006 Luca Risolia" +#define ZC0301_AUTHOR_EMAIL "" +#define ZC0301_MODULE_LICENSE "GPL" +#define ZC0301_MODULE_VERSION "1:1.03" +#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 3) + +/*****************************************************************************/ + +MODULE_DEVICE_TABLE(usb, zc0301_id_table); + +MODULE_AUTHOR(ZC0301_MODULE_AUTHOR " " ZC0301_AUTHOR_EMAIL); +MODULE_DESCRIPTION(ZC0301_MODULE_NAME); +MODULE_VERSION(ZC0301_MODULE_VERSION); +MODULE_LICENSE(ZC0301_MODULE_LICENSE); + +static short video_nr[] = {[0 ... ZC0301_MAX_DEVICES-1] = -1}; +module_param_array(video_nr, short, NULL, 0444); +MODULE_PARM_DESC(video_nr, + "\n<-1|n[,...]> Specify V4L2 minor mode number." + "\n -1 = use next available (default)" + "\n n = use minor number n (integer >= 0)" + "\nYou can specify up to " + __MODULE_STRING(ZC0301_MAX_DEVICES) " cameras this way." + "\nFor example:" + "\nvideo_nr=-1,2,-1 would assign minor number 2 to" + "\nthe second registered camera and use auto for the first" + "\none and for every other camera." + "\n"); + +static short force_munmap[] = {[0 ... ZC0301_MAX_DEVICES-1] = + ZC0301_FORCE_MUNMAP}; +module_param_array(force_munmap, bool, NULL, 0444); +MODULE_PARM_DESC(force_munmap, + "\n<0|1[,...]> Force the application to unmap previously" + "\nmapped buffer memory before calling any VIDIOC_S_CROP or" + "\nVIDIOC_S_FMT ioctl's. Not all the applications support" + "\nthis feature. This parameter is specific for each" + "\ndetected camera." + "\n 0 = do not force memory unmapping" + "\n 1 = force memory unmapping (save memory)" + "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." + "\n"); + +static unsigned int frame_timeout[] = {[0 ... ZC0301_MAX_DEVICES-1] = + ZC0301_FRAME_TIMEOUT}; +module_param_array(frame_timeout, uint, NULL, 0644); +MODULE_PARM_DESC(frame_timeout, + "\n Timeout for a video frame in seconds." + "\nThis parameter is specific for each detected camera." + "\nDefault value is "__MODULE_STRING(ZC0301_FRAME_TIMEOUT)"." + "\n"); + +#ifdef ZC0301_DEBUG +static unsigned short debug = ZC0301_DEBUG_LEVEL; +module_param(debug, ushort, 0644); +MODULE_PARM_DESC(debug, + "\n Debugging information level, from 0 to 3:" + "\n0 = none (use carefully)" + "\n1 = critical errors" + "\n2 = significant informations" + "\n3 = more verbose messages" + "\nLevel 3 is useful for testing only, when only " + "one device is used." + "\nDefault value is "__MODULE_STRING(ZC0301_DEBUG_LEVEL)"." + "\n"); +#endif + +/*****************************************************************************/ + +static u32 +zc0301_request_buffers(struct zc0301_device* cam, u32 count, + enum zc0301_io_method io) +{ + struct v4l2_pix_format* p = &(cam->sensor.pix_format); + struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); + const size_t imagesize = cam->module_param.force_munmap || + io == IO_READ ? + (p->width * p->height * p->priv) / 8 : + (r->width * r->height * p->priv) / 8; + void* buff = NULL; + u32 i; + + if (count > ZC0301_MAX_FRAMES) + count = ZC0301_MAX_FRAMES; + + cam->nbuffers = count; + while (cam->nbuffers > 0) { + if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize)))) + break; + cam->nbuffers--; + } + + for (i = 0; i < cam->nbuffers; i++) { + cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); + cam->frame[i].buf.index = i; + cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); + cam->frame[i].buf.length = imagesize; + cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->frame[i].buf.sequence = 0; + cam->frame[i].buf.field = V4L2_FIELD_NONE; + cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; + cam->frame[i].buf.flags = 0; + } + + return cam->nbuffers; +} + + +static void zc0301_release_buffers(struct zc0301_device* cam) +{ + if (cam->nbuffers) { + vfree(cam->frame[0].bufmem); + cam->nbuffers = 0; + } + cam->frame_current = NULL; +} + + +static void zc0301_empty_framequeues(struct zc0301_device* cam) +{ + u32 i; + + INIT_LIST_HEAD(&cam->inqueue); + INIT_LIST_HEAD(&cam->outqueue); + + for (i = 0; i < ZC0301_MAX_FRAMES; i++) { + cam->frame[i].state = F_UNUSED; + cam->frame[i].buf.bytesused = 0; + } +} + + +static void zc0301_requeue_outqueue(struct zc0301_device* cam) +{ + struct zc0301_frame_t *i; + + list_for_each_entry(i, &cam->outqueue, frame) { + i->state = F_QUEUED; + list_add(&i->frame, &cam->inqueue); + } + + INIT_LIST_HEAD(&cam->outqueue); +} + + +static void zc0301_queue_unusedframes(struct zc0301_device* cam) +{ + unsigned long lock_flags; + u32 i; + + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].state == F_UNUSED) { + cam->frame[i].state = F_QUEUED; + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_add_tail(&cam->frame[i].frame, &cam->inqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + } +} + +/*****************************************************************************/ + +int zc0301_write_reg(struct zc0301_device* cam, u16 index, u16 value) +{ + struct usb_device* udev = cam->usbdev; + int res; + + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0xa0, 0x40, + value, index, NULL, 0, ZC0301_CTRL_TIMEOUT); + if (res < 0) { + DBG(3, "Failed to write a register (index 0x%04X, " + "value 0x%02X, error %d)",index, value, res); + return -1; + } + + return 0; +} + + +int zc0301_read_reg(struct zc0301_device* cam, u16 index) +{ + struct usb_device* udev = cam->usbdev; + u8* buff = cam->control_buffer; + int res; + + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0xa1, 0xc0, + 0x0001, index, buff, 1, ZC0301_CTRL_TIMEOUT); + if (res < 0) + DBG(3, "Failed to read a register (index 0x%04X, error %d)", + index, res); + + PDBGG("Read: index 0x%04X, value: 0x%04X", index, (int)(*buff)); + + return (res >= 0) ? (int)(*buff) : -1; +} + + +int zc0301_i2c_read(struct zc0301_device* cam, u16 address, u8 length) +{ + int err = 0, res, r0, r1; + + err += zc0301_write_reg(cam, 0x0092, address); + err += zc0301_write_reg(cam, 0x0090, 0x02); + + msleep(1); + + res = zc0301_read_reg(cam, 0x0091); + if (res < 0) + err += res; + r0 = zc0301_read_reg(cam, 0x0095); + if (r0 < 0) + err += r0; + r1 = zc0301_read_reg(cam, 0x0096); + if (r1 < 0) + err += r1; + + res = (length <= 1) ? r0 : r0 | (r1 << 8); + + if (err) + DBG(3, "I2C read failed at address 0x%04X, value: 0x%04X", + address, res); + + + PDBGG("I2C read: address 0x%04X, value: 0x%04X", address, res); + + return err ? -1 : res; +} + + +int zc0301_i2c_write(struct zc0301_device* cam, u16 address, u16 value) +{ + int err = 0, res; + + err += zc0301_write_reg(cam, 0x0092, address); + err += zc0301_write_reg(cam, 0x0093, value & 0xff); + err += zc0301_write_reg(cam, 0x0094, value >> 8); + err += zc0301_write_reg(cam, 0x0090, 0x01); + + msleep(1); + + res = zc0301_read_reg(cam, 0x0091); + if (res < 0) + err += res; + + if (err) + DBG(3, "I2C write failed at address 0x%04X, value: 0x%04X", + address, value); + + PDBGG("I2C write: address 0x%04X, value: 0x%04X", address, value); + + return err ? -1 : 0; +} + +/*****************************************************************************/ + +static void zc0301_urb_complete(struct urb *urb, struct pt_regs* regs) +{ + struct zc0301_device* cam = urb->context; + struct zc0301_frame_t** f; + size_t imagesize; + u8 i; + int err = 0; + + if (urb->status == -ENOENT) + return; + + f = &cam->frame_current; + + if (cam->stream == STREAM_INTERRUPT) { + cam->stream = STREAM_OFF; + if ((*f)) + (*f)->state = F_QUEUED; + DBG(3, "Stream interrupted"); + wake_up(&cam->wait_stream); + } + + if (cam->state & DEV_DISCONNECTED) + return; + + if (cam->state & DEV_MISCONFIGURED) { + wake_up_interruptible(&cam->wait_frame); + return; + } + + if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) + goto resubmit_urb; + + if (!(*f)) + (*f) = list_entry(cam->inqueue.next, struct zc0301_frame_t, + frame); + + imagesize = (cam->sensor.pix_format.width * + cam->sensor.pix_format.height * + cam->sensor.pix_format.priv) / 8; + + for (i = 0; i < urb->number_of_packets; i++) { + unsigned int len, status; + void *pos; + u16* soi; + u8 sof; + + len = urb->iso_frame_desc[i].actual_length; + status = urb->iso_frame_desc[i].status; + pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; + + if (status) { + DBG(3, "Error in isochronous frame"); + (*f)->state = F_ERROR; + continue; + } + + sof = (*(soi = pos) == 0xd8ff); + + PDBGG("Isochrnous frame: length %u, #%u i,", len, i); + + if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) +start_of_frame: + if (sof) { + (*f)->state = F_GRABBING; + (*f)->buf.bytesused = 0; + do_gettimeofday(&(*f)->buf.timestamp); + DBG(3, "SOF detected: new video frame"); + } + + if ((*f)->state == F_GRABBING) { + if (sof && (*f)->buf.bytesused) + goto end_of_frame; + + if ((*f)->buf.bytesused + len > imagesize) { + DBG(3, "Video frame size exceeded"); + (*f)->state = F_ERROR; + continue; + } + + memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, len); + (*f)->buf.bytesused += len; + + if ((*f)->buf.bytesused == imagesize) { + u32 b; +end_of_frame: + b = (*f)->buf.bytesused; + (*f)->state = F_DONE; + (*f)->buf.sequence= ++cam->frame_count; + spin_lock(&cam->queue_lock); + list_move_tail(&(*f)->frame, &cam->outqueue); + if (!list_empty(&cam->inqueue)) + (*f) = list_entry(cam->inqueue.next, + struct zc0301_frame_t, + frame); + else + (*f) = NULL; + spin_unlock(&cam->queue_lock); + DBG(3, "Video frame captured: : %lu bytes", + (unsigned long)(b)); + + if (!(*f)) + goto resubmit_urb; + + if (sof) + goto start_of_frame; + } + } + } + +resubmit_urb: + urb->dev = cam->usbdev; + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0 && err != -EPERM) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "usb_submit_urb() failed"); + } + + wake_up_interruptible(&cam->wait_frame); +} + + +static int zc0301_start_transfer(struct zc0301_device* cam) +{ + struct usb_device *udev = cam->usbdev; + struct urb* urb; + const unsigned int wMaxPacketSize[] = {0, 128, 192, 256, 384, + 512, 768, 1023}; + const unsigned int psz = wMaxPacketSize[ZC0301_ALTERNATE_SETTING]; + s8 i, j; + int err = 0; + + for (i = 0; i < ZC0301_URBS; i++) { + cam->transfer_buffer[i] = kzalloc(ZC0301_ISO_PACKETS * psz, + GFP_KERNEL); + if (!cam->transfer_buffer[i]) { + err = -ENOMEM; + DBG(1, "Not enough memory"); + goto free_buffers; + } + } + + for (i = 0; i < ZC0301_URBS; i++) { + urb = usb_alloc_urb(ZC0301_ISO_PACKETS, GFP_KERNEL); + cam->urb[i] = urb; + if (!urb) { + err = -ENOMEM; + DBG(1, "usb_alloc_urb() failed"); + goto free_urbs; + } + urb->dev = udev; + urb->context = cam; + urb->pipe = usb_rcvisocpipe(udev, 1); + urb->transfer_flags = URB_ISO_ASAP; + urb->number_of_packets = ZC0301_ISO_PACKETS; + urb->complete = zc0301_urb_complete; + urb->transfer_buffer = cam->transfer_buffer[i]; + urb->transfer_buffer_length = psz * ZC0301_ISO_PACKETS; + urb->interval = 1; + for (j = 0; j < ZC0301_ISO_PACKETS; j++) { + urb->iso_frame_desc[j].offset = psz * j; + urb->iso_frame_desc[j].length = psz; + } + } + + err = usb_set_interface(udev, 0, ZC0301_ALTERNATE_SETTING); + if (err) { + DBG(1, "usb_set_interface() failed"); + goto free_urbs; + } + + cam->frame_current = NULL; + + for (i = 0; i < ZC0301_URBS; i++) { + err = usb_submit_urb(cam->urb[i], GFP_KERNEL); + if (err) { + for (j = i-1; j >= 0; j--) + usb_kill_urb(cam->urb[j]); + DBG(1, "usb_submit_urb() failed, error %d", err); + goto free_urbs; + } + } + + return 0; + +free_urbs: + for (i = 0; (i < ZC0301_URBS) && cam->urb[i]; i++) + usb_free_urb(cam->urb[i]); + +free_buffers: + for (i = 0; (i < ZC0301_URBS) && cam->transfer_buffer[i]; i++) + kfree(cam->transfer_buffer[i]); + + return err; +} + + +static int zc0301_stop_transfer(struct zc0301_device* cam) +{ + struct usb_device *udev = cam->usbdev; + s8 i; + int err = 0; + + if (cam->state & DEV_DISCONNECTED) + return 0; + + for (i = ZC0301_URBS-1; i >= 0; i--) { + usb_kill_urb(cam->urb[i]); + usb_free_urb(cam->urb[i]); + kfree(cam->transfer_buffer[i]); + } + + err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ + if (err) + DBG(3, "usb_set_interface() failed"); + + return err; +} + + +static int zc0301_stream_interrupt(struct zc0301_device* cam) +{ + long timeout; + + cam->stream = STREAM_INTERRUPT; + timeout = wait_event_timeout(cam->wait_stream, + (cam->stream == STREAM_OFF) || + (cam->state & DEV_DISCONNECTED), + ZC0301_URB_TIMEOUT); + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + else if (cam->stream != STREAM_OFF) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "URB timeout reached. The camera is misconfigured. To " + "use it, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + return 0; +} + +/*****************************************************************************/ + +static int +zc0301_set_compression(struct zc0301_device* cam, + struct v4l2_jpegcompression* compression) +{ + int r, err = 0; + + if ((r = zc0301_read_reg(cam, 0x0008)) < 0) + err += r; + err += zc0301_write_reg(cam, 0x0008, r | 0x11 | compression->quality); + + return err ? -EIO : 0; +} + + +static int zc0301_init(struct zc0301_device* cam) +{ + struct zc0301_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + struct v4l2_queryctrl *qctrl; + struct v4l2_rect* rect; + u8 i = 0; + int err = 0; + + if (!(cam->state & DEV_INITIALIZED)) { + init_waitqueue_head(&cam->open); + qctrl = s->qctrl; + rect = &(s->cropcap.defrect); + cam->compression.quality = ZC0301_COMPRESSION_QUALITY; + } else { /* use current values */ + qctrl = s->_qctrl; + rect = &(s->_rect); + } + + if (s->init) { + err = s->init(cam); + if (err) { + DBG(3, "Sensor initialization failed"); + return err; + } + } + + if ((err = zc0301_set_compression(cam, &cam->compression))) { + DBG(3, "set_compression() failed"); + return err; + } + + if (s->set_crop) + if ((err = s->set_crop(cam, rect))) { + DBG(3, "set_crop() failed"); + return err; + } + + if (s->set_ctrl) { + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (s->qctrl[i].id != 0 && + !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { + ctrl.id = s->qctrl[i].id; + ctrl.value = qctrl[i].default_value; + err = s->set_ctrl(cam, &ctrl); + if (err) { + DBG(3, "Set %s control failed", + s->qctrl[i].name); + return err; + } + DBG(3, "Image sensor supports '%s' control", + s->qctrl[i].name); + } + } + + if (!(cam->state & DEV_INITIALIZED)) { + mutex_init(&cam->fileop_mutex); + spin_lock_init(&cam->queue_lock); + init_waitqueue_head(&cam->wait_frame); + init_waitqueue_head(&cam->wait_stream); + cam->nreadbuffers = 2; + memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); + memcpy(&(s->_rect), &(s->cropcap.defrect), + sizeof(struct v4l2_rect)); + cam->state |= DEV_INITIALIZED; + } + + DBG(2, "Initialization succeeded"); + return 0; +} + + +static void zc0301_release_resources(struct zc0301_device* cam) +{ + DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor); + video_set_drvdata(cam->v4ldev, NULL); + video_unregister_device(cam->v4ldev); + kfree(cam->control_buffer); +} + +/*****************************************************************************/ + +static int zc0301_open(struct inode* inode, struct file* filp) +{ + struct zc0301_device* cam; + int err = 0; + + /* + This is the only safe way to prevent race conditions with + disconnect + */ + if (!down_read_trylock(&zc0301_disconnect)) + return -ERESTARTSYS; + + cam = video_get_drvdata(video_devdata(filp)); + + if (mutex_lock_interruptible(&cam->dev_mutex)) { + up_read(&zc0301_disconnect); + return -ERESTARTSYS; + } + + if (cam->users) { + DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor); + if ((filp->f_flags & O_NONBLOCK) || + (filp->f_flags & O_NDELAY)) { + err = -EWOULDBLOCK; + goto out; + } + mutex_unlock(&cam->dev_mutex); + err = wait_event_interruptible_exclusive(cam->open, + cam->state & DEV_DISCONNECTED + || !cam->users); + if (err) { + up_read(&zc0301_disconnect); + return err; + } + if (cam->state & DEV_DISCONNECTED) { + up_read(&zc0301_disconnect); + return -ENODEV; + } + mutex_lock(&cam->dev_mutex); + } + + + if (cam->state & DEV_MISCONFIGURED) { + err = zc0301_init(cam); + if (err) { + DBG(1, "Initialization failed again. " + "I will retry on next open()."); + goto out; + } + cam->state &= ~DEV_MISCONFIGURED; + } + + if ((err = zc0301_start_transfer(cam))) + goto out; + + filp->private_data = cam; + cam->users++; + cam->io = IO_NONE; + cam->stream = STREAM_OFF; + cam->nbuffers = 0; + cam->frame_count = 0; + zc0301_empty_framequeues(cam); + + DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor); + +out: + mutex_unlock(&cam->dev_mutex); + up_read(&zc0301_disconnect); + return err; +} + + +static int zc0301_release(struct inode* inode, struct file* filp) +{ + struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + + mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */ + + zc0301_stop_transfer(cam); + + zc0301_release_buffers(cam); + + if (cam->state & DEV_DISCONNECTED) { + zc0301_release_resources(cam); + usb_put_dev(cam->usbdev); + mutex_unlock(&cam->dev_mutex); + kfree(cam); + return 0; + } + + cam->users--; + wake_up_interruptible_nr(&cam->open, 1); + + DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor); + + mutex_unlock(&cam->dev_mutex); + + return 0; +} + + +static ssize_t +zc0301_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) +{ + struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + struct zc0301_frame_t* f, * i; + unsigned long lock_flags; + long timeout; + int err = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + if (cam->io == IO_MMAP) { + DBG(3, "Close and open the device again to choose the read " + "method"); + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + if (cam->io == IO_NONE) { + if (!zc0301_request_buffers(cam, cam->nreadbuffers, IO_READ)) { + DBG(1, "read() failed, not enough memory"); + mutex_unlock(&cam->fileop_mutex); + return -ENOMEM; + } + cam->io = IO_READ; + cam->stream = STREAM_ON; + } + + if (list_empty(&cam->inqueue)) { + if (!list_empty(&cam->outqueue)) + zc0301_empty_framequeues(cam); + zc0301_queue_unusedframes(cam); + } + + if (!count) { + mutex_unlock(&cam->fileop_mutex); + return 0; + } + + if (list_empty(&cam->outqueue)) { + if (filp->f_flags & O_NONBLOCK) { + mutex_unlock(&cam->fileop_mutex); + return -EAGAIN; + } + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) { + mutex_unlock(&cam->fileop_mutex); + return timeout; + } + if (cam->state & DEV_DISCONNECTED) { + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + if (!timeout || (cam->state & DEV_MISCONFIGURED)) { + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + } + + f = list_entry(cam->outqueue.prev, struct zc0301_frame_t, frame); + + if (count > f->buf.bytesused) + count = f->buf.bytesused; + + if (copy_to_user(buf, f->bufmem, count)) { + err = -EFAULT; + goto exit; + } + *f_pos += count; + +exit: + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_for_each_entry(i, &cam->outqueue, frame) + i->state = F_UNUSED; + INIT_LIST_HEAD(&cam->outqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + zc0301_queue_unusedframes(cam); + + PDBGG("Frame #%lu, bytes read: %zu", + (unsigned long)f->buf.index, count); + + mutex_unlock(&cam->fileop_mutex); + + return err ? err : count; +} + + +static unsigned int zc0301_poll(struct file *filp, poll_table *wait) +{ + struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + struct zc0301_frame_t* f; + unsigned long lock_flags; + unsigned int mask = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return POLLERR; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + goto error; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + goto error; + } + + if (cam->io == IO_NONE) { + if (!zc0301_request_buffers(cam, cam->nreadbuffers, IO_READ)) { + DBG(1, "poll() failed, not enough memory"); + goto error; + } + cam->io = IO_READ; + cam->stream = STREAM_ON; + } + + if (cam->io == IO_READ) { + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_for_each_entry(f, &cam->outqueue, frame) + f->state = F_UNUSED; + INIT_LIST_HEAD(&cam->outqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + zc0301_queue_unusedframes(cam); + } + + poll_wait(filp, &cam->wait_frame, wait); + + if (!list_empty(&cam->outqueue)) + mask |= POLLIN | POLLRDNORM; + + mutex_unlock(&cam->fileop_mutex); + + return mask; + +error: + mutex_unlock(&cam->fileop_mutex); + return POLLERR; +} + + +static void zc0301_vm_open(struct vm_area_struct* vma) +{ + struct zc0301_frame_t* f = vma->vm_private_data; + f->vma_use_count++; +} + + +static void zc0301_vm_close(struct vm_area_struct* vma) +{ + /* NOTE: buffers are not freed here */ + struct zc0301_frame_t* f = vma->vm_private_data; + f->vma_use_count--; +} + + +static struct vm_operations_struct zc0301_vm_ops = { + .open = zc0301_vm_open, + .close = zc0301_vm_close, +}; + + +static int zc0301_mmap(struct file* filp, struct vm_area_struct *vma) +{ + struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + unsigned long size = vma->vm_end - vma->vm_start, + start = vma->vm_start; + void *pos; + u32 i; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || + size != PAGE_ALIGN(cam->frame[0].buf.length)) { + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + for (i = 0; i < cam->nbuffers; i++) { + if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff) + break; + } + if (i == cam->nbuffers) { + mutex_unlock(&cam->fileop_mutex); + return -EINVAL; + } + + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_RESERVED; + + pos = cam->frame[i].bufmem; + while (size > 0) { /* size is page-aligned */ + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { + mutex_unlock(&cam->fileop_mutex); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + size -= PAGE_SIZE; + } + + vma->vm_ops = &zc0301_vm_ops; + vma->vm_private_data = &cam->frame[i]; + + zc0301_vm_open(vma); + + mutex_unlock(&cam->fileop_mutex); + + return 0; +} + +/*****************************************************************************/ + +static int +zc0301_vidioc_querycap(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_capability cap = { + .driver = "zc0301", + .version = ZC0301_MODULE_VERSION_CODE, + .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING, + }; + + strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); + if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) + strlcpy(cap.bus_info, cam->usbdev->dev.bus_id, + sizeof(cap.bus_info)); + + if (copy_to_user(arg, &cap, sizeof(cap))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_enuminput(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_input i; + + if (copy_from_user(&i, arg, sizeof(i))) + return -EFAULT; + + if (i.index) + return -EINVAL; + + memset(&i, 0, sizeof(i)); + strcpy(i.name, "Camera"); + i.type = V4L2_INPUT_TYPE_CAMERA; + + if (copy_to_user(arg, &i, sizeof(i))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_g_input(struct zc0301_device* cam, void __user * arg) +{ + int index = 0; + + if (copy_to_user(arg, &index, sizeof(index))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_s_input(struct zc0301_device* cam, void __user * arg) +{ + int index; + + if (copy_from_user(&index, arg, sizeof(index))) + return -EFAULT; + + if (index != 0) + return -EINVAL; + + return 0; +} + + +static int +zc0301_vidioc_query_ctrl(struct zc0301_device* cam, void __user * arg) +{ + struct zc0301_sensor* s = &cam->sensor; + struct v4l2_queryctrl qc; + u8 i; + + if (copy_from_user(&qc, arg, sizeof(qc))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (qc.id && qc.id == s->qctrl[i].id) { + memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); + if (copy_to_user(arg, &qc, sizeof(qc))) + return -EFAULT; + return 0; + } + + return -EINVAL; +} + + +static int +zc0301_vidioc_g_ctrl(struct zc0301_device* cam, void __user * arg) +{ + struct zc0301_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + int err = 0; + u8 i; + + if (!s->get_ctrl && !s->set_ctrl) + return -EINVAL; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + + if (!s->get_ctrl) { + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (ctrl.id == s->qctrl[i].id) { + ctrl.value = s->_qctrl[i].default_value; + goto exit; + } + return -EINVAL; + } else + err = s->get_ctrl(cam, &ctrl); + +exit: + if (copy_to_user(arg, &ctrl, sizeof(ctrl))) + return -EFAULT; + + return err; +} + + +static int +zc0301_vidioc_s_ctrl(struct zc0301_device* cam, void __user * arg) +{ + struct zc0301_sensor* s = &cam->sensor; + struct v4l2_control ctrl; + u8 i; + int err = 0; + + if (!s->set_ctrl) + return -EINVAL; + + if (copy_from_user(&ctrl, arg, sizeof(ctrl))) + return -EFAULT; + + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (ctrl.id == s->qctrl[i].id) { + if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) + return -EINVAL; + if (ctrl.value < s->qctrl[i].minimum || + ctrl.value > s->qctrl[i].maximum) + return -ERANGE; + ctrl.value -= ctrl.value % s->qctrl[i].step; + break; + } + + if ((err = s->set_ctrl(cam, &ctrl))) + return err; + + s->_qctrl[i].default_value = ctrl.value; + + return 0; +} + + +static int +zc0301_vidioc_cropcap(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_cropcap* cc = &(cam->sensor.cropcap); + + cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cc->pixelaspect.numerator = 1; + cc->pixelaspect.denominator = 1; + + if (copy_to_user(arg, cc, sizeof(*cc))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_g_crop(struct zc0301_device* cam, void __user * arg) +{ + struct zc0301_sensor* s = &cam->sensor; + struct v4l2_crop crop = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + }; + + memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect)); + + if (copy_to_user(arg, &crop, sizeof(crop))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_s_crop(struct zc0301_device* cam, void __user * arg) +{ + struct zc0301_sensor* s = &cam->sensor; + struct v4l2_crop crop; + struct v4l2_rect* rect; + struct v4l2_rect* bounds = &(s->cropcap.bounds); + const enum zc0301_stream_state stream = cam->stream; + const u32 nbuffers = cam->nbuffers; + u32 i; + int err = 0; + + if (copy_from_user(&crop, arg, sizeof(crop))) + return -EFAULT; + + rect = &(crop.c); + + if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (cam->module_param.force_munmap) + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_S_CROP failed. " + "Unmap the buffers first."); + return -EINVAL; + } + + if (!s->set_crop) { + memcpy(rect, &(s->_rect), sizeof(*rect)); + if (copy_to_user(arg, &crop, sizeof(crop))) + return -EFAULT; + return 0; + } + + rect->left &= ~7L; + rect->top &= ~7L; + if (rect->width < 8) + rect->width = 8; + if (rect->height < 8) + rect->height = 8; + if (rect->width > bounds->width) + rect->width = bounds->width; + if (rect->height > bounds->height) + rect->height = bounds->height; + if (rect->left < bounds->left) + rect->left = bounds->left; + if (rect->top < bounds->top) + rect->top = bounds->top; + if (rect->left + rect->width > bounds->left + bounds->width) + rect->left = bounds->left+bounds->width - rect->width; + if (rect->top + rect->height > bounds->top + bounds->height) + rect->top = bounds->top+bounds->height - rect->height; + rect->width &= ~7L; + rect->height &= ~7L; + + if (cam->stream == STREAM_ON) + if ((err = zc0301_stream_interrupt(cam))) + return err; + + if (copy_to_user(arg, &crop, sizeof(crop))) { + cam->stream = stream; + return -EFAULT; + } + + if (cam->module_param.force_munmap || cam->io == IO_READ) + zc0301_release_buffers(cam); + + if (s->set_crop) + err += s->set_crop(cam, rect); + + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + s->pix_format.width = rect->width; + s->pix_format.height = rect->height; + memcpy(&(s->_rect), rect, sizeof(*rect)); + + if ((cam->module_param.force_munmap || cam->io == IO_READ) && + nbuffers != zc0301_request_buffers(cam, nbuffers, cam->io)) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -ENOMEM; + } + + if (cam->io == IO_READ) + zc0301_empty_framequeues(cam); + else if (cam->module_param.force_munmap) + zc0301_requeue_outqueue(cam); + + cam->stream = stream; + + return 0; +} + + +static int +zc0301_vidioc_enum_fmt(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_fmtdesc fmtd; + + if (copy_from_user(&fmtd, arg, sizeof(fmtd))) + return -EFAULT; + + if (fmtd.index == 0) { + strcpy(fmtd.description, "JPEG"); + fmtd.pixelformat = V4L2_PIX_FMT_JPEG; + fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; + } else + return -EINVAL; + + fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + memset(&fmtd.reserved, 0, sizeof(fmtd.reserved)); + + if (copy_to_user(arg, &fmtd, sizeof(fmtd))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_g_fmt(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_format format; + struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format); + + if (copy_from_user(&format, arg, sizeof(format))) + return -EFAULT; + + if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + pfmt->bytesperline = 0; + pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); + pfmt->field = V4L2_FIELD_NONE; + memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); + + if (copy_to_user(arg, &format, sizeof(format))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_try_s_fmt(struct zc0301_device* cam, unsigned int cmd, + void __user * arg) +{ + struct zc0301_sensor* s = &cam->sensor; + struct v4l2_format format; + struct v4l2_pix_format* pix; + struct v4l2_pix_format* pfmt = &(s->pix_format); + struct v4l2_rect* bounds = &(s->cropcap.bounds); + struct v4l2_rect rect; + const enum zc0301_stream_state stream = cam->stream; + const u32 nbuffers = cam->nbuffers; + u32 i; + int err = 0; + + if (copy_from_user(&format, arg, sizeof(format))) + return -EFAULT; + + pix = &(format.fmt.pix); + + if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memcpy(&rect, &(s->_rect), sizeof(rect)); + + if (!s->set_crop) { + pix->width = rect.width; + pix->height = rect.height; + } else { + rect.width = pix->width; + rect.height = pix->height; + } + + if (rect.width < 8) + rect.width = 8; + if (rect.height < 8) + rect.height = 8; + if (rect.width > bounds->left + bounds->width - rect.left) + rect.width = bounds->left + bounds->width - rect.left; + if (rect.height > bounds->top + bounds->height - rect.top) + rect.height = bounds->top + bounds->height - rect.top; + rect.width &= ~7L; + rect.height &= ~7L; + + pix->width = rect.width; + pix->height = rect.height; + pix->pixelformat = pfmt->pixelformat; + pix->priv = pfmt->priv; + pix->colorspace = pfmt->colorspace; + pix->bytesperline = 0; + pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); + pix->field = V4L2_FIELD_NONE; + + if (cmd == VIDIOC_TRY_FMT) { + if (copy_to_user(arg, &format, sizeof(format))) + return -EFAULT; + return 0; + } + + if (cam->module_param.force_munmap) + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_S_FMT failed. " + "Unmap the buffers first."); + return -EINVAL; + } + + if (cam->stream == STREAM_ON) + if ((err = zc0301_stream_interrupt(cam))) + return err; + + if (copy_to_user(arg, &format, sizeof(format))) { + cam->stream = stream; + return -EFAULT; + } + + if (cam->module_param.force_munmap || cam->io == IO_READ) + zc0301_release_buffers(cam); + + if (s->set_crop) + err += s->set_crop(cam, &rect); + + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -EIO; + } + + memcpy(pfmt, pix, sizeof(*pix)); + memcpy(&(s->_rect), &rect, sizeof(rect)); + + if ((cam->module_param.force_munmap || cam->io == IO_READ) && + nbuffers != zc0301_request_buffers(cam, nbuffers, cam->io)) { + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To " + "use the camera, close and open /dev/video%d again.", + cam->v4ldev->minor); + return -ENOMEM; + } + + if (cam->io == IO_READ) + zc0301_empty_framequeues(cam); + else if (cam->module_param.force_munmap) + zc0301_requeue_outqueue(cam); + + cam->stream = stream; + + return 0; +} + + +static int +zc0301_vidioc_g_jpegcomp(struct zc0301_device* cam, void __user * arg) +{ + if (copy_to_user(arg, &cam->compression, sizeof(cam->compression))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_s_jpegcomp(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_jpegcompression jc; + const enum zc0301_stream_state stream = cam->stream; + int err = 0; + + if (copy_from_user(&jc, arg, sizeof(jc))) + return -EFAULT; + + if (jc.quality != 0) + return -EINVAL; + + if (cam->stream == STREAM_ON) + if ((err = zc0301_stream_interrupt(cam))) + return err; + + err += zc0301_set_compression(cam, &jc); + if (err) { /* atomic, no rollback in ioctl() */ + cam->state |= DEV_MISCONFIGURED; + DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware " + "problems. To use the camera, close and open " + "/dev/video%d again.", cam->v4ldev->minor); + return -EIO; + } + + cam->compression.quality = jc.quality; + + cam->stream = stream; + + return 0; +} + + +static int +zc0301_vidioc_reqbufs(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_requestbuffers rb; + u32 i; + int err; + + if (copy_from_user(&rb, arg, sizeof(rb))) + return -EFAULT; + + if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + rb.memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (cam->io == IO_READ) { + DBG(3, "Close and open the device again to choose the mmap " + "I/O method"); + return -EINVAL; + } + + for (i = 0; i < cam->nbuffers; i++) + if (cam->frame[i].vma_use_count) { + DBG(3, "VIDIOC_REQBUFS failed. " + "Previous buffers are still mapped."); + return -EINVAL; + } + + if (cam->stream == STREAM_ON) + if ((err = zc0301_stream_interrupt(cam))) + return err; + + zc0301_empty_framequeues(cam); + + zc0301_release_buffers(cam); + if (rb.count) + rb.count = zc0301_request_buffers(cam, rb.count, IO_MMAP); + + if (copy_to_user(arg, &rb, sizeof(rb))) { + zc0301_release_buffers(cam); + cam->io = IO_NONE; + return -EFAULT; + } + + cam->io = rb.count ? IO_MMAP : IO_NONE; + + return 0; +} + + +static int +zc0301_vidioc_querybuf(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_buffer b; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + b.index >= cam->nbuffers || cam->io != IO_MMAP) + return -EINVAL; + + memcpy(&b, &cam->frame[b.index].buf, sizeof(b)); + + if (cam->frame[b.index].vma_use_count) + b.flags |= V4L2_BUF_FLAG_MAPPED; + + if (cam->frame[b.index].state == F_DONE) + b.flags |= V4L2_BUF_FLAG_DONE; + else if (cam->frame[b.index].state != F_UNUSED) + b.flags |= V4L2_BUF_FLAG_QUEUED; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_qbuf(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_buffer b; + unsigned long lock_flags; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + b.index >= cam->nbuffers || cam->io != IO_MMAP) + return -EINVAL; + + if (cam->frame[b.index].state != F_UNUSED) + return -EINVAL; + + cam->frame[b.index].state = F_QUEUED; + + spin_lock_irqsave(&cam->queue_lock, lock_flags); + list_add_tail(&cam->frame[b.index].frame, &cam->inqueue); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + PDBGG("Frame #%lu queued", (unsigned long)b.index); + + return 0; +} + + +static int +zc0301_vidioc_dqbuf(struct zc0301_device* cam, struct file* filp, + void __user * arg) +{ + struct v4l2_buffer b; + struct zc0301_frame_t *f; + unsigned long lock_flags; + long timeout; + + if (copy_from_user(&b, arg, sizeof(b))) + return -EFAULT; + + if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP) + return -EINVAL; + + if (list_empty(&cam->outqueue)) { + if (cam->stream == STREAM_OFF) + return -EINVAL; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) + return timeout; + if (cam->state & DEV_DISCONNECTED) + return -ENODEV; + if (!timeout || (cam->state & DEV_MISCONFIGURED)) + return -EIO; + } + + spin_lock_irqsave(&cam->queue_lock, lock_flags); + f = list_entry(cam->outqueue.next, struct zc0301_frame_t, frame); + list_del(cam->outqueue.next); + spin_unlock_irqrestore(&cam->queue_lock, lock_flags); + + f->state = F_UNUSED; + + memcpy(&b, &f->buf, sizeof(b)); + if (f->vma_use_count) + b.flags |= V4L2_BUF_FLAG_MAPPED; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index); + + return 0; +} + + +static int +zc0301_vidioc_streamon(struct zc0301_device* cam, void __user * arg) +{ + int type; + + if (copy_from_user(&type, arg, sizeof(type))) + return -EFAULT; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + if (list_empty(&cam->inqueue)) + return -EINVAL; + + cam->stream = STREAM_ON; + + DBG(3, "Stream on"); + + return 0; +} + + +static int +zc0301_vidioc_streamoff(struct zc0301_device* cam, void __user * arg) +{ + int type, err; + + if (copy_from_user(&type, arg, sizeof(type))) + return -EFAULT; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) + return -EINVAL; + + if (cam->stream == STREAM_ON) + if ((err = zc0301_stream_interrupt(cam))) + return err; + + zc0301_empty_framequeues(cam); + + DBG(3, "Stream off"); + + return 0; +} + + +static int +zc0301_vidioc_g_parm(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_streamparm sp; + + if (copy_from_user(&sp, arg, sizeof(sp))) + return -EFAULT; + + if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + sp.parm.capture.extendedmode = 0; + sp.parm.capture.readbuffers = cam->nreadbuffers; + + if (copy_to_user(arg, &sp, sizeof(sp))) + return -EFAULT; + + return 0; +} + + +static int +zc0301_vidioc_s_parm(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_streamparm sp; + + if (copy_from_user(&sp, arg, sizeof(sp))) + return -EFAULT; + + if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + sp.parm.capture.extendedmode = 0; + + if (sp.parm.capture.readbuffers == 0) + sp.parm.capture.readbuffers = cam->nreadbuffers; + + if (sp.parm.capture.readbuffers > ZC0301_MAX_FRAMES) + sp.parm.capture.readbuffers = ZC0301_MAX_FRAMES; + + if (copy_to_user(arg, &sp, sizeof(sp))) + return -EFAULT; + + cam->nreadbuffers = sp.parm.capture.readbuffers; + + return 0; +} + + +static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp, + unsigned int cmd, void __user * arg) +{ + struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + + switch (cmd) { + + case VIDIOC_QUERYCAP: + return zc0301_vidioc_querycap(cam, arg); + + case VIDIOC_ENUMINPUT: + return zc0301_vidioc_enuminput(cam, arg); + + case VIDIOC_G_INPUT: + return zc0301_vidioc_g_input(cam, arg); + + case VIDIOC_S_INPUT: + return zc0301_vidioc_s_input(cam, arg); + + case VIDIOC_QUERYCTRL: + return zc0301_vidioc_query_ctrl(cam, arg); + + case VIDIOC_G_CTRL: + return zc0301_vidioc_g_ctrl(cam, arg); + + case VIDIOC_S_CTRL_OLD: + case VIDIOC_S_CTRL: + return zc0301_vidioc_s_ctrl(cam, arg); + + case VIDIOC_CROPCAP_OLD: + case VIDIOC_CROPCAP: + return zc0301_vidioc_cropcap(cam, arg); + + case VIDIOC_G_CROP: + return zc0301_vidioc_g_crop(cam, arg); + + case VIDIOC_S_CROP: + return zc0301_vidioc_s_crop(cam, arg); + + case VIDIOC_ENUM_FMT: + return zc0301_vidioc_enum_fmt(cam, arg); + + case VIDIOC_G_FMT: + return zc0301_vidioc_g_fmt(cam, arg); + + case VIDIOC_TRY_FMT: + case VIDIOC_S_FMT: + return zc0301_vidioc_try_s_fmt(cam, cmd, arg); + + case VIDIOC_G_JPEGCOMP: + return zc0301_vidioc_g_jpegcomp(cam, arg); + + case VIDIOC_S_JPEGCOMP: + return zc0301_vidioc_s_jpegcomp(cam, arg); + + case VIDIOC_REQBUFS: + return zc0301_vidioc_reqbufs(cam, arg); + + case VIDIOC_QUERYBUF: + return zc0301_vidioc_querybuf(cam, arg); + + case VIDIOC_QBUF: + return zc0301_vidioc_qbuf(cam, arg); + + case VIDIOC_DQBUF: + return zc0301_vidioc_dqbuf(cam, filp, arg); + + case VIDIOC_STREAMON: + return zc0301_vidioc_streamon(cam, arg); + + case VIDIOC_STREAMOFF: + return zc0301_vidioc_streamoff(cam, arg); + + case VIDIOC_G_PARM: + return zc0301_vidioc_g_parm(cam, arg); + + case VIDIOC_S_PARM_OLD: + case VIDIOC_S_PARM: + return zc0301_vidioc_s_parm(cam, arg); + + case VIDIOC_G_STD: + case VIDIOC_S_STD: + case VIDIOC_QUERYSTD: + case VIDIOC_ENUMSTD: + case VIDIOC_QUERYMENU: + return -EINVAL; + + default: + return -EINVAL; + + } +} + + +static int zc0301_ioctl(struct inode* inode, struct file* filp, + unsigned int cmd, unsigned long arg) +{ + struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + int err = 0; + + if (mutex_lock_interruptible(&cam->fileop_mutex)) + return -ERESTARTSYS; + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + mutex_unlock(&cam->fileop_mutex); + return -ENODEV; + } + + if (cam->state & DEV_MISCONFIGURED) { + DBG(1, "The camera is misconfigured. Close and open it " + "again."); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } + + V4LDBG(3, "zc0301", cmd); + + err = zc0301_ioctl_v4l2(inode, filp, cmd, (void __user *)arg); + + mutex_unlock(&cam->fileop_mutex); + + return err; +} + + +static struct file_operations zc0301_fops = { + .owner = THIS_MODULE, + .open = zc0301_open, + .release = zc0301_release, + .ioctl = zc0301_ioctl, + .read = zc0301_read, + .poll = zc0301_poll, + .mmap = zc0301_mmap, + .llseek = no_llseek, +}; + +/*****************************************************************************/ + +static int +zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct zc0301_device* cam; + static unsigned int dev_nr = 0; + unsigned int i; + int err = 0; + + if (!(cam = kzalloc(sizeof(struct zc0301_device), GFP_KERNEL))) + return -ENOMEM; + + cam->usbdev = udev; + + if (!(cam->control_buffer = kzalloc(4, GFP_KERNEL))) { + DBG(1, "kmalloc() failed"); + err = -ENOMEM; + goto fail; + } + + if (!(cam->v4ldev = video_device_alloc())) { + DBG(1, "video_device_alloc() failed"); + err = -ENOMEM; + goto fail; + } + + mutex_init(&cam->dev_mutex); + + DBG(2, "ZC0301 Image Processor and Control Chip detected " + "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct); + + for (i = 0; zc0301_sensor_table[i]; i++) { + err = zc0301_sensor_table[i](cam); + if (!err) + break; + } + + if (!err) + DBG(2, "%s image sensor detected", cam->sensor.name); + else { + DBG(1, "No supported image sensor detected"); + err = -ENODEV; + goto fail; + } + + if (zc0301_init(cam)) { + DBG(1, "Initialization failed. I will retry on open()."); + cam->state |= DEV_MISCONFIGURED; + } + + strcpy(cam->v4ldev->name, "ZC0301 PC Camera"); + cam->v4ldev->owner = THIS_MODULE; + cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; + cam->v4ldev->hardware = 0; + cam->v4ldev->fops = &zc0301_fops; + cam->v4ldev->minor = video_nr[dev_nr]; + cam->v4ldev->release = video_device_release; + video_set_drvdata(cam->v4ldev, cam); + + mutex_lock(&cam->dev_mutex); + + err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, + video_nr[dev_nr]); + if (err) { + DBG(1, "V4L2 device registration failed"); + if (err == -ENFILE && video_nr[dev_nr] == -1) + DBG(1, "Free /dev/videoX node not found"); + video_nr[dev_nr] = -1; + dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0; + mutex_unlock(&cam->dev_mutex); + goto fail; + } + + DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor); + + cam->module_param.force_munmap = force_munmap[dev_nr]; + cam->module_param.frame_timeout = frame_timeout[dev_nr]; + + dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0; + + usb_set_intfdata(intf, cam); + + mutex_unlock(&cam->dev_mutex); + + return 0; + +fail: + if (cam) { + kfree(cam->control_buffer); + if (cam->v4ldev) + video_device_release(cam->v4ldev); + kfree(cam); + } + return err; +} + + +static void zc0301_usb_disconnect(struct usb_interface* intf) +{ + struct zc0301_device* cam = usb_get_intfdata(intf); + + if (!cam) + return; + + down_write(&zc0301_disconnect); + + mutex_lock(&cam->dev_mutex); + + DBG(2, "Disconnecting %s...", cam->v4ldev->name); + + wake_up_interruptible_all(&cam->open); + + if (cam->users) { + DBG(2, "Device /dev/video%d is open! Deregistration and " + "memory deallocation are deferred on close.", + cam->v4ldev->minor); + cam->state |= DEV_MISCONFIGURED; + zc0301_stop_transfer(cam); + cam->state |= DEV_DISCONNECTED; + wake_up_interruptible(&cam->wait_frame); + wake_up(&cam->wait_stream); + usb_get_dev(cam->usbdev); + } else { + cam->state |= DEV_DISCONNECTED; + zc0301_release_resources(cam); + } + + mutex_unlock(&cam->dev_mutex); + + if (!cam->users) + kfree(cam); + + up_write(&zc0301_disconnect); +} + + +static struct usb_driver zc0301_usb_driver = { + .name = "zc0301", + .id_table = zc0301_id_table, + .probe = zc0301_usb_probe, + .disconnect = zc0301_usb_disconnect, +}; + +/*****************************************************************************/ + +static int __init zc0301_module_init(void) +{ + int err = 0; + + KDBG(2, ZC0301_MODULE_NAME " v" ZC0301_MODULE_VERSION); + KDBG(3, ZC0301_MODULE_AUTHOR); + + if ((err = usb_register(&zc0301_usb_driver))) + KDBG(1, "usb_register() failed"); + + return err; +} + + +static void __exit zc0301_module_exit(void) +{ + usb_deregister(&zc0301_usb_driver); +} + + +module_init(zc0301_module_init); +module_exit(zc0301_module_exit); diff --git a/linux/drivers/media/video/zc0301/zc0301_pas202bcb.c b/linux/drivers/media/video/zc0301/zc0301_pas202bcb.c new file mode 100644 index 000000000..eaadf0252 --- /dev/null +++ b/linux/drivers/media/video/zc0301/zc0301_pas202bcb.c @@ -0,0 +1,361 @@ +/*************************************************************************** + * Plug-in for PAS202BCB image sensor connected to the ZC030! Image * + * Processor and Control Chip * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * Initialization values of the ZC0301 have been taken from the SPCA5XX * + * driver maintained by Michel Xhaard * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +/* + NOTE: Sensor controls are disabled for now, becouse changing them while + streaming sometimes results in out-of-sync video frames. We'll use + the default initialization, until we know how to stop and start video + in the chip. However, the image quality still looks good under various + light conditions. +*/ + +#include +#include "zc0301_sensor.h" + + +static struct zc0301_sensor pas202bcb; + + +static int pas202bcb_init(struct zc0301_device* cam) +{ + int err = 0; + + err += zc0301_write_reg(cam, 0x0002, 0x00); + err += zc0301_write_reg(cam, 0x0003, 0x02); + err += zc0301_write_reg(cam, 0x0004, 0x80); + err += zc0301_write_reg(cam, 0x0005, 0x01); + err += zc0301_write_reg(cam, 0x0006, 0xE0); + err += zc0301_write_reg(cam, 0x0098, 0x00); + err += zc0301_write_reg(cam, 0x009A, 0x03); + err += zc0301_write_reg(cam, 0x011A, 0x00); + err += zc0301_write_reg(cam, 0x011C, 0x03); + err += zc0301_write_reg(cam, 0x009B, 0x01); + err += zc0301_write_reg(cam, 0x009C, 0xE6); + err += zc0301_write_reg(cam, 0x009D, 0x02); + err += zc0301_write_reg(cam, 0x009E, 0x86); + + err += zc0301_i2c_write(cam, 0x02, 0x02); + err += zc0301_i2c_write(cam, 0x0A, 0x01); + err += zc0301_i2c_write(cam, 0x0B, 0x01); + err += zc0301_i2c_write(cam, 0x0D, 0x00); + err += zc0301_i2c_write(cam, 0x12, 0x05); + err += zc0301_i2c_write(cam, 0x13, 0x63); + err += zc0301_i2c_write(cam, 0x15, 0x70); + + err += zc0301_write_reg(cam, 0x0101, 0xB7); + err += zc0301_write_reg(cam, 0x0100, 0x0D); + err += zc0301_write_reg(cam, 0x0189, 0x06); + err += zc0301_write_reg(cam, 0x01AD, 0x00); + err += zc0301_write_reg(cam, 0x01C5, 0x03); + err += zc0301_write_reg(cam, 0x01CB, 0x13); + err += zc0301_write_reg(cam, 0x0250, 0x08); + err += zc0301_write_reg(cam, 0x0301, 0x08); + err += zc0301_write_reg(cam, 0x018D, 0x70); + err += zc0301_write_reg(cam, 0x0008, 0x03); + err += zc0301_write_reg(cam, 0x01C6, 0x04); + err += zc0301_write_reg(cam, 0x01CB, 0x07); + err += zc0301_write_reg(cam, 0x0120, 0x11); + err += zc0301_write_reg(cam, 0x0121, 0x37); + err += zc0301_write_reg(cam, 0x0122, 0x58); + err += zc0301_write_reg(cam, 0x0123, 0x79); + err += zc0301_write_reg(cam, 0x0124, 0x91); + err += zc0301_write_reg(cam, 0x0125, 0xA6); + err += zc0301_write_reg(cam, 0x0126, 0xB8); + err += zc0301_write_reg(cam, 0x0127, 0xC7); + err += zc0301_write_reg(cam, 0x0128, 0xD3); + err += zc0301_write_reg(cam, 0x0129, 0xDE); + err += zc0301_write_reg(cam, 0x012A, 0xE6); + err += zc0301_write_reg(cam, 0x012B, 0xED); + err += zc0301_write_reg(cam, 0x012C, 0xF3); + err += zc0301_write_reg(cam, 0x012D, 0xF8); + err += zc0301_write_reg(cam, 0x012E, 0xFB); + err += zc0301_write_reg(cam, 0x012F, 0xFF); + err += zc0301_write_reg(cam, 0x0130, 0x26); + err += zc0301_write_reg(cam, 0x0131, 0x23); + err += zc0301_write_reg(cam, 0x0132, 0x20); + err += zc0301_write_reg(cam, 0x0133, 0x1C); + err += zc0301_write_reg(cam, 0x0134, 0x16); + err += zc0301_write_reg(cam, 0x0135, 0x13); + err += zc0301_write_reg(cam, 0x0136, 0x10); + err += zc0301_write_reg(cam, 0x0137, 0x0D); + err += zc0301_write_reg(cam, 0x0138, 0x0B); + err += zc0301_write_reg(cam, 0x0139, 0x09); + err += zc0301_write_reg(cam, 0x013A, 0x07); + err += zc0301_write_reg(cam, 0x013B, 0x06); + err += zc0301_write_reg(cam, 0x013C, 0x05); + err += zc0301_write_reg(cam, 0x013D, 0x04); + err += zc0301_write_reg(cam, 0x013E, 0x03); + err += zc0301_write_reg(cam, 0x013F, 0x02); + err += zc0301_write_reg(cam, 0x010A, 0x4C); + err += zc0301_write_reg(cam, 0x010B, 0xF5); + err += zc0301_write_reg(cam, 0x010C, 0xFF); + err += zc0301_write_reg(cam, 0x010D, 0xF9); + err += zc0301_write_reg(cam, 0x010E, 0x51); + err += zc0301_write_reg(cam, 0x010F, 0xF5); + err += zc0301_write_reg(cam, 0x0110, 0xFB); + err += zc0301_write_reg(cam, 0x0111, 0xED); + err += zc0301_write_reg(cam, 0x0112, 0x5F); + err += zc0301_write_reg(cam, 0x0180, 0x00); + err += zc0301_write_reg(cam, 0x0019, 0x00); + err += zc0301_write_reg(cam, 0x0087, 0x20); + err += zc0301_write_reg(cam, 0x0088, 0x21); + + err += zc0301_i2c_write(cam, 0x20, 0x02); + err += zc0301_i2c_write(cam, 0x21, 0x1B); + err += zc0301_i2c_write(cam, 0x03, 0x44); + err += zc0301_i2c_write(cam, 0x0E, 0x01); + err += zc0301_i2c_write(cam, 0x0F, 0x00); + + err += zc0301_write_reg(cam, 0x01A9, 0x14); + err += zc0301_write_reg(cam, 0x01AA, 0x24); + err += zc0301_write_reg(cam, 0x0190, 0x00); + err += zc0301_write_reg(cam, 0x0191, 0x02); + err += zc0301_write_reg(cam, 0x0192, 0x1B); + err += zc0301_write_reg(cam, 0x0195, 0x00); + err += zc0301_write_reg(cam, 0x0196, 0x00); + err += zc0301_write_reg(cam, 0x0197, 0x4D); + err += zc0301_write_reg(cam, 0x018C, 0x10); + err += zc0301_write_reg(cam, 0x018F, 0x20); + err += zc0301_write_reg(cam, 0x001D, 0x44); + err += zc0301_write_reg(cam, 0x001E, 0x6F); + err += zc0301_write_reg(cam, 0x001F, 0xAD); + err += zc0301_write_reg(cam, 0x0020, 0xEB); + err += zc0301_write_reg(cam, 0x0087, 0x0F); + err += zc0301_write_reg(cam, 0x0088, 0x0E); + err += zc0301_write_reg(cam, 0x0180, 0x40); + err += zc0301_write_reg(cam, 0x0192, 0x1B); + err += zc0301_write_reg(cam, 0x0191, 0x02); + err += zc0301_write_reg(cam, 0x0190, 0x00); + err += zc0301_write_reg(cam, 0x0116, 0x1D); + err += zc0301_write_reg(cam, 0x0117, 0x40); + err += zc0301_write_reg(cam, 0x0118, 0x99); + err += zc0301_write_reg(cam, 0x0180, 0x42); + err += zc0301_write_reg(cam, 0x0116, 0x1D); + err += zc0301_write_reg(cam, 0x0117, 0x40); + err += zc0301_write_reg(cam, 0x0118, 0x99); + err += zc0301_write_reg(cam, 0x0007, 0x00); + + err += zc0301_i2c_write(cam, 0x11, 0x01); + + msleep(100); + + return err; +} + + +static int pas202bcb_get_ctrl(struct zc0301_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + { + int r1 = zc0301_i2c_read(cam, 0x04, 1), + r2 = zc0301_i2c_read(cam, 0x05, 1); + if (r1 < 0 || r2 < 0) + return -EIO; + ctrl->value = (r1 << 6) | (r2 & 0x3f); + } + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = zc0301_i2c_read(cam, 0x09, 1)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = zc0301_i2c_read(cam, 0x07, 1)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case V4L2_CID_GAIN: + if ((ctrl->value = zc0301_i2c_read(cam, 0x10, 1)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case ZC0301_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = zc0301_i2c_read(cam, 0x08, 1)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case ZC0301_V4L2_CID_DAC_MAGNITUDE: + if ((ctrl->value = zc0301_i2c_read(cam, 0x0c, 1)) < 0) + return -EIO; + return 0; + default: + return -EINVAL; + } +} + + +static int pas202bcb_set_ctrl(struct zc0301_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += zc0301_i2c_write(cam, 0x04, ctrl->value >> 6); + err += zc0301_i2c_write(cam, 0x05, ctrl->value & 0x3f); + break; + case V4L2_CID_RED_BALANCE: + err += zc0301_i2c_write(cam, 0x09, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += zc0301_i2c_write(cam, 0x07, ctrl->value); + break; + case V4L2_CID_GAIN: + err += zc0301_i2c_write(cam, 0x10, ctrl->value); + break; + case ZC0301_V4L2_CID_GREEN_BALANCE: + err += zc0301_i2c_write(cam, 0x08, ctrl->value); + break; + case ZC0301_V4L2_CID_DAC_MAGNITUDE: + err += zc0301_i2c_write(cam, 0x0c, ctrl->value); + break; + default: + return -EINVAL; + } + err += zc0301_i2c_write(cam, 0x11, 0x01); + + return err ? -EIO : 0; +} + + +static struct zc0301_sensor pas202bcb = { + .name = "PAS202BCB", + .init = &pas202bcb_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x01e5, + .maximum = 0x3fff, + .step = 0x0001, + .default_value = 0x01e5, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x0c, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = ZC0301_V4L2_CID_DAC_MAGNITUDE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "DAC magnitude", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x00, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x01, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x05, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = ZC0301_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x00, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + }, + .get_ctrl = &pas202bcb_get_ctrl, + .set_ctrl = &pas202bcb_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_JPEG, + .priv = 8, + }, +}; + + +int zc0301_probe_pas202bcb(struct zc0301_device* cam) +{ + int r0 = 0, r1 = 0, err = 0; + unsigned int pid = 0; + + err += zc0301_write_reg(cam, 0x0000, 0x01); + err += zc0301_write_reg(cam, 0x0010, 0x0e); + err += zc0301_write_reg(cam, 0x0001, 0x01); + err += zc0301_write_reg(cam, 0x0012, 0x03); + err += zc0301_write_reg(cam, 0x0012, 0x01); + err += zc0301_write_reg(cam, 0x008d, 0x08); + + msleep(10); + + r0 = zc0301_i2c_read(cam, 0x00, 1); + r1 = zc0301_i2c_read(cam, 0x01, 1); + + if (r0 < 0 || r1 < 0 || err) + return -EIO; + + pid = (r0 << 4) | ((r1 & 0xf0) >> 4); + if (pid != 0x017) + return -ENODEV; + + zc0301_attach_sensor(cam, &pas202bcb); + + return 0; +} diff --git a/linux/drivers/media/video/zc0301/zc0301_sensor.h b/linux/drivers/media/video/zc0301/zc0301_sensor.h new file mode 100644 index 000000000..7f11465ce --- /dev/null +++ b/linux/drivers/media/video/zc0301/zc0301_sensor.h @@ -0,0 +1,104 @@ +/*************************************************************************** + * API for image sensors connected to the ZC030! Image Processor and * + * Control Chip * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _ZC0301_SENSOR_H_ +#define _ZC0301_SENSOR_H_ + +#include +#include "compat.h" +#include +#include +#include +#include +#include + +struct zc0301_device; +struct zc0301_sensor; + +/*****************************************************************************/ + +extern int zc0301_probe_pas202bcb(struct zc0301_device* cam); + +#define ZC0301_SENSOR_TABLE \ +/* Weak detections must go at the end of the list */ \ +static int (*zc0301_sensor_table[])(struct zc0301_device*) = { \ + &zc0301_probe_pas202bcb, \ + NULL, \ +}; + +extern struct zc0301_device* +zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id); + +extern void +zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor); + +#define ZC0301_USB_DEVICE(vend, prod, intclass) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_CLASS, \ + .idVendor = (vend), \ + .idProduct = (prod), \ + .bInterfaceClass = (intclass) + +#define ZC0301_ID_TABLE \ +static const struct usb_device_id zc0301_id_table[] = { \ + { ZC0301_USB_DEVICE(0x041e, 0x4017, 0xff), }, \ + { ZC0301_USB_DEVICE(0x041e, 0x401c, 0xff), }, /* PAS106 */ \ + { ZC0301_USB_DEVICE(0x041e, 0x401e, 0xff), }, /* HV7131B */ \ + { ZC0301_USB_DEVICE(0x041e, 0x4034, 0xff), }, /* PAS106 */ \ + { ZC0301_USB_DEVICE(0x041e, 0x4035, 0xff), }, /* PAS106 */ \ + { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202BCB */ \ + { ZC0301_USB_DEVICE(0x0ac8, 0x0301, 0xff), }, \ + { ZC0301_USB_DEVICE(0x10fd, 0x8050, 0xff), }, /* TAS5130D */ \ + { } \ +}; + +/*****************************************************************************/ + +extern int zc0301_write_reg(struct zc0301_device*, u16 index, u16 value); +extern int zc0301_read_reg(struct zc0301_device*, u16 index); +extern int zc0301_i2c_write(struct zc0301_device*, u16 address, u16 value); +extern int zc0301_i2c_read(struct zc0301_device*, u16 address, u8 length); + +/*****************************************************************************/ + +#define ZC0301_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10 +#define ZC0301_V4L2_CID_DAC_MAGNITUDE V4L2_CID_PRIVATE_BASE +#define ZC0301_V4L2_CID_GREEN_BALANCE V4L2_CID_PRIVATE_BASE + 1 + +struct zc0301_sensor { + char name[32]; + + struct v4l2_queryctrl qctrl[ZC0301_MAX_CTRLS]; + struct v4l2_cropcap cropcap; + struct v4l2_pix_format pix_format; + + int (*init)(struct zc0301_device*); + int (*get_ctrl)(struct zc0301_device*, struct v4l2_control* ctrl); + int (*set_ctrl)(struct zc0301_device*, + const struct v4l2_control* ctrl); + int (*set_crop)(struct zc0301_device*, const struct v4l2_rect* rect); + + /* Private */ + struct v4l2_queryctrl _qctrl[ZC0301_MAX_CTRLS]; + struct v4l2_rect _rect; +}; + +#endif /* _ZC0301_SENSOR_H_ */ -- cgit v1.2.3 From 5fee2a6c6c243cc56773187b78a0eafbd688b935 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Apr 2006 18:07:49 -0300 Subject: Fix compilation with V4L1_COMPAT From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/Makefile | 2 ++ 1 file changed, 2 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index d61bc458f..bd3f1a006 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -89,3 +89,5 @@ obj-$(CONFIG_USB_VICAM) += usbvideo/ obj-$(CONFIG_VIDEO_VIVI) += vivi.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +extra-cflags-$(CONFIG_VIDEO_V4L1_COMPAT) += -DCONFIG_VIDEO_V4L1_COMPAT + -- cgit v1.2.3 From f87c0d6c0b3286715f8bed1b4194b0fc712cab57 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Apr 2006 18:08:08 -0300 Subject: A previous patch introduced a dot-comma at wrong places. From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/adv7170.c | 2 +- linux/drivers/media/video/bw-qcam.c | 2 +- linux/drivers/media/video/c-qcam.c | 2 +- linux/drivers/media/video/cpia.c | 2 +- linux/drivers/media/video/meye.c | 2 +- linux/drivers/media/video/ov511.c | 22 +++++++++++----------- linux/drivers/media/video/stradis.c | 8 ++++---- linux/drivers/media/video/w9966.c | 8 ++++---- linux/drivers/media/video/zr36016.c | 2 +- 9 files changed, 25 insertions(+), 25 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/adv7170.c b/linux/drivers/media/video/adv7170.c index cd0b4562a..984ca2a30 100644 --- a/linux/drivers/media/video/adv7170.c +++ b/linux/drivers/media/video/adv7170.c @@ -240,7 +240,7 @@ adv7170_command (struct i2c_client *client, switch (cmd) { case 0: -#if 0 /* keep */; +#if 0 /* keep */ /* This is just for testing!!! */ adv7170_write_block(client, init_common, sizeof(init_common)); diff --git a/linux/drivers/media/video/bw-qcam.c b/linux/drivers/media/video/bw-qcam.c index 62287e3d8..9941128b9 100644 --- a/linux/drivers/media/video/bw-qcam.c +++ b/linux/drivers/media/video/bw-qcam.c @@ -334,7 +334,7 @@ static int qc_detect(struct qcam_device *q) } -#if 0 /* keep */; +#if 0 /* keep */ /* Force camera detection during testing. Sometimes the camera won't be flashing these bits. Possibly unloading the module in the middle of a grab? Or some timeout condition? diff --git a/linux/drivers/media/video/c-qcam.c b/linux/drivers/media/video/c-qcam.c index 4fa309398..2dbd848c4 100644 --- a/linux/drivers/media/video/c-qcam.c +++ b/linux/drivers/media/video/c-qcam.c @@ -626,7 +626,7 @@ static int qcam_do_ioctl(struct inode *inode, struct file *file, qcam->mode = QC_DECIMATION_1; } qcam->mode |= QC_MILLIONS; -#if 0 /* keep */; +#if 0 /* keep */ if(vw->width>=640 && vw->height>=480) { qcam->width = 640; diff --git a/linux/drivers/media/video/cpia.c b/linux/drivers/media/video/cpia.c index 2e66820f1..99ee90628 100644 --- a/linux/drivers/media/video/cpia.c +++ b/linux/drivers/media/video/cpia.c @@ -2430,7 +2430,7 @@ static void set_flicker(struct cam_params *params, volatile u32 *command_flags, #define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \ params->version.firmwareRevision == (y)) /* define for compgain calculation */ -#if 0 /* keep */; +#if 0 /* keep */ #define COMPGAIN(base, curexp, newexp) \ (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5) #define EXP_FROM_COMP(basecomp, curcomp, curexp) \ diff --git a/linux/drivers/media/video/meye.c b/linux/drivers/media/video/meye.c index 73749c740..774d9af5b 100644 --- a/linux/drivers/media/video/meye.c +++ b/linux/drivers/media/video/meye.c @@ -750,7 +750,7 @@ static int mchip_compress_frame(u8 *buf, int bufsize) return len; } -#if 0 /* keep */; +#if 0 /* keep */ /* uncompress one image into a buffer */ static int mchip_uncompress_frame(u8 *img, int imgsize, u8 *buf, int bufsize) { diff --git a/linux/drivers/media/video/ov511.c b/linux/drivers/media/video/ov511.c index 1bf382115..564a88293 100644 --- a/linux/drivers/media/video/ov511.c +++ b/linux/drivers/media/video/ov511.c @@ -638,7 +638,7 @@ ov511_i2c_write_internal(struct usb_ov511 *ov, rc = 0; break; } -#if 0 /* keep */; +#if 0 /* keep */ /* I2C abort */ reg_w(ov, R511_I2C_CTL, 0x10); #endif @@ -1105,7 +1105,7 @@ ov51x_clear_snapshot(struct usb_ov511 *ov) } } -#if 0 /* keep */; +#if 0 /* keep */ /* Checks the status of the snapshot button. Returns 1 if it was pressed since * it was last cleared, and zero in all other cases (including errors) */ static int @@ -1673,7 +1673,7 @@ sensor_set_hue(struct usb_ov511 *ov, unsigned short val) break; case SEN_OV7620: // Hue control is causing problems. I will enable it once it's fixed. -#if 0 /* keep */; +#if 0 /* keep */ rc = i2c_w(ov, 0x7a, (unsigned char)(val >> 8) + 0xb); if (rc < 0) goto out; @@ -1804,7 +1804,7 @@ sensor_get_picture(struct usb_ov511 *ov, struct video_picture *p) return 0; } -#if 0 /* keep */; +#if 0 /* keep */ // FIXME: Exposure range is only 0x00-0x7f in interlace mode /* Sets current exposure for sensor. This only has an effect if auto-exposure * is off */ @@ -2171,7 +2171,7 @@ mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height, case SEN_OV7610: i2c_w(ov, 0x14, qvga?0x24:0x04); // FIXME: Does this improve the image quality or frame rate? -#if 0 /* keep */; +#if 0 /* keep */ i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); i2c_w(ov, 0x24, 0x10); i2c_w(ov, 0x25, qvga?0x40:0x8a); @@ -2193,7 +2193,7 @@ mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height, // i2c_w(ov, 0x2b, 0x00); i2c_w(ov, 0x14, qvga?0xa4:0x84); // FIXME: Enable this once 7620AE uses 7620 initial settings -#if 0 /* keep */; +#if 0 /* keep */ i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20); i2c_w(ov, 0x24, qvga?0x20:0x3a); i2c_w(ov, 0x25, qvga?0x30:0x60); @@ -3435,7 +3435,7 @@ sof: PDEBUG(4, "Starting capture on frame %d", frame->framenum); // Snapshot not reverse-engineered yet. -#if 0 /* keep */; +#if 0 /* keep */ /* Check to see if it's a snapshot frame */ /* FIXME?? Should the snapshot reset go here? Performance? */ if (in[8] & 0x02) { @@ -4193,7 +4193,7 @@ ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file, PDEBUG(4, "VIDIOCSWIN: %dx%d", vw->width, vw->height); -#if 0 /* keep */; +#if 0 /* keep */ if (vw->flags) return -EINVAL; if (vw->clipcount) @@ -4306,7 +4306,7 @@ ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file, rc = mode_init_regs(ov, vm->width, vm->height, vm->format, ov->sub_flag); -#if 0 /* keep */; +#if 0 /* keep */ if (rc < 0) { PDEBUG(1, "Got error while initializing regs "); return ret; @@ -5068,7 +5068,7 @@ ks0127_configure(struct usb_ov511 *ov) int rc; // FIXME: I don't know how to sync or reset it yet -#if 0 /* keep */; +#if 0 /* keep */ if (ov51x_init_ks_sensor(ov) < 0) { err("Failed to initialize the KS0127"); return -1; @@ -5155,7 +5155,7 @@ saa7111a_configure(struct usb_ov511 *ov) }; // FIXME: I don't know how to sync or reset it yet -#if 0 /* keep */; +#if 0 /* keep */ if (ov51x_init_saa_sensor(ov) < 0) { err("Failed to initialize the SAA7111A"); return -1; diff --git a/linux/drivers/media/video/stradis.c b/linux/drivers/media/video/stradis.c index 8cfb32392..f4be8c6af 100644 --- a/linux/drivers/media/video/stradis.c +++ b/linux/drivers/media/video/stradis.c @@ -455,7 +455,7 @@ static irqreturn_t saa7146_irq(int irq, void *dev_id, struct pt_regs *regs) saa->vidinfo.frame_count = 0; saa->vidinfo.h_size = 704; saa->vidinfo.v_size = 480; -#if 0 /* keep */; +#if 0 /* keep */ if (saa->endmarkhead != saa->endmarktail) { saa->audhead = saa->endmark[saa->endmarkhead]; @@ -940,7 +940,7 @@ send_fpga_stuff: if (NewCard) set_genlock_offset(saa, 0); debiwrite(saa, debNormal, IBM_MP2_FRNT_ATTEN, 0, 2); -#if 0 /* keep */; +#if 0 /* keep */ /* enable genlock */ debiwrite(saa, debNormal, XILINX_CTL0, 0x8000, 2); #else @@ -1043,7 +1043,7 @@ static int initialize_ibmmpeg2(struct video_code *microcode) if (i != 0xa55a) { printk(KERN_INFO "stradis%d: %04x != 0xa55a\n", saa->nr, i); -#if 0 /* keep */; +#if 0 /* keep */ return -1; #endif } @@ -2048,7 +2048,7 @@ static int __devinit init_saa7146(struct pci_dev *pdev) dev_err(&pdev->dev, "%d: debi kmalloc failed\n", saa->nr); goto err; } -#if 0 /* keep */; +#if 0 /* keep */ saa->pagedebi = saa->dmadebi + 32768; /* top 4k is for mmu */ saawrite(virt_to_bus(saa->pagedebi) /*|0x800 */ , SAA7146_DEBI_PAGE); for (i = 0; i < 12; i++) /* setup mmu page table */ diff --git a/linux/drivers/media/video/w9966.c b/linux/drivers/media/video/w9966.c index fe368bfb7..d24d4ca3d 100644 --- a/linux/drivers/media/video/w9966.c +++ b/linux/drivers/media/video/w9966.c @@ -158,7 +158,7 @@ static inline void w9966_pdev_release(struct w9966_dev *vdev); static int w9966_rReg(struct w9966_dev* cam, int reg); static int w9966_wReg(struct w9966_dev* cam, int reg, int data); -#if 0 /* keep */; +#if 0 /* keep */ static int w9966_rReg_i2c(struct w9966_dev* cam, int reg); #endif static int w9966_wReg_i2c(struct w9966_dev* cam, int reg, int data); @@ -174,7 +174,7 @@ static inline int w9966_i2c_setscl(struct w9966_dev* cam, int state); static inline int w9966_i2c_getsda(struct w9966_dev* cam); static inline int w9966_i2c_getscl(struct w9966_dev* cam); static int w9966_i2c_wbyte(struct w9966_dev* cam, int data); -#if 0 /* keep */; +#if 0 /* keep */ static int w9966_i2c_rbyte(struct w9966_dev* cam); #endif @@ -623,7 +623,7 @@ static int w9966_i2c_wbyte(struct w9966_dev* cam, int data) // Read a data byte with ack from the i2c-bus // Expects a claimed pdev. -1 on error -#if 0 /* keep */; +#if 0 /* keep */ static int w9966_i2c_rbyte(struct w9966_dev* cam) { unsigned char data = 0x00; @@ -647,7 +647,7 @@ static int w9966_i2c_rbyte(struct w9966_dev* cam) // Read a register from the i2c device. // Expects claimed pdev. -1 on error -#if 0 /* keep */; +#if 0 /* keep */ static int w9966_rReg_i2c(struct w9966_dev* cam, int reg) { int data; diff --git a/linux/drivers/media/video/zr36016.c b/linux/drivers/media/video/zr36016.c index 2a99e8d2e..e5dbb45f1 100644 --- a/linux/drivers/media/video/zr36016.c +++ b/linux/drivers/media/video/zr36016.c @@ -229,7 +229,7 @@ zr36016_basic_test (struct zr36016 *ptr) simple loop for pushing the init datasets - NO USE -- ========================================================================= */ -#if 0 /* keep */; +#if 0 /* keep */ static int zr36016_pushit (struct zr36016 *ptr, u16 startreg, u16 len, -- cgit v1.2.3 From 1c178a2161f89b0d03733613f20015cf0e28909d Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 11 Apr 2006 17:16:22 -0400 Subject: Kbuild: DVB_BT8XX must select DVB_ZL10353 From: Michael Krufky Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/bt8xx/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/bt8xx/Kconfig b/linux/drivers/media/dvb/bt8xx/Kconfig index 376ca48f1..f28d721b8 100644 --- a/linux/drivers/media/dvb/bt8xx/Kconfig +++ b/linux/drivers/media/dvb/bt8xx/Kconfig @@ -7,6 +7,7 @@ config DVB_BT8XX select DVB_CX24110 select DVB_OR51211 select DVB_LGDT330X + select DVB_ZL10353 select FW_LOADER help Support for PCI cards based on the Bt8xx PCI bridge. Examples are -- cgit v1.2.3 From 25205eb3f46ee7ab26238d27127b83e7b9f6f2cf Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Apr 2006 18:19:33 -0300 Subject: Use after free in drivers/media/video/em28xx/em28xx-video.c From: Eric Sesterhenn In several places we use dev->devno right after we kfree() dev. This fixes coverity bug id #1065 Signed-off-by: Eric Sesterhenn Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 1093a68f0..33fe987dd 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1685,8 +1685,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, errCode = em28xx_config(dev); if (errCode) { em28xx_errdev("error configuring device\n"); - kfree(dev); em28xx_devused&=~(1<devno); + kfree(dev); return -ENOMEM; } @@ -1712,8 +1712,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->vdev = video_device_alloc(); if (NULL == dev->vdev) { em28xx_errdev("cannot allocate video_device.\n"); - kfree(dev); em28xx_devused&=~(1<devno); + kfree(dev); return -ENOMEM; } @@ -1721,8 +1721,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, if (NULL == dev->vbi_dev) { em28xx_errdev("cannot allocate video_device.\n"); kfree(dev->vdev); - kfree(dev); em28xx_devused&=~(1<devno); + kfree(dev); return -ENOMEM; } @@ -1765,8 +1765,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, mutex_unlock(&dev->lock); list_del(&dev->devlist); video_device_release(dev->vdev); - kfree(dev); em28xx_devused&=~(1<devno); + kfree(dev); return -ENODEV; } @@ -1777,8 +1777,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, list_del(&dev->devlist); video_device_release(dev->vbi_dev); video_device_release(dev->vdev); - kfree(dev); em28xx_devused&=~(1<devno); + kfree(dev); return -ENODEV; } else { printk("registered VBI\n"); -- cgit v1.2.3 From 0cdd88b619025b1eb047a9f5f2eb8f51efadcb05 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Apr 2006 18:27:18 -0300 Subject: Keep #if 0/#if 1 for newer drivers at the tree From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/pwc/pwc-if.c | 2 +- linux/drivers/media/video/pwc/pwc-uncompress.c | 2 +- linux/drivers/media/video/usbvideo/ibmcam.c | 20 ++++++++++---------- linux/drivers/media/video/usbvideo/ultracam.c | 2 +- linux/drivers/media/video/usbvideo/usbvideo.c | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pwc/pwc-if.c b/linux/drivers/media/video/pwc/pwc-if.c index 41418294a..7c97270d3 100644 --- a/linux/drivers/media/video/pwc/pwc-if.c +++ b/linux/drivers/media/video/pwc/pwc-if.c @@ -320,7 +320,7 @@ static int pwc_allocate_buffers(struct pwc_device *pdev) case 730: case 740: case 750: -#if 0 +#if 0 /* keep */ Trace(TRACE_MEMORY,"private_data(%zu)\n",sizeof(struct pwc_dec23_private)); kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); /* Timon & Kiara */ break; diff --git a/linux/drivers/media/video/pwc/pwc-uncompress.c b/linux/drivers/media/video/pwc/pwc-uncompress.c index b37a89a16..eec2c3c45 100644 --- a/linux/drivers/media/video/pwc/pwc-uncompress.c +++ b/linux/drivers/media/video/pwc/pwc-uncompress.c @@ -118,7 +118,7 @@ int pwc_decompress(struct pwc_device *pdev) return -ENXIO; /* No such device or address: missing decompressor */ } -#if 0 +#if 0 /* keep */ switch (pdev->type) { case 675: diff --git a/linux/drivers/media/video/usbvideo/ibmcam.c b/linux/drivers/media/video/usbvideo/ibmcam.c index 76f771b6a..c5c444692 100644 --- a/linux/drivers/media/video/usbvideo/ibmcam.c +++ b/linux/drivers/media/video/usbvideo/ibmcam.c @@ -258,7 +258,7 @@ static enum ParseState ibmcam_find_header(struct uvd *uvd) /* FIXME: Add frame h (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xFF) && (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00)) { -#if 0 /* This code helps to detect new frame markers */ +#if 0 /* keep */ /* This code helps to detect new frame markers */ info("Header sig: 00 FF 00 %02X", RING_QUEUE_PEEK(&uvd->dp, 3)); #endif frame->header = RING_QUEUE_PEEK(&uvd->dp, 3); @@ -266,7 +266,7 @@ static enum ParseState ibmcam_find_header(struct uvd *uvd) /* FIXME: Add frame h (frame->header == HDRSIG_MODEL1_176x144) || (frame->header == HDRSIG_MODEL1_352x288)) { -#if 0 +#if 0 /* keep */ info("Header found."); #endif RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len); @@ -295,7 +295,7 @@ case IBMCAM_MODEL_4: if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) && (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xFF)) { -#if 0 +#if 0 /* keep */ info("Header found."); #endif RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len); @@ -338,7 +338,7 @@ case IBMCAM_MODEL_4: byte3 = RING_QUEUE_PEEK(&uvd->dp, 2); byte4 = RING_QUEUE_PEEK(&uvd->dp, 3); frame->header = (byte3 << 8) | byte4; -#if 0 +#if 0 /* keep */ info("Header found."); #endif RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len); @@ -1100,7 +1100,7 @@ static void ibmcam_ProcessIsocData(struct uvd *uvd, /* Update the frame's uncompressed length. */ frame->seqRead_Length += copylen; -#if 0 +#if 0 /* keep */ { static unsigned char j=0; memset(frame->data, j++, uvd->max_frame_size); @@ -1139,7 +1139,7 @@ static int ibmcam_veio( cp, sizeof(cp), 1000); -#if 0 +#if 0 /* keep */ info("USB => %02x%02x%02x%02x%02x%02x%02x%02x " "(req=$%02x val=$%04x ind=$%04x)", cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7], @@ -1433,7 +1433,7 @@ static void ibmcam_change_lighting_conditions(struct uvd *uvd) break; } case IBMCAM_MODEL_2: -#if 0 +#if 0 /* keep */ /* * This command apparently requires camera to be stopped. My * experiments showed that it -is- possible to alter the lighting @@ -1611,7 +1611,7 @@ static void ibmcam_set_hue(struct uvd *uvd) } case IBMCAM_MODEL_3: { -#if 0 /* This seems not to work. No problem, will fix programmatically */ +#if 0 /* keep */ /* This seems not to work. No problem, will fix programmatically */ unsigned short hue = 0x05 + (uvd->vpic.hue / (0xFFFF / (0x37 - 0x05 + 1))); RESTRICT_TO_RANGE(hue, 0x05, 0x37); if (uvd->vpic_old.hue == hue) @@ -1860,7 +1860,7 @@ static int ibmcam_model1_setup(struct uvd *uvd) ibmcam_veio(uvd, 0, 0x04, 0x011a); /* Same everywhere */ ibmcam_veio(uvd, 0, 0x2b, 0x011c); ibmcam_veio(uvd, 0, 0x23, 0x012a); /* Same everywhere */ -#if 0 +#if 0 /* keep */ ibmcam_veio(uvd, 0, 0x00, 0x0106); ibmcam_veio(uvd, 0, 0x38, 0x0107); #else @@ -3502,7 +3502,7 @@ case IBMCAM_MODEL_4: ibmcam_veio(uvd, 0, 0x0000, 0x0112); break; case IBMCAM_MODEL_3: -#if 1 +#if 1 /* keep */ ibmcam_veio(uvd, 0, 0x0000, 0x010c); /* Here we are supposed to select video interface alt. setting 0 */ diff --git a/linux/drivers/media/video/usbvideo/ultracam.c b/linux/drivers/media/video/usbvideo/ultracam.c index 10c58b4a2..344f6cca9 100644 --- a/linux/drivers/media/video/usbvideo/ultracam.c +++ b/linux/drivers/media/video/usbvideo/ultracam.c @@ -156,7 +156,7 @@ static int ultracam_veio( cp, sizeof(cp), 1000); -#if 1 +#if 1 /* keep */ info("USB => %02x%02x%02x%02x%02x%02x%02x%02x " "(req=$%02x val=$%04x ind=$%04x)", cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7], diff --git a/linux/drivers/media/video/usbvideo/usbvideo.c b/linux/drivers/media/video/usbvideo/usbvideo.c index 13b37c8c0..4db4569a5 100644 --- a/linux/drivers/media/video/usbvideo/usbvideo.c +++ b/linux/drivers/media/video/usbvideo/usbvideo.c @@ -542,7 +542,7 @@ void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode) frame->curline = 0; frame->seqRead_Length = 0; } -#if 0 +#if 0 /* keep */ { /* For debugging purposes only */ char tmp[20]; usbvideo_VideosizeToString(tmp, sizeof(tmp), frame->request); @@ -1688,7 +1688,7 @@ static void usbvideo_IsocIrq(struct urb *urb, struct pt_regs *regs) /* We don't want to do anything if we are about to be removed! */ if (!CAMERA_IS_OPERATIONAL(uvd)) return; -#if 0 +#if 0 /* keep */ if (urb->actual_length > 0) { info("urb=$%p status=%d. errcount=%d. length=%d.", urb, urb->status, urb->error_count, urb->actual_length); -- cgit v1.2.3 From 734079794a8f64d7443a5bab87ce4febe810385b Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Thu, 13 Apr 2006 14:19:52 +0100 Subject: Fix for CX24123 & low symbol rates From: Vadim Catana - fixed the reception of channels with low symbol rates. ( The VGA1 and VGA2 offsets recommended by cx24109 docs for symbol rates from 1 to 5 MSps do not work. I changed them to values found experimentally. The charge pump current and FILTUNE voltage are now set to values recommended in the docs. This improves reception for symbol rates < 15 MSps. The values written in the SYSSymbolRate registers are calculated with better precision. ) - fixed the cx24123_get_fec() function. It was returning the values for DCII mode. - removed some unused variables Signed-off-by: Vadim Catana Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/cx24123.c | 262 +++++++++++++++++++--------- 1 file changed, 181 insertions(+), 81 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/cx24123.c b/linux/drivers/media/dvb/frontends/cx24123.c index d661c6f9c..e430e6a50 100644 --- a/linux/drivers/media/dvb/frontends/cx24123.c +++ b/linux/drivers/media/dvb/frontends/cx24123.c @@ -29,6 +29,8 @@ #include "dvb_frontend.h" #include "cx24123.h" +#define XTAL 10111000 + static int debug; #define dprintk(args...) \ do { \ @@ -52,6 +54,7 @@ struct cx24123_state u32 VGAarg; u32 bandselectarg; u32 pllarg; + u32 FILTune; /* The Demod/Tuner can't easily provide these, we cache them */ u32 currentfreq; @@ -63,43 +66,33 @@ static struct { u32 symbolrate_low; u32 symbolrate_high; - u32 VCAslope; - u32 VCAoffset; - u32 VGA1offset; - u32 VGA2offset; u32 VCAprogdata; u32 VGAprogdata; + u32 FILTune; } cx24123_AGC_vals[] = { { .symbolrate_low = 1000000, .symbolrate_high = 4999999, - .VCAslope = 0x07, - .VCAoffset = 0x0f, - .VGA1offset = 0x1f8, - .VGA2offset = 0x1f8, - .VGAprogdata = (2 << 18) | (0x1f8 << 9) | 0x1f8, + /* the specs recommend other values for VGA offsets, + but tests show they are wrong */ + .VGAprogdata = (2 << 18) | (0x180 << 9) | 0x1e0, .VCAprogdata = (4 << 18) | (0x07 << 9) | 0x07, + .FILTune = 0x280 /* 0.41 V */ }, { .symbolrate_low = 5000000, .symbolrate_high = 14999999, - .VCAslope = 0x1f, - .VCAoffset = 0x1f, - .VGA1offset = 0x1e0, - .VGA2offset = 0x180, .VGAprogdata = (2 << 18) | (0x180 << 9) | 0x1e0, .VCAprogdata = (4 << 18) | (0x07 << 9) | 0x1f, + .FILTune = 0x317 /* 0.90 V */ }, { .symbolrate_low = 15000000, .symbolrate_high = 45000000, - .VCAslope = 0x3f, - .VCAoffset = 0x3f, - .VGA1offset = 0x180, - .VGA2offset = 0x100, .VGAprogdata = (2 << 18) | (0x100 << 9) | 0x180, .VCAprogdata = (4 << 18) | (0x07 << 9) | 0x3f, + .FILTune = 0x146 /* 2.70 V */ }, }; @@ -112,90 +105,68 @@ static struct { u32 freq_low; u32 freq_high; - u32 bandselect; u32 VCOdivider; - u32 VCOnumber; u32 progdata; } cx24123_bandselect_vals[] = { { .freq_low = 950000, .freq_high = 1018999, - .bandselect = 0x40, .VCOdivider = 4, - .VCOnumber = 7, .progdata = (0 << 18) | (0 << 9) | 0x40, }, { .freq_low = 1019000, .freq_high = 1074999, - .bandselect = 0x80, .VCOdivider = 4, - .VCOnumber = 8, .progdata = (0 << 18) | (0 << 9) | 0x80, }, { .freq_low = 1075000, .freq_high = 1227999, - .bandselect = 0x01, .VCOdivider = 2, - .VCOnumber = 1, .progdata = (0 << 18) | (1 << 9) | 0x01, }, { .freq_low = 1228000, .freq_high = 1349999, - .bandselect = 0x02, .VCOdivider = 2, - .VCOnumber = 2, .progdata = (0 << 18) | (1 << 9) | 0x02, }, { .freq_low = 1350000, .freq_high = 1481999, - .bandselect = 0x04, .VCOdivider = 2, - .VCOnumber = 3, .progdata = (0 << 18) | (1 << 9) | 0x04, }, { .freq_low = 1482000, .freq_high = 1595999, - .bandselect = 0x08, .VCOdivider = 2, - .VCOnumber = 4, .progdata = (0 << 18) | (1 << 9) | 0x08, }, { .freq_low = 1596000, .freq_high = 1717999, - .bandselect = 0x10, .VCOdivider = 2, - .VCOnumber = 5, .progdata = (0 << 18) | (1 << 9) | 0x10, }, { .freq_low = 1718000, .freq_high = 1855999, - .bandselect = 0x20, .VCOdivider = 2, - .VCOnumber = 6, .progdata = (0 << 18) | (1 << 9) | 0x20, }, { .freq_low = 1856000, .freq_high = 2035999, - .bandselect = 0x40, .VCOdivider = 2, - .VCOnumber = 7, .progdata = (0 << 18) | (1 << 9) | 0x40, }, { .freq_low = 2036000, .freq_high = 2149999, - .bandselect = 0x80, .VCOdivider = 2, - .VCOnumber = 8, .progdata = (0 << 18) | (1 << 9) | 0x80, }, }; @@ -207,7 +178,6 @@ static struct { { {0x00, 0x03}, /* Reset system */ {0x00, 0x00}, /* Clear reset */ - {0x01, 0x3b}, /* Apply sensible defaults, from an i2c sniffer */ {0x03, 0x07}, {0x04, 0x10}, {0x05, 0x04}, @@ -217,7 +187,6 @@ static struct { {0x0f, 0xfe}, {0x10, 0x01}, {0x14, 0x01}, - {0x15, 0x98}, {0x16, 0x00}, {0x17, 0x01}, {0x1b, 0x05}, @@ -226,8 +195,6 @@ static struct { {0x1e, 0x00}, {0x20, 0x41}, {0x21, 0x15}, - {0x27, 0x14}, - {0x28, 0x46}, {0x29, 0x00}, {0x2a, 0xb0}, {0x2b, 0x73}, @@ -375,55 +342,103 @@ static int cx24123_set_fec(struct cx24123_state* state, fe_code_rate_t fec) static int cx24123_get_fec(struct cx24123_state* state, fe_code_rate_t *fec) { int ret; - u8 val; ret = cx24123_readreg (state, 0x1b); if (ret < 0) return ret; - val = ret & 0x07; - switch (val) { + ret = ret & 0x07; + + switch (ret) { case 1: *fec = FEC_1_2; break; - case 3: + case 2: *fec = FEC_2_3; break; - case 4: + case 3: *fec = FEC_3_4; break; - case 5: + case 4: *fec = FEC_4_5; break; - case 6: + case 5: *fec = FEC_5_6; break; + case 6: + *fec = FEC_6_7; + break; case 7: *fec = FEC_7_8; break; - case 2: /* *fec = FEC_3_5; break; */ - case 0: /* *fec = FEC_5_11; break; */ - *fec = FEC_AUTO; - break; default: *fec = FEC_NONE; // can't happen + printk("FEC_NONE ?\n"); } return 0; } -/* fixme: Symbol rates < 3MSps may not work because of precision loss */ static int cx24123_set_symbolrate(struct cx24123_state* state, u32 srate) { - u32 val; + u32 tmp, sample_rate, ratio; + u8 pll_mult; + + /* check if symbol rate is within limits */ + if ((srate > state->ops.info.symbol_rate_max) || + (srate < state->ops.info.symbol_rate_min)) + return -EOPNOTSUPP;; + + /* choose the sampling rate high enough for the required operation, + while optimizing the power consumed by the demodulator */ + if (srate < (XTAL*2)/2) + pll_mult = 2; + else if (srate < (XTAL*3)/2) + pll_mult = 3; + else if (srate < (XTAL*4)/2) + pll_mult = 4; + else if (srate < (XTAL*5)/2) + pll_mult = 5; + else if (srate < (XTAL*6)/2) + pll_mult = 6; + else if (srate < (XTAL*7)/2) + pll_mult = 7; + else if (srate < (XTAL*8)/2) + pll_mult = 8; + else + pll_mult = 9; + + + sample_rate = pll_mult * XTAL; - val = (srate / 1185) * 100; + /* + SYSSymbolRate[21:0] = (srate << 23) / sample_rate - /* Compensate for scaling up, by removing 17 symbols per 1Msps */ - val = val - (17 * (srate / 1000000)); + We have to use 32 bit unsigned arithmetic without precision loss. + The maximum srate is 45000000 or 0x02AEA540. This number has + only 6 clear bits on top, hence we can shift it left only 6 bits + at a time. Borrowed from cx24110.c + */ - cx24123_writereg(state, 0x08, (val >> 16) & 0xff ); - cx24123_writereg(state, 0x09, (val >> 8) & 0xff ); - cx24123_writereg(state, 0x0a, (val ) & 0xff ); + tmp = srate << 6; + ratio = tmp / sample_rate; + + tmp = (tmp % sample_rate) << 6; + ratio = (ratio << 6) + (tmp / sample_rate); + + tmp = (tmp % sample_rate) << 6; + ratio = (ratio << 6) + (tmp / sample_rate); + + tmp = (tmp % sample_rate) << 5; + ratio = (ratio << 5) + (tmp / sample_rate); + + + cx24123_writereg(state, 0x01, pll_mult * 6); + + cx24123_writereg(state, 0x08, (ratio >> 16) & 0x3f ); + cx24123_writereg(state, 0x09, (ratio >> 8) & 0xff ); + cx24123_writereg(state, 0x0a, (ratio ) & 0xff ); + + dprintk("%s: srate=%d, ratio=0x%08x, sample_rate=%i\n", __FUNCTION__, srate, ratio, sample_rate); return 0; } @@ -437,6 +452,7 @@ static int cx24123_pll_calculate(struct dvb_frontend* fe, struct dvb_frontend_pa struct cx24123_state *state = fe->demodulator_priv; u32 ndiv = 0, adiv = 0, vco_div = 0; int i = 0; + int pump = 2; /* Defaults for low freq, low rate */ state->VCAarg = cx24123_AGC_vals[0].VCAprogdata; @@ -444,13 +460,14 @@ static int cx24123_pll_calculate(struct dvb_frontend* fe, struct dvb_frontend_pa state->bandselectarg = cx24123_bandselect_vals[0].progdata; vco_div = cx24123_bandselect_vals[0].VCOdivider; - /* For the given symbolerate, determine the VCA and VGA programming bits */ + /* For the given symbol rate, determine the VCA, VGA and FILTUNE programming bits */ for (i = 0; i < sizeof(cx24123_AGC_vals) / sizeof(cx24123_AGC_vals[0]); i++) { if ((cx24123_AGC_vals[i].symbolrate_low <= p->u.qpsk.symbol_rate) && - (cx24123_AGC_vals[i].symbolrate_high >= p->u.qpsk.symbol_rate) ) { + (cx24123_AGC_vals[i].symbolrate_high >= p->u.qpsk.symbol_rate) ) { state->VCAarg = cx24123_AGC_vals[i].VCAprogdata; state->VGAarg = cx24123_AGC_vals[i].VGAprogdata; + state->FILTune = cx24123_AGC_vals[i].FILTune; } } @@ -458,24 +475,28 @@ static int cx24123_pll_calculate(struct dvb_frontend* fe, struct dvb_frontend_pa for (i = 0; i < sizeof(cx24123_bandselect_vals) / sizeof(cx24123_bandselect_vals[0]); i++) { if ((cx24123_bandselect_vals[i].freq_low <= p->frequency) && - (cx24123_bandselect_vals[i].freq_high >= p->frequency) ) { + (cx24123_bandselect_vals[i].freq_high >= p->frequency) ) { state->bandselectarg = cx24123_bandselect_vals[i].progdata; vco_div = cx24123_bandselect_vals[i].VCOdivider; + + /* determine the charge pump current */ + if ( p->frequency < (cx24123_bandselect_vals[i].freq_low + cx24123_bandselect_vals[i].freq_high)/2 ) + pump = 0x01; + else + pump = 0x02; } } /* Determine the N/A dividers for the requested lband freq (in kHz). */ - /* Note: 10111 (kHz) is the Crystal Freq and divider of 10. */ - ndiv = ( ((p->frequency * vco_div) / (10111 / 10) / 2) / 32) & 0x1ff; - adiv = ( ((p->frequency * vco_div) / (10111 / 10) / 2) % 32) & 0x1f; + /* Note: the reference divider R=10, frequency is in KHz, XTAL is in Hz */ + ndiv = ( ((p->frequency * vco_div * 10) / (2 * XTAL / 1000)) / 32) & 0x1ff; + adiv = ( ((p->frequency * vco_div * 10) / (2 * XTAL / 1000)) % 32) & 0x1f; if (adiv == 0) - adiv++; + ndiv++; - /* determine the correct pll frequency values. */ - /* Command 11, refdiv 11, cpump polarity 1, cpump current 3mA 10. */ - state->pllarg = (3 << 19) | (3 << 17) | (1 << 16) | (2 << 14); - state->pllarg |= (ndiv << 5) | adiv; + /* control bits 11, refdiv 11, charge pump polarity 1, charge pump current, ndiv, adiv */ + state->pllarg = (3 << 19) | (3 << 17) | (1 << 16) | (pump << 14) | (ndiv << 5) | adiv; return 0; } @@ -538,6 +559,9 @@ static int cx24123_pll_writereg(struct dvb_frontend* fe, struct dvb_frontend_par static int cx24123_pll_tune(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { struct cx24123_state *state = fe->demodulator_priv; + u8 val; + + dprintk("frequency=%i\n", p->frequency); if (cx24123_pll_calculate(fe, p) != 0) { printk("%s: cx24123_pll_calcutate failed\n",__FUNCTION__); @@ -552,6 +576,11 @@ static int cx24123_pll_tune(struct dvb_frontend* fe, struct dvb_frontend_paramet cx24123_pll_writereg(fe, p, state->bandselectarg); cx24123_pll_writereg(fe, p, state->pllarg); + /* set the FILTUNE voltage */ + val = cx24123_readreg(state, 0x28) & ~0x3; + cx24123_writereg(state, 0x27, state->FILTune >> 2); + cx24123_writereg(state, 0x28, val | (state->FILTune & 0x3)); + return 0; } @@ -624,13 +653,81 @@ static int cx24123_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage return 0; } -static int cx24123_send_diseqc_msg(struct dvb_frontend* fe, - struct dvb_diseqc_master_cmd *cmd) +static int cx24123_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd) { - /* fixme: Implement diseqc */ - printk("%s: No support yet\n",__FUNCTION__); + struct cx24123_state *state = fe->demodulator_priv; + int i, val; + unsigned long timeout; + + dprintk("%s:\n",__FUNCTION__); - return -ENOTSUPP; + /* check if continuous tone has been stoped */ + if (state->config->use_isl6421) + val = cx24123_readlnbreg(state, 0x00) & 0x10; + else + val = cx24123_readreg(state, 0x29) & 0x10; + + + if (val) { + printk("%s: ERROR: attempt to send diseqc command before tone is off\n", __FUNCTION__); + return -ENOTSUPP; + } + + /* select tone mode */ + cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) & 0xf8); + + for (i = 0; i < cmd->msg_len; i++) + cx24123_writereg(state, 0x2C + i, cmd->msg[i]); + + val = cx24123_readreg(state, 0x29); + cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40) | ((cmd->msg_len-3) & 3)); + + timeout = jiffies + msecs_to_jiffies(100); + while (!time_after(jiffies, timeout) && !(cx24123_readreg(state, 0x29) & 0x40)) + ; // wait for LNB ready + + return 0; +} + +static int cx24123_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) +{ + struct cx24123_state *state = fe->demodulator_priv; + int val; + unsigned long timeout; + + dprintk("%s:\n", __FUNCTION__); + + /* check if continuous tone has been stoped */ + if (state->config->use_isl6421) + val = cx24123_readlnbreg(state, 0x00) & 0x10; + else + val = cx24123_readreg(state, 0x29) & 0x10; + + + if (val) { + printk("%s: ERROR: attempt to send diseqc command before tone is off\n", __FUNCTION__); + return -ENOTSUPP; + } + + /* select tone mode */ + val = cx24123_readreg(state, 0x2a) & 0xf8; + cx24123_writereg(state, 0x2a, val | 0x04); + + val = cx24123_readreg(state, 0x29); + + if (burst == SEC_MINI_A) + cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40 | 0x00)); + else if (burst == SEC_MINI_B) + cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40 | 0x08)); + else + return -EINVAL; + + + timeout = jiffies + msecs_to_jiffies(100); + while (!time_after(jiffies, timeout) && !(cx24123_readreg(state, 0x29) & 0x40)) + ; // wait for LNB ready + + return 0; } static int cx24123_read_status(struct dvb_frontend* fe, fe_status_t* status) @@ -642,13 +739,15 @@ static int cx24123_read_status(struct dvb_frontend* fe, fe_status_t* status) *status = 0; if (lock & 0x01) - *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; + *status |= FE_HAS_SIGNAL; + if (sync & 0x02) + *status |= FE_HAS_CARRIER; if (sync & 0x04) *status |= FE_HAS_VITERBI; if (sync & 0x08) - *status |= FE_HAS_CARRIER; + *status |= FE_HAS_SYNC; if (sync & 0x80) - *status |= FE_HAS_SYNC | FE_HAS_LOCK; + *status |= FE_HAS_LOCK; return 0; } @@ -875,6 +974,7 @@ static struct dvb_frontend_ops cx24123_ops = { .read_snr = cx24123_read_snr, .read_ucblocks = cx24123_read_ucblocks, .diseqc_send_master_cmd = cx24123_send_diseqc_msg, + .diseqc_send_burst = cx24123_diseqc_send_burst, .set_tone = cx24123_set_tone, .set_voltage = cx24123_set_voltage, }; -- cgit v1.2.3 From 49368db01d3758e614b39303684cb2ac9be74fab Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Thu, 13 Apr 2006 15:29:13 +0100 Subject: Add several debug messages to cx24123 code From: Mauro Carvalho Chehab Current debug messages at cx24123 are next to useless, since they don't print the values sent/read to registers. With this patch, debug=1 will show comprehensive messages. debug=2 will show also read/write operations at I2C bus. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/cx24123.c | 54 +++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/cx24123.c b/linux/drivers/media/dvb/frontends/cx24123.c index e430e6a50..115ec169b 100644 --- a/linux/drivers/media/dvb/frontends/cx24123.c +++ b/linux/drivers/media/dvb/frontends/cx24123.c @@ -225,6 +225,10 @@ static int cx24123_writereg(struct cx24123_state* state, int reg, int data) struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; int err; + if (debug>1) + printk("cx24123: %s: write reg 0x%02x, value 0x%02x\n", + __FUNCTION__,reg, data); + if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { printk("%s: writereg error(err == %i, reg == 0x%02x," " data == 0x%02x)\n", __FUNCTION__, err, reg, data); @@ -241,6 +245,10 @@ static int cx24123_writelnbreg(struct cx24123_state* state, int reg, int data) struct i2c_msg msg = { .addr = 0x08, .flags = 0, .buf = buf, .len = 2 }; int err; + if (debug>1) + printk("cx24123: %s: writeln addr=0x08, reg 0x%02x, value 0x%02x\n", + __FUNCTION__,reg, data); + if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { printk("%s: writelnbreg error (err == %i, reg == 0x%02x," " data == 0x%02x)\n", __FUNCTION__, err, reg, data); @@ -270,6 +278,9 @@ static int cx24123_readreg(struct cx24123_state* state, u8 reg) return ret; } + if (debug>1) + printk("cx24123: read reg 0x%02x, value 0x%02x\n",reg, ret); + return b1[0]; } @@ -282,14 +293,17 @@ static int cx24123_set_inversion(struct cx24123_state* state, fe_spectral_invers { switch (inversion) { case INVERSION_OFF: + dprintk("%s: inversion off\n",__FUNCTION__); cx24123_writereg(state, 0x0e, cx24123_readreg(state, 0x0e) & 0x7f); cx24123_writereg(state, 0x10, cx24123_readreg(state, 0x10) | 0x80); break; case INVERSION_ON: + dprintk("%s: inversion on\n",__FUNCTION__); cx24123_writereg(state, 0x0e, cx24123_readreg(state, 0x0e) | 0x80); cx24123_writereg(state, 0x10, cx24123_readreg(state, 0x10) | 0x80); break; case INVERSION_AUTO: + dprintk("%s: inversion auto\n",__FUNCTION__); cx24123_writereg(state, 0x10, cx24123_readreg(state, 0x10) & 0x7f); break; default: @@ -305,10 +319,13 @@ static int cx24123_get_inversion(struct cx24123_state* state, fe_spectral_invers val = cx24123_readreg(state, 0x1b) >> 7; - if (val == 0) + if (val == 0) { + dprintk("%s: read inversion off\n",__FUNCTION__); *inversion = INVERSION_OFF; - else + } else { + dprintk("%s: read inversion on\n",__FUNCTION__); *inversion = INVERSION_ON; + } return 0; } @@ -321,18 +338,25 @@ static int cx24123_set_fec(struct cx24123_state* state, fe_code_rate_t fec) /* Hardware has 5/11 and 3/5 but are never unused */ switch (fec) { case FEC_NONE: + dprintk("%s: set FEC to none\n",__FUNCTION__); return cx24123_writereg(state, 0x0f, 0x01); case FEC_1_2: + dprintk("%s: set FEC to 1/2\n",__FUNCTION__); return cx24123_writereg(state, 0x0f, 0x02); case FEC_2_3: + dprintk("%s: set FEC to 2/3\n",__FUNCTION__); return cx24123_writereg(state, 0x0f, 0x04); case FEC_3_4: + dprintk("%s: set FEC to 3/4\n",__FUNCTION__); return cx24123_writereg(state, 0x0f, 0x08); case FEC_5_6: + dprintk("%s: set FEC to 4/5\n",__FUNCTION__); return cx24123_writereg(state, 0x0f, 0x20); case FEC_7_8: + dprintk("%s: set FEC to 5/6\n",__FUNCTION__); return cx24123_writereg(state, 0x0f, 0x80); case FEC_AUTO: + dprintk("%s: set FEC to auto\n",__FUNCTION__); return cx24123_writereg(state, 0x0f, 0xae); default: return -EOPNOTSUPP; @@ -510,6 +534,8 @@ static int cx24123_pll_writereg(struct dvb_frontend* fe, struct dvb_frontend_par struct cx24123_state *state = fe->demodulator_priv; unsigned long timeout; + dprintk("%s: pll writereg called, data=0x%08x\n",__FUNCTION__,data); + /* align the 21 bytes into to bit23 boundary */ data = data << 3; @@ -581,6 +607,9 @@ static int cx24123_pll_tune(struct dvb_frontend* fe, struct dvb_frontend_paramet cx24123_writereg(state, 0x27, state->FILTune >> 2); cx24123_writereg(state, 0x28, val | (state->FILTune & 0x3)); + dprintk("%s: pll tune VCA=%d, band=%d, pll=%d\n",__FUNCTION__,state->VCAarg, + state->bandselectarg,state->pllarg); + return 0; } @@ -589,6 +618,8 @@ static int cx24123_initfe(struct dvb_frontend* fe) struct cx24123_state *state = fe->demodulator_priv; int i; + dprintk("%s: init frontend\n",__FUNCTION__); + /* Configure the demod to a good set of defaults */ for (i = 0; i < sizeof(cx24123_regdata) / sizeof(cx24123_regdata[0]); i++) cx24123_writereg(state, cx24123_regdata[i].reg, cx24123_regdata[i].data); @@ -616,10 +647,13 @@ static int cx24123_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage switch (voltage) { case SEC_VOLTAGE_13: + dprintk("%s: isl6421 voltage = 13V\n",__FUNCTION__); return cx24123_writelnbreg(state, 0x0, val & 0x32); /* V 13v */ case SEC_VOLTAGE_18: + dprintk("%s: isl6421 voltage = 18V\n",__FUNCTION__); return cx24123_writelnbreg(state, 0x0, val | 0x04); /* H 18v */ case SEC_VOLTAGE_OFF: + dprintk("%s: isl5421 voltage off\n",__FUNCTION__); return cx24123_writelnbreg(state, 0x0, val & 0x30); default: return -EINVAL; @@ -780,6 +814,8 @@ static int cx24123_read_ber(struct dvb_frontend* fe, u32* ber) else state->snr = 0; + dprintk("%s: BER = %d, S/N index = %d\n",__FUNCTION__,state->lastber, state->snr); + *ber = state->lastber; return 0; @@ -790,6 +826,8 @@ static int cx24123_read_signal_strength(struct dvb_frontend* fe, u16* signal_str struct cx24123_state *state = fe->demodulator_priv; *signal_strength = cx24123_readreg(state, 0x3b) << 8; /* larger = better */ + dprintk("%s: Signal strength = %d\n",__FUNCTION__,*signal_strength); + return 0; } @@ -798,6 +836,8 @@ static int cx24123_read_snr(struct dvb_frontend* fe, u16* snr) struct cx24123_state *state = fe->demodulator_priv; *snr = state->snr; + dprintk("%s: read S/N index = %d\n",__FUNCTION__,*snr); + return 0; } @@ -806,6 +846,8 @@ static int cx24123_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) struct cx24123_state *state = fe->demodulator_priv; *ucblocks = state->lastber; + dprintk("%s: ucblocks (ber) = %d\n",__FUNCTION__,*ucblocks); + return 0; } @@ -813,6 +855,8 @@ static int cx24123_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par { struct cx24123_state *state = fe->demodulator_priv; + dprintk("%s: set_frontend\n",__FUNCTION__); + if (state->config->set_ts_params) state->config->set_ts_params(fe, 0); @@ -836,6 +880,8 @@ static int cx24123_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_par { struct cx24123_state *state = fe->demodulator_priv; + dprintk("%s: get_frontend\n",__FUNCTION__); + if (cx24123_get_inversion(state, &p->inversion) != 0) { printk("%s: Failed to get inversion status\n",__FUNCTION__); return -EREMOTEIO; @@ -862,8 +908,10 @@ static int cx24123_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) switch (tone) { case SEC_TONE_ON: + dprintk("%s: isl6421 sec tone on\n",__FUNCTION__); return cx24123_writelnbreg(state, 0x0, val | 0x10); case SEC_TONE_OFF: + dprintk("%s: isl6421 sec tone off\n",__FUNCTION__); return cx24123_writelnbreg(state, 0x0, val & 0x2f); default: printk("%s: CASE reached default with tone=%d\n", __FUNCTION__, tone); @@ -980,7 +1028,7 @@ static struct dvb_frontend_ops cx24123_ops = { }; module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24123/cx24109 hardware"); MODULE_AUTHOR("Steven Toth"); -- cgit v1.2.3 From 39dadf89c3e1dc382496e39a8f2d9a6d3d713073 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Thu, 13 Apr 2006 15:40:59 +0100 Subject: Always wait for diseqc queue to become ready before transmitting a diseqc message From: Yeasah Pell The previous DISEQC code didn't wait, so it was unreliable Signed-off-by: Yeasah Pell Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/cx24123.c | 32 ++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/cx24123.c b/linux/drivers/media/dvb/frontends/cx24123.c index 115ec169b..fa6cdba08 100644 --- a/linux/drivers/media/dvb/frontends/cx24123.c +++ b/linux/drivers/media/dvb/frontends/cx24123.c @@ -687,15 +687,27 @@ static int cx24123_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage return 0; } +/* wait for diseqc queue to become ready (or timeout) */ +static void cx24123_wait_for_diseqc(struct cx24123_state *state) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(200); + while (!(cx24123_readreg(state, 0x29) & 0x40)) { + if(time_after(jiffies, timeout)) { + printk("%s: diseqc queue not ready, command may be lost.\n", __FUNCTION__); + break; + } + msleep(10); + } +} + static int cx24123_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd) { struct cx24123_state *state = fe->demodulator_priv; int i, val; - unsigned long timeout; dprintk("%s:\n",__FUNCTION__); - /* check if continuous tone has been stoped */ + /* check if continuous tone has been stopped */ if (state->config->use_isl6421) val = cx24123_readlnbreg(state, 0x00) & 0x10; else @@ -707,6 +719,9 @@ static int cx24123_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_ma return -ENOTSUPP; } + /* wait for diseqc queue ready */ + cx24123_wait_for_diseqc(state); + /* select tone mode */ cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) & 0xf8); @@ -716,9 +731,8 @@ static int cx24123_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_ma val = cx24123_readreg(state, 0x29); cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40) | ((cmd->msg_len-3) & 3)); - timeout = jiffies + msecs_to_jiffies(100); - while (!time_after(jiffies, timeout) && !(cx24123_readreg(state, 0x29) & 0x40)) - ; // wait for LNB ready + /* wait for diseqc message to finish sending */ + cx24123_wait_for_diseqc(state); return 0; } @@ -727,7 +741,6 @@ static int cx24123_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t { struct cx24123_state *state = fe->demodulator_priv; int val; - unsigned long timeout; dprintk("%s:\n", __FUNCTION__); @@ -743,6 +756,8 @@ static int cx24123_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t return -ENOTSUPP; } + cx24123_wait_for_diseqc(state); + /* select tone mode */ val = cx24123_readreg(state, 0x2a) & 0xf8; cx24123_writereg(state, 0x2a, val | 0x04); @@ -756,10 +771,7 @@ static int cx24123_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t else return -EINVAL; - - timeout = jiffies + msecs_to_jiffies(100); - while (!time_after(jiffies, timeout) && !(cx24123_readreg(state, 0x29) & 0x40)) - ; // wait for LNB ready + cx24123_wait_for_diseqc(state); return 0; } -- cgit v1.2.3 From 32de3cc3a4bb7d3624f65bbd00edfb8e4601d736 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Thu, 13 Apr 2006 11:41:43 -0400 Subject: cx88: added support for KWorld MCE 200 Deluxe From: Valentin Zagura This patch adds support for KWorld MCE 200 Deluxe. Raw video is working perfectly, MPEG capture using cx88-blackbird is also working, but the quality could be improved. FIXME: tested TV input only, disabled composite, svideo and radio until they can be tested also. Signed-off-by: Valentin Zagura Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/cx88-cards.c | 36 +++++++++++++++++++++++++++ linux/drivers/media/video/cx88/cx88-tvaudio.c | 3 ++- linux/drivers/media/video/cx88/cx88.h | 1 + 3 files changed, 39 insertions(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index 839e91a98..3e6b2641b 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -1127,6 +1127,38 @@ struct cx88_board cx88_boards[] = { }}, .dvb = 1, }, + [CX88_BOARD_KWORLD_MCE200_DELUXE] = { + /* FIXME: tested TV input only, disabled composite, + svideo and radio until they can be tested also. */ + .name = "Kworld MCE 200 Deluxe", + .tuner_type = TUNER_TENA_9533_DI, + .radio_type = UNSET, + .tda9887_conf = TDA9887_PRESENT, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x0000BDE6 +#if 0 + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x0000BDE6, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x0000BDE6, +#endif + }}, +#if 0 + .radio = { + .gpio0 = 0x0000BDE6, + .type = CX88_RADIO, + }, +#endif + .blackbird = 1, + }, }; const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards); @@ -1348,6 +1380,10 @@ struct cx88_subid cx88_subids[] = { .subvendor = 0x7063, .subdevice = 0x5500, .card = CX88_BOARD_PCHDTV_HD5500, + },{ + .subvendor = 0x17de, + .subdevice = 0x0841, + .card = CX88_BOARD_KWORLD_MCE200_DELUXE, }, }; const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids); diff --git a/linux/drivers/media/video/cx88/cx88-tvaudio.c b/linux/drivers/media/video/cx88/cx88-tvaudio.c index 68c157427..683c8c45c 100644 --- a/linux/drivers/media/video/cx88/cx88-tvaudio.c +++ b/linux/drivers/media/video/cx88/cx88-tvaudio.c @@ -151,7 +151,8 @@ static void set_audio_finish(struct cx88_core *core, u32 ctl) if (cx88_boards[core->board].blackbird) { /* sets sound input from external adc */ - if (core->board == CX88_BOARD_HAUPPAUGE_ROSLYN) + if ((core->board == CX88_BOARD_HAUPPAUGE_ROSLYN)|| + (core->board == CX88_BOARD_KWORLD_MCE200_DELUXE)) cx_clear(AUD_CTL, EN_I2SIN_ENABLE); else cx_set(AUD_CTL, EN_I2SIN_ENABLE); diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h index aad9260e4..88fff918d 100644 --- a/linux/drivers/media/video/cx88/cx88.h +++ b/linux/drivers/media/video/cx88/cx88.h @@ -198,6 +198,7 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT 45 #define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID 46 #define CX88_BOARD_PCHDTV_HD5500 47 +#define CX88_BOARD_KWORLD_MCE200_DELUXE 48 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, -- cgit v1.2.3 From a35445cc21e79df1aae4b39c514ca6f67c69693f Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Thu, 13 Apr 2006 12:06:28 -0400 Subject: cx88-tvaudio: replace 'if' with 'switch..case' From: Michael Krufky This patch cleans up a potential mess that has yet to occur in the card-specific part of cx88-tvaudio.c that sets sound input from external adc. It may be a good idea to move this setting into cx88-cards.c in the future. Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/cx88-tvaudio.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-tvaudio.c b/linux/drivers/media/video/cx88/cx88-tvaudio.c index 683c8c45c..d1e20eca6 100644 --- a/linux/drivers/media/video/cx88/cx88-tvaudio.c +++ b/linux/drivers/media/video/cx88/cx88-tvaudio.c @@ -151,11 +151,14 @@ static void set_audio_finish(struct cx88_core *core, u32 ctl) if (cx88_boards[core->board].blackbird) { /* sets sound input from external adc */ - if ((core->board == CX88_BOARD_HAUPPAUGE_ROSLYN)|| - (core->board == CX88_BOARD_KWORLD_MCE200_DELUXE)) + switch (core->board) { + case CX88_BOARD_HAUPPAUGE_ROSLYN: + case CX88_BOARD_KWORLD_MCE200_DELUXE: cx_clear(AUD_CTL, EN_I2SIN_ENABLE); - else + break; + default: cx_set(AUD_CTL, EN_I2SIN_ENABLE); + } cx_write(AUD_I2SINPUTCNTL, 4); cx_write(AUD_BAUDRATE, 1); -- cgit v1.2.3 From 0bbcd2e449bc5a2096a93314b888344ee2648693 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Thu, 13 Apr 2006 12:29:04 -0400 Subject: KWorld HardwareMpegTV XPert: update GPIO for s-video and composite From: Michael Krufky - updated s-video and composite input GPIO settings. Thanks-to: Todd Duffin - s-video and composite inputs are still #if 0'd until we can get audio working properly on those inputs. Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/cx88-cards.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index 3e6b2641b..2577039bf 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -1070,9 +1070,11 @@ struct cx88_board cx88_boards[] = { },{ .type = CX88_VMUX_COMPOSITE1, .vmux = 1, + .gpio0 = 0x3de6, },{ .type = CX88_VMUX_SVIDEO, .vmux = 2, + .gpio0 = 0x3de6, #endif }}, .radio = { -- cgit v1.2.3 From e9fa8055bea1f9d661203178037432f86e600fc3 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Thu, 13 Apr 2006 21:24:13 +0100 Subject: Various correctness fixes to tuning. From: Yeasah Pell *) Sets an additional tuner parameter (demodulator sample gain) that wasn't being set before. *) Removes the low symbol rate tuner parameter tweaks in the previous patch -- it appears those tweaks are not necessary with the demodulator sample gain set correctly. *) Cleanup and document the demodulator register initialization sequence. *) Change set_fec routine to disable FEC auto scan when a specific code rate is selected. *) Remove error message when reported FEC is invalid (which happens sometimes when the card has no signal) Signed-off-by: Yeasah Pell Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/cx24123.c | 172 +++++++++++++++++----------- 1 file changed, 108 insertions(+), 64 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/cx24123.c b/linux/drivers/media/dvb/frontends/cx24123.c index fa6cdba08..2aac17451 100644 --- a/linux/drivers/media/dvb/frontends/cx24123.c +++ b/linux/drivers/media/dvb/frontends/cx24123.c @@ -76,23 +76,23 @@ static struct .symbolrate_high = 4999999, /* the specs recommend other values for VGA offsets, but tests show they are wrong */ - .VGAprogdata = (2 << 18) | (0x180 << 9) | 0x1e0, - .VCAprogdata = (4 << 18) | (0x07 << 9) | 0x07, - .FILTune = 0x280 /* 0.41 V */ + .VGAprogdata = (1 << 19) | (0x180 << 9) | 0x1e0, + .VCAprogdata = (2 << 19) | (0x07 << 9) | 0x07, + .FILTune = 0x27f /* 0.41 V */ }, { .symbolrate_low = 5000000, .symbolrate_high = 14999999, - .VGAprogdata = (2 << 18) | (0x180 << 9) | 0x1e0, - .VCAprogdata = (4 << 18) | (0x07 << 9) | 0x1f, + .VGAprogdata = (1 << 19) | (0x180 << 9) | 0x1e0, + .VCAprogdata = (2 << 19) | (0x07 << 9) | 0x1f, .FILTune = 0x317 /* 0.90 V */ }, { .symbolrate_low = 15000000, .symbolrate_high = 45000000, - .VGAprogdata = (2 << 18) | (0x100 << 9) | 0x180, - .VCAprogdata = (4 << 18) | (0x07 << 9) | 0x3f, - .FILTune = 0x146 /* 2.70 V */ + .VGAprogdata = (1 << 19) | (0x100 << 9) | 0x180, + .VCAprogdata = (2 << 19) | (0x07 << 9) | 0x3f, + .FILTune = 0x145 /* 2.70 V */ }, }; @@ -178,45 +178,44 @@ static struct { { {0x00, 0x03}, /* Reset system */ {0x00, 0x00}, /* Clear reset */ - {0x03, 0x07}, - {0x04, 0x10}, - {0x05, 0x04}, - {0x06, 0x31}, - {0x0d, 0x02}, - {0x0e, 0x03}, - {0x0f, 0xfe}, - {0x10, 0x01}, - {0x14, 0x01}, - {0x16, 0x00}, - {0x17, 0x01}, - {0x1b, 0x05}, - {0x1c, 0x80}, - {0x1d, 0x00}, - {0x1e, 0x00}, - {0x20, 0x41}, - {0x21, 0x15}, - {0x29, 0x00}, - {0x2a, 0xb0}, - {0x2b, 0x73}, - {0x2c, 0x00}, + {0x03, 0x07}, /* QPSK, DVB, Auto Acquisition (default) */ + {0x04, 0x10}, /* MPEG */ + {0x05, 0x04}, /* MPEG */ + {0x06, 0x31}, /* MPEG (default) */ + {0x0b, 0x00}, /* Freq search start point (default) */ + {0x0c, 0x00}, /* Demodulator sample gain (default) */ + {0x0d, 0x02}, /* Frequency search range = Fsymbol / 4 (default) */ + {0x0e, 0x03}, /* Default non-inverted, FEC 3/4 (default) */ + {0x0f, 0xfe}, /* FEC search mask (all supported codes) */ + {0x10, 0x01}, /* Default search inversion, no repeat (default) */ + {0x16, 0x00}, /* Enable reading of frequency */ + {0x17, 0x01}, /* Enable EsNO Ready Counter */ + {0x1c, 0x80}, /* Enable error counter */ + {0x20, 0x00}, /* Tuner burst clock rate = 500KHz */ + {0x21, 0x15}, /* Tuner burst mode, word length = 0x15 */ + {0x28, 0x00}, /* Enable FILTERV with positive pol., DiSEqC 2.x off */ + {0x29, 0x00}, /* DiSEqC LNB_DC off */ + {0x2a, 0xb0}, /* DiSEqC Parameters (default) */ + {0x2b, 0x73}, /* DiSEqC Tone Frequency (default) */ + {0x2c, 0x00}, /* DiSEqC Message (0x2c - 0x31) */ {0x2d, 0x00}, {0x2e, 0x00}, {0x2f, 0x00}, {0x30, 0x00}, {0x31, 0x00}, - {0x32, 0x8c}, - {0x33, 0x00}, + {0x32, 0x8c}, /* DiSEqC Parameters (default) */ + {0x33, 0x00}, /* Interrupts off (0x33 - 0x34) */ {0x34, 0x00}, - {0x35, 0x03}, - {0x36, 0x02}, - {0x37, 0x3a}, - {0x3a, 0x00}, /* Enable AGC accumulator */ - {0x44, 0x00}, - {0x45, 0x00}, - {0x46, 0x05}, - {0x56, 0x41}, - {0x57, 0xff}, - {0x67, 0x83}, + {0x35, 0x03}, /* DiSEqC Tone Amplitude (default) */ + {0x36, 0x02}, /* DiSEqC Parameters (default) */ + {0x37, 0x3a}, /* DiSEqC Parameters (default) */ + {0x3a, 0x00}, /* Enable AGC accumulator (for signal strength) */ + {0x44, 0x00}, /* Constellation (default) */ + {0x45, 0x00}, /* Symbol count (default) */ + {0x46, 0x0d}, /* Symbol rate estimator on (default) */ + {0x56, 0x41}, /* Various (default) */ + {0x57, 0xff}, /* Error Counter Window (default) */ + {0x67, 0x83}, /* Non-DCII symbol clock */ }; static int cx24123_writereg(struct cx24123_state* state, int reg, int data) @@ -291,20 +290,23 @@ static int cx24123_readlnbreg(struct cx24123_state* state, u8 reg) static int cx24123_set_inversion(struct cx24123_state* state, fe_spectral_inversion_t inversion) { + u8 nom_reg = cx24123_readreg(state, 0x0e); + u8 auto_reg = cx24123_readreg(state, 0x10); + switch (inversion) { case INVERSION_OFF: dprintk("%s: inversion off\n",__FUNCTION__); - cx24123_writereg(state, 0x0e, cx24123_readreg(state, 0x0e) & 0x7f); - cx24123_writereg(state, 0x10, cx24123_readreg(state, 0x10) | 0x80); + cx24123_writereg(state, 0x0e, nom_reg & ~0x80); + cx24123_writereg(state, 0x10, auto_reg | 0x80); break; case INVERSION_ON: dprintk("%s: inversion on\n",__FUNCTION__); - cx24123_writereg(state, 0x0e, cx24123_readreg(state, 0x0e) | 0x80); - cx24123_writereg(state, 0x10, cx24123_readreg(state, 0x10) | 0x80); + cx24123_writereg(state, 0x0e, nom_reg | 0x80); + cx24123_writereg(state, 0x10, auto_reg | 0x80); break; case INVERSION_AUTO: dprintk("%s: inversion auto\n",__FUNCTION__); - cx24123_writereg(state, 0x10, cx24123_readreg(state, 0x10) & 0x7f); + cx24123_writereg(state, 0x10, auto_reg & ~0x80); break; default: return -EINVAL; @@ -332,35 +334,56 @@ static int cx24123_get_inversion(struct cx24123_state* state, fe_spectral_invers static int cx24123_set_fec(struct cx24123_state* state, fe_code_rate_t fec) { + u8 nom_reg = cx24123_readreg(state, 0x0e) & ~0x07; + if ( (fec < FEC_NONE) || (fec > FEC_AUTO) ) fec = FEC_AUTO; - /* Hardware has 5/11 and 3/5 but are never unused */ switch (fec) { - case FEC_NONE: - dprintk("%s: set FEC to none\n",__FUNCTION__); - return cx24123_writereg(state, 0x0f, 0x01); case FEC_1_2: dprintk("%s: set FEC to 1/2\n",__FUNCTION__); - return cx24123_writereg(state, 0x0f, 0x02); + cx24123_writereg(state, 0x0e, nom_reg | 0x01); + cx24123_writereg(state, 0x0f, 0x02); + break; case FEC_2_3: dprintk("%s: set FEC to 2/3\n",__FUNCTION__); - return cx24123_writereg(state, 0x0f, 0x04); + cx24123_writereg(state, 0x0e, nom_reg | 0x02); + cx24123_writereg(state, 0x0f, 0x04); + break; case FEC_3_4: dprintk("%s: set FEC to 3/4\n",__FUNCTION__); - return cx24123_writereg(state, 0x0f, 0x08); - case FEC_5_6: + cx24123_writereg(state, 0x0e, nom_reg | 0x03); + cx24123_writereg(state, 0x0f, 0x08); + break; + case FEC_4_5: dprintk("%s: set FEC to 4/5\n",__FUNCTION__); - return cx24123_writereg(state, 0x0f, 0x20); - case FEC_7_8: + cx24123_writereg(state, 0x0e, nom_reg | 0x04); + cx24123_writereg(state, 0x0f, 0x10); + break; + case FEC_5_6: dprintk("%s: set FEC to 5/6\n",__FUNCTION__); - return cx24123_writereg(state, 0x0f, 0x80); + cx24123_writereg(state, 0x0e, nom_reg | 0x05); + cx24123_writereg(state, 0x0f, 0x20); + break; + case FEC_6_7: + dprintk("%s: set FEC to 6/7\n",__FUNCTION__); + cx24123_writereg(state, 0x0e, nom_reg | 0x06); + cx24123_writereg(state, 0x0f, 0x40); + break; + case FEC_7_8: + dprintk("%s: set FEC to 7/8\n",__FUNCTION__); + cx24123_writereg(state, 0x0e, nom_reg | 0x07); + cx24123_writereg(state, 0x0f, 0x80); + break; case FEC_AUTO: dprintk("%s: set FEC to auto\n",__FUNCTION__); - return cx24123_writereg(state, 0x0f, 0xae); + cx24123_writereg(state, 0x0f, 0xfe); + break; default: return -EOPNOTSUPP; } + + return 0; } static int cx24123_get_fec(struct cx24123_state* state, fe_code_rate_t *fec) @@ -395,16 +418,31 @@ static int cx24123_get_fec(struct cx24123_state* state, fe_code_rate_t *fec) *fec = FEC_7_8; break; default: - *fec = FEC_NONE; // can't happen - printk("FEC_NONE ?\n"); + /* this can happen when there's no lock */ + *fec = FEC_NONE; } return 0; } +/* Approximation of closest integer of log2(a/b). It actually gives the + lowest integer i such that 2^i >= round(a/b) */ +static u32 cx24123_int_log2(u32 a, u32 b) +{ + u32 exp, nearest = 0; + u32 div = a / b; + if(a % b >= b / 2) ++div; + if(div < (1 << 31)) + { + for(exp = 1; div > exp; nearest++) + exp += exp; + } + return nearest; +} + static int cx24123_set_symbolrate(struct cx24123_state* state, u32 srate) { - u32 tmp, sample_rate, ratio; + u32 tmp, sample_rate, ratio, sample_gain; u8 pll_mult; /* check if symbol rate is within limits */ @@ -462,7 +500,12 @@ static int cx24123_set_symbolrate(struct cx24123_state* state, u32 srate) cx24123_writereg(state, 0x09, (ratio >> 8) & 0xff ); cx24123_writereg(state, 0x0a, (ratio ) & 0xff ); - dprintk("%s: srate=%d, ratio=0x%08x, sample_rate=%i\n", __FUNCTION__, srate, ratio, sample_rate); + /* also set the demodulator sample gain */ + sample_gain = cx24123_int_log2(sample_rate, srate); + tmp = cx24123_readreg(state, 0x0c) & ~0xe0; + cx24123_writereg(state, 0x0c, tmp | sample_gain << 5); + + dprintk("%s: srate=%d, ratio=0x%08x, sample_rate=%i sample_gain=%d\n", __FUNCTION__, srate, ratio, sample_rate, sample_gain); return 0; } @@ -1014,12 +1057,13 @@ static struct dvb_frontend_ops cx24123_ops = { .frequency_min = 950000, .frequency_max = 2150000, .frequency_stepsize = 1011, /* kHz for QPSK frontends */ - .frequency_tolerance = 29500, + .frequency_tolerance = 5000, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_RECOVER }, -- cgit v1.2.3 From 08d886ca73a91cab1c9433467c9ff597a1503fdd Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Thu, 13 Apr 2006 21:26:22 +0100 Subject: Tweak bandselect setup fox cx24123 From: Yeasah Pell *) Allow forcing the bandselect value with a module parameter to facilitate determining the correct bandselect frequencies. *) Changes the bandselect frequency thresholds based on experiments with the above parameter in conjunction with the values in the spec. Signed-off-by: Yeasah Pell Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/cx24123.c | 108 ++++++++++++++++++---------- 1 file changed, 71 insertions(+), 37 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/cx24123.c b/linux/drivers/media/dvb/frontends/cx24123.c index 2aac17451..56280cd58 100644 --- a/linux/drivers/media/dvb/frontends/cx24123.c +++ b/linux/drivers/media/dvb/frontends/cx24123.c @@ -31,6 +31,7 @@ #define XTAL 10111000 +static int force_band; static int debug; #define dprintk(args...) \ do { \ @@ -109,66 +110,88 @@ static struct u32 progdata; } cx24123_bandselect_vals[] = { + /* band 1 */ { .freq_low = 950000, - .freq_high = 1018999, - .VCOdivider = 4, - .progdata = (0 << 18) | (0 << 9) | 0x40, - }, - { - .freq_low = 1019000, .freq_high = 1074999, .VCOdivider = 4, - .progdata = (0 << 18) | (0 << 9) | 0x80, + .progdata = (0 << 19) | (0 << 9) | 0x40, }, + + /* band 2 */ { .freq_low = 1075000, - .freq_high = 1227999, - .VCOdivider = 2, - .progdata = (0 << 18) | (1 << 9) | 0x01, + .freq_high = 1177999, + .VCOdivider = 4, + .progdata = (0 << 19) | (0 << 9) | 0x80, }, + + /* band 3 */ { - .freq_low = 1228000, - .freq_high = 1349999, + .freq_low = 1178000, + .freq_high = 1295999, .VCOdivider = 2, - .progdata = (0 << 18) | (1 << 9) | 0x02, + .progdata = (0 << 19) | (1 << 9) | 0x01, }, + + /* band 4 */ { - .freq_low = 1350000, - .freq_high = 1481999, + .freq_low = 1296000, + .freq_high = 1431999, .VCOdivider = 2, - .progdata = (0 << 18) | (1 << 9) | 0x04, + .progdata = (0 << 19) | (1 << 9) | 0x02, }, + + /* band 5 */ { - .freq_low = 1482000, - .freq_high = 1595999, + .freq_low = 1432000, + .freq_high = 1575999, .VCOdivider = 2, - .progdata = (0 << 18) | (1 << 9) | 0x08, + .progdata = (0 << 19) | (1 << 9) | 0x04, }, + + /* band 6 */ { - .freq_low = 1596000, + .freq_low = 1576000, .freq_high = 1717999, .VCOdivider = 2, - .progdata = (0 << 18) | (1 << 9) | 0x10, + .progdata = (0 << 19) | (1 << 9) | 0x08, }, + + /* band 7 */ { .freq_low = 1718000, .freq_high = 1855999, .VCOdivider = 2, - .progdata = (0 << 18) | (1 << 9) | 0x20, + .progdata = (0 << 19) | (1 << 9) | 0x10, }, + + /* band 8 */ { .freq_low = 1856000, .freq_high = 2035999, .VCOdivider = 2, - .progdata = (0 << 18) | (1 << 9) | 0x40, + .progdata = (0 << 19) | (1 << 9) | 0x20, }, + + /* band 9 */ { .freq_low = 2036000, - .freq_high = 2149999, + .freq_high = 2150000, + .VCOdivider = 2, + .progdata = (0 << 19) | (1 << 9) | 0x40, + }, +#if 0 +/* This band is not useful with the /2 divider, as its center frequency + is approximately 2300MHz, which is outside of the tunable range. It is + useful only with the /4 divider, as used in band #2. */ + { + .freq_low = 2150000, + .freq_high = 2356000, .VCOdivider = 2, - .progdata = (0 << 18) | (1 << 9) | 0x80, + .progdata = (0 << 19) | (1 << 9) | 0x80, }, +#endif }; static struct { @@ -520,6 +543,8 @@ static int cx24123_pll_calculate(struct dvb_frontend* fe, struct dvb_frontend_pa u32 ndiv = 0, adiv = 0, vco_div = 0; int i = 0; int pump = 2; + int band = 0; + int num_bands = sizeof(cx24123_bandselect_vals) / sizeof(cx24123_bandselect_vals[0]); /* Defaults for low freq, low rate */ state->VCAarg = cx24123_AGC_vals[0].VCAprogdata; @@ -538,21 +563,27 @@ static int cx24123_pll_calculate(struct dvb_frontend* fe, struct dvb_frontend_pa } } - /* For the given frequency, determine the bandselect programming bits */ - for (i = 0; i < sizeof(cx24123_bandselect_vals) / sizeof(cx24123_bandselect_vals[0]); i++) + /* determine the band to use */ + if(force_band < 1 || force_band > num_bands) { - if ((cx24123_bandselect_vals[i].freq_low <= p->frequency) && - (cx24123_bandselect_vals[i].freq_high >= p->frequency) ) { - state->bandselectarg = cx24123_bandselect_vals[i].progdata; - vco_div = cx24123_bandselect_vals[i].VCOdivider; - - /* determine the charge pump current */ - if ( p->frequency < (cx24123_bandselect_vals[i].freq_low + cx24123_bandselect_vals[i].freq_high)/2 ) - pump = 0x01; - else - pump = 0x02; + for (i = 0; i < num_bands; i++) + { + if ((cx24123_bandselect_vals[i].freq_low <= p->frequency) && + (cx24123_bandselect_vals[i].freq_high >= p->frequency) ) + band = i; } } + else + band = force_band - 1; + + state->bandselectarg = cx24123_bandselect_vals[band].progdata; + vco_div = cx24123_bandselect_vals[band].VCOdivider; + + /* determine the charge pump current */ + if ( p->frequency < (cx24123_bandselect_vals[band].freq_low + cx24123_bandselect_vals[band].freq_high)/2 ) + pump = 0x01; + else + pump = 0x02; /* Determine the N/A dividers for the requested lband freq (in kHz). */ /* Note: the reference divider R=10, frequency is in KHz, XTAL is in Hz */ @@ -1086,6 +1117,9 @@ static struct dvb_frontend_ops cx24123_ops = { module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); +module_param(force_band, int, 0644); +MODULE_PARM_DESC(force_band, "Force a specific band select (1-9, default:off)."); + MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24123/cx24109 hardware"); MODULE_AUTHOR("Steven Toth"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 46d6f90f18a1189b459e50eb8ad5e92170593fce Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Thu, 13 Apr 2006 21:29:07 +0100 Subject: Fix KNC1 card frontend detection From: Andrew de Quincey Since I reordered the CI/frontend detection, it turns out the frontend needs to have a GPIO set to power it on; otherwise frontend init fails. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/ttpci/budget-av.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/ttpci/budget-av.c b/linux/drivers/media/dvb/ttpci/budget-av.c index 8a7cd7d50..dc78aadf0 100644 --- a/linux/drivers/media/dvb/ttpci/budget-av.c +++ b/linux/drivers/media/dvb/ttpci/budget-av.c @@ -1017,12 +1017,14 @@ static void frontend_init(struct budget_av *budget_av) struct saa7146_dev * saa = budget_av->budget.dev; struct dvb_frontend * fe = NULL; + /* Enable / PowerON Frontend */ + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); + + /* additional setup necessary for the PLUS cards */ switch (saa->pci->subsystem_device) { case SUBID_DVBS_KNC1_PLUS: case SUBID_DVBC_KNC1_PLUS: case SUBID_DVBT_KNC1_PLUS: - // Enable / PowerON Frontend - saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI); break; } -- cgit v1.2.3 From 27f958661fe4bcefb4a51e4afad99c3adebd1993 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Thu, 13 Apr 2006 16:41:52 -0400 Subject: cx88-blackbird: allow proper detection of PAL vs. NTSC video standard From: Michael Krufky - removed test for CX88_BOARD_HAUPPAUGE_ROSLYN prior to determining whether the video standard in use is NTSC or PAL. Signed-off-by: Michael Krufky Acked-by: Hans Verkuil --- linux/drivers/media/video/cx88/cx88-blackbird.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index 6fe32a92a..b4a13f1e5 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -1719,16 +1719,12 @@ static int __devinit blackbird_probe(struct pci_dev *pci_dev, memcpy(&dev->params,&default_mpeg_params,sizeof(default_mpeg_params)); memcpy(&dev->dnr_params,&default_dnr_params,sizeof(default_dnr_params)); - if (core->board == CX88_BOARD_HAUPPAUGE_ROSLYN) { - - if (core->tuner_formats & V4L2_STD_525_60) { - dev->height = 480; - dev->params.vi_frame_rate = 30; - } else { - dev->height = 576; - dev->params.vi_frame_rate = 25; - } - + if (core->tuner_formats & V4L2_STD_525_60) { + dev->height = 480; + dev->params.vi_frame_rate = 30; + } else { + dev->height = 576; + dev->params.vi_frame_rate = 25; } err = cx8802_init_common(dev); -- cgit v1.2.3 From 7ad4e6ab6365e5667ed9e1714140dbb7c2ca5790 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Thu, 13 Apr 2006 17:43:50 -0400 Subject: cx88-blackbird: fix typo From: Michael Krufky BLACKBIRD_FIELD1_SAA7115 was repeated. Corrected version reads: blackbird_api_cmd(dev, BLACKBIRD_API_SET_CAPTURE_LINES, 2, 0, BLACKBIRD_FIELD1_SAA7115, BLACKBIRD_FIELD2_SAA7115 ) Thanks-to: Valentin Zagura for pointing this out. Signed-off-by: Michael Krufky Acked-by: Hans Verkuil --- linux/drivers/media/video/cx88/cx88-blackbird.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index b4a13f1e5..e1e9580b2 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -1302,7 +1302,7 @@ static int blackbird_initialize_codec(struct cx8802_dev *dev) blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0x180, 0x180); */ blackbird_api_cmd(dev, BLACKBIRD_API_SET_CAPTURE_LINES, 2, 0, BLACKBIRD_FIELD1_SAA7115, - BLACKBIRD_FIELD1_SAA7115 + BLACKBIRD_FIELD2_SAA7115 ); /* blackbird_api_cmd(dev, IVTV_API_ASSIGN_PLACEHOLDER, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); */ -- cgit v1.2.3 From 19d2eff7919ee22a390dfd6958d1fb61fae6c564 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Fri, 14 Apr 2006 01:41:25 -0400 Subject: cx88-blackbird: use firmware api commands defined in cx2341x.h From: Michael Krufky This patch removes all of the BLACKBIRD_API #define's, instead using the equivalent CX2341X_ENC #define's in cx2341x.h Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/cx88-blackbird.c | 123 ++++++++---------------- 1 file changed, 42 insertions(+), 81 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index e1e9580b2..27f03a4e0 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -33,9 +33,10 @@ #include #endif #include +#include +#include #include "cx88.h" -#include MODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder cards"); MODULE_AUTHOR("Jelle Foks , Gerd Knorr [SuSE Labs]"); @@ -69,8 +70,6 @@ static LIST_HEAD(cx8802_devlist); #endif #define IVTV_API_STD_TIMEOUT 500 -#define BLACKBIRD_API_PING 0x80 -#define BLACKBIRD_API_BEGIN_CAPTURE 0x81 enum blackbird_capture_type { BLACKBIRD_MPEG_CAPTURE, BLACKBIRD_RAW_CAPTURE, @@ -84,21 +83,14 @@ enum blackbird_capture_bits { BLACKBIRD_RAW_BITS_PASSTHRU_CAPTURE = 0x08, BLACKBIRD_RAW_BITS_TO_HOST_CAPTURE = 0x10 }; -#define BLACKBIRD_API_END_CAPTURE 0x82 enum blackbird_capture_end { BLACKBIRD_END_AT_GOP, /* stop at the end of gop, generate irq */ BLACKBIRD_END_NOW, /* stop immediately, no irq */ }; -#define BLACKBIRD_API_SET_AUDIO_ID 0x89 -#define BLACKBIRD_API_SET_VIDEO_ID 0x8B -#define BLACKBIRD_API_SET_PCR_ID 0x8D -#define BLACKBIRD_API_SET_FRAMERATE 0x8F enum blackbird_framerate { BLACKBIRD_FRAMERATE_NTSC_30, /* NTSC: 30fps */ BLACKBIRD_FRAMERATE_PAL_25 /* PAL: 25fps */ }; -#define BLACKBIRD_API_SET_RESOLUTION 0x91 -#define BLACKBIRD_API_SET_VIDEO_BITRATE 0x95 enum blackbird_video_bitrate_type { BLACKBIRD_VIDEO_VBR, BLACKBIRD_VIDEO_CBR @@ -109,8 +101,6 @@ enum blackbird_mux_rate { /* dvd mux rate: multiply by 400 to get the actual rate */ BLACKBIRD_MUX_RATE_DVD = 25200 }; -#define BLACKBIRD_API_SET_GOP_STRUCTURE 0x97 -#define BLACKBIRD_API_SET_ASPECT_RATIO 0x99 enum blackbird_aspect_ratio { BLACKBIRD_ASPECT_RATIO_FORBIDDEN, BLACKBIRD_ASPECT_RATIO_1_1_SQUARE, @@ -119,7 +109,6 @@ enum blackbird_aspect_ratio { BLACKBIRD_ASPECT_RATIO_221_100, BLACKBIRD_ASPECT_RATIO_RESERVED }; -#define BLACKBIRD_API_SET_DNR_MODE 0x9B enum blackbird_dnr_bits { BLACKBIRD_DNR_BITS_MANUAL, BLACKBIRD_DNR_BITS_AUTO_SPATIAL, @@ -133,9 +122,6 @@ enum blackbird_median_filter { BLACKBIRD_MEDIAN_FILTER_HV, BLACKBIRD_MEDIAN_FILTER_DIAGONAL }; -#define BLACKBIRD_API_SET_MANUAL_DNR 0x9D -#define BLACKBIRD_API_SET_DNR_MEDIAN 0x9F -#define BLACKBIRD_API_SET_SPATIAL_FILTER 0xA1 enum blackbird_spatial_filter_luma { BLACKBIRD_SPATIAL_FILTER_LUMA_DISABLED, BLACKBIRD_SPATIAL_FILTER_LUMA_1D_HORIZ, @@ -147,12 +133,10 @@ enum blackbird_spatial_filter_chroma { BLACKBIRD_SPATIAL_FILTER_CHROMA_DISABLED, BLACKBIRD_SPATIAL_FILTER_CHROMA_1D_HORIZ /* default */ }; -#define BLACKBIRD_API_SET_3_2_PULLDOWN 0xB1 enum blackbird_pulldown { BLACKBIRD_3_2_PULLDOWN_DISABLED, BLACKBIRD_3_2_PULLDOWN_ENABLED }; -#define BLACKBIRD_API_SET_VBI_LINE_NO 0xB7 enum blackbird_vbi_line_bits { BLACKBIRD_VBI_LINE_BITS_TOP_FIELD, BLACKBIRD_VBI_LINE_BITS_BOT_FIELD = (1 << 31), @@ -166,7 +150,6 @@ enum blackbird_vbi_slicing { BLACKBIRD_VBI_SLICING_NONE, BLACKBIRD_VBI_SLICING_CLOSED_CAPTION }; -#define BLACKBIRD_API_SET_STREAM_TYPE 0xB9 enum blackbird_stream_type { BLACKBIRD_STREAM_PROGRAM, BLACKBIRD_STREAM_TRANSPORT, @@ -182,13 +165,11 @@ enum blackbird_stream_type { BLACKBIRD_STREAM_VCD, BLACKBIRD_STREAM_UNKNOWN12 /* svcd/xvcd ? */ }; -#define BLACKBIRD_API_SET_OUTPUT_PORT 0xBB enum blackbird_stream_port { BLACKBIRD_OUTPUT_PORT_MEMORY, BLACKBIRD_OUTPUT_PORT_STREAMING, BLACKBIRD_OUTPUT_PORT_SERIAL }; -#define BLACKBIRD_API_SET_AUDIO_PARAMS 0xBD enum blackbird_audio_bits_sample_rate { BLACKBIRD_AUDIO_BITS_44100HZ, BLACKBIRD_AUDIO_BITS_48000HZ, @@ -263,26 +244,20 @@ enum blackbird_audio_bits_original { BLACKBIRD_AUDIO_BITS_COPY, BLACKBIRD_AUDIO_BITS_ORIGINAL = 0x1 << 16, }; -#define BLACKBIRD_API_HALT 0xC3 -#define BLACKBIRD_API_GET_VERSION 0xC4 -#define BLACKBIRD_API_SET_GOP_CLOSURE 0xC5 enum blackbird_gop_closure { BLACKBIRD_GOP_CLOSURE_OFF, BLACKBIRD_GOP_CLOSURE_ON, }; -#define BLACKBIRD_API_DATA_XFER_STATUS 0xC6 enum blackbird_data_xfer_status { BLACKBIRD_MORE_BUFFERS_FOLLOW, BLACKBIRD_LAST_BUFFER, }; -#define BLACKBIRD_API_PROGRAM_INDEX_INFO 0xC7 enum blackbird_picture_mask { BLACKBIRD_PICTURE_MASK_NONE, BLACKBIRD_PICTURE_MASK_I_FRAMES, BLACKBIRD_PICTURE_MASK_I_P_FRAMES = 0x3, BLACKBIRD_PICTURE_MASK_ALL_FRAMES = 0x7, }; -#define BLACKBIRD_API_SET_VBI_PARAMS 0xC8 enum blackbird_vbi_mode_bits { BLACKBIRD_VBI_BITS_SLICED, BLACKBIRD_VBI_BITS_RAW, @@ -294,33 +269,23 @@ enum blackbird_vbi_insertion_bits { BLACKBIRD_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1, BLACKBIRD_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1, }; -#define BLACKBIRD_API_SET_DMA_BLOCK_SIZE 0xC9 enum blackbird_dma_unit { BLACKBIRD_DMA_BYTES, BLACKBIRD_DMA_FRAMES, }; -#define BLACKBIRD_API_DMA_TRANSFER_INFO 0xCA -#define BLACKBIRD_API_DMA_TRANSFER_STAT 0xCB enum blackbird_dma_transfer_status_bits { BLACKBIRD_DMA_TRANSFER_BITS_DONE = 0x01, BLACKBIRD_DMA_TRANSFER_BITS_ERROR = 0x04, BLACKBIRD_DMA_TRANSFER_BITS_LL_ERROR = 0x10, }; -#define BLACKBIRD_API_SET_DMA2HOST_ADDR 0xCC -#define BLACKBIRD_API_INIT_VIDEO_INPUT 0xCD -#define BLACKBIRD_API_SET_FRAMESKIP 0xD0 -#define BLACKBIRD_API_PAUSE 0xD2 enum blackbird_pause { BLACKBIRD_PAUSE_ENCODING, BLACKBIRD_RESUME_ENCODING, }; -#define BLACKBIRD_API_REFRESH_INPUT 0xD3 -#define BLACKBIRD_API_SET_COPYRIGHT 0xD4 enum blackbird_copyright { BLACKBIRD_COPYRIGHT_OFF, BLACKBIRD_COPYRIGHT_ON, }; -#define BLACKBIRD_API_SET_NOTIFICATION 0xD5 enum blackbird_notification_type { BLACKBIRD_NOTIFICATION_REFRESH, }; @@ -331,7 +296,6 @@ enum blackbird_notification_status { enum blackbird_notification_mailbox { BLACKBIRD_NOTIFICATION_NO_MAILBOX = -1, }; -#define BLACKBIRD_API_SET_CAPTURE_LINES 0xD6 enum blackbird_field1_lines { BLACKBIRD_FIELD1_SAA7114 = 0x00EF, /* 239 */ BLACKBIRD_FIELD1_SAA7115 = 0x00F0, /* 240 */ @@ -342,12 +306,10 @@ enum blackbird_field2_lines { BLACKBIRD_FIELD2_SAA7115 = 0x00F0, /* 240 */ BLACKBIRD_FIELD2_MICRONAS = 0x0106, /* 262 */ }; -#define BLACKBIRD_API_SET_CUSTOM_DATA 0xD7 enum blackbird_custom_data_type { BLACKBIRD_CUSTOM_EXTENSION_USR_DATA, BLACKBIRD_CUSTOM_PRIVATE_PACKET, }; -#define BLACKBIRD_API_MUTE_VIDEO 0xD9 enum blackbird_mute { BLACKBIRD_UNMUTE, BLACKBIRD_MUTE, @@ -362,7 +324,6 @@ enum blackbird_mute_video_shift { BLACKBIRD_MUTE_VIDEO_U_SHIFT = 16, BLACKBIRD_MUTE_VIDEO_Y_SHIFT = 24, }; -#define BLACKBIRD_API_MUTE_AUDIO 0xDA /* Registers */ #define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8 /*| IVTV_REG_OFFSET*/) @@ -803,33 +764,33 @@ static void blackbird_set_default_params(struct cx8802_dev *dev) params->vi_type = V4L2_MPEG_VI_1; else params->vi_type = V4L2_MPEG_VI_2; - blackbird_api_cmd(dev, BLACKBIRD_API_SET_STREAM_TYPE, 1, 0, mpeg_stream_types[params->st_type]); + blackbird_api_cmd(dev, CX2341X_ENC_SET_STREAM_TYPE, 1, 0, mpeg_stream_types[params->st_type]); /* assign framerate */ if( params->vi_frame_rate <= 25 ) { params->vi_frame_rate = 25; - blackbird_api_cmd(dev, BLACKBIRD_API_SET_FRAMERATE, 1, 0, BLACKBIRD_FRAMERATE_PAL_25); + blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_RATE, 1, 0, BLACKBIRD_FRAMERATE_PAL_25); } else { params->vi_frame_rate = 30; - blackbird_api_cmd(dev, BLACKBIRD_API_SET_FRAMERATE, 1, 0, BLACKBIRD_FRAMERATE_NTSC_30); + blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_RATE, 1, 0, BLACKBIRD_FRAMERATE_NTSC_30); } /* assign aspect ratio */ if( params->vi_aspect_ratio >= ARRAY_SIZE(mpeg_stream_ratios) ) params->vi_aspect_ratio = V4L2_MPEG_ASPECT_4_3; - blackbird_api_cmd(dev, BLACKBIRD_API_SET_ASPECT_RATIO, 1, 0, mpeg_stream_ratios[params->vi_aspect_ratio]); + blackbird_api_cmd(dev, CX2341X_ENC_SET_ASPECT_RATIO, 1, 0, mpeg_stream_ratios[params->vi_aspect_ratio]); /* assign gop properties */ - blackbird_api_cmd(dev, BLACKBIRD_API_SET_GOP_STRUCTURE, 2, 0, params->vi_frames_per_gop, params->vi_bframes_count+1); + blackbird_api_cmd(dev, CX2341X_ENC_SET_GOP_PROPERTIES, 2, 0, params->vi_frames_per_gop, params->vi_bframes_count+1); /* assign gop closure */ - blackbird_api_cmd(dev, BLACKBIRD_API_SET_GOP_CLOSURE, 1, 0, params->closed_gops); + blackbird_api_cmd(dev, CX2341X_ENC_SET_GOP_CLOSURE, 1, 0, params->closed_gops); /* assign 3 2 pulldown */ - blackbird_api_cmd(dev, BLACKBIRD_API_SET_3_2_PULLDOWN, 1, 0, params->pulldown); + blackbird_api_cmd(dev, CX2341X_ENC_SET_3_2_PULLDOWN, 1, 0, params->pulldown); /* make sure the params are within bounds */ if( params->st_bitrate.mode >= ARRAY_SIZE(mpeg_video_bitrates) ) @@ -919,7 +880,7 @@ static void blackbird_set_default_params(struct cx8802_dev *dev) params->au_bitrate.target = params->au_bitrate.max = 0; au_params |= 0; } - blackbird_api_cmd(dev, BLACKBIRD_API_SET_AUDIO_PARAMS, 1, 0, au_params ); + blackbird_api_cmd(dev, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, 0, au_params ); /* assign bitrates */ if( params->vi_bitrate.mode ) @@ -946,7 +907,7 @@ static void blackbird_set_default_params(struct cx8802_dev *dev) params->vi_bitrate.max = params->st_bitrate.max - params->au_bitrate.max; params->vi_bitrate.target = params->st_bitrate.target - params->au_bitrate.target; } - blackbird_api_cmd(dev, BLACKBIRD_API_SET_VIDEO_BITRATE, 4, 0, + blackbird_api_cmd(dev, CX2341X_ENC_SET_BIT_RATE, 4, 0, mpeg_video_bitrates[params->vi_bitrate.mode], params->vi_bitrate.target * 1000, /* kbps -> bps */ params->vi_bitrate.max * 1000 / BLACKBIRD_PEAK_RATE_DIVISOR, /* peak/400 */ @@ -975,7 +936,7 @@ void blackbird_set_params(struct cx8802_dev *dev, struct v4l2_mpeg_compression * { UPDATE_PARAM( st_type ); UPDATE_PARAM( vi_type ); - blackbird_api_cmd(dev, BLACKBIRD_API_SET_STREAM_TYPE, 1, 0, mpeg_stream_types[params->st_type]); + blackbird_api_cmd(dev, CX2341X_ENC_SET_STREAM_TYPE, 1, 0, mpeg_stream_types[params->st_type]); } /* assign framerate */ @@ -987,9 +948,9 @@ void blackbird_set_params(struct cx8802_dev *dev, struct v4l2_mpeg_compression * { UPDATE_PARAM( vi_frame_rate ); if( params->vi_frame_rate == 25 ) - blackbird_api_cmd(dev, BLACKBIRD_API_SET_FRAMERATE, 1, 0, BLACKBIRD_FRAMERATE_PAL_25); + blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_RATE, 1, 0, BLACKBIRD_FRAMERATE_PAL_25); else - blackbird_api_cmd(dev, BLACKBIRD_API_SET_FRAMERATE, 1, 0, BLACKBIRD_FRAMERATE_NTSC_30); + blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_RATE, 1, 0, BLACKBIRD_FRAMERATE_NTSC_30); } /* assign aspect ratio */ @@ -998,7 +959,7 @@ void blackbird_set_params(struct cx8802_dev *dev, struct v4l2_mpeg_compression * IF_PARAM( vi_aspect_ratio ) { UPDATE_PARAM( vi_aspect_ratio ); - blackbird_api_cmd(dev, BLACKBIRD_API_SET_ASPECT_RATIO, 1, 0, mpeg_stream_ratios[params->vi_aspect_ratio]); + blackbird_api_cmd(dev, CX2341X_ENC_SET_ASPECT_RATIO, 1, 0, mpeg_stream_ratios[params->vi_aspect_ratio]); } /* assign gop properties */ @@ -1006,21 +967,21 @@ void blackbird_set_params(struct cx8802_dev *dev, struct v4l2_mpeg_compression * { UPDATE_PARAM( vi_frames_per_gop ); UPDATE_PARAM( vi_bframes_count ); - blackbird_api_cmd(dev, BLACKBIRD_API_SET_GOP_STRUCTURE, 2, 0, params->vi_frames_per_gop, params->vi_bframes_count+1); + blackbird_api_cmd(dev, CX2341X_ENC_SET_GOP_PROPERTIES, 2, 0, params->vi_frames_per_gop, params->vi_bframes_count+1); } /* assign gop closure */ IF_PARAM( closed_gops ) { UPDATE_PARAM( closed_gops ); - blackbird_api_cmd(dev, BLACKBIRD_API_SET_GOP_CLOSURE, 1, 0, params->closed_gops); + blackbird_api_cmd(dev, CX2341X_ENC_SET_GOP_CLOSURE, 1, 0, params->closed_gops); } /* assign 3 2 pulldown */ IF_PARAM( pulldown ) { UPDATE_PARAM( pulldown ); - blackbird_api_cmd(dev, BLACKBIRD_API_SET_3_2_PULLDOWN, 1, 0, params->pulldown); + blackbird_api_cmd(dev, CX2341X_ENC_SET_3_2_PULLDOWN, 1, 0, params->pulldown); } /* make sure the params are within bounds */ @@ -1119,7 +1080,7 @@ void blackbird_set_params(struct cx8802_dev *dev, struct v4l2_mpeg_compression * UPDATE_PARAM( au_type ); UPDATE_PARAM( au_sample_rate ); UPDATE_PARAM( au_bitrate ); - blackbird_api_cmd(dev, BLACKBIRD_API_SET_AUDIO_PARAMS, 1, 0, au_params ); + blackbird_api_cmd(dev, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, 0, au_params ); } /* assign bitrates */ @@ -1153,7 +1114,7 @@ void blackbird_set_params(struct cx8802_dev *dev, struct v4l2_mpeg_compression * ) { UPDATE_PARAM( vi_bitrate ); - blackbird_api_cmd(dev, BLACKBIRD_API_SET_VIDEO_BITRATE, 4, 0, + blackbird_api_cmd(dev, CX2341X_ENC_SET_BIT_RATE, 4, 0, mpeg_video_bitrates[params->vi_bitrate.mode], params->vi_bitrate.target * 1000, /* kbps -> bps */ params->vi_bitrate.max * 1000 / BLACKBIRD_PEAK_RATE_DIVISOR, /* peak/400 */ @@ -1180,7 +1141,7 @@ static void blackbird_set_default_dnr_params(struct cx8802_dev *dev) dev->dnr_params.mode = BLACKBIRD_DNR_BITS_MANUAL; if( dev->dnr_params.type > BLACKBIRD_MEDIAN_FILTER_DIAGONAL ) dev->dnr_params.type = BLACKBIRD_MEDIAN_FILTER_DISABLED; - blackbird_api_cmd(dev, BLACKBIRD_API_SET_DNR_MODE, 2, 0, + blackbird_api_cmd(dev, CX2341X_ENC_SET_DNR_FILTER_MODE, 2, 0, dev->dnr_params.mode, dev->dnr_params.type ); @@ -1190,7 +1151,7 @@ static void blackbird_set_default_dnr_params(struct cx8802_dev *dev) dev->dnr_params.spatial = 15; if( dev->dnr_params.temporal > 31 ) dev->dnr_params.temporal = 31; - blackbird_api_cmd(dev, BLACKBIRD_API_SET_MANUAL_DNR, 2, 0, + blackbird_api_cmd(dev, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2, 0, dev->dnr_params.spatial, dev->dnr_params.temporal ); @@ -1210,7 +1171,7 @@ void blackbird_set_dnr_params(struct cx8802_dev *dev, struct blackbird_dnr* dnr_ { UPDATE_DNR_PARAM( mode ); UPDATE_DNR_PARAM( type ); - blackbird_api_cmd(dev, BLACKBIRD_API_SET_DNR_MODE, 2, 0, dnr_params->mode, dnr_params->type); + blackbird_api_cmd(dev, CX2341X_ENC_SET_DNR_FILTER_MODE, 2, 0, dnr_params->mode, dnr_params->type); } /* assign dnr filter props*/ @@ -1222,7 +1183,7 @@ void blackbird_set_dnr_params(struct cx8802_dev *dev, struct blackbird_dnr* dnr_ { UPDATE_DNR_PARAM( spatial ); UPDATE_DNR_PARAM( temporal ); - blackbird_api_cmd(dev, BLACKBIRD_API_SET_MANUAL_DNR, 2, 0, dnr_params->spatial, dnr_params->temporal); + blackbird_api_cmd(dev, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2, 0, dnr_params->spatial, dnr_params->temporal); } } @@ -1230,17 +1191,17 @@ static void blackbird_codec_settings(struct cx8802_dev *dev) { /* assign output port */ - blackbird_api_cmd(dev, BLACKBIRD_API_SET_OUTPUT_PORT, 1, 0, BLACKBIRD_OUTPUT_PORT_STREAMING); /* Host */ + blackbird_api_cmd(dev, CX2341X_ENC_SET_OUTPUT_PORT, 1, 0, BLACKBIRD_OUTPUT_PORT_STREAMING); /* Host */ /* assign frame size */ - blackbird_api_cmd(dev, BLACKBIRD_API_SET_RESOLUTION, 2, 0, + blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, dev->height, dev->width); /* assign coring levels (luma_h, luma_l, chroma_h, chroma_l) */ - blackbird_api_cmd(dev, BLACKBIRD_API_SET_DNR_MEDIAN, 4, 0, 0, 255, 0, 255); + blackbird_api_cmd(dev, CX2341X_ENC_SET_CORING_LEVELS, 4, 0, 0, 255, 0, 255); /* assign spatial filter type: luma_t: horiz_only, chroma_t: horiz_only */ - blackbird_api_cmd(dev, BLACKBIRD_API_SET_SPATIAL_FILTER, 2, 0, + blackbird_api_cmd(dev, CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2, 0, BLACKBIRD_SPATIAL_FILTER_LUMA_1D_HORIZ, BLACKBIRD_SPATIAL_FILTER_CHROMA_1D_HORIZ ); @@ -1259,7 +1220,7 @@ static int blackbird_initialize_codec(struct cx8802_dev *dev) int retval; dprintk(1,"Initialize codec\n"); - retval = blackbird_api_cmd(dev, BLACKBIRD_API_PING, 0, 0); /* ping */ + retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */ if (retval < 0) { /* ping was not successful, reset and upload firmware */ cx_write(MO_SRST_IO, 0); /* SYS_RSTO=0 */ @@ -1274,13 +1235,13 @@ static int blackbird_initialize_codec(struct cx8802_dev *dev) if (dev->mailbox < 0) return -1; - retval = blackbird_api_cmd(dev, BLACKBIRD_API_PING, 0, 0); /* ping */ + retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */ if (retval < 0) { dprintk(0, "ERROR: Firmware ping failed!\n"); return -1; } - retval = blackbird_api_cmd(dev, BLACKBIRD_API_GET_VERSION, 0, 1, &version); + retval = blackbird_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1, &version); if (retval < 0) { dprintk(0, "ERROR: Firmware get encoder version failed!\n"); return -1; @@ -1300,35 +1261,35 @@ static int blackbird_initialize_codec(struct cx8802_dev *dev) /* blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0xef, 0xef); blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0xf0, 0xf0); blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0x180, 0x180); */ - blackbird_api_cmd(dev, BLACKBIRD_API_SET_CAPTURE_LINES, 2, 0, + blackbird_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0, BLACKBIRD_FIELD1_SAA7115, BLACKBIRD_FIELD2_SAA7115 ); /* blackbird_api_cmd(dev, IVTV_API_ASSIGN_PLACEHOLDER, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); */ - blackbird_api_cmd(dev, BLACKBIRD_API_SET_CUSTOM_DATA, 12, 0, + blackbird_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0, BLACKBIRD_CUSTOM_EXTENSION_USR_DATA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); /* initialize the video input */ - blackbird_api_cmd(dev, BLACKBIRD_API_INIT_VIDEO_INPUT, 0, 0); + blackbird_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0); msleep(1); - blackbird_api_cmd(dev, BLACKBIRD_API_MUTE_VIDEO, 1, 0, BLACKBIRD_UNMUTE); + blackbird_api_cmd(dev, CX2341X_ENC_MUTE_VIDEO, 1, 0, BLACKBIRD_UNMUTE); msleep(1); - blackbird_api_cmd(dev, BLACKBIRD_API_MUTE_AUDIO, 1, 0, BLACKBIRD_UNMUTE); + blackbird_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, BLACKBIRD_UNMUTE); msleep(1); /* start capturing to the host interface */ - /* blackbird_api_cmd(dev, BLACKBIRD_API_BEGIN_CAPTURE, 2, 0, 0, 0x13); */ - blackbird_api_cmd(dev, BLACKBIRD_API_BEGIN_CAPTURE, 2, 0, + /* blackbird_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0, 0, 0x13); */ + blackbird_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0, BLACKBIRD_MPEG_CAPTURE, BLACKBIRD_RAW_BITS_NONE ); msleep(10); - blackbird_api_cmd(dev, BLACKBIRD_API_REFRESH_INPUT, 0,0); + blackbird_api_cmd(dev, CX2341X_ENC_REFRESH_INPUT, 0,0); return 0; } @@ -1485,7 +1446,7 @@ static int mpeg_do_ioctl(struct inode *inode, struct file *file, #endif #if 0 cx88_set_scale(core, f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); - blackbird_api_cmd(dev, BLACKBIRD_API_SET_RESOLUTION, 2, 0, + blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, f->fmt.pix.height, f->fmt.pix.width); #endif dprintk(0,"VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", @@ -1595,8 +1556,8 @@ static int mpeg_release(struct inode *inode, struct file *file) { struct cx8802_fh *fh = file->private_data; - /* blackbird_api_cmd(fh->dev, BLACKBIRD_API_END_CAPTURE, 3, 0, BLACKBIRD_END_NOW, 0, 0x13); */ - blackbird_api_cmd(fh->dev, BLACKBIRD_API_END_CAPTURE, 3, 0, + /* blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, BLACKBIRD_END_NOW, 0, 0x13); */ + blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, BLACKBIRD_END_NOW, BLACKBIRD_MPEG_CAPTURE, BLACKBIRD_RAW_BITS_NONE -- cgit v1.2.3 From 67d2f6581bdc504d2728d7dc27f09e79ca689988 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Fri, 14 Apr 2006 11:33:20 -0400 Subject: cx88-blackbird: use standard filename for cx23416 encoder firmware From: Michael Krufky cx88-blackbird uses the same encoder firmware for the cx23416 chip that is used by ivtv and pvrusb2. This patch alters the driver to expect the cx23416 encoder firmware to be located in the file, "v4l-cx2341x-enc.fw", instead of using "blackbird-fw-enc.bin" Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/cx88-blackbird.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index 27f03a4e0..1808796b4 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -57,7 +57,7 @@ static LIST_HEAD(cx8802_devlist); /* ------------------------------------------------------------------ */ -#define BLACKBIRD_FIRM_ENC_FILENAME "blackbird-fw-enc.bin" +#define BLACKBIRD_FIRM_ENC_FILENAME "v4l-cx2341x-enc.fw" #define BLACKBIRD_FIRM_IMAGE_SIZE 256*1024 /* defines below are from ivtv-driver.h */ -- cgit v1.2.3 From d89b7f889f262116a190464c1a3decb5aa5a1849 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 16 Apr 2006 11:17:34 -0400 Subject: KWorld HardwareMpegTV XPert: Enable Blackbird MPEG encoder support From: Michael Krufky - clear I2SIN to deliver the audio stream to the cx23416 mpeg encoder. - enable blackbird support on the KWorld HardwareMpegTV XPert Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/cx88-cards.c | 2 -- linux/drivers/media/video/cx88/cx88-tvaudio.c | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index 2577039bf..383af5609 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -1082,9 +1082,7 @@ struct cx88_board cx88_boards[] = { .gpio0 = 0x3de6, .gpio2 = 0x00ff, }, -#if 0 .blackbird = 1, -#endif }, [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID] = { .name = "DViCO FusionHDTV DVB-T Hybrid", diff --git a/linux/drivers/media/video/cx88/cx88-tvaudio.c b/linux/drivers/media/video/cx88/cx88-tvaudio.c index d1e20eca6..78f6d2c76 100644 --- a/linux/drivers/media/video/cx88/cx88-tvaudio.c +++ b/linux/drivers/media/video/cx88/cx88-tvaudio.c @@ -154,6 +154,7 @@ static void set_audio_finish(struct cx88_core *core, u32 ctl) switch (core->board) { case CX88_BOARD_HAUPPAUGE_ROSLYN: case CX88_BOARD_KWORLD_MCE200_DELUXE: + case CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT: cx_clear(AUD_CTL, EN_I2SIN_ENABLE); break; default: -- cgit v1.2.3 From 4b1ac579cb9d7f7733fa2d9e14132b18599d81f1 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 16 Apr 2006 12:19:24 -0400 Subject: cxusb: lgh064f: set auxiliary byte in pll_set From: Michael Krufky This changeset removes pll_init, and instead sets the tuner auxiliary byte in pll_set. Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/dvb-usb/cxusb.c | 36 ++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index d63f6ec4f..a34068f50 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -313,6 +313,36 @@ static int cxusb_mt352_demod_init(struct dvb_frontend* fe) return 0; } +static int cxusb_lgh064f_pll_set_i2c(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fep) +{ + struct dvb_usb_device *d = fe->dvb->priv; + int ret = 0; + u8 b[5]; + struct i2c_msg msg = { .addr = d->pll_addr, .flags = 0, + .buf = &b[1], .len = 4 }; + + dvb_usb_pll_set(fe,fep,b); + + if (i2c_transfer (&d->i2c_adap, &msg, 1) != 1) { + err("tuner i2c write failed for pll_set."); + ret = -EREMOTEIO; + } + msleep(1); + + /* Set the Auxiliary Byte. */ + b[3] &= ~0x20; + b[3] |= 0x18; + b[4] = 0x50; + if (i2c_transfer(&d->i2c_adap, &msg, 1) != 1) { + err("tuner i2c write failed writing auxiliary byte."); + ret = -EREMOTEIO; + } + msleep(1); + + return ret; +} + static struct cx22702_config cxusb_cx22702_config = { .demod_address = 0x63, @@ -325,7 +355,7 @@ static struct cx22702_config cxusb_cx22702_config = { static struct lgdt330x_config cxusb_lgdt3303_config = { .demod_address = 0x0e, .demod_chip = LGDT3303, - .pll_set = dvb_usb_pll_set_i2c, + .pll_set = cxusb_lgh064f_pll_set_i2c, }; static struct mt352_config cxusb_dee1601_config = { @@ -353,11 +383,7 @@ static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_device *d) static int cxusb_lgh064f_tuner_attach(struct dvb_usb_device *d) { - u8 bpll[4] = { 0x00, 0x00, 0x18, 0x50 }; - /* bpll[2] : unset bit 3, set bits 4&5 - bpll[3] : 0x50 - digital, 0x20 - analog */ d->pll_addr = 0x61; - memcpy(d->pll_init, bpll, 4); d->pll_desc = &dvb_pll_tdvs_tua6034; return 0; } -- cgit v1.2.3 From 8df57f53c08fe52b578d075f996e30f4aa5b1849 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 16 Apr 2006 22:17:42 +0200 Subject: From: Hans Verkuil Add support for TCL M2523_5N_E tuner. Signed-off-by: Hans Verkuil --- linux/drivers/media/video/tveeprom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/tveeprom.c b/linux/drivers/media/video/tveeprom.c index 8dad3c6a8..5b9c4446b 100644 --- a/linux/drivers/media/video/tveeprom.c +++ b/linux/drivers/media/video/tveeprom.c @@ -222,7 +222,7 @@ hauppauge_tuner[] = /* 110-119 */ { TUNER_ABSENT, "Thompson DTT75105"}, { TUNER_ABSENT, "Conexant_CX24109"}, - { TUNER_ABSENT, "TCL M2523_5N_E"}, + { TUNER_TCL_2002N, "TCL M2523_5N_E"}, { TUNER_ABSENT, "TCL M2523_3DB_E"}, { TUNER_ABSENT, "Philips 8275A"}, { TUNER_ABSENT, "Microtune MT2060"}, -- cgit v1.2.3 From 2bc21bcdb502df4abb44c04f0f66b66e849894ba Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 17 Apr 2006 17:10:38 -0400 Subject: cx88-blackbird: revert video standard detection patch From: Michael Krufky This patch reverts changeset 328131dbb141 This patch was wrong... a better solution will come shortly. Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/cx88-blackbird.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index e1e9580b2..935706680 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -1719,12 +1719,16 @@ static int __devinit blackbird_probe(struct pci_dev *pci_dev, memcpy(&dev->params,&default_mpeg_params,sizeof(default_mpeg_params)); memcpy(&dev->dnr_params,&default_dnr_params,sizeof(default_dnr_params)); - if (core->tuner_formats & V4L2_STD_525_60) { - dev->height = 480; - dev->params.vi_frame_rate = 30; - } else { - dev->height = 576; - dev->params.vi_frame_rate = 25; + if (core->board == CX88_BOARD_HAUPPAUGE_ROSLYN) { + + if (core->tuner_formats & V4L2_STD_525_60) { + dev->height = 480; + dev->params.vi_frame_rate = 30; + } else { + dev->height = 576; + dev->params.vi_frame_rate = 25; + } + } err = cx8802_init_common(dev); -- cgit v1.2.3 From 5f35d55636599845c22433ac77c66277e4c25ad5 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 17 Apr 2006 23:22:09 -0400 Subject: KWorld HardwareMpegTV XPert: set encoder video standard based on tvnorm From: Michael Krufky The KWorld HardwareMpegTV XPert uses a multistandard tuner, tda8290 + tda8275. Without checking the video standard in blackbird_probe, the encoder defaults to PAL, even if the incoming video stream is NTSC. This patch checks the video standard set by the cx2388x decoder, and sets the encoding height and frame accordingly. This patch is designed to only affect the KWorld HardwareMpegTV XPert. Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/cx88-blackbird.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index 935706680..73580db8b 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -1719,8 +1719,8 @@ static int __devinit blackbird_probe(struct pci_dev *pci_dev, memcpy(&dev->params,&default_mpeg_params,sizeof(default_mpeg_params)); memcpy(&dev->dnr_params,&default_dnr_params,sizeof(default_dnr_params)); - if (core->board == CX88_BOARD_HAUPPAUGE_ROSLYN) { - + switch (core->board) { + case CX88_BOARD_HAUPPAUGE_ROSLYN: if (core->tuner_formats & V4L2_STD_525_60) { dev->height = 480; dev->params.vi_frame_rate = 30; @@ -1728,7 +1728,16 @@ static int __devinit blackbird_probe(struct pci_dev *pci_dev, dev->height = 576; dev->params.vi_frame_rate = 25; } - + break; + case CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT: + if (core->tvnorm->id & V4L2_STD_525_60) { + dev->height = 480; + dev->params.vi_frame_rate = 30; + } else { + dev->height = 576; + dev->params.vi_frame_rate = 25; + } + break; } err = cx8802_init_common(dev); -- cgit v1.2.3 From a9ccefdfb8e688f194b7c5aa82f7526c65555215 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 18 Apr 2006 21:47:08 +0100 Subject: create standalone fe_lgh064f header From: Michael Krufky move all pll_set functions for lg-tdvs-h06xf atsc tuners to a standalone header Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/frontends/fe_lgh06xf.h | 66 ++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 linux/drivers/media/dvb/frontends/fe_lgh06xf.h (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/fe_lgh06xf.h b/linux/drivers/media/dvb/frontends/fe_lgh06xf.h new file mode 100644 index 000000000..c5e8b5ff1 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/fe_lgh06xf.h @@ -0,0 +1,66 @@ +/* + * fe_lgh06xf.h - ATSC Tuner support for LG TDVS H06xF + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef FE_LGH06XF_H +#define FE_LGH06XF_H +#include "dvb-pll.h" + +static int lg_h06xf_pll_set(struct dvb_frontend* fe, struct i2c_adapter* i2c_adap, + struct dvb_frontend_parameters* params) +{ + u8 buf[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + int err; + + dvb_pll_configure(&dvb_pll_tdvs_tua6034, buf, params->frequency, 0); + if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) { + printk(KERN_WARNING "lg_h06xf: %s error " + "(addr %02x <- %02x, err = %i)\n", + __FUNCTION__, buf[0], buf[1], err); + if (err < 0) + return err; + else + return -EREMOTEIO; + } + + /* Set the Auxiliary Byte. */ +#if 0 + buf[2] &= ~0x20; + buf[2] |= 0x18; + buf[3] = 0x50; +#else + buf[0] = buf[2]; + buf[0] &= ~0x20; + buf[0] |= 0x18; + buf[1] = 0x50; + msg.len = 2; +#endif + if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) { + printk(KERN_WARNING "lg_h06xf: %s error " + "(addr %02x <- %02x, err = %i)\n", + __FUNCTION__, buf[0], buf[1], err); + if (err < 0) + return err; + else + return -EREMOTEIO; + } + + return 0; +} +#endif -- cgit v1.2.3 From ee251cb9eca08f2c9c0aef8507efab64bfa32e99 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 18 Apr 2006 21:47:08 +0100 Subject: dvb-bt8xx: use fe_lgh06xf.h From: Michael Krufky - removed duplicated tuning code for LG TDVS H064F Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c | 25 +------------------------ linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h | 1 + 2 files changed, 2 insertions(+), 24 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c index 0e01863ab..93aaaa4e5 100644 --- a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -569,30 +569,7 @@ static struct mt352_config digitv_alps_tded4_config = { static int tdvs_tua6034_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; - u8 buf[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; - int err; - - dvb_pll_configure(&dvb_pll_tdvs_tua6034, buf, params->frequency, 0); - dprintk("%s: tuner at 0x%02x bytes: 0x%02x 0x%02x 0x%02x 0x%02x\n", - __FUNCTION__, msg.addr, buf[0],buf[1],buf[2],buf[3]); - if ((err = i2c_transfer(card->i2c_adapter, &msg, 1)) != 1) { - printk(KERN_WARNING "dvb-bt8xx: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, buf[0], buf[1], err); - if (err < 0) - return err; - else - return -EREMOTEIO; - } - - /* Set the Auxiliary Byte. */ - buf[2] &= ~0x20; - buf[2] |= 0x18; - buf[3] = 0x50; - i2c_transfer(card->i2c_adapter, &msg, 1); - - return 0; + return lg_h06xf_pll_set(fe, card->i2c_adapter, params); } static struct lgdt330x_config tdvs_tua6034_config = { diff --git a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h index 626718774..ad261b593 100644 --- a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h +++ b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h @@ -40,6 +40,7 @@ #include "cx24110.h" #include "or51211.h" #include "lgdt330x.h" +#include "fe_lgh06xf.h" #include "zl10353.h" struct dvb_bt8xx_card { -- cgit v1.2.3 From 69f7906e3c0f9c63177f85f00be69c60d5c21d1d Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 18 Apr 2006 21:47:08 +0100 Subject: cx88-dvb: use fe_lgh06xf.h From: Michael Krufky - removed duplicated tuning code for LG TDVS H062F/H064F Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/cx88-dvb.c | 35 +++++++++++++++---------------- 1 file changed, 17 insertions(+), 18 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index 6d9417687..ecd33d6fb 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -52,6 +52,7 @@ #endif #ifdef HAVE_LGDT330X # include "lgdt330x.h" +# include "fe_lgh06xf.h" #endif #ifdef HAVE_NXT200X # include "nxt200x.h" @@ -379,7 +380,7 @@ static struct or51132_config pchdtv_hd3000 = { #endif #ifdef HAVE_LGDT330X -static int lgdt330x_pll_set(struct dvb_frontend* fe, +static int lgdt3302_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { /* FIXME make this routine use the tuner-simple code. @@ -393,9 +394,6 @@ static int lgdt330x_pll_set(struct dvb_frontend* fe, { .addr = dev->core->pll_addr, .flags = 0, .buf = buf, .len = 4 }; int err; - /* Put the analog decoder in standby to keep it quiet */ - cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); - dvb_pll_configure(core->pll_desc, buf, params->frequency, 0); dprintk(1, "%s: tuner at 0x%02x bytes: 0x%02x 0x%02x 0x%02x 0x%02x\n", __FUNCTION__, msg.addr, buf[0],buf[1],buf[2],buf[3]); @@ -408,16 +406,21 @@ static int lgdt330x_pll_set(struct dvb_frontend* fe, else return -EREMOTEIO; } - if (core->tuner_type == TUNER_LG_TDVS_H062F) { - /* Set the Auxiliary Byte. */ - buf[2] &= ~0x20; - buf[2] |= 0x18; - buf[3] = 0x50; - i2c_transfer(&core->i2c_adap, &msg, 1); - } return 0; } +static int lgdt3303_pll_set(struct dvb_frontend* fe, + struct dvb_frontend_parameters* params) +{ + struct cx8802_dev *dev= fe->dvb->priv; + struct cx88_core *core = dev->core; + + /* Put the analog decoder in standby to keep it quiet */ + cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); + + return lg_h06xf_pll_set(fe, &core->i2c_adap, params); +} + static int lgdt330x_pll_rf_set(struct dvb_frontend* fe, int index) { struct cx8802_dev *dev= fe->dvb->priv; @@ -445,7 +448,7 @@ static struct lgdt330x_config fusionhdtv_3_gold = { .demod_address = 0x0e, .demod_chip = LGDT3302, .serial_mpeg = 0x04, /* TPSERIAL for 3302 in TOP_CONTROL */ - .pll_set = lgdt330x_pll_set, + .pll_set = lgdt3302_pll_set, .set_ts_params = lgdt330x_set_ts_param, }; @@ -453,7 +456,7 @@ static struct lgdt330x_config fusionhdtv_5_gold = { .demod_address = 0x0e, .demod_chip = LGDT3303, .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ - .pll_set = lgdt330x_pll_set, + .pll_set = lgdt3303_pll_set, .set_ts_params = lgdt330x_set_ts_param, }; @@ -461,7 +464,7 @@ static struct lgdt330x_config pchdtv_hd5500 = { .demod_address = 0x59, .demod_chip = LGDT3303, .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ - .pll_set = lgdt330x_pll_set, + .pll_set = lgdt3303_pll_set, .set_ts_params = lgdt330x_set_ts_param, }; #endif @@ -664,8 +667,6 @@ static int dvb_register(struct cx8802_dev *dev) mdelay(100); cx_set(MO_GP0_IO, 1); mdelay(200); - dev->core->pll_addr = 0x61; - dev->core->pll_desc = &dvb_pll_tdvs_tua6034; dev->dvb.frontend = lgdt330x_attach(&fusionhdtv_5_gold, &dev->core->i2c_adap); } @@ -680,8 +681,6 @@ static int dvb_register(struct cx8802_dev *dev) mdelay(100); cx_set(MO_GP0_IO, 1); mdelay(200); - dev->core->pll_addr = 0x61; - dev->core->pll_desc = &dvb_pll_tdvs_tua6034; dev->dvb.frontend = lgdt330x_attach(&pchdtv_hd5500, &dev->core->i2c_adap); } -- cgit v1.2.3 From 8ba26ea79a5fc37b78bdb661bfd2e18a75e06198 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 18 Apr 2006 21:47:08 +0100 Subject: cxusb: use fe_lgh06xf.h From: Michael Krufky - removed duplicated tuning code for LG TDVS H064F Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/dvb-usb/cxusb.c | 34 ++------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index a34068f50..d3fcc90db 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -27,6 +27,7 @@ #include "cx22702.h" #include "lgdt330x.h" +#include "fe_lgh06xf.h" #include "mt352.h" #include "mt352_priv.h" @@ -317,30 +318,7 @@ static int cxusb_lgh064f_pll_set_i2c(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) { struct dvb_usb_device *d = fe->dvb->priv; - int ret = 0; - u8 b[5]; - struct i2c_msg msg = { .addr = d->pll_addr, .flags = 0, - .buf = &b[1], .len = 4 }; - - dvb_usb_pll_set(fe,fep,b); - - if (i2c_transfer (&d->i2c_adap, &msg, 1) != 1) { - err("tuner i2c write failed for pll_set."); - ret = -EREMOTEIO; - } - msleep(1); - - /* Set the Auxiliary Byte. */ - b[3] &= ~0x20; - b[3] |= 0x18; - b[4] = 0x50; - if (i2c_transfer(&d->i2c_adap, &msg, 1) != 1) { - err("tuner i2c write failed writing auxiliary byte."); - ret = -EREMOTEIO; - } - msleep(1); - - return ret; + return lg_h06xf_pll_set(fe, &d->i2c_adap, fep); } static struct cx22702_config cxusb_cx22702_config = { @@ -381,13 +359,6 @@ static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_device *d) return 0; } -static int cxusb_lgh064f_tuner_attach(struct dvb_usb_device *d) -{ - d->pll_addr = 0x61; - d->pll_desc = &dvb_pll_tdvs_tua6034; - return 0; -} - static int cxusb_dee1601_tuner_attach(struct dvb_usb_device *d) { d->pll_addr = 0x61; @@ -572,7 +543,6 @@ static struct dvb_usb_properties cxusb_bluebird_lgh064f_properties = { .streaming_ctrl = cxusb_streaming_ctrl, .power_ctrl = cxusb_power_ctrl, .frontend_attach = cxusb_lgdt3303_frontend_attach, - .tuner_attach = cxusb_lgh064f_tuner_attach, .i2c_algo = &cxusb_i2c_algo, -- cgit v1.2.3 From 8c5d880b38882ff74c96c188743e43f413f36b7d Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 18 Apr 2006 21:47:08 +0100 Subject: b2c2-flexcop: use fe_lgh06xf.h From: Michael Krufky - removed duplicated tuning code for LG TDVS H061F Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c | 34 ++----------------------- 1 file changed, 2 insertions(+), 32 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index 26aa3a58b..26f79ffef 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -14,6 +14,7 @@ #include "stv0297_cs2.h" #include "mt312.h" #include "lgdt330x.h" +#include "fe_lgh06xf.h" #include "dvb-pll.h" /* lnb control */ @@ -301,38 +302,7 @@ static int lgdt3303_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct flexcop_device *fc = fe->dvb->priv; - u8 buf[4]; - struct i2c_msg msg = - { .addr = 0x61, .flags = 0, .buf = buf, .len = 4 }; - int err; - - dvb_pll_configure(&dvb_pll_tdvs_tua6034,buf, params->frequency, 0); - dprintk(1, "%s: tuner at 0x%02x bytes: 0x%02x 0x%02x 0x%02x 0x%02x\n", - __FUNCTION__, msg.addr, buf[0],buf[1],buf[2],buf[3]); - if ((err = i2c_transfer(&fc->i2c_adap, &msg, 1)) != 1) { - printk(KERN_WARNING "lgdt3303: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, buf[0], buf[1], err); - if (err < 0) - return err; - else - return -EREMOTEIO; - } - - buf[0] = 0x86 | 0x18; - buf[1] = 0x50; - msg.len = 2; - if ((err = i2c_transfer(&fc->i2c_adap, &msg, 1)) != 1) { - printk(KERN_WARNING "lgdt3303: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, buf[0], buf[1], err); - if (err < 0) - return err; - else - return -EREMOTEIO; - } - - return 0; + return lg_h06xf_pll_set(fe, &fc->i2c_adap, params); } static struct lgdt330x_config air2pc_atsc_hd5000_config = { -- cgit v1.2.3 From 0b7b4dd67a96eb2d7e89b62e39266c073a925f3d Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:09 +0100 Subject: DVB core changes for PLL refactoring From: Andrew de Quincey Add tuner_ops structure. Add calls into dvb_frontend to support the new tuner architecture. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 15 ++++ linux/drivers/media/dvb/dvb-core/dvb_frontend.h | 112 ++++++++++++++++++++++++ 2 files changed, 127 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 12e890e06..bc752929f 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -215,6 +215,11 @@ static void dvb_frontend_init(struct dvb_frontend *fe) if (fe->ops->init) fe->ops->init(fe); + if (fe->ops->tuner_ops.init) { + fe->ops->tuner_ops.init(fe); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 0); + } } void dvb_frontend_reinitialise(struct dvb_frontend *fe) @@ -577,6 +582,11 @@ static int dvb_frontend_thread(void *data) if (dvb_powerdown_on_sleep) if (fe->ops->set_voltage) fe->ops->set_voltage(fe, SEC_VOLTAGE_OFF); + if (fe->ops->tuner_ops.sleep) { + fe->ops->tuner_ops.sleep(fe); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 0); + } if (fe->ops->sleep) fe->ops->sleep(fe); } @@ -1091,6 +1101,11 @@ int dvb_unregister_frontend(struct dvb_frontend* fe) mutex_lock(&frontend_mutex); dvb_unregister_device (fepriv->dvbdev); dvb_frontend_stop (fe); + if (fe->ops->tuner_ops.release) { + fe->ops->tuner_ops.release(fe); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 0); + } if (fe->ops->release) fe->ops->release(fe); else diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h index 5926a3b74..e0148a9e6 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -49,6 +49,115 @@ struct dvb_frontend_tune_settings { struct dvb_frontend; +struct dvb_tuner_info { + char name[128]; + + u32 frequency_min; + u32 frequency_max; + u32 frequency_step; + + u32 bandwidth_min; + u32 bandwidth_max; + u32 bandwidth_step; +}; + +struct dvb_tuner_ops { + /** + * Description of the tuner. + */ + struct dvb_tuner_info info; + + /** + * Cleanup an attached tuner. + * + * @param fe dvb_frontend structure to clean it up from. + * @return 0 on success, <0 on failure. + */ + int (*release)(struct dvb_frontend *fe); + + /** + * Initialise a tuner. + * + * @param fe dvb_frontend structure. + * @return 0 on success, <0 on failure. + */ + int (*init)(struct dvb_frontend *fe); + + /** + * Set a tuner into low power mode. + * + * @param fe dvb_frontend structure. + * @return 0 on success, <0 on failure. + */ + int (*sleep)(struct dvb_frontend *fe); + + /** + * This is for simple PLLs - set all parameters in one go. + * + * @param fe The dvb_frontend structure. + * @param p The parameters to set. + * @return 0 on success, <0 on failure. + */ + int (*set_params)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p); + + /** + * This is support for demods like the mt352 - fills out the supplied buffer with what to write. + * + * @param fe The dvb_frontend structure. + * @param p The parameters to set. + * @param buf The buffer to fill with data. For an i2c tuner, the first byte should be the tuner i2c address in linux format. + * @param buf_len Size of buffer in bytes. + * @return Number of bytes used, or <0 on failure. + */ + int (*pllbuf)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p, u8 *buf, int buf_len); + + /** + * Get the frequency the tuner was actually set to. + * + * @param fe The dvb_frontend structure. + * @param frequency Where to put it. + * @return 0 on success, or <0 on failure. + */ + int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency); + + /** + * Get the bandwidth the tuner was actually set to. + * + * @param fe The dvb_frontend structure. + * @param bandwidth Where to put it. + * @return 0 on success, or <0 on failure. + */ + int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth); + + /** + * Get the tuner's status. + * + * @param fe The dvb_frontend structure. + * @param status Where to put it. + * @return 0 on success, or <0 on failure. + */ +#define TUNER_STATUS_LOCKED 1 + int (*get_status)(struct dvb_frontend *fe, u32 *status); + + /** + * Set the frequency of the tuner - for complex tuners. + * + * @param fe The dvb_frontend structure. + * @param frequency What to set. + * @return 0 on success, or <0 on failure. + */ + int (*set_frequency)(struct dvb_frontend *fe, u32 frequency); + + /** + * Set the bandwidth of the tuner - for complex tuners. + * + * @param fe The dvb_frontend structure. + * @param bandwidth What to set. + * @return 0 on success, or <0 on failure. + */ + int (*set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth); +}; + struct dvb_frontend_ops { struct dvb_frontend_info info; @@ -86,6 +195,8 @@ struct dvb_frontend_ops { int (*enable_high_lnb_voltage)(struct dvb_frontend* fe, long arg); int (*dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd); int (*i2c_gate_ctrl)(struct dvb_frontend* fe, int enable); + + struct dvb_tuner_ops tuner_ops; }; #define MAX_EVENT 8 @@ -103,6 +214,7 @@ struct dvb_frontend { struct dvb_frontend_ops* ops; struct dvb_adapter *dvb; void* demodulator_priv; + void* tuner_priv; void* frontend_priv; void* misc_priv; }; -- cgit v1.2.3 From 8743017f89633dc04c721dd9f332605aa9e48246 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:09 +0100 Subject: Convert at76c651 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Add i2c gate control function. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/at76c651.c | 25 ++++++++++++++++--------- linux/drivers/media/dvb/frontends/at76c651.h | 4 ---- 2 files changed, 16 insertions(+), 13 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/at76c651.c b/linux/drivers/media/dvb/frontends/at76c651.c index ddcb99cc5..556689a80 100644 --- a/linux/drivers/media/dvb/frontends/at76c651.c +++ b/linux/drivers/media/dvb/frontends/at76c651.c @@ -246,9 +246,10 @@ static int at76c651_set_parameters(struct dvb_frontend* fe, int ret; struct at76c651_state* state = fe->demodulator_priv; - at76c651_writereg(state, 0x0c, 0xc3); - state->config->pll_set(fe, p); - at76c651_writereg(state, 0x0c, 0xc2); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } if ((ret = at76c651_set_symbol_rate(state, p->u.qam.symbol_rate))) return ret; @@ -268,12 +269,6 @@ static int at76c651_set_defaults(struct dvb_frontend* fe) at76c651_set_bbfreq(state); at76c651_set_auto_config(state); - if (state->config->pll_init) { - at76c651_writereg(state, 0x0c, 0xc3); - state->config->pll_init(fe); - at76c651_writereg(state, 0x0c, 0xc2); - } - return 0; } @@ -344,6 +339,17 @@ static int at76c651_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) return 0; } +static int at76c651_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct at76c651_state* state = fe->demodulator_priv; + + if (enable) { + return at76c651_writereg(state, 0x0c, 0xc3); + } else { + return at76c651_writereg(state, 0x0c, 0xc2); + } +} + static int at76c651_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *fesettings) { fesettings->min_delay_ms = 50; @@ -414,6 +420,7 @@ static struct dvb_frontend_ops at76c651_ops = { .release = at76c651_release, .init = at76c651_set_defaults, + .i2c_gate_ctrl = at76c651_i2c_gate_ctrl, .set_frontend = at76c651_set_parameters, .get_tune_settings = at76c651_get_tune_settings, diff --git a/linux/drivers/media/dvb/frontends/at76c651.h b/linux/drivers/media/dvb/frontends/at76c651.h index 34054df93..c31db11a2 100644 --- a/linux/drivers/media/dvb/frontends/at76c651.h +++ b/linux/drivers/media/dvb/frontends/at76c651.h @@ -35,10 +35,6 @@ struct at76c651_config { /* the demodulator's i2c address */ u8 demod_address; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* at76c651_attach(const struct at76c651_config* config, -- cgit v1.2.3 From 97c96aeb1a40db79b142796649ee61c880377e2e Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:09 +0100 Subject: Convert cx22700 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Add i2c gate control function. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/cx22700.c | 26 +++++++++++++++++--------- linux/drivers/media/dvb/frontends/cx22700.h | 4 ---- 2 files changed, 17 insertions(+), 13 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/cx22700.c b/linux/drivers/media/dvb/frontends/cx22700.c index 755f774f6..02fee9047 100644 --- a/linux/drivers/media/dvb/frontends/cx22700.c +++ b/linux/drivers/media/dvb/frontends/cx22700.c @@ -247,12 +247,6 @@ static int cx22700_init (struct dvb_frontend* fe) cx22700_writereg (state, 0x00, 0x01); - if (state->config->pll_init) { - cx22700_writereg (state, 0x0a, 0x00); /* open i2c bus switch */ - state->config->pll_init(fe); - cx22700_writereg (state, 0x0a, 0x01); /* close i2c bus switch */ - } - return 0; } @@ -333,9 +327,11 @@ static int cx22700_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par cx22700_writereg (state, 0x00, 0x02); /* XXX CHECKME: soft reset*/ cx22700_writereg (state, 0x00, 0x00); - cx22700_writereg (state, 0x0a, 0x00); /* open i2c bus switch */ - state->config->pll_set(fe, p); - cx22700_writereg (state, 0x0a, 0x01); /* close i2c bus switch */ + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } + cx22700_set_inversion (state, p->inversion); cx22700_set_tps (state, &p->u.ofdm); cx22700_writereg (state, 0x37, 0x01); /* PAL loop filter off */ @@ -353,6 +349,17 @@ static int cx22700_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_par return cx22700_get_tps (state, &p->u.ofdm); } +static int cx22700_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct cx22700_state* state = fe->demodulator_priv; + + if (enable) { + return cx22700_writereg(state, 0x0a, 0x00); + } else { + return cx22700_writereg(state, 0x0a, 0x01); + } +} + static int cx22700_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) { fesettings->min_delay_ms = 150; @@ -413,6 +420,7 @@ static struct dvb_frontend_ops cx22700_ops = { .release = cx22700_release, .init = cx22700_init, + .i2c_gate_ctrl = cx22700_i2c_gate_ctrl, .set_frontend = cx22700_set_frontend, .get_frontend = cx22700_get_frontend, diff --git a/linux/drivers/media/dvb/frontends/cx22700.h b/linux/drivers/media/dvb/frontends/cx22700.h index c9145b458..dcd8979c1 100644 --- a/linux/drivers/media/dvb/frontends/cx22700.h +++ b/linux/drivers/media/dvb/frontends/cx22700.h @@ -29,10 +29,6 @@ struct cx22700_config { /* the demodulator's i2c address */ u8 demod_address; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, -- cgit v1.2.3 From ba718ac0ff424d206df56b1a7a5126358f0a15ee Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:09 +0100 Subject: Convert cx22702 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Add i2c gate control function. Remove dvb-pll support - done elsewhere now. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/cx22702.c | 24 ++++-------------------- linux/drivers/media/dvb/frontends/cx22702.h | 7 ------- 2 files changed, 4 insertions(+), 27 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/cx22702.c b/linux/drivers/media/dvb/frontends/cx22702.c index 0fc899f81..a129fc9cb 100644 --- a/linux/drivers/media/dvb/frontends/cx22702.c +++ b/linux/drivers/media/dvb/frontends/cx22702.c @@ -211,22 +211,10 @@ static int cx22702_set_tps (struct dvb_frontend* fe, struct dvb_frontend_paramet u8 val; struct cx22702_state* state = fe->demodulator_priv; - /* set PLL */ - cx22702_i2c_gate_ctrl(fe, 1); - if (state->config->pll_set) { - state->config->pll_set(fe, p); - } else if (state->config->pll_desc) { - u8 pllbuf[4]; - struct i2c_msg msg = { .addr = state->config->pll_address, - .buf = pllbuf, .len = 4 }; - dvb_pll_configure(state->config->pll_desc, pllbuf, - p->frequency, - p->u.ofdm.bandwidth); - i2c_transfer(state->i2c, &msg, 1); - } else { - BUG(); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); } - cx22702_i2c_gate_ctrl(fe, 0); /* set inversion */ cx22702_set_inversion (state, p->inversion); @@ -358,10 +346,6 @@ static int cx22702_init (struct dvb_frontend* fe) cx22702_writereg (state, 0xf8, (state->config->output_mode << 1) & 0x02); - /* init PLL */ - if (state->config->pll_init) - state->config->pll_init(fe); - cx22702_i2c_gate_ctrl(fe, 0); return 0; @@ -530,6 +514,7 @@ static struct dvb_frontend_ops cx22702_ops = { .release = cx22702_release, .init = cx22702_init, + .i2c_gate_ctrl = cx22702_i2c_gate_ctrl, .set_frontend = cx22702_set_tps, .get_frontend = cx22702_get_frontend, @@ -540,7 +525,6 @@ static struct dvb_frontend_ops cx22702_ops = { .read_signal_strength = cx22702_read_signal_strength, .read_snr = cx22702_read_snr, .read_ucblocks = cx22702_read_ucblocks, - .i2c_gate_ctrl = cx22702_i2c_gate_ctrl, }; module_param(debug, int, 0644); diff --git a/linux/drivers/media/dvb/frontends/cx22702.h b/linux/drivers/media/dvb/frontends/cx22702.h index 5633976a5..7f2f241e5 100644 --- a/linux/drivers/media/dvb/frontends/cx22702.h +++ b/linux/drivers/media/dvb/frontends/cx22702.h @@ -39,13 +39,6 @@ struct cx22702_config #define CX22702_PARALLEL_OUTPUT 0 #define CX22702_SERIAL_OUTPUT 1 u8 output_mode; - - /* PLL maintenance */ - u8 pll_address; - struct dvb_pll_desc *pll_desc; - - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* cx22702_attach(const struct cx22702_config* config, -- cgit v1.2.3 From 9a857c4fc991c3689dc1d14e9eb3781d2cb23494 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:09 +0100 Subject: Convert mt312 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Add i2c gate control function. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/mt312.c | 25 ++++++++++++++++--------- linux/drivers/media/dvb/frontends/mt312.h | 4 ---- 2 files changed, 16 insertions(+), 13 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/mt312.c b/linux/drivers/media/dvb/frontends/mt312.c index d3aea83cf..46e12a8ac 100644 --- a/linux/drivers/media/dvb/frontends/mt312.c +++ b/linux/drivers/media/dvb/frontends/mt312.c @@ -277,12 +277,6 @@ static int mt312_initfe(struct dvb_frontend* fe) if ((ret = mt312_writereg(state, CS_SW_LIM, 0x69)) < 0) return ret; - if (state->config->pll_init) { - mt312_writereg(state, GPP_CTRL, 0x40); - state->config->pll_init(fe); - mt312_writereg(state, GPP_CTRL, 0x00); - } - return 0; } @@ -529,9 +523,10 @@ static int mt312_set_frontend(struct dvb_frontend* fe, return -EINVAL; } - mt312_writereg(state, GPP_CTRL, 0x40); - state->config->pll_set(fe, p); - mt312_writereg(state, GPP_CTRL, 0x00); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } /* sr = (u16)(sr * 256.0 / 1000000.0) */ sr = mt312_div(p->u.qpsk.symbol_rate * 4, 15625); @@ -578,6 +573,17 @@ static int mt312_get_frontend(struct dvb_frontend* fe, return 0; } +static int mt312_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct mt312_state* state = fe->demodulator_priv; + + if (enable) { + return mt312_writereg(state, GPP_CTRL, 0x40); + } else { + return mt312_writereg(state, GPP_CTRL, 0x00); + } +} + static int mt312_sleep(struct dvb_frontend* fe) { struct mt312_state *state = fe->demodulator_priv; @@ -633,6 +639,7 @@ static struct dvb_frontend_ops vp310_mt312_ops = { .init = mt312_initfe, .sleep = mt312_sleep, + .i2c_gate_ctrl = mt312_i2c_gate_ctrl, .set_frontend = mt312_set_frontend, .get_frontend = mt312_get_frontend, diff --git a/linux/drivers/media/dvb/frontends/mt312.h b/linux/drivers/media/dvb/frontends/mt312.h index 074d844f0..666a1bd1c 100644 --- a/linux/drivers/media/dvb/frontends/mt312.h +++ b/linux/drivers/media/dvb/frontends/mt312.h @@ -32,10 +32,6 @@ struct mt312_config { /* the demodulator's i2c address */ u8 demod_address; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; struct dvb_frontend* vp310_mt312_attach(const struct mt312_config* config, -- cgit v1.2.3 From a66d7de8bc7a3f03f0996b55a783a5b54773022d Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:09 +0100 Subject: Convert stv0297 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Add i2c gate control function. Remove extra exported pll gate control function. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/stv0297.c | 18 ++++++++++-------- linux/drivers/media/dvb/frontends/stv0297.h | 5 ----- 2 files changed, 10 insertions(+), 13 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/stv0297.c b/linux/drivers/media/dvb/frontends/stv0297.c index 54f4414a7..7e8855723 100644 --- a/linux/drivers/media/dvb/frontends/stv0297.c +++ b/linux/drivers/media/dvb/frontends/stv0297.c @@ -276,12 +276,14 @@ static int stv0297_set_inversion(struct stv0297_state *state, fe_spectral_invers return 0; } -int stv0297_enable_plli2c(struct dvb_frontend *fe) +static int stv0297_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) { struct stv0297_state *state = fe->demodulator_priv; - stv0297_writereg(state, 0x87, 0x78); - stv0297_writereg(state, 0x86, 0xc8); + if (enable) { + stv0297_writereg(state, 0x87, 0x78); + stv0297_writereg(state, 0x86, 0xc8); + } return 0; } @@ -296,9 +298,6 @@ static int stv0297_init(struct dvb_frontend *fe) stv0297_writereg(state, state->config->inittab[i], state->config->inittab[i+1]); msleep(200); - if (state->config->pll_init) - state->config->pll_init(fe); - return 0; } @@ -421,7 +420,10 @@ static int stv0297_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par } stv0297_init(fe); - state->config->pll_set(fe, p); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } /* clear software interrupts */ stv0297_writereg(state, 0x82, 0x0); @@ -668,6 +670,7 @@ static struct dvb_frontend_ops stv0297_ops = { .init = stv0297_init, .sleep = stv0297_sleep, + .i2c_gate_ctrl = stv0297_i2c_gate_ctrl, .set_frontend = stv0297_set_frontend, .get_frontend = stv0297_get_frontend, @@ -684,4 +687,3 @@ MODULE_AUTHOR("Dennis Noermann and Andrew de Quincey"); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(stv0297_attach); -EXPORT_SYMBOL(stv0297_enable_plli2c); diff --git a/linux/drivers/media/dvb/frontends/stv0297.h b/linux/drivers/media/dvb/frontends/stv0297.h index 9e53f019d..8ff793c90 100644 --- a/linux/drivers/media/dvb/frontends/stv0297.h +++ b/linux/drivers/media/dvb/frontends/stv0297.h @@ -37,14 +37,9 @@ struct stv0297_config /* does the "inversion" need inverted? */ u8 invert:1; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* stv0297_attach(const struct stv0297_config* config, struct i2c_adapter* i2c); -extern int stv0297_enable_plli2c(struct dvb_frontend* fe); #endif // STV0297_H -- cgit v1.2.3 From f9378e87cce2131f14020bac98334a1c92d71c83 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:09 +0100 Subject: Convert tda8083 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/tda8083.c | 8 +++++--- linux/drivers/media/dvb/frontends/tda8083.h | 4 ---- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/tda8083.c b/linux/drivers/media/dvb/frontends/tda8083.c index 91baa9ced..0aeaec890 100644 --- a/linux/drivers/media/dvb/frontends/tda8083.c +++ b/linux/drivers/media/dvb/frontends/tda8083.c @@ -293,7 +293,11 @@ static int tda8083_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par { struct tda8083_state* state = fe->demodulator_priv; - state->config->pll_set(fe, p); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } + tda8083_set_inversion (state, p->inversion); tda8083_set_fec (state, p->u.qpsk.fec_inner); tda8083_set_symbolrate (state, p->u.qpsk.symbol_rate); @@ -334,8 +338,6 @@ static int tda8083_init(struct dvb_frontend* fe) for (i=0; i<44; i++) tda8083_writereg (state, i, tda8083_init_tab[i]); - if (state->config->pll_init) state->config->pll_init(fe); - tda8083_writereg (state, 0x00, 0x3c); tda8083_writereg (state, 0x00, 0x04); diff --git a/linux/drivers/media/dvb/frontends/tda8083.h b/linux/drivers/media/dvb/frontends/tda8083.h index 466663307..e7a48f61e 100644 --- a/linux/drivers/media/dvb/frontends/tda8083.h +++ b/linux/drivers/media/dvb/frontends/tda8083.h @@ -33,10 +33,6 @@ struct tda8083_config { /* the demodulator's i2c address */ u8 demod_address; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, -- cgit v1.2.3 From 079b0dc26bb807e48641259c368379da67df640b Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:10 +0100 Subject: Convert tda80xx to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Add i2c gate control function. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/tda80xx.c | 19 ++++++++++++++++--- linux/drivers/media/dvb/frontends/tda80xx.h | 4 ---- 2 files changed, 16 insertions(+), 7 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/tda80xx.c b/linux/drivers/media/dvb/frontends/tda80xx.c index 051be86b3..7954e8732 100644 --- a/linux/drivers/media/dvb/frontends/tda80xx.c +++ b/linux/drivers/media/dvb/frontends/tda80xx.c @@ -523,9 +523,10 @@ static int tda80xx_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par { struct tda80xx_state* state = fe->demodulator_priv; - tda80xx_writereg(state, 0x1c, 0x80); - state->config->pll_set(fe, p); - tda80xx_writereg(state, 0x1c, 0x00); + if (fe->ops->tuner_ops->set_params) { + fe->ops->tuner_ops->set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } tda80xx_set_parameters(state, p->inversion, p->u.qpsk.symbol_rate, p->u.qpsk.fec_inner); tda80xx_set_clk(state); @@ -628,6 +629,17 @@ static void tda80xx_release(struct dvb_frontend* fe) kfree(state); } +static int tda80xx_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct tda80xx_state* state = fe->demodulator_priv; + + if (enable) { + return tda80xx_writereg(state, 0x1c, 0x80); + } else { + return tda80xx_writereg(state, 0x1c, 0x00); + } +} + static struct dvb_frontend_ops tda80xx_ops; struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config, @@ -709,6 +721,7 @@ static struct dvb_frontend_ops tda80xx_ops = { .init = tda80xx_init, .sleep = tda80xx_sleep, + .i2c_gate_ctrl = tda80xx_i2c_gate_ctrl, .set_frontend = tda80xx_set_frontend, .get_frontend = tda80xx_get_frontend, diff --git a/linux/drivers/media/dvb/frontends/tda80xx.h b/linux/drivers/media/dvb/frontends/tda80xx.h index cd639a0aa..435dd9477 100644 --- a/linux/drivers/media/dvb/frontends/tda80xx.h +++ b/linux/drivers/media/dvb/frontends/tda80xx.h @@ -39,10 +39,6 @@ struct tda80xx_config /* Register setting to use for 18v */ u8 volt18setting; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config, -- cgit v1.2.3 From 27c56fe609a585291d4da99709ad8f2fa230a6be Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:10 +0100 Subject: Convert ves1820 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/ves1820.c | 9 +++++---- linux/drivers/media/dvb/frontends/ves1820.h | 4 ---- 2 files changed, 5 insertions(+), 8 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/ves1820.c b/linux/drivers/media/dvb/frontends/ves1820.c index ad8647a3c..9810e2dcb 100644 --- a/linux/drivers/media/dvb/frontends/ves1820.c +++ b/linux/drivers/media/dvb/frontends/ves1820.c @@ -204,9 +204,6 @@ static int ves1820_init(struct dvb_frontend* fe) ves1820_writereg(state, 0x34, state->pwm); - if (state->config->pll_init) - state->config->pll_init(fe); - return 0; } @@ -223,7 +220,11 @@ static int ves1820_set_parameters(struct dvb_frontend* fe, struct dvb_frontend_p if (real_qam < 0 || real_qam > 4) return -EINVAL; - state->config->pll_set(fe, p); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } + ves1820_set_symbolrate(state, p->u.qam.symbol_rate); ves1820_writereg(state, 0x34, state->pwm); diff --git a/linux/drivers/media/dvb/frontends/ves1820.h b/linux/drivers/media/dvb/frontends/ves1820.h index 355f130b1..520f09522 100644 --- a/linux/drivers/media/dvb/frontends/ves1820.h +++ b/linux/drivers/media/dvb/frontends/ves1820.h @@ -39,10 +39,6 @@ struct ves1820_config /* SELAGC control */ u8 selagc:1; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, -- cgit v1.2.3 From 0d09d01434eb3d027d7ea74534ad8ee7047612aa Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:10 +0100 Subject: Convert ves1x93 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Add i2c gate control function. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/ves1x93.c | 25 ++++++++++++++++--------- linux/drivers/media/dvb/frontends/ves1x93.h | 4 ---- 2 files changed, 16 insertions(+), 13 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/ves1x93.c b/linux/drivers/media/dvb/frontends/ves1x93.c index 821df8e83..660aa7bb9 100644 --- a/linux/drivers/media/dvb/frontends/ves1x93.c +++ b/linux/drivers/media/dvb/frontends/ves1x93.c @@ -278,12 +278,6 @@ static int ves1x93_init (struct dvb_frontend* fe) } } - if (state->config->pll_init) { - ves1x93_writereg(state, 0x00, 0x11); - state->config->pll_init(fe); - ves1x93_writereg(state, 0x00, 0x01); - } - return 0; } @@ -395,9 +389,10 @@ static int ves1x93_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par { struct ves1x93_state* state = fe->demodulator_priv; - ves1x93_writereg(state, 0x00, 0x11); - state->config->pll_set(fe, p); - ves1x93_writereg(state, 0x00, 0x01); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } ves1x93_set_inversion (state, p->inversion); ves1x93_set_fec (state, p->u.qpsk.fec_inner); ves1x93_set_symbolrate (state, p->u.qpsk.symbol_rate); @@ -442,6 +437,17 @@ static void ves1x93_release(struct dvb_frontend* fe) kfree(state); } +static int ves1x93_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct ves1x93_state* state = fe->demodulator_priv; + + if (enable) { + return ves1x93_writereg(state, 0x00, 0x11); + } else { + return ves1x93_writereg(state, 0x00, 0x01); + } +} + static struct dvb_frontend_ops ves1x93_ops; struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, @@ -523,6 +529,7 @@ static struct dvb_frontend_ops ves1x93_ops = { .init = ves1x93_init, .sleep = ves1x93_sleep, + .i2c_gate_ctrl = ves1x93_i2c_gate_ctrl, .set_frontend = ves1x93_set_frontend, .get_frontend = ves1x93_get_frontend, diff --git a/linux/drivers/media/dvb/frontends/ves1x93.h b/linux/drivers/media/dvb/frontends/ves1x93.h index 1627e37c5..ba88ae085 100644 --- a/linux/drivers/media/dvb/frontends/ves1x93.h +++ b/linux/drivers/media/dvb/frontends/ves1x93.h @@ -38,10 +38,6 @@ struct ves1x93_config /* should PWM be inverted? */ u8 invert_pwm:1; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, -- cgit v1.2.3 From 0eb9bcafc9d4a9704b01ee1261a60cfaa579f658 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:10 +0100 Subject: Convert stv0299 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Add i2c gate control function. Remove extra exported function. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/stv0299.c | 35 ++++++++++++++--------------- linux/drivers/media/dvb/frontends/stv0299.h | 5 ----- 2 files changed, 17 insertions(+), 23 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/stv0299.c b/linux/drivers/media/dvb/frontends/stv0299.c index 5bcd00f79..5a8131023 100644 --- a/linux/drivers/media/dvb/frontends/stv0299.c +++ b/linux/drivers/media/dvb/frontends/stv0299.c @@ -131,13 +131,6 @@ static int stv0299_readregs (struct stv0299_state* state, u8 reg1, u8 *b, u8 len return ret == 2 ? 0 : ret; } -int stv0299_enable_plli2c (struct dvb_frontend* fe) -{ - struct stv0299_state* state = fe->demodulator_priv; - - return stv0299_writeregI(state, 0x05, 0xb5); /* enable i2c repeater on stv0299 */ -} - static int stv0299_set_FEC (struct stv0299_state* state, fe_code_rate_t fec) { dprintk ("%s\n", __FUNCTION__); @@ -457,12 +450,6 @@ static int stv0299_init (struct dvb_frontend* fe) for (i=0; !(state->config->inittab[i] == 0xff && state->config->inittab[i+1] == 0xff); i+=2) stv0299_writeregI(state, state->config->inittab[i], state->config->inittab[i+1]); - if (state->config->pll_init) { - stv0299_writeregI(state, 0x05, 0xb5); /* enable i2c repeater on stv0299 */ - state->config->pll_init(fe, state->i2c); - stv0299_writeregI(state, 0x05, 0x35); /* disable i2c repeater on stv0299 */ - } - return 0; } @@ -560,9 +547,10 @@ static int stv0299_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par if (state->config->invert) invval = (~invval) & 1; stv0299_writeregI(state, 0x0c, (stv0299_readreg(state, 0x0c) & 0xfe) | invval); - stv0299_writeregI(state, 0x05, 0xb5); /* enable i2c repeater on stv0299 */ - state->config->pll_set(fe, state->i2c, p); - stv0299_writeregI(state, 0x05, 0x35); /* disable i2c repeater on stv0299 */ + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } stv0299_set_FEC (state, p->u.qpsk.fec_inner); stv0299_set_symbolrate (fe, p->u.qpsk.symbol_rate); @@ -611,6 +599,17 @@ static int stv0299_sleep(struct dvb_frontend* fe) return 0; } +static int stv0299_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct stv0299_state* state = fe->demodulator_priv; + + if (enable) { + return stv0299_writeregI(state, 0x05, 0xb5); + } else { + return stv0299_writeregI(state, 0x05, 0x35); + } +} + static int stv0299_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) { struct stv0299_state* state = fe->demodulator_priv; @@ -695,6 +694,7 @@ static struct dvb_frontend_ops stv0299_ops = { .init = stv0299_init, .sleep = stv0299_sleep, + .i2c_gate_ctrl = stv0299_i2c_gate_ctrl, .set_frontend = stv0299_set_frontend, .get_frontend = stv0299_get_frontend, @@ -721,9 +721,8 @@ MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); MODULE_DESCRIPTION("ST STV0299 DVB Demodulator driver"); MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Peter Schildmann, Felix Domke, " - "Andreas Oberritter, Andrew de Quincey, Kenneth Aafløy"); + "Andreas Oberritter, Andrew de Quincey, Kenneth Aafly"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(stv0299_enable_plli2c); EXPORT_SYMBOL(stv0299_writereg); EXPORT_SYMBOL(stv0299_attach); diff --git a/linux/drivers/media/dvb/frontends/stv0299.h b/linux/drivers/media/dvb/frontends/stv0299.h index 32c87b4c2..1504828e4 100644 --- a/linux/drivers/media/dvb/frontends/stv0299.h +++ b/linux/drivers/media/dvb/frontends/stv0299.h @@ -87,14 +87,9 @@ struct stv0299_config /* Set the symbol rate */ int (*set_symbol_rate)(struct dvb_frontend* fe, u32 srate, u32 ratio); - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend *fe, struct i2c_adapter *i2c); - int (*pll_set)(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dvb_frontend_parameters *params); }; extern int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data); -extern int stv0299_enable_plli2c (struct dvb_frontend* fe); extern struct dvb_frontend* stv0299_attach(const struct stv0299_config* config, struct i2c_adapter* i2c); -- cgit v1.2.3 From 2083f24577e41c4d2457226399956dcfa110da8c Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:10 +0100 Subject: Convert sp887x to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Add i2c gate control function. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/sp887x.c | 34 +++++++++++++++++++----------- linux/drivers/media/dvb/frontends/sp887x.h | 6 ------ 2 files changed, 22 insertions(+), 18 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/sp887x.c b/linux/drivers/media/dvb/frontends/sp887x.c index eb8a60219..543dfa145 100644 --- a/linux/drivers/media/dvb/frontends/sp887x.c +++ b/linux/drivers/media/dvb/frontends/sp887x.c @@ -208,15 +208,6 @@ static int sp887x_initial_setup (struct dvb_frontend* fe, const struct firmware /* bit 0x010: enable data valid signal */ sp887x_writereg(state, 0xd00, 0x010); sp887x_writereg(state, 0x0d1, 0x000); - - /* setup the PLL */ - if (state->config->pll_init) { - sp887x_writereg(state, 0x206, 0x001); - state->config->pll_init(fe); - sp887x_writereg(state, 0x206, 0x000); - } - - printk ("done.\n"); return 0; }; @@ -362,9 +353,16 @@ static int sp887x_setup_frontend_parameters (struct dvb_frontend* fe, sp887x_microcontroller_stop(state); /* setup the PLL */ - sp887x_writereg(state, 0x206, 0x001); - actual_freq = state->config->pll_set(fe, p); - sp887x_writereg(state, 0x206, 0x000); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } + if (fe->ops->tuner_ops.get_frequency) { + fe->ops->tuner_ops.get_frequency(fe, &actual_freq); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } else { + actual_freq = p->frequency; + } /* read status reg in order to clear demodulator_priv; + + if (enable) { + return sp887x_writereg(state, 0x206, 0x001); + } else { + return sp887x_writereg(state, 0x206, 0x000); + } +} + static int sp887x_sleep(struct dvb_frontend* fe) { struct sp887x_state* state = fe->demodulator_priv; @@ -589,6 +598,7 @@ static struct dvb_frontend_ops sp887x_ops = { .init = sp887x_init, .sleep = sp887x_sleep, + .i2c_gate_ctrl = sp887x_i2c_gate_ctrl, .set_frontend = sp887x_setup_frontend_parameters, .get_tune_settings = sp887x_get_tune_settings, diff --git a/linux/drivers/media/dvb/frontends/sp887x.h b/linux/drivers/media/dvb/frontends/sp887x.h index 6a05d8f8e..c44b0ebdf 100644 --- a/linux/drivers/media/dvb/frontends/sp887x.h +++ b/linux/drivers/media/dvb/frontends/sp887x.h @@ -13,12 +13,6 @@ struct sp887x_config /* the demodulator's i2c address */ u8 demod_address; - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - - /* this should return the actual frequency tuned to */ - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); - /* request firmware for device */ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); }; -- cgit v1.2.3 From 55da3e06806a376eb5e0b51bf41f5ba320d6d9fd Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:10 +0100 Subject: Convert sp8870 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Add i2c gate control function. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/sp8870.c | 26 ++++++++++++++++---------- linux/drivers/media/dvb/frontends/sp8870.h | 4 ---- 2 files changed, 16 insertions(+), 14 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/sp8870.c b/linux/drivers/media/dvb/frontends/sp8870.c index 73829e647..4d553c0da 100644 --- a/linux/drivers/media/dvb/frontends/sp8870.c +++ b/linux/drivers/media/dvb/frontends/sp8870.c @@ -262,9 +262,10 @@ static int sp8870_set_frontend_parameters (struct dvb_frontend* fe, sp8870_microcontroller_stop(state); // set tuner parameters - sp8870_writereg(state, 0x206, 0x001); - state->config->pll_set(fe, p); - sp8870_writereg(state, 0x206, 0x000); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } // sample rate correction bit [23..17] sp8870_writereg(state, 0x0319, 0x000A); @@ -349,13 +350,6 @@ static int sp8870_init (struct dvb_frontend* fe) sp8870_writereg(state, 0x0D00, 0x010); sp8870_writereg(state, 0x0D01, 0x000); - /* setup PLL */ - if (state->config->pll_init) { - sp8870_writereg(state, 0x206, 0x001); - state->config->pll_init(fe); - sp8870_writereg(state, 0x206, 0x000); - } - return 0; } @@ -541,6 +535,17 @@ static int sp8870_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend return 0; } +static int sp8870_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct sp8870_state* state = fe->demodulator_priv; + + if (enable) { + return sp8870_writereg(state, 0x206, 0x001); + } else { + return sp8870_writereg(state, 0x206, 0x000); + } +} + static void sp8870_release(struct dvb_frontend* fe) { struct sp8870_state* state = fe->demodulator_priv; @@ -597,6 +602,7 @@ static struct dvb_frontend_ops sp8870_ops = { .init = sp8870_init, .sleep = sp8870_sleep, + .i2c_gate_ctrl = sp8870_i2c_gate_ctrl, .set_frontend = sp8870_set_frontend, .get_tune_settings = sp8870_get_tune_settings, diff --git a/linux/drivers/media/dvb/frontends/sp8870.h b/linux/drivers/media/dvb/frontends/sp8870.h index f3b555dbc..93afbb969 100644 --- a/linux/drivers/media/dvb/frontends/sp8870.h +++ b/linux/drivers/media/dvb/frontends/sp8870.h @@ -31,10 +31,6 @@ struct sp8870_config /* the demodulator's i2c address */ u8 demod_address; - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); - /* request firmware for device */ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); }; -- cgit v1.2.3 From 14db495e904eb1d7698b02e28cd00dacf569aa66 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:10 +0100 Subject: Convert tda1004x to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Add i2c gate control function. Remove europa specific hack (moved into appropriate card driver) Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/tda1004x.c | 47 +++++++++------------------- linux/drivers/media/dvb/frontends/tda1004x.h | 5 --- 2 files changed, 15 insertions(+), 37 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/tda1004x.c b/linux/drivers/media/dvb/frontends/tda1004x.c index b83dafa4e..d9abce753 100644 --- a/linux/drivers/media/dvb/frontends/tda1004x.c +++ b/linux/drivers/media/dvb/frontends/tda1004x.c @@ -600,13 +600,6 @@ static int tda10045_init(struct dvb_frontend* fe) tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0); // wake up the ADC - // Init the PLL - if (state->config->pll_init) { - tda1004x_enable_tuner_i2c(state); - state->config->pll_init(fe); - tda1004x_disable_tuner_i2c(state); - } - // tda setup tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer tda1004x_write_mask(state, TDA1004X_AUTO, 8, 0); // select HP stream @@ -635,16 +628,6 @@ static int tda10046_init(struct dvb_frontend* fe) return -EIO; } - // Init the tuner PLL - if (state->config->pll_init) { - tda1004x_enable_tuner_i2c(state); - if (state->config->pll_init(fe)) { - printk(KERN_ERR "tda1004x: pll init failed\n"); - return -EIO; - } - tda1004x_disable_tuner_i2c(state); - } - // tda setup tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer tda1004x_write_byteI(state, TDA1004X_AUTO, 0x87); // 100 ppm crystal, select HP stream @@ -712,12 +695,10 @@ static int tda1004x_set_fe(struct dvb_frontend* fe, } // set frequency - tda1004x_enable_tuner_i2c(state); - if (state->config->pll_set(fe, fe_params)) { - printk(KERN_ERR "tda1004x: pll set failed\n"); - return -EIO; + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, fe_params); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); } - tda1004x_disable_tuner_i2c(state); // Hardcoded to use auto as much as possible on the TDA10045 as it // is very unreliable if AUTO mode is _not_ used. @@ -1183,16 +1164,6 @@ static int tda1004x_sleep(struct dvb_frontend* fe) break; case TDA1004X_DEMOD_TDA10046: - if (state->config->pll_sleep != NULL) { - tda1004x_enable_tuner_i2c(state); - state->config->pll_sleep(fe); - if (state->config->if_freq != TDA10046_FREQ_052) { - /* special hack for Philips EUROPA Based boards: - * keep the I2c bridge open for tuner access in analog mode - */ - tda1004x_disable_tuner_i2c(state); - } - } /* set outputs to tristate */ tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0xff); tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1); @@ -1202,6 +1173,17 @@ static int tda1004x_sleep(struct dvb_frontend* fe) return 0; } +static int tda1004x_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct tda1004x_state* state = fe->demodulator_priv; + + if (enable) { + return tda1004x_enable_tuner_i2c(state); + } else { + return tda1004x_disable_tuner_i2c(state); + } +} + static int tda1004x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) { fesettings->min_delay_ms = 800; @@ -1235,6 +1217,7 @@ static struct dvb_frontend_ops tda10045_ops = { .init = tda10045_init, .sleep = tda1004x_sleep, + .i2c_gate_ctrl = tda1004x_i2c_gate_ctrl, .set_frontend = tda1004x_set_fe, .get_frontend = tda1004x_get_fe, diff --git a/linux/drivers/media/dvb/frontends/tda1004x.h b/linux/drivers/media/dvb/frontends/tda1004x.h index cc0c4af64..b877b23ed 100644 --- a/linux/drivers/media/dvb/frontends/tda1004x.h +++ b/linux/drivers/media/dvb/frontends/tda1004x.h @@ -66,11 +66,6 @@ struct tda1004x_config /* AGC configuration */ enum tda10046_agc agc_config; - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - void (*pll_sleep)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); - /* request firmware for device */ /* set this to NULL if the card has a firmware EEPROM */ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); -- cgit v1.2.3 From 436c44011002f45d14d26754b55f979c63551e81 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:10 +0100 Subject: Convert s5h1420 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Add i2c gate control function. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/s5h1420.c | 39 ++++++++++++++++++----------- linux/drivers/media/dvb/frontends/s5h1420.h | 4 --- 2 files changed, 25 insertions(+), 18 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/s5h1420.c b/linux/drivers/media/dvb/frontends/s5h1420.c index d69477596..5dee51154 100644 --- a/linux/drivers/media/dvb/frontends/s5h1420.c +++ b/linux/drivers/media/dvb/frontends/s5h1420.c @@ -584,7 +584,6 @@ static int s5h1420_set_frontend(struct dvb_frontend* fe, struct s5h1420_state* state = fe->demodulator_priv; int frequency_delta; struct dvb_frontend_tune_settings fesettings; - u32 tmp; /* check if we should do a fast-tune */ memcpy(&fesettings.parameters, p, sizeof(struct dvb_frontend_parameters)); @@ -596,10 +595,17 @@ static int s5h1420_set_frontend(struct dvb_frontend* fe, (state->fec_inner == p->u.qpsk.fec_inner) && (state->symbol_rate == p->u.qpsk.symbol_rate)) { - if (state->config->pll_set) { - s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) | 1); - state->config->pll_set(fe, p, &tmp); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } + if (fe->ops->tuner_ops.get_frequency) { + u32 tmp; + fe->ops->tuner_ops.get_frequency(fe, &tmp); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); s5h1420_setfreqoffset(state, p->frequency - tmp); + } else { + s5h1420_setfreqoffset(state, 0); } return 0; } @@ -646,9 +652,9 @@ static int s5h1420_set_frontend(struct dvb_frontend* fe, s5h1420_writereg(state, 0x05, s5h1420_readreg(state, 0x05) | 1); /* set tuner PLL */ - if (state->config->pll_set) { - s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) | 1); - state->config->pll_set(fe, p, &tmp); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); s5h1420_setfreqoffset(state, 0); } @@ -708,6 +714,17 @@ static int s5h1420_get_tune_settings(struct dvb_frontend* fe, return 0; } +static int s5h1420_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct s5h1420_state* state = fe->demodulator_priv; + + if (enable) { + return s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) | 1); + } else { + return s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) & 0xfe); + } +} + static int s5h1420_init (struct dvb_frontend* fe) { struct s5h1420_state* state = fe->demodulator_priv; @@ -717,13 +734,6 @@ static int s5h1420_init (struct dvb_frontend* fe) msleep(10); s5h1420_reset(state); - /* init PLL */ - if (state->config->pll_init) { - s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) | 1); - state->config->pll_init(fe); - s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) & 0xfe); - } - return 0; } @@ -800,6 +810,7 @@ static struct dvb_frontend_ops s5h1420_ops = { .init = s5h1420_init, .sleep = s5h1420_sleep, + .i2c_gate_ctrl = s5h1420_i2c_gate_ctrl, .set_frontend = s5h1420_set_frontend, .get_frontend = s5h1420_get_frontend, diff --git a/linux/drivers/media/dvb/frontends/s5h1420.h b/linux/drivers/media/dvb/frontends/s5h1420.h index 73296f13c..4e39015fa 100644 --- a/linux/drivers/media/dvb/frontends/s5h1420.h +++ b/linux/drivers/media/dvb/frontends/s5h1420.h @@ -32,10 +32,6 @@ struct s5h1420_config /* does the inversion require inversion? */ u8 invert:1; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u32* freqout); }; extern struct dvb_frontend* s5h1420_attach(const struct s5h1420_config* config, -- cgit v1.2.3 From 221ca03a6bb8452d31108a42fb3c711abc7f8a90 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:10 +0100 Subject: Convert l64871 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/l64781.c | 7 ++++--- linux/drivers/media/dvb/frontends/l64781.h | 4 ---- 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/l64781.c b/linux/drivers/media/dvb/frontends/l64781.c index 1c7c91224..fa4a87e00 100644 --- a/linux/drivers/media/dvb/frontends/l64781.c +++ b/linux/drivers/media/dvb/frontends/l64781.c @@ -141,7 +141,10 @@ static int apply_frontend_param (struct dvb_frontend* fe, struct dvb_frontend_pa u8 val0x06; int bw = p->bandwidth - BANDWIDTH_8_MHZ; - state->config->pll_set(fe, param); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, param); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } if (param->inversion != INVERSION_ON && param->inversion != INVERSION_OFF) @@ -463,8 +466,6 @@ static int l64781_init(struct dvb_frontend* fe) /* Everything is two's complement, soft bit and CSI_OUT too */ l64781_writereg (state, 0x1e, 0x09); - if (state->config->pll_init) state->config->pll_init(fe); - /* delay a bit after first init attempt */ if (state->first) { state->first = 0; diff --git a/linux/drivers/media/dvb/frontends/l64781.h b/linux/drivers/media/dvb/frontends/l64781.h index 947f65f87..83b8bc210 100644 --- a/linux/drivers/media/dvb/frontends/l64781.h +++ b/linux/drivers/media/dvb/frontends/l64781.h @@ -29,10 +29,6 @@ struct l64781_config { /* the demodulator's i2c address */ u8 demod_address; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; -- cgit v1.2.3 From 34c1d39d5c2da047879453b7daca02c6dc6ee651 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:10 +0100 Subject: Convert mt352 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointer from structure. Use standardised pllbuf function. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/mt352.c | 7 +++++-- linux/drivers/media/dvb/frontends/mt352.h | 6 ------ 2 files changed, 5 insertions(+), 8 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/mt352.c b/linux/drivers/media/dvb/frontends/mt352.c index aaaec909d..a7a347a3c 100644 --- a/linux/drivers/media/dvb/frontends/mt352.c +++ b/linux/drivers/media/dvb/frontends/mt352.c @@ -286,9 +286,12 @@ static int mt352_set_parameters(struct dvb_frontend* fe, mt352_calc_nominal_rate(state, op->bandwidth, buf+4); mt352_calc_input_freq(state, buf+6); - state->config.pll_set(fe, param, buf+8); - mt352_write(fe, buf, sizeof(buf)); + if (fe->ops->tuner_ops.pllbuf) { + fe->ops->tuner_ops.pllbuf(fe, param, buf+8, 5); + buf[8] <<= 1; + mt352_write(fe, buf, sizeof(buf)); + } if (state->config.no_tuner) { /* start decoding */ mt352_write(fe, fsm_go, 2); diff --git a/linux/drivers/media/dvb/frontends/mt352.h b/linux/drivers/media/dvb/frontends/mt352.h index 03040cd59..9e7ff4b8f 100644 --- a/linux/drivers/media/dvb/frontends/mt352.h +++ b/linux/drivers/media/dvb/frontends/mt352.h @@ -49,12 +49,6 @@ struct mt352_config /* Initialise the demodulator and PLL. Cannot be NULL */ int (*demod_init)(struct dvb_frontend* fe); - - /* PLL setup - fill out the supplied 5 byte buffer with your PLL settings. - * byte0: Set to pll i2c address (nonlinux; left shifted by 1) - * byte1-4: PLL configuration. - */ - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf); }; extern struct dvb_frontend* mt352_attach(const struct mt352_config* config, -- cgit v1.2.3 From a43e69c7722a1ae7357fe55d2f31bb785e7e37d6 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:10 +0100 Subject: Convert tda10021 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Add i2c gate control function. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/tda10021.c | 27 +++++++++++++++++---------- linux/drivers/media/dvb/frontends/tda10021.h | 4 ---- 2 files changed, 17 insertions(+), 14 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/tda10021.c b/linux/drivers/media/dvb/frontends/tda10021.c index 876519586..e49cfa027 100644 --- a/linux/drivers/media/dvb/frontends/tda10021.c +++ b/linux/drivers/media/dvb/frontends/tda10021.c @@ -225,13 +225,6 @@ static int tda10021_init (struct dvb_frontend *fe) //Activate PLL tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef); - - if (state->config->pll_init) { - lock_tuner(state); - state->config->pll_init(fe); - unlock_tuner(state); - } - return 0; } @@ -259,9 +252,10 @@ static int tda10021_set_parameters (struct dvb_frontend *fe, //printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->u.qam.symbol_rate); - lock_tuner(state); - state->config->pll_set(fe, p); - unlock_tuner(state); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } tda10021_set_symbolrate (state, p->u.qam.symbol_rate); tda10021_writereg (state, 0x34, state->pwm); @@ -376,6 +370,18 @@ static int tda10021_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_pa return 0; } +static int tda10021_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct tda10021_state* state = fe->demodulator_priv; + + if (enable) { + lock_tuner(state); + } else { + unlock_tuner(state); + } + return 0; +} + static int tda10021_sleep(struct dvb_frontend* fe) { struct tda10021_state* state = fe->demodulator_priv; @@ -448,6 +454,7 @@ static struct dvb_frontend_ops tda10021_ops = { .init = tda10021_init, .sleep = tda10021_sleep, + .i2c_gate_ctrl = tda10021_i2c_gate_ctrl, .set_frontend = tda10021_set_parameters, .get_frontend = tda10021_get_frontend, diff --git a/linux/drivers/media/dvb/frontends/tda10021.h b/linux/drivers/media/dvb/frontends/tda10021.h index 53be939e8..3fc338e0e 100644 --- a/linux/drivers/media/dvb/frontends/tda10021.h +++ b/linux/drivers/media/dvb/frontends/tda10021.h @@ -30,10 +30,6 @@ struct tda10021_config { /* the demodulator's i2c address */ u8 demod_address; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* tda10021_attach(const struct tda10021_config* config, -- cgit v1.2.3 From 38bffdc81d82ff8ba137f6765c84618f07ac9286 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:10 +0100 Subject: Convert cx24110 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Remove unneeded sleep function completely. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/cx24110.c | 19 ++++++------------- linux/drivers/media/dvb/frontends/cx24110.h | 5 ----- 2 files changed, 6 insertions(+), 18 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/cx24110.c b/linux/drivers/media/dvb/frontends/cx24110.c index a8d0edd02..8d98ffb61 100644 --- a/linux/drivers/media/dvb/frontends/cx24110.c +++ b/linux/drivers/media/dvb/frontends/cx24110.c @@ -366,17 +366,6 @@ static int cx24110_initfe(struct dvb_frontend* fe) cx24110_writereg(state, cx24110_regdata[i].reg, cx24110_regdata[i].data); }; - if (state->config->pll_init) state->config->pll_init(fe); - - return 0; -} - -static int cx24110_sleep(struct dvb_frontend *fe) -{ - struct cx24110_state *state = fe->demodulator_priv; - - if (state->config->pll_sleep) - return state->config->pll_sleep(fe); return 0; } @@ -548,7 +537,12 @@ static int cx24110_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par { struct cx24110_state *state = fe->demodulator_priv; - state->config->pll_set(fe, p); + + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } + cx24110_set_inversion (state, p->inversion); cx24110_set_fec (state, p->u.qpsk.fec_inner); cx24110_set_symbolrate (state, p->u.qpsk.symbol_rate); @@ -651,7 +645,6 @@ static struct dvb_frontend_ops cx24110_ops = { .release = cx24110_release, .init = cx24110_initfe, - .sleep = cx24110_sleep, .set_frontend = cx24110_set_frontend, .get_frontend = cx24110_get_frontend, .read_status = cx24110_read_status, diff --git a/linux/drivers/media/dvb/frontends/cx24110.h b/linux/drivers/media/dvb/frontends/cx24110.h index 609ac642b..b354a64e0 100644 --- a/linux/drivers/media/dvb/frontends/cx24110.h +++ b/linux/drivers/media/dvb/frontends/cx24110.h @@ -31,11 +31,6 @@ struct cx24110_config { /* the demodulator's i2c address */ u8 demod_address; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); - int (*pll_sleep)(struct dvb_frontend* fe); }; extern struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, -- cgit v1.2.3 From 00010492edce8b02fd88596c9a5b1e6402992ae6 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:10 +0100 Subject: Convert dvb_dummy_fe to refactored tuner code From: Andrew de Quincey Make it call the tuner calls - in case someone wants to use it to develop a tuner without a demod. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/dvb_dummy_fe.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c b/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c index 645946a99..78ea4ff03 100644 --- a/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c +++ b/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c @@ -77,6 +77,11 @@ static int dvb_dummy_fe_get_frontend(struct dvb_frontend* fe, struct dvb_fronten static int dvb_dummy_fe_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { + if (fe->ops->tuner_ops->set_params) { + fe->ops->tuner_ops->set_params(fe, p); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } + return 0; } -- cgit v1.2.3 From 6493103733613f60f5426f45903d3a7389283fb8 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:10 +0100 Subject: Convert or51132 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove dvb-pll code - done elsewhere now. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/or51132.c | 12 ++++-------- linux/drivers/media/dvb/frontends/or51132.h | 2 -- 2 files changed, 4 insertions(+), 10 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/or51132.c b/linux/drivers/media/dvb/frontends/or51132.c index c25ec7344..19f75e6ed 100644 --- a/linux/drivers/media/dvb/frontends/or51132.c +++ b/linux/drivers/media/dvb/frontends/or51132.c @@ -331,7 +331,6 @@ static int or51132_set_parameters(struct dvb_frontend* fe, struct dvb_frontend_parameters *param) { int ret; - u8 buf[4]; struct or51132_state* state = fe->demodulator_priv; const struct firmware *fw; const char *fwname; @@ -384,13 +383,10 @@ static int or51132_set_parameters(struct dvb_frontend* fe, or51132_setmode(fe); } - dvb_pll_configure(state->config->pll_desc, buf, - param->frequency, 0); - dprintk("set_parameters tuner bytes: 0x%02x 0x%02x " - "0x%02x 0x%02x\n",buf[0],buf[1],buf[2],buf[3]); - if (i2c_writebytes(state, state->config->pll_address, buf, 4)) - printk(KERN_WARNING "or51132: set_parameters error " - "writing to tuner\n"); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, param); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } /* Set to current mode */ or51132_setmode(fe); diff --git a/linux/drivers/media/dvb/frontends/or51132.h b/linux/drivers/media/dvb/frontends/or51132.h index 622cdd183..89658883a 100644 --- a/linux/drivers/media/dvb/frontends/or51132.h +++ b/linux/drivers/media/dvb/frontends/or51132.h @@ -29,8 +29,6 @@ struct or51132_config { /* The demodulator's i2c address */ u8 demod_address; - u8 pll_address; - struct dvb_pll_desc *pll_desc; /* Need to set device param for start_dma */ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); -- cgit v1.2.3 From 9e0f7fefa77a40b756221e8ad5af80d9418e50d7 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:10 +0100 Subject: Convert nxt200x to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Standardise the calls used to retrieve tuner data. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/nxt200x.c | 16 +++++++++------- linux/drivers/media/dvb/frontends/nxt200x.h | 4 ---- 2 files changed, 9 insertions(+), 11 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/nxt200x.c b/linux/drivers/media/dvb/frontends/nxt200x.c index 9e3535394..809ffd40f 100644 --- a/linux/drivers/media/dvb/frontends/nxt200x.c +++ b/linux/drivers/media/dvb/frontends/nxt200x.c @@ -333,17 +333,17 @@ static int nxt200x_writetuner (struct nxt200x_state* state, u8* data) dprintk("%s\n", __FUNCTION__); - dprintk("Tuner Bytes: %02X %02X %02X %02X\n", data[0], data[1], data[2], data[3]); + dprintk("Tuner Bytes: %02X %02X %02X %02X\n", data[1], data[2], data[3], data[4]); /* if NXT2004, write directly to tuner. if NXT2002, write through NXT chip. * direct write is required for Philips TUV1236D and ALPS TDHU2 */ switch (state->demod_chip) { case NXT2004: - if (i2c_writebytes(state, state->config->pll_address, data, 4)) + if (i2c_writebytes(state, data[0], data+1, 4)) printk(KERN_WARNING "nxt200x: error writing to tuner\n"); /* wait until we have a lock */ while (count < 20) { - i2c_readbytes(state, state->config->pll_address, &buf, 1); + i2c_readbytes(state, data[0], &buf, 1); if (buf & 0x40) return 0; msleep(100); @@ -361,10 +361,10 @@ static int nxt200x_writetuner (struct nxt200x_state* state, u8* data) nxt200x_writebytes(state, 0x34, &buf, 1); /* write actual tuner bytes */ - nxt200x_writebytes(state, 0x36, data, 4); + nxt200x_writebytes(state, 0x36, data+1, 4); /* set tuner i2c address */ - buf = state->config->pll_address; + buf = data[0] << 1; nxt200x_writebytes(state, 0x35, &buf, 1); /* write UC Opmode to begin transfer */ @@ -534,7 +534,7 @@ static int nxt200x_setup_frontend_parameters (struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { struct nxt200x_state* state = fe->demodulator_priv; - u8 buf[4]; + u8 buf[5]; /* stop the micro first */ nxt200x_microcontroller_stop(state); @@ -548,7 +548,9 @@ static int nxt200x_setup_frontend_parameters (struct dvb_frontend* fe, } /* get tuning information */ - dvb_pll_configure(state->config->pll_desc, buf, p->frequency, 0); + if (fe->ops->tuner_ops.pllbuf) { + fe->ops->tuner_ops.pllbuf(fe, p, buf, 5); + } /* set additional params */ switch (p->u.vsb.modulation) { diff --git a/linux/drivers/media/dvb/frontends/nxt200x.h b/linux/drivers/media/dvb/frontends/nxt200x.h index 1d9d70bc3..34d617358 100644 --- a/linux/drivers/media/dvb/frontends/nxt200x.h +++ b/linux/drivers/media/dvb/frontends/nxt200x.h @@ -38,10 +38,6 @@ struct nxt200x_config /* the demodulator's i2c address */ u8 demod_address; - /* tuner information */ - u8 pll_address; - struct dvb_pll_desc *pll_desc; - /* used to set pll input */ int (*set_pll_input)(u8* buf, int input); -- cgit v1.2.3 From ffb483d0d47968d903cfe9027501b43d63a88ddb Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:11 +0100 Subject: Convert nxt6000 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Add i2c gate control function. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/nxt6000.c | 25 ++++++++++++++++--------- linux/drivers/media/dvb/frontends/nxt6000.h | 4 ---- 2 files changed, 16 insertions(+), 13 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/nxt6000.c b/linux/drivers/media/dvb/frontends/nxt6000.c index a16eeba00..bca83266a 100644 --- a/linux/drivers/media/dvb/frontends/nxt6000.c +++ b/linux/drivers/media/dvb/frontends/nxt6000.c @@ -207,12 +207,6 @@ static void nxt6000_setup(struct dvb_frontend* fe) nxt6000_writereg(state, SUB_DIAG_MODE_SEL, 0); nxt6000_writereg(state, TS_FORMAT, 0); - - if (state->config->pll_init) { - nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01); /* open i2c bus switch */ - state->config->pll_init(fe); - nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00); /* close i2c bus switch */ - } } static void nxt6000_dump_status(struct nxt6000_state *state) @@ -469,9 +463,10 @@ static int nxt6000_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par struct nxt6000_state* state = fe->demodulator_priv; int result; - nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01); /* open i2c bus switch */ - state->config->pll_set(fe, param); - nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00); /* close i2c bus switch */ + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, param); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } if ((result = nxt6000_set_bandwidth(state, param->u.ofdm.bandwidth)) < 0) return result; @@ -532,6 +527,17 @@ static int nxt6000_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_fron return 0; } +static int nxt6000_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct nxt6000_state* state = fe->demodulator_priv; + + if (enable) { + return nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01); + } else { + return nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00); + } +} + static struct dvb_frontend_ops nxt6000_ops; struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, @@ -584,6 +590,7 @@ static struct dvb_frontend_ops nxt6000_ops = { .release = nxt6000_release, .init = nxt6000_init, + .i2c_gate_ctrl = nxt6000_i2c_gate_ctrl, .get_tune_settings = nxt6000_fe_get_tune_settings, diff --git a/linux/drivers/media/dvb/frontends/nxt6000.h b/linux/drivers/media/dvb/frontends/nxt6000.h index b7d9bead3..117031d11 100644 --- a/linux/drivers/media/dvb/frontends/nxt6000.h +++ b/linux/drivers/media/dvb/frontends/nxt6000.h @@ -31,10 +31,6 @@ struct nxt6000_config /* should clock inversion be used? */ u8 clock_inversion:1; - - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; extern struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, -- cgit v1.2.3 From b6c785b318edd67bd77b5c8d3748f827a7935f60 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:11 +0100 Subject: Convert zl10353 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Use standardised pllbuf call. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/zl10353.c | 8 +++++--- linux/drivers/media/dvb/frontends/zl10353.h | 5 ----- 2 files changed, 5 insertions(+), 8 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/zl10353.c b/linux/drivers/media/dvb/frontends/zl10353.c index 10bb871fa..c81b8902c 100644 --- a/linux/drivers/media/dvb/frontends/zl10353.c +++ b/linux/drivers/media/dvb/frontends/zl10353.c @@ -129,7 +129,6 @@ static int zl10353_sleep(struct dvb_frontend *fe) static int zl10353_set_parameters(struct dvb_frontend *fe, struct dvb_frontend_parameters *param) { - struct zl10353_state *state = fe->demodulator_priv; u8 pllbuf[6] = { 0x67 }; /* These settings set "auto-everything" and start the FSM. */ @@ -146,8 +145,11 @@ static int zl10353_set_parameters(struct dvb_frontend *fe, zl10353_single_write(fe, 0x66, 0xE9); zl10353_single_write(fe, 0x62, 0x0A); - state->config.pll_set(fe, param, pllbuf + 1); - zl10353_write(fe, pllbuf, sizeof(pllbuf)); + if (fe->ops->tuner_ops.pllbuf) { + fe->ops->tuner_ops.pllbuf(fe, param, pllbuf+1, 5); + pllbuf[1] <<= 1; + zl10353_write(fe, pllbuf, sizeof(pllbuf)); + } zl10353_single_write(fe, 0x70, 0x01); udelay(250); diff --git a/linux/drivers/media/dvb/frontends/zl10353.h b/linux/drivers/media/dvb/frontends/zl10353.h index 5cc4ae718..63c36ec02 100644 --- a/linux/drivers/media/dvb/frontends/zl10353.h +++ b/linux/drivers/media/dvb/frontends/zl10353.h @@ -28,11 +28,6 @@ struct zl10353_config { /* demodulator's I2C address */ u8 demod_address; - - /* function which configures the PLL buffer (for secondary I2C - * connected tuner) or tunes the PLL (for direct connected tuner) */ - int (*pll_set)(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params, u8 *pllbuf); }; extern struct dvb_frontend* zl10353_attach(const struct zl10353_config *config, -- cgit v1.2.3 From e498804a03d7eb9e55b9ba2004df3a91906b0456 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:11 +0100 Subject: Convert cx24123 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/cx24123.c | 3 --- linux/drivers/media/dvb/frontends/cx24123.h | 4 ---- 2 files changed, 7 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/cx24123.c b/linux/drivers/media/dvb/frontends/cx24123.c index 56280cd58..89b3a6c63 100644 --- a/linux/drivers/media/dvb/frontends/cx24123.c +++ b/linux/drivers/media/dvb/frontends/cx24123.c @@ -698,9 +698,6 @@ static int cx24123_initfe(struct dvb_frontend* fe) for (i = 0; i < sizeof(cx24123_regdata) / sizeof(cx24123_regdata[0]); i++) cx24123_writereg(state, cx24123_regdata[i].reg, cx24123_regdata[i].data); - if (state->config->pll_init) - state->config->pll_init(fe); - /* Configure the LNB for 14V */ if (state->config->use_isl6421) cx24123_writelnbreg(state, 0x0, 0x2a); diff --git a/linux/drivers/media/dvb/frontends/cx24123.h b/linux/drivers/media/dvb/frontends/cx24123.h index 0c922b5e9..7ec4ee856 100644 --- a/linux/drivers/media/dvb/frontends/cx24123.h +++ b/linux/drivers/media/dvb/frontends/cx24123.h @@ -35,10 +35,6 @@ struct cx24123_config */ int use_isl6421; - /* PLL maintenance */ - int (*pll_init)(struct dvb_frontend* fe); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); - /* Need to set device param for start_dma */ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); -- cgit v1.2.3 From ff2297269dc60818ddbbf8e48bc4800c0eed9519 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:11 +0100 Subject: Convert dib3000* to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Remove unneeded tuner calls. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/dib3000.h | 4 ---- linux/drivers/media/dvb/frontends/dib3000mb.c | 8 +++----- linux/drivers/media/dvb/frontends/dib3000mc.c | 8 +++----- 3 files changed, 6 insertions(+), 14 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/dib3000.h b/linux/drivers/media/dvb/frontends/dib3000.h index 2d5475b5c..ec927628d 100644 --- a/linux/drivers/media/dvb/frontends/dib3000.h +++ b/linux/drivers/media/dvb/frontends/dib3000.h @@ -30,10 +30,6 @@ struct dib3000_config { /* the demodulator's i2c address */ u8 demod_address; - - /* PLL maintenance and the i2c address of the PLL */ - int (*pll_init)(struct dvb_frontend *fe); - int (*pll_set)(struct dvb_frontend *fe, struct dvb_frontend_parameters* params); }; struct dib_fe_xfer_ops diff --git a/linux/drivers/media/dvb/frontends/dib3000mb.c b/linux/drivers/media/dvb/frontends/dib3000mb.c index ae589adb1..f2f8071ad 100644 --- a/linux/drivers/media/dvb/frontends/dib3000mb.c +++ b/linux/drivers/media/dvb/frontends/dib3000mb.c @@ -60,8 +60,9 @@ static int dib3000mb_set_frontend(struct dvb_frontend* fe, fe_code_rate_t fe_cr = FEC_NONE; int search_state, seq; - if (tuner && state->config.pll_set) { - state->config.pll_set(fe, fep); + if (tuner && fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, fep); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); deb_setf("bandwidth: "); switch (ofdm->bandwidth) { @@ -386,9 +387,6 @@ static int dib3000mb_fe_init(struct dvb_frontend* fe, int mobile_mode) wr(DIB3000MB_REG_DATA_IN_DIVERSITY, DIB3000MB_DATA_DIVERSITY_IN_OFF); - if (state->config.pll_init) - state->config.pll_init(fe); - return 0; } diff --git a/linux/drivers/media/dvb/frontends/dib3000mc.c b/linux/drivers/media/dvb/frontends/dib3000mc.c index 3b303dbb6..68a443b3d 100644 --- a/linux/drivers/media/dvb/frontends/dib3000mc.c +++ b/linux/drivers/media/dvb/frontends/dib3000mc.c @@ -462,8 +462,9 @@ static int dib3000mc_set_frontend(struct dvb_frontend* fe, int search_state,auto_val; u16 val; - if (tuner && state->config.pll_set) { /* initial call from dvb */ - state->config.pll_set(fe,fep); + if (tuner && fe->ops->tuner_ops.set_params) { /* initial call from dvb */ + fe->ops->tuner_ops.set_params(fe, fep); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); state->last_tuned_freq = fep->frequency; // if (!scanboost) { @@ -642,9 +643,6 @@ static int dib3000mc_fe_init(struct dvb_frontend* fe, int mobile_mode) set_or(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_DIV_IN_OFF); - if (state->config.pll_init) - state->config.pll_init(fe); - deb_info("init end\n"); return 0; } -- cgit v1.2.3 From 2d1d22049e38a7b369d113ec29ec44d387236c5a Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:11 +0100 Subject: Convert lgdt330x to refactored tuner code From: Andrew de Quincey Convert to tuner_ops calls. Remove pll function pointers from structure. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/lgdt330x.c | 6 ++++-- linux/drivers/media/dvb/frontends/lgdt330x.h | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/lgdt330x.c b/linux/drivers/media/dvb/frontends/lgdt330x.c index 25ea3a838..cf1b0fefe 100644 --- a/linux/drivers/media/dvb/frontends/lgdt330x.c +++ b/linux/drivers/media/dvb/frontends/lgdt330x.c @@ -400,8 +400,10 @@ static int lgdt330x_set_parameters(struct dvb_frontend* fe, } /* Tune to the specified frequency */ - if (state->config->pll_set) - state->config->pll_set(fe, param); + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, param); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } /* Keep track of the new frequency */ /* FIXME this is the wrong way to do this... */ diff --git a/linux/drivers/media/dvb/frontends/lgdt330x.h b/linux/drivers/media/dvb/frontends/lgdt330x.h index 2a6529ccc..bad903c6f 100644 --- a/linux/drivers/media/dvb/frontends/lgdt330x.h +++ b/linux/drivers/media/dvb/frontends/lgdt330x.h @@ -43,7 +43,6 @@ struct lgdt330x_config /* PLL interface */ int (*pll_rf_set) (struct dvb_frontend* fe, int index); - int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); /* Need to set device param for start_dma */ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); -- cgit v1.2.3 From 5963aaf6c15a377623fb24e82b5dbdd558617aad Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:11 +0100 Subject: Convert bsbe1/bsru6 to refactored tuner code From: Andrew de Quincey Convert to tuner_ops call API. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/bsbe1.h | 4 ++-- linux/drivers/media/dvb/frontends/bsru6.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/bsbe1.h b/linux/drivers/media/dvb/frontends/bsbe1.h index 78573b22a..6b52d5abf 100644 --- a/linux/drivers/media/dvb/frontends/bsbe1.h +++ b/linux/drivers/media/dvb/frontends/bsbe1.h @@ -89,12 +89,13 @@ static int alps_bsbe1_set_symbol_rate(struct dvb_frontend* fe, u32 srate, u32 ra return 0; } -static int alps_bsbe1_pll_set(struct dvb_frontend* fe, struct i2c_adapter *i2c, struct dvb_frontend_parameters* params) +static int alps_bsbe1_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { int ret; u8 data[4]; u32 div; struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + struct i2c_adapter *i2c = fe->tuner_priv; if ((params->frequency < 950000) || (params->frequency > 2150000)) return -EINVAL; @@ -117,7 +118,6 @@ static struct stv0299_config alps_bsbe1_config = { .skip_reinit = 0, .min_delay_ms = 100, .set_symbol_rate = alps_bsbe1_set_symbol_rate, - .pll_set = alps_bsbe1_pll_set, }; #endif diff --git a/linux/drivers/media/dvb/frontends/bsru6.h b/linux/drivers/media/dvb/frontends/bsru6.h index 2a5366ce7..5d13c5e4d 100644 --- a/linux/drivers/media/dvb/frontends/bsru6.h +++ b/linux/drivers/media/dvb/frontends/bsru6.h @@ -101,11 +101,12 @@ static int alps_bsru6_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ra return 0; } -static int alps_bsru6_pll_set(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dvb_frontend_parameters *params) +static int alps_bsru6_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { u8 buf[4]; u32 div; struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; + struct i2c_adapter *i2c = fe->tuner_priv; if ((params->frequency < 950000) || (params->frequency > 2150000)) return -EINVAL; @@ -134,7 +135,6 @@ static struct stv0299_config alps_bsru6_config = { .volt13_op0_op1 = STV0299_VOLT13_OP1, .min_delay_ms = 100, .set_symbol_rate = alps_bsru6_set_symbol_rate, - .pll_set = alps_bsru6_pll_set, }; #endif -- cgit v1.2.3 From 125957dd28e6d92d67e7830f296679a0ee18c073 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:11 +0100 Subject: Convert pluto2 to refactored tuner code From: Andrew de Quincey Rename pll calls to appropriate tuner calls. Remove pll functions from demod structures. Hook tuner call into tuner_ops. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/pluto2/pluto2.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/pluto2/pluto2.c b/linux/drivers/media/dvb/pluto2/pluto2.c index c9afad038..5cc609d98 100644 --- a/linux/drivers/media/dvb/pluto2/pluto2.c +++ b/linux/drivers/media/dvb/pluto2/pluto2.c @@ -424,8 +424,8 @@ static inline u32 divide(u32 numerator, u32 denominator) } /* LG Innotek TDTE-E001P (Infineon TUA6034) */ -static int lg_tdtpe001p_pll_set(struct dvb_frontend *fe, - struct dvb_frontend_parameters *p) +static int lg_tdtpe001p_tuner_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) { struct pluto *pluto = frontend_to_pluto(fe); struct i2c_msg msg; @@ -473,6 +473,8 @@ static int lg_tdtpe001p_pll_set(struct dvb_frontend *fe, msg.buf = buf; msg.len = sizeof(buf); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); ret = i2c_transfer(&pluto->i2c_adap, &msg, 1); if (ret < 0) return ret; @@ -497,8 +499,6 @@ static struct tda1004x_config pluto2_fe_config __devinitdata = { .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .pll_set = lg_tdtpe001p_pll_set, - .pll_sleep = NULL, .request_firmware = pluto2_request_firmware, }; @@ -511,6 +511,7 @@ static int __devinit frontend_init(struct pluto *pluto) dev_err(&pluto->pdev->dev, "could not attach frontend\n"); return -ENODEV; } + pluto->fe->ops->tuner_ops.set_params = lg_tdtpe001p_tuner_set_params; ret = dvb_register_frontend(&pluto->dvb_adapter, pluto->fe); if (ret < 0) { -- cgit v1.2.3 From 23d5a271ff3463d69ffaf4114d523e1075ad8886 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:11 +0100 Subject: Convert ttpci/av7110 to refactored tuner code From: Andrew de Quincey Rename pll calls to appropriate tuner calls. Remove pll functions from demod structures. Hook tuner call into tuner_ops. Add pll gate control calls where appropriate. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/ttpci/av7110.c | 60 +++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 19 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/ttpci/av7110.c b/linux/drivers/media/dvb/ttpci/av7110.c index 6dedb175a..9ede95193 100644 --- a/linux/drivers/media/dvb/ttpci/av7110.c +++ b/linux/drivers/media/dvb/ttpci/av7110.c @@ -1552,7 +1552,7 @@ static int get_firmware(struct av7110* av7110) #endif -static int alps_bsrv2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int alps_bsrv2_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { struct av7110* av7110 = (struct av7110*) fe->dvb->priv; u8 pwr = 0; @@ -1575,6 +1575,8 @@ static int alps_bsrv2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_param // NOTE: since we're using a prescaler of 2, we set the // divisor frequency to 62.5kHz and divide by 125 above + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) return -EIO; return 0; @@ -1584,10 +1586,9 @@ static struct ves1x93_config alps_bsrv2_config = { .demod_address = 0x08, .xin = 90100000UL, .invert_pwm = 0, - .pll_set = alps_bsrv2_pll_set, }; -static int alps_tdbe2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int alps_tdbe2_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { struct av7110* av7110 = fe->dvb->priv; u32 div; @@ -1601,6 +1602,8 @@ static int alps_tdbe2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_param data[2] = 0x85 | ((div >> 10) & 0x60); data[3] = (params->frequency < 174000000 ? 0x88 : params->frequency < 470000000 ? 0x84 : 0x81); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) return -EIO; return 0; @@ -1611,14 +1614,12 @@ static struct ves1820_config alps_tdbe2_config = { .xin = 57840000UL, .invert = 1, .selagc = VES1820_SELAGC_SIGNAMPERR, - .pll_set = alps_tdbe2_pll_set, }; -static int grundig_29504_451_pll_set(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params) +static int grundig_29504_451_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { struct av7110* av7110 = fe->dvb->priv; u32 div; @@ -1631,6 +1632,8 @@ static int grundig_29504_451_pll_set(struct dvb_frontend* fe, data[2] = 0x8e; data[3] = 0x00; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) return -EIO; return 0; @@ -1638,13 +1641,11 @@ static int grundig_29504_451_pll_set(struct dvb_frontend* fe, static struct tda8083_config grundig_29504_451_config = { .demod_address = 0x68, - .pll_set = grundig_29504_451_pll_set, }; -static int philips_cd1516_pll_set(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params) +static int philips_cd1516_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { struct av7110* av7110 = fe->dvb->priv; u32 div; @@ -1659,6 +1660,8 @@ static int philips_cd1516_pll_set(struct dvb_frontend* fe, data[2] = 0x8e; data[3] = (f < 174000000 ? 0xa1 : f < 470000000 ? 0x92 : 0x34); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) return -EIO; return 0; @@ -1669,12 +1672,11 @@ static struct ves1820_config philips_cd1516_config = { .xin = 57840000UL, .invert = 1, .selagc = VES1820_SELAGC_SIGNAMPERR, - .pll_set = philips_cd1516_pll_set, }; -static int alps_tdlb7_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int alps_tdlb7_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { struct av7110* av7110 = fe->dvb->priv; u32 div, pwr; @@ -1693,6 +1695,8 @@ static int alps_tdlb7_pll_set(struct dvb_frontend* fe, struct dvb_frontend_param data[2] = 0x85; data[3] = pwr << 6; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) return -EIO; return 0; @@ -1708,7 +1712,6 @@ static int alps_tdlb7_request_firmware(struct dvb_frontend* fe, const struct fir static struct sp8870_config alps_tdlb7_config = { .demod_address = 0x71, - .pll_set = alps_tdlb7_pll_set, .request_firmware = alps_tdlb7_request_firmware, }; @@ -1806,7 +1809,7 @@ static u8 nexusca_stv0297_inittab[] = { 0xff, 0xff, }; -static int nexusca_stv0297_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int nexusca_stv0297_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { struct av7110* av7110 = fe->dvb->priv; u32 div; @@ -1832,7 +1835,8 @@ static int nexusca_stv0297_pll_set(struct dvb_frontend* fe, struct dvb_frontend_ else return -EINVAL; - stv0297_enable_plli2c(fe); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) { printk("nexusca: pll transfer failed!\n"); return -EIO; @@ -1840,8 +1844,8 @@ static int nexusca_stv0297_pll_set(struct dvb_frontend* fe, struct dvb_frontend_ // wait for PLL lock for(i = 0; i < 20; i++) { - - stv0297_enable_plli2c(fe); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&av7110->i2c_adap, &readmsg, 1) == 1) if (data[0] & 0x40) break; msleep(10); @@ -1855,12 +1859,11 @@ static struct stv0297_config nexusca_stv0297_config = { .demod_address = 0x1C, .inittab = nexusca_stv0297_inittab, .invert = 1, - .pll_set = nexusca_stv0297_pll_set, }; -static int grundig_29504_401_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int grundig_29504_401_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { struct av7110* av7110 = (struct av7110*) fe->dvb->priv; u32 div; @@ -1887,13 +1890,14 @@ static int grundig_29504_401_pll_set(struct dvb_frontend* fe, struct dvb_fronten data[2] = ((div >> 10) & 0x60) | cfg; data[3] = (cpump << 6) | band_select; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) return -EIO; return 0; } static struct l64781_config grundig_29504_401_config = { .demod_address = 0x55, - .pll_set = grundig_29504_401_pll_set, }; @@ -2079,6 +2083,7 @@ static int frontend_init(struct av7110 *av7110) case 0x0000: // Fujitsu/Siemens DVB-Cable (ves1820/Philips CD1516(??)) av7110->fe = ves1820_attach(&philips_cd1516_config, &av7110->i2c_adap, read_pwm(av7110)); + av7110->fe->ops->tuner_ops.set_params = philips_cd1516_tuner_set_params; break; } @@ -2091,6 +2096,7 @@ static int frontend_init(struct av7110 *av7110) // try the ALPS BSRV2 first of all av7110->fe = ves1x93_attach(&alps_bsrv2_config, &av7110->i2c_adap); if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = alps_bsrv2_tuner_set_params; av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst; av7110->fe->ops->set_tone = av7110_set_tone; @@ -2101,6 +2107,9 @@ static int frontend_init(struct av7110 *av7110) // try the ALPS BSRU6 now av7110->fe = stv0299_attach(&alps_bsru6_config, &av7110->i2c_adap); if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = alps_bsru6_tuner_set_params; + av7110->fe->tuner_priv = &av7110->i2c_adap; + av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst; av7110->fe->ops->set_tone = av7110_set_tone; @@ -2111,6 +2120,7 @@ static int frontend_init(struct av7110 *av7110) // Try the grundig 29504-451 av7110->fe = tda8083_attach(&grundig_29504_451_config, &av7110->i2c_adap); if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = grundig_29504_451_tuner_set_params; av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst; av7110->fe->ops->set_tone = av7110_set_tone; @@ -2124,11 +2134,13 @@ static int frontend_init(struct av7110 *av7110) /* Siemens DVB-C (full-length card) VES1820/Philips CD1516 */ av7110->fe = ves1820_attach(&philips_cd1516_config, &av7110->i2c_adap, read_pwm(av7110)); + av7110->fe->ops->tuner_ops.set_params = philips_cd1516_tuner_set_params; break; case 0x0003: /* Hauppauge DVB-C 2.1 VES1820/ALPS TDBE2 */ av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); + av7110->fe->ops->tuner_ops.set_params = alps_tdbe2_tuner_set_params; break; } break; @@ -2137,17 +2149,20 @@ static int frontend_init(struct av7110 *av7110) // ALPS TDLB7 av7110->fe = sp8870_attach(&alps_tdlb7_config, &av7110->i2c_adap); + av7110->fe->ops->tuner_ops.set_params = alps_tdlb7_tuner_set_params; break; case 0x0002: // Hauppauge/TT DVB-C premium rev2.X av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); + av7110->fe->ops->tuner_ops.set_params = alps_tdbe2_tuner_set_params; break; case 0x0004: // Galaxis DVB-S rev1.3 /* ALPS BSRV2 */ av7110->fe = ves1x93_attach(&alps_bsrv2_config, &av7110->i2c_adap); if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = alps_bsrv2_tuner_set_params; av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst; av7110->fe->ops->set_tone = av7110_set_tone; @@ -2159,6 +2174,7 @@ static int frontend_init(struct av7110 *av7110) /* Grundig 29504-451 */ av7110->fe = tda8083_attach(&grundig_29504_451_config, &av7110->i2c_adap); if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = grundig_29504_451_tuner_set_params; av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst; av7110->fe->ops->set_tone = av7110_set_tone; @@ -2169,12 +2185,15 @@ static int frontend_init(struct av7110 *av7110) case 0x0008: // Hauppauge/TT DVB-T av7110->fe = l64781_attach(&grundig_29504_401_config, &av7110->i2c_adap); + av7110->fe->ops->tuner_ops.set_params = grundig_29504_401_tuner_set_params; break; case 0x000A: // Hauppauge/TT Nexus-CA rev1.X av7110->fe = stv0297_attach(&nexusca_stv0297_config, &av7110->i2c_adap); if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = nexusca_stv0297_tuner_set_params; + /* set TDA9819 into DVB mode */ saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9198 pin9(STD) saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9198 pin30(VIF) @@ -2189,6 +2208,9 @@ static int frontend_init(struct av7110 *av7110) /* ALPS BSBE1 */ av7110->fe = stv0299_attach(&alps_bsbe1_config, &av7110->i2c_adap); if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = alps_bsbe1_tuner_set_params; + av7110->fe->tuner_priv = &av7110->i2c_adap; + if (lnbp21_init(av7110->fe, &av7110->i2c_adap, 0, 0)) { printk("dvb-ttpci: LNBP21 not found!\n"); if (av7110->fe->ops->release) -- cgit v1.2.3 From 723f41841a035658485bd3e1275a8054a815b0e3 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:11 +0100 Subject: Convert ttpci/budget to refactored tuner code From: Andrew de Quincey Rename pll calls to appropriate tuner calls. Remove pll functions from demod structures. Hook tuner call into tuner_ops. Add pll gate control calls where appropriate. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/ttpci/budget.c | 43 ++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 13 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/ttpci/budget.c b/linux/drivers/media/dvb/ttpci/budget.c index c23c02d95..a231975cb 100644 --- a/linux/drivers/media/dvb/ttpci/budget.c +++ b/linux/drivers/media/dvb/ttpci/budget.c @@ -186,7 +186,7 @@ static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t m return 0; } -static int alps_bsrv2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int alps_bsrv2_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct budget* budget = (struct budget*) fe->dvb->priv; u8 pwr = 0; @@ -209,6 +209,8 @@ static int alps_bsrv2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_param // NOTE: since we're using a prescaler of 2, we set the // divisor frequency to 62.5kHz and divide by 125 above + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; return 0; } @@ -218,10 +220,9 @@ static struct ves1x93_config alps_bsrv2_config = .demod_address = 0x08, .xin = 90100000UL, .invert_pwm = 0, - .pll_set = alps_bsrv2_pll_set, }; -static int alps_tdbe2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int alps_tdbe2_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct budget* budget = (struct budget*) fe->dvb->priv; u32 div; @@ -235,6 +236,8 @@ static int alps_tdbe2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_param data[2] = 0x85 | ((div >> 10) & 0x60); data[3] = (params->frequency < 174000000 ? 0x88 : params->frequency < 470000000 ? 0x84 : 0x81); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; return 0; } @@ -244,10 +247,9 @@ static struct ves1820_config alps_tdbe2_config = { .xin = 57840000UL, .invert = 1, .selagc = VES1820_SELAGC_SIGNAMPERR, - .pll_set = alps_tdbe2_pll_set, }; -static int grundig_29504_401_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int grundig_29504_401_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct budget* budget = (struct budget*) fe->dvb->priv; u32 div; @@ -274,16 +276,17 @@ static int grundig_29504_401_pll_set(struct dvb_frontend* fe, struct dvb_fronten data[2] = ((div >> 10) & 0x60) | cfg; data[3] = (cpump << 6) | band_select; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; return 0; } static struct l64781_config grundig_29504_401_config = { .demod_address = 0x55, - .pll_set = grundig_29504_401_pll_set, }; -static int grundig_29504_451_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int grundig_29504_451_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct budget* budget = (struct budget*) fe->dvb->priv; u32 div; @@ -296,16 +299,17 @@ static int grundig_29504_451_pll_set(struct dvb_frontend* fe, struct dvb_fronten data[2] = 0x8e; data[3] = 0x00; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; return 0; } static struct tda8083_config grundig_29504_451_config = { .demod_address = 0x68, - .pll_set = grundig_29504_451_pll_set, }; -static int s5h1420_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u32* freqout) +static int s5h1420_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct budget* budget = (struct budget*) fe->dvb->priv; u32 div; @@ -326,16 +330,16 @@ static int s5h1420_pll_set(struct dvb_frontend* fe, struct dvb_frontend_paramete else data[3] = 0xc0; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; - *freqout = div * 1000; return 0; } static struct s5h1420_config s5h1420_config = { .demod_address = 0x53, .invert = 1, - .pll_set = s5h1420_pll_set, }; static u8 read_pwm(struct budget* budget) @@ -359,6 +363,7 @@ static void frontend_init(struct budget *budget) // try the ALPS BSRV2 first of all budget->dvb_frontend = ves1x93_attach(&alps_bsrv2_config, &budget->i2c_adap); if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = alps_bsrv2_tuner_set_params; budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_diseqc_send_master_cmd; budget->dvb_frontend->ops->diseqc_send_burst = budget_diseqc_send_burst; budget->dvb_frontend->ops->set_tone = budget_set_tone; @@ -368,6 +373,8 @@ static void frontend_init(struct budget *budget) // try the ALPS BSRU6 now budget->dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget->i2c_adap); if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_diseqc_send_master_cmd; budget->dvb_frontend->ops->diseqc_send_burst = budget_diseqc_send_burst; budget->dvb_frontend->ops->set_tone = budget_set_tone; @@ -378,18 +385,26 @@ static void frontend_init(struct budget *budget) case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659)) budget->dvb_frontend = ves1820_attach(&alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget)); - if (budget->dvb_frontend) break; + if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = alps_tdbe2_tuner_set_params; + break; + } break; case 0x1005: // Hauppauge/TT Nova-T budget (L64781/Grundig 29504-401(tsa5060)) budget->dvb_frontend = l64781_attach(&grundig_29504_401_config, &budget->i2c_adap); - if (budget->dvb_frontend) break; + if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = grundig_29504_401_tuner_set_params; + break; + } break; case 0x4f60: // Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/ALPS BSRU6(tsa5059)) budget->dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget->i2c_adap); if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; budget->dvb_frontend->ops->set_voltage = siemens_budget_set_voltage; budget->dvb_frontend->ops->dishnetwork_send_legacy_command = NULL; } @@ -398,6 +413,7 @@ static void frontend_init(struct budget *budget) case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI rev GR (tda8083/Grundig 29504-451(tsa5522)) budget->dvb_frontend = tda8083_attach(&grundig_29504_451_config, &budget->i2c_adap); if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = grundig_29504_451_tuner_set_params; budget->dvb_frontend->ops->set_voltage = siemens_budget_set_voltage; budget->dvb_frontend->ops->dishnetwork_send_legacy_command = NULL; } @@ -406,6 +422,7 @@ static void frontend_init(struct budget *budget) case 0x1016: // Hauppauge/TT Nova-S SE (samsung s5h1420/????(tda8260)) budget->dvb_frontend = s5h1420_attach(&s5h1420_config, &budget->i2c_adap); if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = s5h1420_tuner_set_params; if (lnbp21_init(budget->dvb_frontend, &budget->i2c_adap, 0, 0)) { printk("%s: No LNBP21 found!\n", __FUNCTION__); goto error_out; -- cgit v1.2.3 From c8b6337e94e58893dc2a840cbf837164f5a082fc Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:11 +0100 Subject: Convert ttpci/budget-ci to refactored tuner code From: Andrew de Quincey Rename pll calls to appropriate tuner calls. Remove pll functions from demod structures. Hook tuner call into tuner_ops. Add pll gate control calls where appropriate. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/ttpci/budget-ci.c | 46 +++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 14 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/ttpci/budget-ci.c b/linux/drivers/media/dvb/ttpci/budget-ci.c index e64a609cf..ed610e56b 100644 --- a/linux/drivers/media/dvb/ttpci/budget-ci.c +++ b/linux/drivers/media/dvb/ttpci/budget-ci.c @@ -620,10 +620,10 @@ static int philips_su1278_tt_set_symbol_rate(struct dvb_frontend *fe, u32 srate, return 0; } -static int philips_su1278_tt_pll_set(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct dvb_frontend_parameters *params) +static int philips_su1278_tt_tuner_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) { + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; u32 div; u8 buf[4]; struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; @@ -649,7 +649,9 @@ static int philips_su1278_tt_pll_set(struct dvb_frontend *fe, else if (params->frequency < 2150000) buf[3] |= 0xC0; - if (i2c_transfer(i2c, &msg, 1) != 1) + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &msg, 1) != 1) return -EIO; return 0; } @@ -665,12 +667,11 @@ static struct stv0299_config philips_su1278_tt_config = { .volt13_op0_op1 = STV0299_VOLT13_OP1, .min_delay_ms = 50, .set_symbol_rate = philips_su1278_tt_set_symbol_rate, - .pll_set = philips_su1278_tt_pll_set, }; -static int philips_tdm1316l_pll_init(struct dvb_frontend *fe) +static int philips_tdm1316l_tuner_init(struct dvb_frontend *fe) { struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; @@ -679,6 +680,8 @@ static int philips_tdm1316l_pll_init(struct dvb_frontend *fe) sizeof(td1316_init) }; // setup PLL configuration + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) return -EIO; msleep(1); @@ -687,14 +690,18 @@ static int philips_tdm1316l_pll_init(struct dvb_frontend *fe) tuner_msg.addr = 0x65; tuner_msg.buf = disable_mc44BC374c; tuner_msg.len = sizeof(disable_mc44BC374c); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) { + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1); } return 0; } -static int philips_tdm1316l_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; u8 tuner_buf[4]; @@ -770,6 +777,8 @@ static int philips_tdm1316l_pll_set(struct dvb_frontend *fe, struct dvb_frontend tuner_buf[2] = 0xca; tuner_buf[3] = (cp << 5) | (filter << 3) | band; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) return -EIO; @@ -793,13 +802,10 @@ static struct tda1004x_config philips_tdm1316l_config = { .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .pll_init = philips_tdm1316l_pll_init, - .pll_set = philips_tdm1316l_pll_set, - .pll_sleep = NULL, .request_firmware = philips_tdm1316l_request_firmware, }; -static int dvbc_philips_tdm1316l_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; u8 tuner_buf[5]; @@ -857,13 +863,15 @@ static int dvbc_philips_tdm1316l_pll_set(struct dvb_frontend *fe, struct dvb_fro tuner_buf[3] = (cp << 5) | (filter << 3) | band; tuner_buf[4] = 0x80; - stv0297_enable_plli2c(fe); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) return -EIO; msleep(50); - stv0297_enable_plli2c(fe); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) return -EIO; @@ -969,7 +977,6 @@ static struct stv0297_config dvbc_philips_tdm1316l_config = { .demod_address = 0x1c, .inittab = dvbc_philips_tdm1316l_inittab, .invert = 0, - .pll_set = dvbc_philips_tdm1316l_pll_set, }; @@ -982,6 +989,8 @@ static void frontend_init(struct budget_ci *budget_ci) budget_ci->budget.dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops->tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; break; } break; @@ -990,6 +999,7 @@ static void frontend_init(struct budget_ci *budget_ci) budget_ci->budget.dvb_frontend = stv0299_attach(&philips_su1278_tt_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops->tuner_ops.set_params = philips_su1278_tt_tuner_set_params; break; } break; @@ -999,6 +1009,7 @@ static void frontend_init(struct budget_ci *budget_ci) budget_ci->budget.dvb_frontend = stv0297_attach(&dvbc_philips_tdm1316l_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops->tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params; break; } break; @@ -1008,6 +1019,8 @@ static void frontend_init(struct budget_ci *budget_ci) budget_ci->budget.dvb_frontend = tda10045_attach(&philips_tdm1316l_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops->tuner_ops.init = philips_tdm1316l_tuner_init; + budget_ci->budget.dvb_frontend->ops->tuner_ops.set_params = philips_tdm1316l_tuner_set_params; break; } break; @@ -1017,6 +1030,8 @@ static void frontend_init(struct budget_ci *budget_ci) budget_ci->budget.dvb_frontend = tda10046_attach(&philips_tdm1316l_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops->tuner_ops.init = philips_tdm1316l_tuner_init; + budget_ci->budget.dvb_frontend->ops->tuner_ops.set_params = philips_tdm1316l_tuner_set_params; break; } break; @@ -1024,6 +1039,9 @@ static void frontend_init(struct budget_ci *budget_ci) case 0x1017: // TT S-1500 PCI budget_ci->budget.dvb_frontend = stv0299_attach(&alps_bsbe1_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops->tuner_ops.set_params = alps_bsbe1_tuner_set_params; + budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; + budget_ci->budget.dvb_frontend->ops->dishnetwork_send_legacy_command = NULL; if (lnbp21_init(budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0)) { printk("%s: No LNBP21 found!\n", __FUNCTION__); -- cgit v1.2.3 From c909af6ccd04556d842621075cc9e7c278066095 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:11 +0100 Subject: Convert ttpci/budget-av to refactored tuner code From: Andrew de Quincey Rename pll calls to appropriate tuner calls. Remove pll functions from demod structures. Hook tuner call into tuner_ops. Add pll gate control calls where appropriate. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/ttpci/budget-av.c | 85 +++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 27 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/ttpci/budget-av.c b/linux/drivers/media/dvb/ttpci/budget-av.c index dc78aadf0..9bd069e4e 100644 --- a/linux/drivers/media/dvb/ttpci/budget-av.c +++ b/linux/drivers/media/dvb/ttpci/budget-av.c @@ -472,12 +472,12 @@ static int philips_su1278_ty_ci_set_symbol_rate(struct dvb_frontend *fe, u32 sra return 0; } -static int philips_su1278_ty_ci_pll_set(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct dvb_frontend_parameters *params) +static int philips_su1278_ty_ci_tuner_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) { u32 div; u8 buf[4]; + struct budget *budget = (struct budget *) fe->dvb->priv; struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; if ((params->frequency < 950000) || (params->frequency > 2150000)) @@ -501,7 +501,9 @@ static int philips_su1278_ty_ci_pll_set(struct dvb_frontend *fe, else if (params->frequency < 2150000) buf[3] |= 0xC0; - if (i2c_transfer(i2c, &msg, 1) != 1) + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) return -EIO; return 0; } @@ -509,9 +511,8 @@ static int philips_su1278_ty_ci_pll_set(struct dvb_frontend *fe, #define MIN2(a,b) ((a) < (b) ? (a) : (b)) #define MIN3(a,b,c) MIN2(MIN2(a,b),c) -static int philips_su1278sh2_tua6100_pll_set(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct dvb_frontend_parameters *params) +static int philips_su1278sh2_tua6100_tuner_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) { u8 reg0 [2] = { 0x00, 0x00 }; u8 reg1 [4] = { 0x01, 0x00, 0x00, 0x00 }; @@ -521,6 +522,7 @@ static int philips_su1278sh2_tua6100_pll_set(struct dvb_frontend *fe, int R, A, N, P, M; struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = NULL,.len = 0 }; int freq = params->frequency; + struct budget *budget = (struct budget *) fe->dvb->priv; first_ZF = (freq) / 1000; @@ -620,21 +622,25 @@ static int philips_su1278sh2_tua6100_pll_set(struct dvb_frontend *fe, reg0[1] |= 0x03; /* already enabled - do not reenable i2c repeater or TX fails */ + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); msg.buf = reg0; msg.len = sizeof(reg0); - if (i2c_transfer(i2c, &msg, 1) != 1) + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) return -EIO; - stv0299_enable_plli2c(fe); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); msg.buf = reg1; msg.len = sizeof(reg1); - if (i2c_transfer(i2c, &msg, 1) != 1) + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) return -EIO; - stv0299_enable_plli2c(fe); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); msg.buf = reg2; msg.len = sizeof(reg2); - if (i2c_transfer(i2c, &msg, 1) != 1) + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) return -EIO; return 0; @@ -692,7 +698,6 @@ static struct stv0299_config typhoon_config = { .volt13_op0_op1 = STV0299_VOLT13_OP0, .min_delay_ms = 100, .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, - .pll_set = philips_su1278_ty_ci_pll_set, }; @@ -706,7 +711,6 @@ static struct stv0299_config cinergy_1200s_config = { .volt13_op0_op1 = STV0299_VOLT13_OP0, .min_delay_ms = 100, .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, - .pll_set = philips_su1278_ty_ci_pll_set, }; static struct stv0299_config cinergy_1200s_1894_0010_config = { @@ -719,10 +723,9 @@ static struct stv0299_config cinergy_1200s_1894_0010_config = { .volt13_op0_op1 = STV0299_VOLT13_OP0, .min_delay_ms = 100, .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, - .pll_set = philips_su1278sh2_tua6100_pll_set, }; -static int philips_cu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_cu1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct budget *budget = (struct budget *) fe->dvb->priv; u8 buf[4]; @@ -738,6 +741,8 @@ static int philips_cu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_p buf[3] = (params->frequency < 150000000 ? 0x01 : params->frequency < 445000000 ? 0x02 : 0x04); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) return -EIO; return 0; @@ -745,19 +750,20 @@ static int philips_cu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_p static struct tda10021_config philips_cu1216_config = { .demod_address = 0x0c, - .pll_set = philips_cu1216_pll_set, }; -static int philips_tu1216_pll_init(struct dvb_frontend *fe) +static int philips_tu1216_tuner_init(struct dvb_frontend *fe) { struct budget *budget = (struct budget *) fe->dvb->priv; static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab }; struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; // setup PLL configuration + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) return -EIO; msleep(1); @@ -765,7 +771,7 @@ static int philips_tu1216_pll_init(struct dvb_frontend *fe) return 0; } -static int philips_tu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_tu1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct budget *budget = (struct budget *) fe->dvb->priv; u8 tuner_buf[4]; @@ -839,6 +845,8 @@ static int philips_tu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_p tuner_buf[2] = 0xca; tuner_buf[3] = (cp << 5) | (filter << 3) | band; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) return -EIO; @@ -862,9 +870,6 @@ static struct tda1004x_config philips_tu1216_config = { .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .pll_init = philips_tu1216_pll_init, - .pll_set = philips_tu1216_pll_set, - .pll_sleep = NULL, .request_firmware = philips_tu1216_request_firmware, }; @@ -911,13 +916,13 @@ static u8 philips_sd1878_inittab[] = { 0xff, 0xff }; -static int philips_sd1878_tda8261_pll_set(struct dvb_frontend *fe, - struct i2c_adapter *i2c, - struct dvb_frontend_parameters *params) +static int philips_sd1878_tda8261_tuner_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) { u8 buf[4]; int rc; struct i2c_msg tuner_msg = {.addr=0x60,.flags=0,.buf=buf,.len=sizeof(buf)}; + struct budget *budget = (struct budget *) fe->dvb->priv; if((params->frequency < 950000) || (params->frequency > 2150000)) return -EINVAL; @@ -926,7 +931,9 @@ static int philips_sd1878_tda8261_pll_set(struct dvb_frontend *fe, params->frequency, 0); if(rc < 0) return rc; - if(i2c_transfer(i2c, &tuner_msg, 1) != 1) + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); + if(i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) return -EIO; return 0; @@ -977,7 +984,6 @@ static struct stv0299_config philips_sd1878_config = { .volt13_op0_op1 = STV0299_VOLT13_OP0, .min_delay_ms = 100, .set_symbol_rate = philips_sd1878_ci_set_symbol_rate, - .pll_set = philips_sd1878_tda8261_pll_set, }; static u8 read_pwm(struct budget_av *budget_av) @@ -1035,9 +1041,15 @@ static void frontend_init(struct budget_av *budget_av) if (saa->pci->subsystem_vendor == 0x1894) { fe = stv0299_attach(&cinergy_1200s_1894_0010_config, &budget_av->budget.i2c_adap); + if (fe) { + fe->ops->tuner_ops.set_params = philips_su1278sh2_tua6100_tuner_set_params; + } } else { fe = stv0299_attach(&typhoon_config, &budget_av->budget.i2c_adap); + if (fe) { + fe->ops->tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; + } } break; @@ -1047,17 +1059,26 @@ static void frontend_init(struct budget_av *budget_av) case SUBID_DVBS_EASYWATCH: fe = stv0299_attach(&philips_sd1878_config, &budget_av->budget.i2c_adap); + if (fe) { + fe->ops->tuner_ops.set_params = philips_sd1878_tda8261_tuner_set_params; + } break; case SUBID_DVBS_KNC1_PLUS: case SUBID_DVBS_TYPHOON: fe = stv0299_attach(&typhoon_config, &budget_av->budget.i2c_adap); + if (fe) { + fe->ops->tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; + } break; case SUBID_DVBS_CINERGY1200: fe = stv0299_attach(&cinergy_1200s_config, &budget_av->budget.i2c_adap); + if (fe) { + fe->ops->tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; + } break; case SUBID_DVBC_KNC1: @@ -1065,18 +1086,28 @@ static void frontend_init(struct budget_av *budget_av) fe = tda10021_attach(&philips_cu1216_config, &budget_av->budget.i2c_adap, read_pwm(budget_av)); + if (fe) { + fe->ops->tuner_ops.set_params = philips_cu1216_tuner_set_params; + } break; case SUBID_DVBT_KNC1: case SUBID_DVBT_KNC1_PLUS: fe = tda10046_attach(&philips_tu1216_config, &budget_av->budget.i2c_adap); + if (fe) { + fe->ops->tuner_ops.init = philips_tu1216_tuner_init; + fe->ops->tuner_ops.set_params = philips_tu1216_tuner_set_params; + } break; case SUBID_DVBC_CINERGY1200: fe = tda10021_attach(&philips_cu1216_config, &budget_av->budget.i2c_adap, read_pwm(budget_av)); + if (fe) { + fe->ops->tuner_ops.set_params = philips_cu1216_tuner_set_params; + } break; case SUBID_DVBT_CINERGY1200: -- cgit v1.2.3 From 5b88163932499c2b28e2b6a67df10c0557bc179e Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:11 +0100 Subject: Convert ttpci/budget-patch to refactored tuner code From: Andrew de Quincey Rename pll calls to appropriate tuner calls. Remove pll functions from demod structures. Hook tuner call into tuner_ops. Add pll gate control calls where appropriate. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/ttpci/budget-patch.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/ttpci/budget-patch.c b/linux/drivers/media/dvb/ttpci/budget-patch.c index 25e40fc1f..843d6245a 100644 --- a/linux/drivers/media/dvb/ttpci/budget-patch.c +++ b/linux/drivers/media/dvb/ttpci/budget-patch.c @@ -258,7 +258,7 @@ static int budget_patch_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_c return 0; } -static int alps_bsrv2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int alps_bsrv2_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; u8 pwr = 0; @@ -281,7 +281,10 @@ static int alps_bsrv2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_param // NOTE: since we're using a prescaler of 2, we set the // divisor frequency to 62.5kHz and divide by 125 above - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) + return -EIO; return 0; } @@ -289,10 +292,9 @@ static struct ves1x93_config alps_bsrv2_config = { .demod_address = 0x08, .xin = 90100000UL, .invert_pwm = 0, - .pll_set = alps_bsrv2_pll_set, }; -static int grundig_29504_451_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int grundig_29504_451_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; u32 div; @@ -305,13 +307,15 @@ static int grundig_29504_451_pll_set(struct dvb_frontend* fe, struct dvb_fronten data[2] = 0x8e; data[3] = 0x00; - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) + return -EIO; return 0; } static struct tda8083_config grundig_29504_451_config = { .demod_address = 0x68, - .pll_set = grundig_29504_451_pll_set, }; static void frontend_init(struct budget_patch* budget) @@ -323,6 +327,7 @@ static void frontend_init(struct budget_patch* budget) // try the ALPS BSRV2 first of all budget->dvb_frontend = ves1x93_attach(&alps_bsrv2_config, &budget->i2c_adap); if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = alps_bsrv2_tuner_set_params; budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_patch_diseqc_send_master_cmd; budget->dvb_frontend->ops->diseqc_send_burst = budget_patch_diseqc_send_burst; budget->dvb_frontend->ops->set_tone = budget_patch_set_tone; @@ -332,6 +337,9 @@ static void frontend_init(struct budget_patch* budget) // try the ALPS BSRU6 now budget->dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget->i2c_adap); if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_diseqc_send_master_cmd; budget->dvb_frontend->ops->diseqc_send_burst = budget_diseqc_send_burst; budget->dvb_frontend->ops->set_tone = budget_set_tone; @@ -341,6 +349,7 @@ static void frontend_init(struct budget_patch* budget) // Try the grundig 29504-451 budget->dvb_frontend = tda8083_attach(&grundig_29504_451_config, &budget->i2c_adap); if (budget->dvb_frontend) { + budget->dvb_frontend->ops->tuner_ops.set_params = grundig_29504_451_tuner_set_params; budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_diseqc_send_master_cmd; budget->dvb_frontend->ops->diseqc_send_burst = budget_diseqc_send_burst; budget->dvb_frontend->ops->set_tone = budget_set_tone; -- cgit v1.2.3 From a64635eb8a6fb3b447060edc2df5b1df6d650685 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:11 +0100 Subject: Convert saa7134-dvb to refactored tuner code From: Andrew de Quincey Rename pll calls to appropriate tuner calls. Remove pll functions from demod structures. Hook tuner call into tuner_ops. Attach dvb-pll where possible. Add pll gate control calls where appropriate. Move europa specific code from tda1104x into card driver using ops overrides (this is how it should ebe done instead of hacking card specific code into the demod driver). Signed-off-by: Andrew de Quincey --- linux/drivers/media/video/saa7134/saa7134-dvb.c | 221 +++++++++++++++--------- linux/drivers/media/video/saa7134/saa7134.h | 1 + 2 files changed, 144 insertions(+), 78 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index 985ef0422..cb0ffa50f 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -133,9 +133,9 @@ static int mt352_aver777_init(struct dvb_frontend* fe) return 0; } -static int mt352_pinnacle_pll_set(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params, - u8* pllbuf) +static int mt352_pinnacle_tuner_pllbuf(struct dvb_frontend* fe, + struct dvb_frontend_parameters* params, + u8* pllbuf, int buf_len) { u8 off[] = { 0x00, 0xf1}; u8 on[] = { 0x00, 0x71}; @@ -144,6 +144,9 @@ static int mt352_pinnacle_pll_set(struct dvb_frontend* fe, struct saa7134_dev *dev = fe->dvb->priv; struct v4l2_frequency f; + if (buf_len < 5) + return -EINVAL; + /* set frequency (mt2050) */ f.tuner = 0; f.type = V4L2_TUNER_DIGITAL_TV; @@ -157,21 +160,24 @@ static int mt352_pinnacle_pll_set(struct dvb_frontend* fe, /* mt352 setup */ mt352_pinnacle_init(fe); - pllbuf[0] = 0xc2; + pllbuf[0] = 0x61; pllbuf[1] = 0x00; pllbuf[2] = 0x00; pllbuf[3] = 0x80; pllbuf[4] = 0x00; - return 0; + return 5; } -static int mt352_aver777_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, u8* pllbuf) +static int mt352_aver777_tuner_pllbuf(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, u8* pllbuf, int buf_len) { - pllbuf[0] = 0xc2; + if (buf_len < 5) + return -EINVAL; + + pllbuf[0] = 0x61; dvb_pll_configure(&dvb_pll_philips_td1316, pllbuf+1, params->frequency, params->u.ofdm.bandwidth); - return 0; + return 5; } static struct mt352_config pinnacle_300i = { @@ -180,13 +186,11 @@ static struct mt352_config pinnacle_300i = { .if2 = 36150, .no_tuner = 1, .demod_init = mt352_pinnacle_init, - .pll_set = mt352_pinnacle_pll_set, }; static struct mt352_config avermedia_777 = { .demod_address = 0xf, .demod_init = mt352_aver777_init, - .pll_set = mt352_aver777_pll_set, }; #endif @@ -269,6 +273,8 @@ static int philips_tda6651_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb_ tuner_buf[2] = 0xca; tuner_buf[3] = (cp << 5) | (filter << 3) | band; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) return -EIO; msleep(1); @@ -282,6 +288,8 @@ static int philips_tda6651_pll_init(u8 addr, struct dvb_frontend *fe) struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; /* setup PLL configuration */ + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) return -EIO; msleep(1); @@ -291,12 +299,12 @@ static int philips_tda6651_pll_init(u8 addr, struct dvb_frontend *fe) /* ------------------------------------------------------------------ */ -static int philips_tu1216_pll_60_init(struct dvb_frontend *fe) +static int philips_tu1216_tuner_60_init(struct dvb_frontend *fe) { return philips_tda6651_pll_init(0x60, fe); } -static int philips_tu1216_pll_60_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_tu1216_tuner_60_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { return philips_tda6651_pll_set(0x60, fe, params); } @@ -316,20 +324,17 @@ static struct tda1004x_config philips_tu1216_60_config = { .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .pll_init = philips_tu1216_pll_60_init, - .pll_set = philips_tu1216_pll_60_set, - .pll_sleep = NULL, .request_firmware = philips_tu1216_request_firmware, }; /* ------------------------------------------------------------------ */ -static int philips_tu1216_pll_61_init(struct dvb_frontend *fe) +static int philips_tu1216_tuner_61_init(struct dvb_frontend *fe) { return philips_tda6651_pll_init(0x61, fe); } -static int philips_tu1216_pll_61_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_tu1216_tuner_61_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { return philips_tda6651_pll_set(0x61, fe, params); } @@ -342,21 +347,20 @@ static struct tda1004x_config philips_tu1216_61_config = { .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .pll_init = philips_tu1216_pll_61_init, - .pll_set = philips_tu1216_pll_61_set, - .pll_sleep = NULL, .request_firmware = philips_tu1216_request_firmware, }; /* ------------------------------------------------------------------ */ -static int philips_europa_pll_init(struct dvb_frontend *fe) +static int philips_europa_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab }; struct i2c_msg init_msg = {.addr = 0x61,.flags = 0,.buf = msg,.len = sizeof(msg) }; /* setup PLL configuration */ + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1) return -EIO; msleep(1); @@ -366,18 +370,20 @@ static int philips_europa_pll_init(struct dvb_frontend *fe) init_msg.len = 0x02; msg[0] = 0x00; msg[1] = 0x40; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1) return -EIO; return 0; } -static int philips_td1316_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_td1316_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { return philips_tda6651_pll_set(0x61, fe, params); } -static void philips_europa_analog(struct dvb_frontend *fe) +static int philips_europa_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; /* this message actually turns the tuner back to analog mode */ @@ -392,7 +398,20 @@ static void philips_europa_analog(struct dvb_frontend *fe) analog_msg.len = 0x02; msg[0] = 0x00; msg[1] = 0x14; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &analog_msg, 1); + return 0; +} + +static int philips_europa_demod_sleep(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + + if (dev->original_demod_sleep) + dev->original_demod_sleep(fe); + fe->ops->i2c_gate_ctrl(fe, 1); + return 0; } static struct tda1004x_config philips_europa_config = { @@ -403,21 +422,20 @@ static struct tda1004x_config philips_europa_config = { .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_IFO_AUTO_POS, .if_freq = TDA10046_FREQ_052, - .pll_init = philips_europa_pll_init, - .pll_set = philips_td1316_pll_set, - .pll_sleep = philips_europa_analog, .request_firmware = NULL, }; /* ------------------------------------------------------------------ */ -static int philips_fmd1216_pll_init(struct dvb_frontend *fe) +static int philips_fmd1216_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; /* this message is to set up ATC and ALC */ static u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0xa0 }; struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) }; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) return -EIO; msleep(1); @@ -425,22 +443,27 @@ static int philips_fmd1216_pll_init(struct dvb_frontend *fe) return 0; } -static void philips_fmd1216_analog(struct dvb_frontend *fe) +static int philips_fmd1216_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; /* this message actually turns the tuner back to analog mode */ static u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0x60 }; struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) }; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); msleep(1); fmd1216_init[2] = 0x86; fmd1216_init[3] = 0x54; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); msleep(1); + return 0; } -static int philips_fmd1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_fmd1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct saa7134_dev *dev = fe->dvb->priv; u8 tuner_buf[4]; @@ -517,6 +540,8 @@ static int philips_fmd1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_ tuner_buf[2] = 0x80 | (cp << 6) | (mode << 3) | 4; tuner_buf[3] = 0x40 | band; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) return -EIO; return 0; @@ -529,9 +554,6 @@ static struct tda1004x_config medion_cardbus = { .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_IFO_AUTO_NEG, .if_freq = TDA10046_FREQ_3613, - .pll_init = philips_fmd1216_pll_init, - .pll_set = philips_fmd1216_pll_set, - .pll_sleep = philips_fmd1216_analog, .request_firmware = NULL, }; @@ -579,12 +601,12 @@ static struct tda827x_data tda827x_dvbt[] = { { .lomax = 0, .spd = 0, .bs = 0, .bp = 0, .cp = 0, .gc3 = 0, .div1p5 = 0} }; -static int philips_tda827x_pll_init(struct dvb_frontend *fe) +static int philips_tda827x_tuner_init(struct dvb_frontend *fe) { return 0; } -static int philips_tda827x_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_tda827x_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct saa7134_dev *dev = fe->dvb->priv; u8 tuner_buf[14]; @@ -631,6 +653,8 @@ static int philips_tda827x_pll_set(struct dvb_frontend *fe, struct dvb_frontend_ tuner_buf[13] = 0x40; tuner_msg.len = 14; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) return -EIO; @@ -639,18 +663,23 @@ static int philips_tda827x_pll_set(struct dvb_frontend *fe, struct dvb_frontend_ tuner_buf[0] = 0x30; tuner_buf[1] = 0x50 + tda827x_dvbt[i].cp; tuner_msg.len = 2; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); return 0; } -static void philips_tda827x_pll_sleep(struct dvb_frontend *fe) +static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; static u8 tda827x_sleep[] = { 0x30, 0xd0}; struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tda827x_sleep, .len = sizeof(tda827x_sleep) }; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); + return 0; } static struct tda1004x_config tda827x_lifeview_config = { @@ -660,9 +689,6 @@ static struct tda1004x_config tda827x_lifeview_config = { .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_TDA827X, .if_freq = TDA10046_FREQ_045, - .pll_init = philips_tda827x_pll_init, - .pll_set = philips_tda827x_pll_set, - .pll_sleep = philips_tda827x_pll_sleep, .request_firmware = NULL, }; @@ -754,6 +780,8 @@ static int philips_tda827xa_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb tuner_buf[12] = 0x00; tuner_buf[13] = 0x39; // lpsel msg.len = 14; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) return -EIO; @@ -761,10 +789,14 @@ static int philips_tda827xa_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb msg.len = 2; reg2[0] = 0x60; reg2[1] = 0x3c; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &msg, 1); reg2[0] = 0xa0; reg2[1] = 0x40; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &msg, 1); msleep(2); @@ -772,36 +804,43 @@ static int philips_tda827xa_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb reg2[0] = 0x30; reg2[1] = 0x10 + tda827xa_dvbt[i].scr; msg.len = 2; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &msg, 1); msleep(550); reg2[0] = 0x50; reg2[1] = 0x4f + (tda827xa_dvbt[i].gc3 << 4); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &msg, 1); return 0; } -static void philips_tda827xa_pll_sleep(u8 addr, struct dvb_frontend *fe) +static int philips_tda827xa_tuner_sleep(u8 addr, struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; static u8 tda827xa_sleep[] = { 0x30, 0x90}; struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tda827xa_sleep, .len = sizeof(tda827xa_sleep) }; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); - + return 0; } /* ------------------------------------------------------------------ */ -static int philips_tiger_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_tiger_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { int ret; struct saa7134_dev *dev = fe->dvb->priv; static u8 tda8290_close[] = { 0x21, 0xc0}; static u8 tda8290_open[] = { 0x21, 0x80}; struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2}; + /* close tda8290 i2c bridge */ tda8290_msg.buf = tda8290_close; ret = i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1); @@ -817,7 +856,7 @@ static int philips_tiger_pll_set(struct dvb_frontend *fe, struct dvb_frontend_pa return ret; } -static int philips_tiger_dvb_mode(struct dvb_frontend *fe) +static int philips_tiger_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; static u8 data[] = { 0x3c, 0x33, 0x6a}; @@ -828,14 +867,15 @@ static int philips_tiger_dvb_mode(struct dvb_frontend *fe) return 0; } -static void philips_tiger_analog_mode(struct dvb_frontend *fe) +static int philips_tiger_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; static u8 data[] = { 0x3c, 0x33, 0x68}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); - philips_tda827xa_pll_sleep( 0x61, fe); + philips_tda827xa_tuner_sleep( 0x61, fe); + return 0; } static struct tda1004x_config philips_tiger_config = { @@ -845,15 +885,12 @@ static struct tda1004x_config philips_tiger_config = { .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_TDA827X, .if_freq = TDA10046_FREQ_045, - .pll_init = philips_tiger_dvb_mode, - .pll_set = philips_tiger_pll_set, - .pll_sleep = philips_tiger_analog_mode, .request_firmware = NULL, }; /* ------------------------------------------------------------------ */ -static int lifeview_trio_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int lifeview_trio_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { int ret; @@ -861,16 +898,12 @@ static int lifeview_trio_pll_set(struct dvb_frontend *fe, struct dvb_frontend_pa return ret; } -static int lifeview_trio_dvb_mode(struct dvb_frontend *fe) +static int lifeview_trio_tuner_sleep(struct dvb_frontend *fe) { + philips_tda827xa_tuner_sleep(0x60, fe); return 0; } -static void lifeview_trio_analog_mode(struct dvb_frontend *fe) -{ - philips_tda827xa_pll_sleep(0x60, fe); -} - static struct tda1004x_config lifeview_trio_config = { .demod_address = 0x09, .invert = 1, @@ -878,15 +911,12 @@ static struct tda1004x_config lifeview_trio_config = { .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_TDA827X_GPL, .if_freq = TDA10046_FREQ_045, - .pll_init = lifeview_trio_dvb_mode, - .pll_set = lifeview_trio_pll_set, - .pll_sleep = lifeview_trio_analog_mode, .request_firmware = NULL, }; /* ------------------------------------------------------------------ */ -static int ads_duo_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int ads_duo_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { int ret; @@ -894,7 +924,7 @@ static int ads_duo_pll_set(struct dvb_frontend *fe, struct dvb_frontend_paramete return ret; } -static int ads_duo_dvb_mode(struct dvb_frontend *fe) +static int ads_duo_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; /* route TDA8275a AGC input to the channel decoder */ @@ -902,12 +932,13 @@ static int ads_duo_dvb_mode(struct dvb_frontend *fe) return 0; } -static void ads_duo_analog_mode(struct dvb_frontend *fe) +static int ads_duo_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; /* route TDA8275a AGC input to the analog IF chip*/ saa_writeb(SAA7134_GPIO_GPSTATUS2, 0x20); - philips_tda827xa_pll_sleep( 0x61, fe); + philips_tda827xa_tuner_sleep( 0x61, fe); + return 0; } static struct tda1004x_config ads_tech_duo_config = { @@ -917,31 +948,24 @@ static struct tda1004x_config ads_tech_duo_config = { .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_TDA827X_GPL, .if_freq = TDA10046_FREQ_045, - .pll_init = ads_duo_dvb_mode, - .pll_set = ads_duo_pll_set, - .pll_sleep = ads_duo_analog_mode, .request_firmware = NULL, }; /* ------------------------------------------------------------------ */ -static int tevion_dvb220rf_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int tevion_dvb220rf_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { int ret; ret = philips_tda827xa_pll_set(0x60, fe, params); return ret; } -static int tevion_dvb220rf_pll_init(struct dvb_frontend *fe) +static int tevion_dvb220rf_tuner_sleep(struct dvb_frontend *fe) { + philips_tda827xa_tuner_sleep( 0x61, fe); return 0; } -static void tevion_dvb220rf_pll_sleep(struct dvb_frontend *fe) -{ - philips_tda827xa_pll_sleep( 0x61, fe); -} - static struct tda1004x_config tevion_dvbt220rf_config = { .demod_address = 0x08, .invert = 1, @@ -949,9 +973,6 @@ static struct tda1004x_config tevion_dvbt220rf_config = { .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_TDA827X, .if_freq = TDA10046_FREQ_045, - .pll_init = tevion_dvb220rf_pll_init, - .pll_set = tevion_dvb220rf_pll_set, - .pll_sleep = tevion_dvb220rf_pll_sleep, .request_firmware = NULL, }; @@ -962,8 +983,6 @@ static struct tda1004x_config tevion_dvbt220rf_config = { #ifdef HAVE_NXT200X static struct nxt200x_config avertvhda180 = { .demod_address = 0x0a, - .pll_address = 0x61, - .pll_desc = &dvb_pll_tdhu2, }; static int nxt200x_set_pll_input(u8 *buf, int input) @@ -977,8 +996,6 @@ static int nxt200x_set_pll_input(u8 *buf, int input) static struct nxt200x_config kworldatsc110 = { .demod_address = 0x0a, - .pll_address = 0x61, - .pll_desc = &dvb_pll_tuv1236d, .set_pll_input = nxt200x_set_pll_input, }; #endif @@ -1004,78 +1021,126 @@ static int dvb_init(struct saa7134_dev *dev) printk("%s: pinnacle 300i dvb setup\n",dev->name); dev->dvb.frontend = mt352_attach(&pinnacle_300i, &dev->i2c_adap); + dev->dvb.frontend->ops->tuner_ops.pllbuf = mt352_pinnacle_tuner_pllbuf; break; case SAA7134_BOARD_AVERMEDIA_777: printk("%s: avertv 777 dvb setup\n",dev->name); dev->dvb.frontend = mt352_attach(&avermedia_777, &dev->i2c_adap); + dev->dvb.frontend->ops->tuner_ops.pllbuf = mt352_aver777_tuner_pllbuf; break; #endif #ifdef HAVE_TDA1004X case SAA7134_BOARD_MD7134: dev->dvb.frontend = tda10046_attach(&medion_cardbus, &dev->i2c_adap); + dev->dvb.frontend->ops->tuner_ops.init = philips_fmd1216_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_fmd1216_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_fmd1216_tuner_set_params; break; case SAA7134_BOARD_PHILIPS_TOUGH: dev->dvb.frontend = tda10046_attach(&philips_tu1216_60_config, &dev->i2c_adap); + dev->dvb.frontend->ops->tuner_ops.init = philips_tu1216_tuner_60_init; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tu1216_tuner_60_set_params; break; case SAA7134_BOARD_FLYDVBTDUO: dev->dvb.frontend = tda10046_attach(&tda827x_lifeview_config, &dev->i2c_adap); + dev->dvb.frontend->ops->tuner_ops.init = philips_tda827x_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_tda827x_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tda827x_tuner_set_params; break; case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS: dev->dvb.frontend = tda10046_attach(&tda827x_lifeview_config, &dev->i2c_adap); + dev->dvb.frontend->ops->tuner_ops.init = philips_tda827x_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_tda827x_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tda827x_tuner_set_params; break; case SAA7134_BOARD_PHILIPS_EUROPA: dev->dvb.frontend = tda10046_attach(&philips_europa_config, &dev->i2c_adap); + dev->original_demod_sleep = dev->dvb.frontend->ops->sleep; + dev->dvb.frontend->ops->sleep = philips_europa_demod_sleep; + dev->dvb.frontend->ops->tuner_ops.init = philips_europa_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_europa_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_td1316_tuner_set_params; break; case SAA7134_BOARD_VIDEOMATE_DVBT_300: dev->dvb.frontend = tda10046_attach(&philips_europa_config, &dev->i2c_adap); + dev->dvb.frontend->ops->tuner_ops.init = philips_europa_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_europa_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_td1316_tuner_set_params; break; case SAA7134_BOARD_VIDEOMATE_DVBT_200: dev->dvb.frontend = tda10046_attach(&philips_tu1216_61_config, &dev->i2c_adap); + dev->dvb.frontend->ops->tuner_ops.init = philips_tu1216_tuner_61_init; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tu1216_tuner_61_set_params; break; case SAA7134_BOARD_PHILIPS_TIGER: dev->dvb.frontend = tda10046_attach(&philips_tiger_config, &dev->i2c_adap); + dev->dvb.frontend->ops->tuner_ops.init = philips_tiger_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_tiger_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tiger_tuner_set_params; break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: dev->dvb.frontend = tda10046_attach(&philips_tiger_config, &dev->i2c_adap); + dev->dvb.frontend->ops->tuner_ops.init = philips_tiger_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_tiger_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tiger_tuner_set_params; break; case SAA7134_BOARD_FLYDVBT_LR301: dev->dvb.frontend = tda10046_attach(&tda827x_lifeview_config, &dev->i2c_adap); + dev->dvb.frontend->ops->tuner_ops.init = philips_tda827x_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_tda827x_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tda827x_tuner_set_params; break; case SAA7134_BOARD_FLYDVB_TRIO: dev->dvb.frontend = tda10046_attach(&lifeview_trio_config, &dev->i2c_adap); + dev->dvb.frontend->ops->tuner_ops.sleep = lifeview_trio_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = lifeview_trio_tuner_set_params; break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: dev->dvb.frontend = tda10046_attach(&ads_tech_duo_config, &dev->i2c_adap); + dev->dvb.frontend->ops->tuner_ops.init = ads_duo_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = ads_duo_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = ads_duo_tuner_set_params; break; case SAA7134_BOARD_TEVION_DVBT_220RF: dev->dvb.frontend = tda10046_attach(&tevion_dvbt220rf_config, &dev->i2c_adap); + dev->dvb.frontend->ops->tuner_ops.sleep = tevion_dvb220rf_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = tevion_dvb220rf_tuner_set_params; break; case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: dev->dvb.frontend = tda10046_attach(&ads_tech_duo_config, &dev->i2c_adap); + dev->dvb.frontend->ops->tuner_ops.init = ads_duo_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = ads_duo_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = ads_duo_tuner_set_params; break; #endif #ifdef HAVE_NXT200X case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180: dev->dvb.frontend = nxt200x_attach(&avertvhda180, &dev->i2c_adap); + if (dev->dvb.frontend) { + dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->i2c_adap, &dvb_pll_tdhu2); + } break; case SAA7134_BOARD_KWORLD_ATSC110: dev->dvb.frontend = nxt200x_attach(&kworldatsc110, &dev->i2c_adap); + if (dev->dvb.frontend) { + dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->i2c_adap, &dvb_pll_tuv1236d); + } break; #endif default: diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index 21562ed12..a53d63505 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -555,6 +555,7 @@ struct saa7134_dev { #ifdef HAVE_VIDEO_BUF_DVB /* SAA7134_MPEG_DVB only */ struct videobuf_dvb dvb; + int (*original_demod_sleep)(struct dvb_frontend* fe); #endif }; -- cgit v1.2.3 From 00089ee063613032c306945cb04599456f6712dd Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:11 +0100 Subject: Convert dvb-ttusb-budget to refactored tuner code From: Andrew de Quincey Rename pll calls to appropriate tuner calls. Remove pll functions from demod structures. Hook tuner call into tuner_ops. Add pll gate control calls where appropriate. Signed-off-by: Andrew de Quincey --- .../media/dvb/ttusb-budget/dvb-ttusb-budget.c | 37 ++++++++++++---------- 1 file changed, 20 insertions(+), 17 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index e9347c42d..f2aa23d76 100644 --- a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -1056,7 +1056,7 @@ static u32 functionality(struct i2c_adapter *adapter) -static int alps_tdmb7_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int alps_tdmb7_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; u8 data[4]; @@ -1076,14 +1076,13 @@ static int alps_tdmb7_pll_set(struct dvb_frontend* fe, struct dvb_frontend_param static struct cx22700_config alps_tdmb7_config = { .demod_address = 0x43, - .pll_set = alps_tdmb7_pll_set, }; -static int philips_tdm1316l_pll_init(struct dvb_frontend* fe) +static int philips_tdm1316l_tuner_init(struct dvb_frontend* fe) { struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; @@ -1105,7 +1104,7 @@ static int philips_tdm1316l_pll_init(struct dvb_frontend* fe) return 0; } -static int philips_tdm1316l_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int philips_tdm1316l_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; u8 tuner_buf[4]; @@ -1184,8 +1183,6 @@ static struct tda1004x_config philips_tdm1316l_config = { .demod_address = 0x8, .invert = 1, .invert_oclk = 0, - .pll_init = philips_tdm1316l_pll_init, - .pll_set = philips_tdm1316l_pll_set, .request_firmware = philips_tdm1316l_request_firmware, }; @@ -1307,7 +1304,7 @@ static int alps_stv0299_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 return 0; } -static int philips_tsa5059_pll_set(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dvb_frontend_parameters *params) +static int philips_tsa5059_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; u8 buf[4]; @@ -1330,7 +1327,7 @@ static int philips_tsa5059_pll_set(struct dvb_frontend *fe, struct i2c_adapter * if (ttusb->revision == TTUSB_REV_2_2) buf[3] |= 0x20; - if (i2c_transfer(i2c, &msg, 1) != 1) + if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1) return -EIO; return 0; @@ -1346,10 +1343,9 @@ static struct stv0299_config alps_stv0299_config = { .volt13_op0_op1 = STV0299_VOLT13_OP1, .min_delay_ms = 100, .set_symbol_rate = alps_stv0299_set_symbol_rate, - .pll_set = philips_tsa5059_pll_set, }; -static int ttusb_novas_grundig_29504_491_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int ttusb_novas_grundig_29504_491_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; u8 buf[4]; @@ -1372,10 +1368,9 @@ static int ttusb_novas_grundig_29504_491_pll_set(struct dvb_frontend *fe, struct static struct tda8083_config ttusb_novas_grundig_29504_491_config = { .demod_address = 0x68, - .pll_set = ttusb_novas_grundig_29504_491_pll_set, }; -static int alps_tdbe2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int alps_tdbe2_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct ttusb* ttusb = fe->dvb->priv; u32 div; @@ -1401,7 +1396,6 @@ static struct ves1820_config alps_tdbe2_config = { .xin = 57840000UL, .invert = 1, .selagc = VES1820_SELAGC_SIGNAMPERR, - .pll_set = alps_tdbe2_pll_set, }; static u8 read_pwm(struct ttusb* ttusb) @@ -1425,6 +1419,8 @@ static void frontend_init(struct ttusb* ttusb) // try the stv0299 based first ttusb->fe = stv0299_attach(&alps_stv0299_config, &ttusb->i2c_adap); if (ttusb->fe != NULL) { + ttusb->fe->ops->tuner_ops.set_params = philips_tsa5059_tuner_set_params; + if(ttusb->revision == TTUSB_REV_2_2) { // ALPS BSBE1 alps_stv0299_config.inittab = alps_bsbe1_inittab; ttusb->fe->ops->set_voltage = lnbp21_set_voltage; @@ -1437,28 +1433,35 @@ static void frontend_init(struct ttusb* ttusb) // Grundig 29504-491 ttusb->fe = tda8083_attach(&ttusb_novas_grundig_29504_491_config, &ttusb->i2c_adap); if (ttusb->fe != NULL) { + ttusb->fe->ops->tuner_ops.set_params = ttusb_novas_grundig_29504_491_tuner_set_params; ttusb->fe->ops->set_voltage = ttusb_set_voltage; break; } - break; case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659)) ttusb->fe = ves1820_attach(&alps_tdbe2_config, &ttusb->i2c_adap, read_pwm(ttusb)); - if (ttusb->fe != NULL) + if (ttusb->fe != NULL) { + ttusb->fe->ops->tuner_ops.set_params = alps_tdbe2_tuner_set_params; break; + } break; case 0x1005: // Hauppauge/TT Nova-USB-t budget (tda10046/Philips td1316(tda6651tt) OR cx22700/ALPS TDMB7(??)) // try the ALPS TDMB7 first ttusb->fe = cx22700_attach(&alps_tdmb7_config, &ttusb->i2c_adap); - if (ttusb->fe != NULL) + if (ttusb->fe != NULL) { + ttusb->fe->ops->tuner_ops.set_params = alps_tdmb7_tuner_set_params; break; + } // Philips td1316 ttusb->fe = tda10046_attach(&philips_tdm1316l_config, &ttusb->i2c_adap); - if (ttusb->fe != NULL) + if (ttusb->fe != NULL) { + ttusb->fe->ops->tuner_ops.init = philips_tdm1316l_tuner_init; + ttusb->fe->ops->tuner_ops.set_params = philips_tdm1316l_tuner_set_params; break; + } break; } -- cgit v1.2.3 From 736aea2b42ae112490327d150bd93d92860f420a Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:11 +0100 Subject: Convert core dvb-usb pll code to refactored tuner code From: Andrew de Quincey Rename pll calls to appropriate tuner calls. Add pll gate control calls where appropriate. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c | 24 +++++++++++++++--------- linux/drivers/media/dvb/dvb-usb/dvb-usb.h | 6 +++--- 2 files changed, 18 insertions(+), 12 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c b/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c index 9b254532a..b067c72f8 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c @@ -46,7 +46,7 @@ int dvb_usb_i2c_exit(struct dvb_usb_device *d) return 0; } -int dvb_usb_pll_init_i2c(struct dvb_frontend *fe) +int dvb_usb_tuner_init_i2c(struct dvb_frontend *fe) { struct dvb_usb_device *d = fe->dvb->priv; struct i2c_msg msg = { .addr = d->pll_addr, .flags = 0, .buf = d->pll_init, .len = 4 }; @@ -63,6 +63,8 @@ int dvb_usb_pll_init_i2c(struct dvb_frontend *fe) deb_pll("pll-buf: %x %x %x %x\n",d->pll_init[0],d->pll_init[1], d->pll_init[2],d->pll_init[3]); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&d->i2c_adap, &msg, 1) != 1) { err("tuner i2c write failed for pll_init."); ret = -EREMOTEIO; @@ -73,38 +75,42 @@ int dvb_usb_pll_init_i2c(struct dvb_frontend *fe) d->tuner_pass_ctrl(fe,0,d->pll_addr); return ret; } -EXPORT_SYMBOL(dvb_usb_pll_init_i2c); +EXPORT_SYMBOL(dvb_usb_tuner_init_i2c); -int dvb_usb_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep, u8 b[5]) +int dvb_usb_tuner_pllbuf(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep, u8 *b, int buf_len) { struct dvb_usb_device *d = fe->dvb->priv; + if (buf_len != 5) + return -EINVAL; if (d->pll_desc == NULL) return 0; deb_pll("pll addr: %x, freq: %d %p\n",d->pll_addr,fep->frequency,d->pll_desc); - b[0] = d->pll_addr << 1; + b[0] = d->pll_addr; dvb_pll_configure(d->pll_desc,&b[1],fep->frequency,fep->u.ofdm.bandwidth); deb_pll("pll-buf: %x %x %x %x %x\n",b[0],b[1],b[2],b[3],b[4]); - return 0; + return 5; } -EXPORT_SYMBOL(dvb_usb_pll_set); +EXPORT_SYMBOL(dvb_usb_tuner_pllbuf); -int dvb_usb_pll_set_i2c(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) +int dvb_usb_tuner_set_frequency_i2c(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) { struct dvb_usb_device *d = fe->dvb->priv; int ret = 0; u8 b[5]; struct i2c_msg msg = { .addr = d->pll_addr, .flags = 0, .buf = &b[1], .len = 4 }; - dvb_usb_pll_set(fe,fep,b); + dvb_usb_tuner_pllbuf(fe,fep,b,5); if (d->tuner_pass_ctrl) d->tuner_pass_ctrl(fe,1,d->pll_addr); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&d->i2c_adap, &msg, 1) != 1) { err("tuner i2c write failed for pll_set."); ret = -EREMOTEIO; @@ -116,4 +122,4 @@ int dvb_usb_pll_set_i2c(struct dvb_frontend *fe, struct dvb_frontend_parameters return ret; } -EXPORT_SYMBOL(dvb_usb_pll_set_i2c); +EXPORT_SYMBOL(dvb_usb_tuner_set_frequency_i2c); diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb.h b/linux/drivers/media/dvb/dvb-usb/dvb-usb.h index b14e842d0..2696ee9d8 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb.h +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb.h @@ -341,9 +341,9 @@ extern int dvb_usb_generic_write(struct dvb_usb_device *, u8 *, u16); extern int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *, u8[], u32 *, int *); /* commonly used pll init and set functions */ -extern int dvb_usb_pll_init_i2c(struct dvb_frontend *); -extern int dvb_usb_pll_set(struct dvb_frontend *, struct dvb_frontend_parameters *, u8[]); -extern int dvb_usb_pll_set_i2c(struct dvb_frontend *, struct dvb_frontend_parameters *); +extern int dvb_usb_tuner_init_i2c(struct dvb_frontend *); +extern int dvb_usb_tuner_pllbuf(struct dvb_frontend *, struct dvb_frontend_parameters *, u8 *buf, int buf_len); +extern int dvb_usb_tuner_set_params_i2c(struct dvb_frontend *, struct dvb_frontend_parameters *); /* commonly used firmware download types and function */ struct hexline { -- cgit v1.2.3 From cb1177c2cb118cae0e624ad92607614242dc76d5 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:11 +0100 Subject: Convert dvb-usb/umt-010 to refactored tuner code From: Andrew de Quincey Hook tuner call into tuner_ops. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/dvb-usb/umt-010.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-usb/umt-010.c b/linux/drivers/media/dvb/dvb-usb/umt-010.c index 8eeaa3db3..26efac60e 100644 --- a/linux/drivers/media/dvb/dvb-usb/umt-010.c +++ b/linux/drivers/media/dvb/dvb-usb/umt-010.c @@ -57,7 +57,7 @@ static int umt_mt352_frontend_attach(struct dvb_usb_device *d) memset(&umt_config,0,sizeof(struct mt352_config)); umt_config.demod_init = umt_mt352_demod_init; umt_config.demod_address = 0xf; - umt_config.pll_set = dvb_usb_pll_set; + d->fe->ops->tuner_ops.pllbuf = dvb_usb_tuner_pllbuf; d->fe = mt352_attach(&umt_config, &d->i2c_adap); -- cgit v1.2.3 From afd115760b632be6ad38728153fe6603cba4bd62 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:12 +0100 Subject: Convert digtv to refactored tuner code From: Andrew de Quincey Rename pll calls to appropriate tuner calls. Remove pll functions from demod structures. Hook tuner call into tuner_ops. Add pll gate control calls where appropriate. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/dvb-usb/digitv.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-usb/digitv.c b/linux/drivers/media/dvb/dvb-usb/digitv.c index c2950c6e9..eebf0d144 100644 --- a/linux/drivers/media/dvb/dvb-usb/digitv.c +++ b/linux/drivers/media/dvb/dvb-usb/digitv.c @@ -112,27 +112,30 @@ static int digitv_mt352_demod_init(struct dvb_frontend *fe) static struct mt352_config digitv_mt352_config = { .demod_init = digitv_mt352_demod_init, - .pll_set = dvb_usb_pll_set, }; -static int digitv_nxt6000_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) +static int digitv_nxt6000_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) { struct dvb_usb_device *d = fe->dvb->priv; u8 b[5]; - dvb_usb_pll_set(fe,fep,b); + dvb_usb_tuner_pllbuf(fe,fep,b, 5); return digitv_ctrl_msg(d,USB_WRITE_TUNER,0,&b[1],4,NULL,0); } static struct nxt6000_config digitv_nxt6000_config = { .clock_inversion = 1, - .pll_set = digitv_nxt6000_pll_set, }; static int digitv_frontend_attach(struct dvb_usb_device *d) { - if ((d->fe = mt352_attach(&digitv_mt352_config, &d->i2c_adap)) != NULL || - (d->fe = nxt6000_attach(&digitv_nxt6000_config, &d->i2c_adap)) != NULL) + if ((d->fe = mt352_attach(&digitv_mt352_config, &d->i2c_adap)) != NULL) { + d->fe->ops->tuner_ops.pllbuf = dvb_usb_tuner_pllbuf; return 0; + } + if ((d->fe = nxt6000_attach(&digitv_nxt6000_config, &d->i2c_adap)) != NULL) { + d->fe->ops->tuner_ops.set_params = digitv_nxt6000_tuner_set_params; + return 0; + } return -EIO; } -- cgit v1.2.3 From db775ea3727eb7ccf1d0d1ab2b59c884087fec20 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:12 +0100 Subject: Convert dibusb* to refactored tuner code From: Andrew de Quincey Hook tuner call into tuner_ops. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/dvb-usb/dibusb-common.c | 4 ++-- linux/drivers/media/dvb/dvb-usb/dibusb-mb.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-usb/dibusb-common.c b/linux/drivers/media/dvb/dvb-usb/dibusb-common.c index 2d52b7667..b0435c8f8 100644 --- a/linux/drivers/media/dvb/dvb-usb/dibusb-common.c +++ b/linux/drivers/media/dvb/dvb-usb/dibusb-common.c @@ -173,8 +173,8 @@ int dibusb_dib3000mc_frontend_attach(struct dvb_usb_device *d) struct dib3000_config demod_cfg; struct dibusb_state *st = d->priv; - demod_cfg.pll_set = dvb_usb_pll_set_i2c; - demod_cfg.pll_init = dvb_usb_pll_init_i2c; + d->fe->ops->tuner_ops.init = dvb_usb_tuner_init_i2c; + d->fe->ops->tuner_ops.set_params = dvb_usb_tuner_set_params_i2c; for (demod_cfg.demod_address = 0x8; demod_cfg.demod_address < 0xd; demod_cfg.demod_address++) if ((d->fe = dib3000mc_attach(&demod_cfg,&d->i2c_adap,&st->ops)) != NULL) { diff --git a/linux/drivers/media/dvb/dvb-usb/dibusb-mb.c b/linux/drivers/media/dvb/dvb-usb/dibusb-mb.c index 1cf2344c3..a7bcb9961 100644 --- a/linux/drivers/media/dvb/dvb-usb/dibusb-mb.c +++ b/linux/drivers/media/dvb/dvb-usb/dibusb-mb.c @@ -20,8 +20,8 @@ static int dibusb_dib3000mb_frontend_attach(struct dvb_usb_device *d) struct dibusb_state *st = d->priv; demod_cfg.demod_address = 0x8; - demod_cfg.pll_set = dvb_usb_pll_set_i2c; - demod_cfg.pll_init = dvb_usb_pll_init_i2c; + d->fe->ops->tuner_ops.init = dvb_usb_tuner_init_i2c; + d->fe->ops->tuner_ops.set_params = dvb_usb_tuner_set_params_i2c; if ((d->fe = dib3000mb_attach(&demod_cfg,&d->i2c_adap,&st->ops)) == NULL) return -ENODEV; -- cgit v1.2.3 From e18ac1866bfbb2f1f3222042bcb1ccf557f4c98e Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:12 +0100 Subject: Convert dvb-pll to be a refactored tuner From: Andrew de Quincey Rename pll calls to appropriate tuner calls. Remove pll functions from demod structures. Hook tuner call into tuner_ops. Remove BUG_ON() and convert to a soft error. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/dvb-pll.c | 173 +++++++++++++++++++++++++++- linux/drivers/media/dvb/frontends/dvb-pll.h | 16 ++- 2 files changed, 187 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.c b/linux/drivers/media/dvb/frontends/dvb-pll.c index b6e2c387a..5f463e8ae 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.c +++ b/linux/drivers/media/dvb/frontends/dvb-pll.c @@ -419,6 +419,19 @@ struct dvb_pll_desc dvb_pll_thomson_fe6600 = { }; EXPORT_SYMBOL(dvb_pll_thomson_fe6600); +struct dvb_pll_priv { + /* i2c details */ + int pll_i2c_address; + struct i2c_adapter *i2c; + + /* the PLL descriptor */ + struct dvb_pll_desc *pll_desc; + + /* cached frequency/bandwidth */ + u32 frequency; + u32 bandwidth; +}; + /* ----------------------------------------------------------- */ /* code */ @@ -443,7 +456,8 @@ int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, if (debug) printk("pll: %s: freq=%d bw=%d | i=%d/%d\n", desc->name, freq, bandwidth, i, desc->count); - BUG_ON(i == desc->count); + if (i == desc->count) + return -EINVAL; div = (freq + desc->entries[i].offset) / desc->entries[i].stepsize; buf[0] = div >> 8; @@ -462,6 +476,163 @@ int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, } EXPORT_SYMBOL(dvb_pll_configure); +static int dvb_pll_release(struct dvb_frontend *fe) +{ + if (fe->tuner_priv) + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int dvb_pll_sleep(struct dvb_frontend *fe) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + u8 buf[4]; + struct i2c_msg msg = + { .addr = priv->pll_i2c_address, .flags = 0, .buf = buf, .len = sizeof(buf) }; + int i; + int result; + + for (i = 0; i < priv->pll_desc->count; i++) { + if (priv->pll_desc->entries[i].limit == 0) + break; + } + if (i == priv->pll_desc->count) + return 0; + + buf[0] = 0; + buf[1] = 0; + buf[2] = priv->pll_desc->entries[i].config; + buf[3] = priv->pll_desc->entries[i].cb; + + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); + if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { + return result; + } + + return 0; +} + +static int dvb_pll_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + u8 buf[4]; + struct i2c_msg msg = + { .addr = priv->pll_i2c_address, .flags = 0, .buf = buf, .len = sizeof(buf) }; + int result; + u32 div; + int i; + u32 bandwidth = 0; + + if (priv->i2c == NULL) + return -EINVAL; + + // DVBT bandwidth only just now + if (fe->ops->info.type == FE_OFDM) { + bandwidth = params->u.ofdm.bandwidth; + } + + if ((result = dvb_pll_configure(priv->pll_desc, buf, params->frequency, bandwidth)) != 0) + return result; + + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); + if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { + return result; + } + + // calculate the frequency we set it to + for (i = 0; i < priv->pll_desc->count; i++) { + if (params->frequency > priv->pll_desc->entries[i].limit) + continue; + break; + } + div = (params->frequency + priv->pll_desc->entries[i].offset) / priv->pll_desc->entries[i].stepsize; + priv->frequency = (div * priv->pll_desc->entries[i].stepsize) - priv->pll_desc->entries[i].offset; + priv->bandwidth = bandwidth; + + return 0; +} + +static int dvb_pll_pllbuf(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, u8 *buf, int buf_len) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + int result; + u32 div; + int i; + u32 bandwidth = 0; + + if (buf_len < 5) + return -EINVAL; + + // DVBT bandwidth only just now + if (fe->ops->info.type == FE_OFDM) { + bandwidth = params->u.ofdm.bandwidth; + } + + if ((result = dvb_pll_configure(priv->pll_desc, buf+1, params->frequency, bandwidth)) != 0) + return result; + buf[0] = priv->pll_i2c_address; + + // calculate the frequency we set it to + for (i = 0; i < priv->pll_desc->count; i++) { + if (params->frequency > priv->pll_desc->entries[i].limit) + continue; + break; + } + div = (params->frequency + priv->pll_desc->entries[i].offset) / priv->pll_desc->entries[i].stepsize; + priv->frequency = (div * priv->pll_desc->entries[i].stepsize) - priv->pll_desc->entries[i].offset; + priv->bandwidth = bandwidth; + + return 5; +} + +static int dvb_pll_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int dvb_pll_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +static struct dvb_tuner_ops dvb_pll_tuner_ops = { + .release = dvb_pll_release, + .sleep = dvb_pll_sleep, + .set_params = dvb_pll_set_params, + .pllbuf = dvb_pll_pllbuf, + .get_frequency = dvb_pll_get_frequency, + .get_bandwidth = dvb_pll_get_bandwidth, +}; + +int dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc) +{ + struct dvb_pll_priv *priv = NULL; + + priv = kzalloc(sizeof(struct dvb_pll_priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + priv->pll_i2c_address = pll_addr; + priv->i2c = i2c; + priv->pll_desc = desc; + + memcpy(&fe->ops->tuner_ops, &dvb_pll_tuner_ops, sizeof(struct dvb_tuner_ops)); + strncpy(fe->ops->tuner_ops.info.name, desc->name, 128); + fe->ops->tuner_ops.info.frequency_min = desc->min; + fe->ops->tuner_ops.info.frequency_min = desc->max; + + fe->tuner_priv = priv; + return 0; +} +EXPORT_SYMBOL(dvb_pll_attach); + MODULE_DESCRIPTION("dvb pll library"); MODULE_AUTHOR("Gerd Knorr"); MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.h b/linux/drivers/media/dvb/frontends/dvb-pll.h index 2b8461784..3908ae1d4 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.h +++ b/linux/drivers/media/dvb/frontends/dvb-pll.h @@ -5,6 +5,9 @@ #ifndef __DVB_PLL_H__ #define __DVB_PLL_H__ +#include +#include "dvb_frontend.h" + struct dvb_pll_desc { char *name; u32 min; @@ -44,7 +47,18 @@ extern struct dvb_pll_desc dvb_pll_philips_td1316; extern struct dvb_pll_desc dvb_pll_thomson_fe6600; -int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, +extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, u32 freq, int bandwidth); +/** + * Attach a dvb-pll to the supplied frontend structure. + * + * @param fe Frontend to attach to. + * @param pll_addr i2c address of the PLL (if used). + * @param i2c i2c adapter to use (set to NULL if not used). + * @param desc dvb_pll_desc to use. + * @return 0 on success, nonzero on failure. + */ +extern int dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc); + #endif -- cgit v1.2.3 From e89b3300af319ff478847f9dd0855c395febdf69 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:12 +0100 Subject: Convert flexcop-fe-tuner to refactored tuner code From: Andrew de Quincey Rename pll calls to appropriate tuner calls. Remove pll functions from demod structures. Hook tuner call into tuner_ops. Attach dvb-pll where possible. Add pll gate control calls where appropriate. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c | 43 ++++++++++++++++--------- 1 file changed, 27 insertions(+), 16 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index 26f79ffef..f93a3c78a 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -167,11 +167,12 @@ static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend* fe, u32 srate, return 0; } -static int samsung_tbmu24112_pll_set(struct dvb_frontend* fe, struct i2c_adapter *i2c, struct dvb_frontend_parameters* params) +static int samsung_tbmu24112_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { u8 buf[4]; u32 div; struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; + struct flexcop_device *fc = fe->dvb->priv; div = params->frequency / 125; @@ -182,8 +183,11 @@ static int samsung_tbmu24112_pll_set(struct dvb_frontend* fe, struct i2c_adapter if (params->frequency < 1500000) buf[3] |= 0x10; - if (i2c_transfer(i2c, &msg, 1) != 1) + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&fc->i2c_adap, &msg, 1) != 1) { return -EIO; + } return 0; } @@ -242,7 +246,6 @@ static struct stv0299_config samsung_tbmu24112_config = { .volt13_op0_op1 = STV0299_VOLT13_OP1, .min_delay_ms = 100, .set_symbol_rate = samsung_tbmu24112_set_symbol_rate, - .pll_set = samsung_tbmu24112_pll_set, }; /* dvb-t mt352 */ @@ -265,11 +268,14 @@ static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe) return 0; } -static int samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf) +static int samsung_tdtc9251dh0_pllbuf(struct dvb_frontend* fe, struct dvb_frontend_parameters *params, u8* pllbuf, int buf_len) { u32 div; unsigned char bs = 0; + if (buf_len < 5) + return -EINVAL; + #define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; @@ -277,19 +283,18 @@ static int samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_front if (params->frequency >= 161000000 && params->frequency <= 439000000) bs = 0x0a; if (params->frequency >= 447000000 && params->frequency <= 863000000) bs = 0x08; - pllbuf[0] = 0xc2; /* Note: non-linux standard PLL i2c address */ + pllbuf[0] = 0x61; pllbuf[1] = div >> 8; pllbuf[2] = div & 0xff; pllbuf[3] = 0xcc; pllbuf[4] = bs; - return 0; + return 5; } static struct mt352_config samsung_tdtc9251dh0_config = { .demod_address = 0x0f, .demod_init = samsung_tdtc9251dh0_demod_init, - .pll_set = samsung_tdtc9251dh0_pll_set, }; static int flexcop_fe_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) @@ -298,8 +303,7 @@ static int flexcop_fe_request_firmware(struct dvb_frontend* fe, const struct fir return request_firmware(fw, name, fc->dev); } -static int lgdt3303_pll_set(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params) +static int lgdt3303_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { struct flexcop_device *fc = fe->dvb->priv; return lg_h06xf_pll_set(fe, &fc->i2c_adap, params); @@ -309,14 +313,11 @@ static struct lgdt330x_config air2pc_atsc_hd5000_config = { .demod_address = 0x59, .demod_chip = LGDT3303, .serial_mpeg = 0x04, - .pll_set = lgdt3303_pll_set, .clock_polarity_flip = 1, }; static struct nxt200x_config samsung_tbmv_config = { .demod_address = 0x0a, - .pll_address = 0xc2, - .pll_desc = &dvb_pll_samsung_tbmv, }; static struct bcm3510_config air2pc_atsc_first_gen_config = { @@ -324,7 +325,7 @@ static struct bcm3510_config air2pc_atsc_first_gen_config = { .request_firmware = flexcop_fe_request_firmware, }; -static int skystar23_samsung_tbdu18132_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int skystar23_samsung_tbdu18132_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { u8 buf[4]; u32 div; @@ -341,6 +342,8 @@ static int skystar23_samsung_tbdu18132_pll_set(struct dvb_frontend* fe, struct d if (params->frequency < 1550000) buf[3] |= 0x02; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&fc->i2c_adap, &msg, 1) != 1) return -EIO; return 0; @@ -349,10 +352,9 @@ static int skystar23_samsung_tbdu18132_pll_set(struct dvb_frontend* fe, struct d static struct mt312_config skystar23_samsung_tbdu18132_config = { .demod_address = 0x0e, - .pll_set = skystar23_samsung_tbdu18132_pll_set, }; -int alps_tdee4_stv0297_pll_set (struct dvb_frontend* fe, struct dvb_frontend_parameters *fep) +int alps_tdee4_stv0297_tuner_set_params (struct dvb_frontend* fe, struct dvb_frontend_parameters *fep) { struct flexcop_device *fc = fe->dvb->priv; u8 buf[4]; @@ -387,6 +389,8 @@ int alps_tdee4_stv0297_pll_set (struct dvb_frontend* fe, struct dvb_frontend_par else if (fep->frequency <= 822000000) buf[3] = 0x08; else buf[3] = 0x88; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); deb_tuner("tuner buffer for %d Hz: %x %x %x %x\n",fep->frequency, buf[0],buf[1],buf[2],buf[3]); ret = fc->i2c_request(fc,FC_WRITE,FC_I2C_PORT_TUNER,0x61,buf[0],&buf[1],3); deb_tuner("tuner write returned: %d\n",ret); @@ -397,7 +401,6 @@ int alps_tdee4_stv0297_pll_set (struct dvb_frontend* fe, struct dvb_frontend_par static struct stv0297_config alps_tdee4_stv0297_config = { .demod_address = 0x1c, - .pll_set = alps_tdee4_stv0297_pll_set, .fclk = STV0297_PAL_FCLK, .demodfreq = STV0297_PAL_IF - STV0297_PAL_FCLK, }; @@ -411,6 +414,8 @@ int flexcop_frontend_init(struct flexcop_device *fc) if ((fc->fe = stv0299_attach(&samsung_tbmu24112_config, &fc->i2c_adap)) != NULL) { ops = fc->fe->ops; + ops->tuner_ops.set_params = samsung_tbmu24112_tuner_set_params; + ops->set_voltage = flexcop_set_voltage; fc->fe_sleep = ops->sleep; @@ -422,16 +427,19 @@ int flexcop_frontend_init(struct flexcop_device *fc) /* try the air dvb-t (mt352/Samsung tdtc9251dh0(??)) */ if ((fc->fe = mt352_attach(&samsung_tdtc9251dh0_config, &fc->i2c_adap)) != NULL ) { fc->dev_type = FC_AIR_DVB; + fc->fe->ops->tuner_ops.pllbuf = samsung_tdtc9251dh0_pllbuf; info("found the mt352 at i2c address: 0x%02x",samsung_tdtc9251dh0_config.demod_address); } else /* try the air atsc 2nd generation (nxt2002) */ if ((fc->fe = nxt200x_attach(&samsung_tbmv_config, &fc->i2c_adap)) != NULL) { fc->dev_type = FC_AIR_ATSC2; + dvb_pll_attach(fc->fe, 0x61, &fc->i2c_adap, &dvb_pll_samsung_tbmv); info("found the nxt2002 at i2c address: 0x%02x",samsung_tbmv_config.demod_address); } else /* try the air atsc 3nd generation (lgdt3303) */ if ((fc->fe = lgdt330x_attach(&air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) { fc->dev_type = FC_AIR_ATSC3; + fc->fe->ops->tuner_ops.set_params = lgdt3303_tuner_set_params; info("found the lgdt3303 at i2c address: 0x%02x",air2pc_atsc_hd5000_config.demod_address); } else /* try the air atsc 1nd generation (bcm3510)/panasonic ct10s */ @@ -442,12 +450,15 @@ int flexcop_frontend_init(struct flexcop_device *fc) /* try the cable dvb (stv0297) */ if ((fc->fe = stv0297_cs2_attach(&alps_tdee4_stv0297_config, &fc->i2c_adap)) != NULL) { fc->dev_type = FC_CABLE; + fc->fe->ops->tuner_ops.set_params = alps_tdee4_stv0297_tuner_set_params; info("found the stv0297 at i2c address: 0x%02x",alps_tdee4_stv0297_config.demod_address); } else /* try the sky v2.3 (vp310/Samsung tbdu18132(tsa5059)) */ if ((fc->fe = vp310_mt312_attach(&skystar23_samsung_tbdu18132_config, &fc->i2c_adap)) != NULL) { ops = fc->fe->ops; + ops->tuner_ops.set_params = skystar23_samsung_tbdu18132_tuner_set_params; + ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd; ops->diseqc_send_burst = flexcop_diseqc_send_burst; ops->set_tone = flexcop_set_tone; -- cgit v1.2.3 From 11c5c446082a8711347f3afcf3ca5066c2bf9e89 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:12 +0100 Subject: Convert bt8xx to refactored tuner code From: Andrew de Quincey Rename pll calls to appropriate tuner calls. Remove pll functions from demod structures. Hook tuner call into tuner_ops. Attach dvb-pll where possible. Add pll gate control calls where appropriate. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c | 71 +++++++++++++++++++------------ 1 file changed, 44 insertions(+), 27 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c index 93aaaa4e5..d40a3703f 100644 --- a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -148,12 +148,15 @@ static int thomson_dtt7579_demod_init(struct dvb_frontend* fe) return 0; } -static int thomson_dtt7579_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf) +static int thomson_dtt7579_tuner_pllbuf(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf, int buf_len) { u32 div; unsigned char bs = 0; unsigned char cp = 0; + if (buf_len < 5) + return -EINVAL; + div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; if (params->frequency < 542000000) @@ -170,27 +173,25 @@ static int thomson_dtt7579_pll_set(struct dvb_frontend* fe, struct dvb_frontend_ else bs = 0x08; - pllbuf[0] = 0xc0; // Note: non-linux standard PLL i2c address + pllbuf[0] = 0x60; pllbuf[1] = div >> 8; pllbuf[2] = div & 0xff; pllbuf[3] = cp; pllbuf[4] = bs; - return 0; + return 5; } static struct mt352_config thomson_dtt7579_config = { .demod_address = 0x0f, .demod_init = thomson_dtt7579_demod_init, - .pll_set = thomson_dtt7579_pll_set, }; static struct zl10353_config thomson_dtt7579_zl10353_config = { .demod_address = 0x0f, - .pll_set = thomson_dtt7579_pll_set, }; -static int cx24108_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int cx24108_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { u32 freq = params->frequency; @@ -243,7 +244,7 @@ static int cx24108_pll_set(struct dvb_frontend* fe, struct dvb_frontend_paramete return 0; } -static int pinnsat_pll_init(struct dvb_frontend* fe) +static int pinnsat_tuner_init(struct dvb_frontend* fe) { struct dvb_bt8xx_card *card = fe->dvb->priv; @@ -253,7 +254,7 @@ static int pinnsat_pll_init(struct dvb_frontend* fe) return 0; } -static int pinnsat_pll_sleep(struct dvb_frontend* fe) +static int pinnsat_tuner_sleep(struct dvb_frontend* fe) { struct dvb_bt8xx_card *card = fe->dvb->priv; @@ -264,12 +265,9 @@ static int pinnsat_pll_sleep(struct dvb_frontend* fe) static struct cx24110_config pctvsat_config = { .demod_address = 0x55, - .pll_init = pinnsat_pll_init, - .pll_set = cx24108_pll_set, - .pll_sleep = pinnsat_pll_sleep, }; -static int microtune_mt7202dtf_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int microtune_mt7202dtf_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; u8 cfg, cpump, band_select; @@ -303,6 +301,8 @@ static int microtune_mt7202dtf_pll_set(struct dvb_frontend* fe, struct dvb_front data[2] = ((div >> 10) & 0x60) | cfg; data[3] = (cpump << 6) | band_select; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(card->i2c_adapter, &msg, 1); return (div * 166666 - 36000000); } @@ -316,7 +316,6 @@ static int microtune_mt7202dtf_request_firmware(struct dvb_frontend* fe, const s static struct sp887x_config microtune_mt7202dtf_config = { .demod_address = 0x70, - .pll_set = microtune_mt7202dtf_pll_set, .request_firmware = microtune_mt7202dtf_request_firmware, }; @@ -343,12 +342,14 @@ static int advbt771_samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe) return 0; } -static int advbt771_samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf) +static int advbt771_samsung_tdtc9251dh0_tuner_pllbuf(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf, int buf_len) { u32 div; unsigned char bs = 0; unsigned char cp = 0; + if (buf_len < 5) return -EINVAL; + div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; if (params->frequency < 150000000) @@ -389,19 +390,18 @@ static int advbt771_samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct else bs = 0x08; - pllbuf[0] = 0xc2; // Note: non-linux standard PLL i2c address + pllbuf[0] = 0x61; pllbuf[1] = div >> 8; pllbuf[2] = div & 0xff; pllbuf[3] = cp; pllbuf[4] = bs; - return 0; + return 5; } static struct mt352_config advbt771_samsung_tdtc9251dh0_config = { .demod_address = 0x0f, .demod_init = advbt771_samsung_tdtc9251dh0_demod_init, - .pll_set = advbt771_samsung_tdtc9251dh0_pll_set, }; static struct dst_config dst_config = { @@ -461,7 +461,7 @@ static struct or51211_config or51211_config = { .sleep = or51211_sleep, }; -static int vp3021_alps_tded4_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int vp3021_alps_tded4_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; u8 buf[4]; @@ -484,6 +484,8 @@ static int vp3021_alps_tded4_pll_set(struct dvb_frontend* fe, struct dvb_fronten else return -EINVAL; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(card->i2c_adapter, &msg, 1); return 0; } @@ -491,7 +493,6 @@ static int vp3021_alps_tded4_pll_set(struct dvb_frontend* fe, struct dvb_fronten static struct nxt6000_config vp3021_alps_tded4_config = { .demod_address = 0x0a, .clock_inversion = 1, - .pll_set = vp3021_alps_tded4_pll_set, }; static int digitv_alps_tded4_demod_init(struct dvb_frontend* fe) @@ -512,14 +513,17 @@ static int digitv_alps_tded4_demod_init(struct dvb_frontend* fe) return 0; } -static int digitv_alps_tded4_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf) +static int digitv_alps_tded4_tuner_pllbuf(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf, int buf_len) { u32 div; struct dvb_ofdm_parameters *op = ¶ms->u.ofdm; + if (buf_len < 5) + return -EINVAL; + div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; - pllbuf[0] = 0xc2; + pllbuf[0] = 0x61; pllbuf[1] = (div >> 8) & 0x7F; pllbuf[2] = div & 0xFF; pllbuf[3] = 0x85; @@ -536,7 +540,7 @@ static int digitv_alps_tded4_pll_set(struct dvb_frontend* fe, struct dvb_fronten if (op->bandwidth == 8) pllbuf[4] |= 0x04; - return 0; + return 5; } static void digitv_alps_tded4_reset(struct dvb_bt8xx_card *bt) @@ -563,10 +567,9 @@ static void digitv_alps_tded4_reset(struct dvb_bt8xx_card *bt) static struct mt352_config digitv_alps_tded4_config = { .demod_address = 0x0a, .demod_init = digitv_alps_tded4_demod_init, - .pll_set = digitv_alps_tded4_pll_set, }; -static int tdvs_tua6034_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) +static int tdvs_tua6034_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; return lg_h06xf_pll_set(fe, card->i2c_adapter, params); @@ -576,7 +579,6 @@ static struct lgdt330x_config tdvs_tua6034_config = { .demod_address = 0x0e, .demod_chip = LGDT3303, .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ - .pll_set = tdvs_tua6034_pll_set, }; static void lgdt330x_reset(struct dvb_bt8xx_card *bt) @@ -606,6 +608,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) card->i2c_adapter); if (card->fe != NULL) { + card->fe->ops->tuner_ops.pllbuf = thomson_dtt7579_tuner_pllbuf; card->fe->ops->info.frequency_min = 174000000; card->fe->ops->info.frequency_max = 862000000; } @@ -614,8 +617,10 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE: lgdt330x_reset(card); card->fe = lgdt330x_attach(&tdvs_tua6034_config, card->i2c_adapter); - if (card->fe != NULL) + if (card->fe != NULL) { + card->fe->ops->tuner_ops.set_params = tdvs_tua6034_tuner_set_params; dprintk ("dvb_bt8xx: lgdt330x detected\n"); + } break; case BTTV_BOARD_NEBULA_DIGITV: @@ -628,6 +633,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) digitv_alps_tded4_reset(card); card->fe = nxt6000_attach(&vp3021_alps_tded4_config, card->i2c_adapter); if (card->fe != NULL) { + card->fe->ops->tuner_ops.set_params = vp3021_alps_tded4_tuner_set_params; dprintk ("dvb_bt8xx: an nxt6000 was detected on your digitv card\n"); break; } @@ -636,17 +642,23 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) digitv_alps_tded4_reset(card); card->fe = mt352_attach(&digitv_alps_tded4_config, card->i2c_adapter); - if (card->fe != NULL) + if (card->fe != NULL) { + card->fe->ops->tuner_ops.pllbuf = digitv_alps_tded4_tuner_pllbuf; dprintk ("dvb_bt8xx: an mt352 was detected on your digitv card\n"); + } break; case BTTV_BOARD_AVDVBT_761: card->fe = sp887x_attach(µtune_mt7202dtf_config, card->i2c_adapter); + if (card->fe) { + card->fe->ops->tuner_ops.set_params = microtune_mt7202dtf_tuner_set_params; + } break; case BTTV_BOARD_AVDVBT_771: card->fe = mt352_attach(&advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter); if (card->fe != NULL) { + card->fe->ops->tuner_ops.pllbuf = advbt771_samsung_tdtc9251dh0_tuner_pllbuf; card->fe->ops->info.frequency_min = 174000000; card->fe->ops->info.frequency_max = 862000000; } @@ -675,6 +687,11 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) case BTTV_BOARD_PINNACLESAT: card->fe = cx24110_attach(&pctvsat_config, card->i2c_adapter); + if (card->fe) { + card->fe->ops->tuner_ops.init = pinnsat_tuner_init; + card->fe->ops->tuner_ops.sleep = pinnsat_tuner_sleep; + card->fe->ops->tuner_ops.set_params = cx24108_tuner_set_params; + } break; case BTTV_BOARD_PC_HDTV: -- cgit v1.2.3 From e2e414107e1ee3aa4df0be1e15f0b157cb96243e Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:12 +0100 Subject: Convert cxusb to refactored tuner code From: Andrew de Quincey Rename pll calls to appropriate tuner calls. Remove pll functions from demod structures. Hook tuner call into tuner_ops. Attach dvb-pll where possible. --- linux/drivers/media/dvb/dvb-usb/cxusb.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index d3fcc90db..eba635536 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -314,8 +314,8 @@ static int cxusb_mt352_demod_init(struct dvb_frontend* fe) return 0; } -static int cxusb_lgh064f_pll_set_i2c(struct dvb_frontend *fe, - struct dvb_frontend_parameters *fep) +static int cxusb_lgh064f_tuner_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fep) { struct dvb_usb_device *d = fe->dvb->priv; return lg_h06xf_pll_set(fe, &d->i2c_adap, fep); @@ -325,28 +325,22 @@ static struct cx22702_config cxusb_cx22702_config = { .demod_address = 0x63, .output_mode = CX22702_PARALLEL_OUTPUT, - - .pll_init = dvb_usb_pll_init_i2c, - .pll_set = dvb_usb_pll_set_i2c, }; static struct lgdt330x_config cxusb_lgdt3303_config = { .demod_address = 0x0e, .demod_chip = LGDT3303, - .pll_set = cxusb_lgh064f_pll_set_i2c, }; static struct mt352_config cxusb_dee1601_config = { .demod_address = 0x0f, .demod_init = cxusb_dee1601_demod_init, - .pll_set = dvb_usb_pll_set, }; struct mt352_config cxusb_mt352_config = { /* used in both lgz201 and th7579 */ .demod_address = 0x0f, .demod_init = cxusb_mt352_demod_init, - .pll_set = dvb_usb_pll_set, }; /* Callbacks for DVB USB */ @@ -388,8 +382,11 @@ static int cxusb_cx22702_frontend_attach(struct dvb_usb_device *d) cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, &b, 1); - if ((d->fe = cx22702_attach(&cxusb_cx22702_config, &d->i2c_adap)) != NULL) + if ((d->fe = cx22702_attach(&cxusb_cx22702_config, &d->i2c_adap)) != NULL) { + d->fe->ops->tuner_ops.init = dvb_usb_tuner_init_i2c; + d->fe->ops->tuner_ops.set_params = dvb_usb_tuner_set_params_i2c; return 0; + } return -EIO; } @@ -401,8 +398,10 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_device *d) cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, NULL, 0); - if ((d->fe = lgdt330x_attach(&cxusb_lgdt3303_config, &d->i2c_adap)) != NULL) + if ((d->fe = lgdt330x_attach(&cxusb_lgdt3303_config, &d->i2c_adap)) != NULL) { + d->fe->ops->tuner_ops.set_params = cxusb_lgh064f_tuner_set_params; return 0; + } return -EIO; } @@ -414,8 +413,10 @@ static int cxusb_mt352_frontend_attach(struct dvb_usb_device *d) cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, NULL, 0); - if ((d->fe = mt352_attach(&cxusb_mt352_config, &d->i2c_adap)) != NULL) + if ((d->fe = mt352_attach(&cxusb_mt352_config, &d->i2c_adap)) != NULL) { + d->fe->ops->tuner_ops.pllbuf = dvb_usb_tuner_pllbuf; return 0; + } return -EIO; } @@ -427,8 +428,10 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_device *d) cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, NULL, 0); - if ((d->fe = mt352_attach(&cxusb_dee1601_config, &d->i2c_adap)) != NULL) + if ((d->fe = mt352_attach(&cxusb_dee1601_config, &d->i2c_adap)) != NULL) { + d->fe->ops->tuner_ops.pllbuf = dvb_usb_tuner_pllbuf; return 0; + } return -EIO; } -- cgit v1.2.3 From b567a613dbe04986b2731674f5a0a71bdabd8065 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:12 +0100 Subject: Sort out support for non-attached tuners on mt352 From: Andrew de Quincey Make it use set_params/pllbuf as appropriate so it works with dvb_pll_attach()ed tuners. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/mt352.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/mt352.c b/linux/drivers/media/dvb/frontends/mt352.c index a7a347a3c..3c86018e7 100644 --- a/linux/drivers/media/dvb/frontends/mt352.c +++ b/linux/drivers/media/dvb/frontends/mt352.c @@ -287,18 +287,32 @@ static int mt352_set_parameters(struct dvb_frontend* fe, mt352_calc_nominal_rate(state, op->bandwidth, buf+4); mt352_calc_input_freq(state, buf+6); + // if there is no secondary tuner, call set_params to set up a potential + // tuner attached elsewhere + if (state->config.no_tuner) { + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, param); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } + + /* start decoding only */ + mt352_write(fe, fsm_go, 2); + } + + // retrieve the pllbuf - we do this even if there is no + // secondary tuner simply so we have a record of what was sent for + // debugging. if (fe->ops->tuner_ops.pllbuf) { fe->ops->tuner_ops.pllbuf(fe, param, buf+8, 5); buf[8] <<= 1; mt352_write(fe, buf, sizeof(buf)); } - if (state->config.no_tuner) { - /* start decoding */ - mt352_write(fe, fsm_go, 2); - } else { - /* start tuning */ + + // send PLL and start tuning and then decoding + if (!state->config.no_tuner) { mt352_write(fe, tuner_go, 2); } + return 0; } -- cgit v1.2.3 From 3b3dfc47ebdc7d825d84ab44ee4f00136e90df45 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:47:12 +0100 Subject: Add support for non-attached tuners to zl10353 From: Andrew de Quincey Add no_tuner setting like in mt352. Update to use correct tuner API calls. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/zl10353.c | 24 +++++++++++++++++++++++- linux/drivers/media/dvb/frontends/zl10353.h | 3 +++ 2 files changed, 26 insertions(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/zl10353.c b/linux/drivers/media/dvb/frontends/zl10353.c index c81b8902c..f4851907a 100644 --- a/linux/drivers/media/dvb/frontends/zl10353.c +++ b/linux/drivers/media/dvb/frontends/zl10353.c @@ -129,6 +129,8 @@ static int zl10353_sleep(struct dvb_frontend *fe) static int zl10353_set_parameters(struct dvb_frontend *fe, struct dvb_frontend_parameters *param) { + struct zl10353_state *state = fe->demodulator_priv; + u8 pllbuf[6] = { 0x67 }; /* These settings set "auto-everything" and start the FSM. */ @@ -145,12 +147,32 @@ static int zl10353_set_parameters(struct dvb_frontend *fe, zl10353_single_write(fe, 0x66, 0xE9); zl10353_single_write(fe, 0x62, 0x0A); + // if there is no attached secondary tuner, we call set_params to program + // a potential tuner attached somewhere else + if (state->config.no_tuner) { + if (fe->ops->tuner_ops.set_params) { + fe->ops->tuner_ops.set_params(fe, param); + if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0); + } + } + + // if pllbuf is defined, retrieve the settings if (fe->ops->tuner_ops.pllbuf) { fe->ops->tuner_ops.pllbuf(fe, param, pllbuf+1, 5); pllbuf[1] <<= 1; - zl10353_write(fe, pllbuf, sizeof(pllbuf)); + } else { + // fake pllbuf settings + pllbuf[1] = 0x61 << 1; + pllbuf[2] = 0; + pllbuf[3] = 0; + pllbuf[3] = 0; + pllbuf[4] = 0; } + // there is no call to _just_ start decoding, so we send the pllbuf anyway + // even if there isn't a PLL attached to the secondary bus + zl10353_write(fe, pllbuf, sizeof(pllbuf)); + zl10353_single_write(fe, 0x70, 0x01); udelay(250); zl10353_single_write(fe, 0xE4, 0x00); diff --git a/linux/drivers/media/dvb/frontends/zl10353.h b/linux/drivers/media/dvb/frontends/zl10353.h index 63c36ec02..9770cb840 100644 --- a/linux/drivers/media/dvb/frontends/zl10353.h +++ b/linux/drivers/media/dvb/frontends/zl10353.h @@ -28,6 +28,9 @@ struct zl10353_config { /* demodulator's I2C address */ u8 demod_address; + + /* set if no pll is connected to the secondary i2c bus */ + int no_tuner; }; extern struct dvb_frontend* zl10353_attach(const struct zl10353_config *config, -- cgit v1.2.3 From 35fecb9c806696f349aa6dd80fe699aad52a53a8 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 18 Apr 2006 21:56:10 +0100 Subject: Convert cx88-dvb to refactored tuner code From: Andrew de Quincey Rename pll calls to appropriate tuner calls. Remove pll functions from demod structures. Hook tuner call into tuner_ops. Attach dvb-pll where possible. Add pll gate control calls where appropriate. Sort out the hybrid cards to work with the revamped mt352/sl10353 demods supporting external tuners. Signed-off-by: Andrew de Quincey --- linux/drivers/media/video/cx88/cx88-dvb.c | 134 +++++++++++++++++------------- 1 file changed, 74 insertions(+), 60 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index ecd33d6fb..14b12b6c6 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -115,21 +115,6 @@ static struct videobuf_queue_ops dvb_qops = { /* ------------------------------------------------------------------ */ -#if defined(HAVE_MT352) || defined(HAVE_ZL10353) -static int zarlink_pll_set(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params, - u8 *pllbuf) -{ - struct cx8802_dev *dev = fe->dvb->priv; - - pllbuf[0] = dev->core->pll_addr << 1; - dvb_pll_configure(dev->core->pll_desc, pllbuf + 1, - params->frequency, - params->u.ofdm.bandwidth); - return 0; -} -#endif - #ifdef HAVE_MT352 static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe) { @@ -198,19 +183,16 @@ static int dntv_live_dvbt_demod_init(struct dvb_frontend* fe) static struct mt352_config dvico_fusionhdtv = { .demod_address = 0x0F, .demod_init = dvico_fusionhdtv_demod_init, - .pll_set = zarlink_pll_set, }; static struct mt352_config dntv_live_dvbt_config = { .demod_address = 0x0f, .demod_init = dntv_live_dvbt_demod_init, - .pll_set = zarlink_pll_set, }; static struct mt352_config dvico_fusionhdtv_dual = { .demod_address = 0x0F, .demod_init = dvico_dual_demod_init, - .pll_set = zarlink_pll_set, }; #ifdef HAVE_VP3054_I2C @@ -248,6 +230,8 @@ static int philips_fmd1216_pll_init(struct dvb_frontend *fe) .buf = fmd1216_init, .len = sizeof(fmd1216_init) }; int err; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if ((err = i2c_transfer(&dev->core->i2c_adap, &msg, 1)) != 1) { if (err < 0) return err; @@ -258,14 +242,14 @@ static int philips_fmd1216_pll_init(struct dvb_frontend *fe) return 0; } -static int dntv_live_dvbt_pro_pll_set(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params, - u8* pllbuf) +static int dntv_live_dvbt_pro_tuner_set_params(struct dvb_frontend* fe, + struct dvb_frontend_parameters* params) { struct cx8802_dev *dev= fe->dvb->priv; + u8 buf[4]; struct i2c_msg msg = { .addr = dev->core->pll_addr, .flags = 0, - .buf = pllbuf+1, .len = 4 }; + .buf = buf, .len = 4 }; int err; /* Switch PLL to DVB mode */ @@ -274,14 +258,16 @@ static int dntv_live_dvbt_pro_pll_set(struct dvb_frontend* fe, return err; /* Tune PLL */ - pllbuf[0] = dev->core->pll_addr << 1; - dvb_pll_configure(dev->core->pll_desc, pllbuf+1, + dvb_pll_configure(dev->core->pll_desc, buf, params->frequency, params->u.ofdm.bandwidth); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if ((err = i2c_transfer(&dev->core->i2c_adap, &msg, 1)) != 1) { + printk(KERN_WARNING "cx88-dvb: %s error " "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, pllbuf[0], pllbuf[1], err); + __FUNCTION__, dev->core->pll_addr, buf[0], err); if (err < 0) return err; else @@ -295,27 +281,27 @@ static struct mt352_config dntv_live_dvbt_pro_config = { .demod_address = 0x0f, .no_tuner = 1, .demod_init = dntv_live_dvbt_pro_demod_init, - .pll_set = dntv_live_dvbt_pro_pll_set, }; #endif #endif #ifdef HAVE_ZL10353 -static int dvico_hybrid_tune_pll(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params, - u8 *pllbuf) +static int dvico_hybrid_tuner_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) { + u8 pllbuf[4]; struct cx8802_dev *dev= fe->dvb->priv; struct i2c_msg msg = { .addr = dev->core->pll_addr, .flags = 0, - .buf = pllbuf + 1, .len = 4 }; + .buf = pllbuf, .len = 4 }; int err; - pllbuf[0] = dev->core->pll_addr << 1; - dvb_pll_configure(dev->core->pll_desc, pllbuf + 1, + dvb_pll_configure(dev->core->pll_desc, pllbuf, params->frequency, params->u.ofdm.bandwidth); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if ((err = i2c_transfer(&dev->core->i2c_adap, &msg, 1)) != 1) { printk(KERN_WARNING "cx88-dvb: %s error " "(addr %02x <- %02x, err = %i)\n", @@ -331,12 +317,11 @@ static int dvico_hybrid_tune_pll(struct dvb_frontend *fe, static struct zl10353_config dvico_fusionhdtv_hybrid = { .demod_address = 0x0F, - .pll_set = dvico_hybrid_tune_pll, + .no_tuner = 1, }; static struct zl10353_config dvico_fusionhdtv_plus_v1_1 = { .demod_address = 0x0F, - .pll_set = zarlink_pll_set, }; #endif @@ -344,21 +329,15 @@ static struct zl10353_config dvico_fusionhdtv_plus_v1_1 = { static struct cx22702_config connexant_refboard_config = { .demod_address = 0x43, .output_mode = CX22702_SERIAL_OUTPUT, - .pll_address = 0x60, - .pll_desc = &dvb_pll_thomson_dtt7579, }; static struct cx22702_config hauppauge_novat_config = { .demod_address = 0x43, .output_mode = CX22702_SERIAL_OUTPUT, - .pll_address = 0x61, - .pll_desc = &dvb_pll_thomson_dtt759x, }; static struct cx22702_config hauppauge_hvr1100_config = { .demod_address = 0x63, .output_mode = CX22702_SERIAL_OUTPUT, - .pll_address = 0x61, - .pll_desc = &dvb_pll_fmd1216me, }; #endif @@ -373,15 +352,13 @@ static int or51132_set_ts_param(struct dvb_frontend* fe, static struct or51132_config pchdtv_hd3000 = { .demod_address = 0x15, - .pll_address = 0x61, - .pll_desc = &dvb_pll_thomson_dtt761x, .set_ts_params = or51132_set_ts_param, }; #endif #ifdef HAVE_LGDT330X -static int lgdt3302_pll_set(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params) +static int lgdt3302_tuner_set_params(struct dvb_frontend* fe, + struct dvb_frontend_parameters* params) { /* FIXME make this routine use the tuner-simple code. * It could probably be shared with a number of ATSC @@ -397,6 +374,9 @@ static int lgdt3302_pll_set(struct dvb_frontend* fe, dvb_pll_configure(core->pll_desc, buf, params->frequency, 0); dprintk(1, "%s: tuner at 0x%02x bytes: 0x%02x 0x%02x 0x%02x 0x%02x\n", __FUNCTION__, msg.addr, buf[0],buf[1],buf[2],buf[3]); + + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if ((err = i2c_transfer(&core->i2c_adap, &msg, 1)) != 1) { printk(KERN_WARNING "cx88-dvb: %s error " "(addr %02x <- %02x, err = %i)\n", @@ -409,8 +389,8 @@ static int lgdt3302_pll_set(struct dvb_frontend* fe, return 0; } -static int lgdt3303_pll_set(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params) +static int lgdt3303_tuner_set_params(struct dvb_frontend* fe, + struct dvb_frontend_parameters* params) { struct cx8802_dev *dev= fe->dvb->priv; struct cx88_core *core = dev->core; @@ -448,7 +428,6 @@ static struct lgdt330x_config fusionhdtv_3_gold = { .demod_address = 0x0e, .demod_chip = LGDT3302, .serial_mpeg = 0x04, /* TPSERIAL for 3302 in TOP_CONTROL */ - .pll_set = lgdt3302_pll_set, .set_ts_params = lgdt330x_set_ts_param, }; @@ -456,7 +435,6 @@ static struct lgdt330x_config fusionhdtv_5_gold = { .demod_address = 0x0e, .demod_chip = LGDT3303, .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ - .pll_set = lgdt3303_pll_set, .set_ts_params = lgdt330x_set_ts_param, }; @@ -464,7 +442,6 @@ static struct lgdt330x_config pchdtv_hd5500 = { .demod_address = 0x59, .demod_chip = LGDT3303, .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ - .pll_set = lgdt3303_pll_set, .set_ts_params = lgdt330x_set_ts_param, }; #endif @@ -489,8 +466,6 @@ static int nxt200x_set_pll_input(u8* buf, int input) static struct nxt200x_config ati_hdtvwonder = { .demod_address = 0x0a, - .pll_address = 0x61, - .pll_desc = &dvb_pll_tuv1236d, .set_pll_input = nxt200x_set_pll_input, .set_ts_params = nxt200x_set_ts_param, }; @@ -542,6 +517,9 @@ static int dvb_register(struct cx8802_dev *dev) case CX88_BOARD_HAUPPAUGE_DVB_T1: dev->dvb.frontend = cx22702_attach(&hauppauge_novat_config, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->core->i2c_adap, &dvb_pll_thomson_dtt759x); + } break; case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: case CX88_BOARD_CONEXANT_DVB_T1: @@ -549,44 +527,55 @@ static int dvb_register(struct cx8802_dev *dev) case CX88_BOARD_WINFAST_DTV1000: dev->dvb.frontend = cx22702_attach(&connexant_refboard_config, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x60, &dev->core->i2c_adap, &dvb_pll_thomson_dtt7579); + } break; case CX88_BOARD_HAUPPAUGE_HVR1100: case CX88_BOARD_HAUPPAUGE_HVR1100LP: dev->dvb.frontend = cx22702_attach(&hauppauge_hvr1100_config, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->core->i2c_adap, &dvb_pll_fmd1216me); + } break; #endif #if defined(HAVE_MT352) || defined(HAVE_ZL10353) case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: - dev->core->pll_addr = 0x60; - dev->core->pll_desc = &dvb_pll_thomson_dtt7579; #ifdef HAVE_MT352 dev->dvb.frontend = mt352_attach(&dvico_fusionhdtv, &dev->core->i2c_adap); - if (dev->dvb.frontend != NULL) + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x60, &dev->core->i2c_adap, &dvb_pll_thomson_dtt7579); break; + } #endif #ifdef HAVE_ZL10353 /* ZL10353 replaces MT352 on later cards */ dev->dvb.frontend = zl10353_attach(&dvico_fusionhdtv_plus_v1_1, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x60, &dev->core->i2c_adap, &dvb_pll_thomson_dtt7579); + } #endif break; #endif /* HAVE_MT352 || HAVE_ZL10353 */ #ifdef HAVE_MT352 case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1: - dev->core->pll_addr = 0x61; - dev->core->pll_desc = &dvb_pll_lg_z201; dev->dvb.frontend = mt352_attach(&dvico_fusionhdtv, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->core->i2c_adap, &dvb_pll_lg_z201); + } break; case CX88_BOARD_KWORLD_DVB_T: case CX88_BOARD_DNTV_LIVE_DVB_T: case CX88_BOARD_ADSTECH_DVB_T_PCI: - dev->core->pll_addr = 0x61; - dev->core->pll_desc = &dvb_pll_unknown_1; dev->dvb.frontend = mt352_attach(&dntv_live_dvbt_config, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->core->i2c_adap, &dvb_pll_unknown_1); + } break; case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: #ifdef HAVE_VP3054_I2C @@ -594,6 +583,9 @@ static int dvb_register(struct cx8802_dev *dev) dev->core->pll_desc = &dvb_pll_fmd1216me; dev->dvb.frontend = mt352_attach(&dntv_live_dvbt_pro_config, &((struct vp3054_i2c_state *)dev->card_priv)->adap); + if (dev->dvb.frontend != NULL) { + dev->dvb.frontend->ops->tuner_ops.set_params = dntv_live_dvbt_pro_tuner_set_params; + } #else printk("%s: built without vp3054 support\n", dev->core->name); #endif @@ -601,10 +593,11 @@ static int dvb_register(struct cx8802_dev *dev) case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL: /* The tin box says DEE1601, but it seems to be DTT7579 * compatible, with a slightly different MT352 AGC gain. */ - dev->core->pll_addr = 0x61; - dev->core->pll_desc = &dvb_pll_thomson_dtt7579; dev->dvb.frontend = mt352_attach(&dvico_fusionhdtv_dual, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->core->i2c_adap, &dvb_pll_thomson_dtt7579); + } break; #endif #ifdef HAVE_ZL10353 @@ -613,12 +606,18 @@ static int dvb_register(struct cx8802_dev *dev) dev->core->pll_desc = &dvb_pll_thomson_fe6600; dev->dvb.frontend = zl10353_attach(&dvico_fusionhdtv_hybrid, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dev->dvb.frontend->ops->tuner_ops.set_params = dvico_hybrid_tuner_set_params; + } break; #endif #ifdef HAVE_OR51132 case CX88_BOARD_PCHDTV_HD3000: dev->dvb.frontend = or51132_attach(&pchdtv_hd3000, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->core->i2c_adap, &dvb_pll_thomson_dtt761x); + } break; #endif #ifdef HAVE_LGDT330X @@ -639,6 +638,9 @@ static int dvb_register(struct cx8802_dev *dev) dev->core->pll_desc = &dvb_pll_microtune_4042; dev->dvb.frontend = lgdt330x_attach(&fusionhdtv_3_gold, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dev->dvb.frontend->ops->tuner_ops.set_params = lgdt3302_tuner_set_params; + } } break; case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T: @@ -655,6 +657,9 @@ static int dvb_register(struct cx8802_dev *dev) dev->core->pll_desc = &dvb_pll_thomson_dtt761x; dev->dvb.frontend = lgdt330x_attach(&fusionhdtv_3_gold, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dev->dvb.frontend->ops->tuner_ops.set_params = lgdt3302_tuner_set_params; + } } break; case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD: @@ -669,6 +674,9 @@ static int dvb_register(struct cx8802_dev *dev) mdelay(200); dev->dvb.frontend = lgdt330x_attach(&fusionhdtv_5_gold, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dev->dvb.frontend->ops->tuner_ops.set_params = lgdt3303_tuner_set_params; + } } break; case CX88_BOARD_PCHDTV_HD5500: @@ -683,6 +691,9 @@ static int dvb_register(struct cx8802_dev *dev) mdelay(200); dev->dvb.frontend = lgdt330x_attach(&pchdtv_hd5500, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dev->dvb.frontend->ops->tuner_ops.set_params = lgdt3303_tuner_set_params; + } } break; #endif @@ -690,6 +701,9 @@ static int dvb_register(struct cx8802_dev *dev) case CX88_BOARD_ATI_HDTVWONDER: dev->dvb.frontend = nxt200x_attach(&ati_hdtvwonder, &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->core->i2c_adap, &dvb_pll_tuv1236d); + } break; #endif #ifdef HAVE_CX24123 -- cgit v1.2.3 From b641e6d90fe3ff6e71a613888d1c1ec22842adda Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Wed, 19 Apr 2006 01:37:20 +0100 Subject: Trim documentation From: Andrew de Quincey Trim excess documentation down to the essentials. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/dvb-core/dvb_frontend.h | 85 ++----------------------- 1 file changed, 7 insertions(+), 78 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h index e0148a9e6..05ec9954b 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -62,99 +62,28 @@ struct dvb_tuner_info { }; struct dvb_tuner_ops { - /** - * Description of the tuner. - */ + struct dvb_tuner_info info; - /** - * Cleanup an attached tuner. - * - * @param fe dvb_frontend structure to clean it up from. - * @return 0 on success, <0 on failure. - */ int (*release)(struct dvb_frontend *fe); - - /** - * Initialise a tuner. - * - * @param fe dvb_frontend structure. - * @return 0 on success, <0 on failure. - */ int (*init)(struct dvb_frontend *fe); - - /** - * Set a tuner into low power mode. - * - * @param fe dvb_frontend structure. - * @return 0 on success, <0 on failure. - */ int (*sleep)(struct dvb_frontend *fe); - /** - * This is for simple PLLs - set all parameters in one go. - * - * @param fe The dvb_frontend structure. - * @param p The parameters to set. - * @return 0 on success, <0 on failure. - */ + /** This is for simple PLLs - set all parameters in one go. */ int (*set_params)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p); - /** - * This is support for demods like the mt352 - fills out the supplied buffer with what to write. - * - * @param fe The dvb_frontend structure. - * @param p The parameters to set. - * @param buf The buffer to fill with data. For an i2c tuner, the first byte should be the tuner i2c address in linux format. - * @param buf_len Size of buffer in bytes. - * @return Number of bytes used, or <0 on failure. - */ - int (*pllbuf)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p, u8 *buf, int buf_len); - - /** - * Get the frequency the tuner was actually set to. - * - * @param fe The dvb_frontend structure. - * @param frequency Where to put it. - * @return 0 on success, or <0 on failure. - */ - int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency); + /** This is support for demods like the mt352 - fills out the supplied buffer with what to write. */ + int (*calc_regs)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p, u8 *buf, int buf_len); - /** - * Get the bandwidth the tuner was actually set to. - * - * @param fe The dvb_frontend structure. - * @param bandwidth Where to put it. - * @return 0 on success, or <0 on failure. - */ + int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency); int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth); - /** - * Get the tuner's status. - * - * @param fe The dvb_frontend structure. - * @param status Where to put it. - * @return 0 on success, or <0 on failure. - */ #define TUNER_STATUS_LOCKED 1 int (*get_status)(struct dvb_frontend *fe, u32 *status); - /** - * Set the frequency of the tuner - for complex tuners. - * - * @param fe The dvb_frontend structure. - * @param frequency What to set. - * @return 0 on success, or <0 on failure. - */ + /** These are provided seperately from set_params in order to facilitate silicon + * tuners which require sophisticated tuning loops, controlling each parameter seperately. */ int (*set_frequency)(struct dvb_frontend *fe, u32 frequency); - - /** - * Set the bandwidth of the tuner - for complex tuners. - * - * @param fe The dvb_frontend structure. - * @param bandwidth What to set. - * @return 0 on success, or <0 on failure. - */ int (*set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth); }; -- cgit v1.2.3 From 8c360c7605e4751038ad67e9fd79e5c1c96b81d6 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Wed, 19 Apr 2006 01:38:49 +0100 Subject: Convert calls from _pllbuf() to _calc_reg() From: Andrew de Quincey Johannes Stezenbach suggested this change - definite improvement. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c | 4 ++-- linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c | 12 ++++++------ linux/drivers/media/dvb/dvb-usb/cxusb.c | 4 ++-- linux/drivers/media/dvb/dvb-usb/digitv.c | 4 ++-- linux/drivers/media/dvb/dvb-usb/dvb-usb.h | 2 +- linux/drivers/media/dvb/dvb-usb/umt-010.c | 2 +- linux/drivers/media/dvb/frontends/dvb-pll.c | 4 ++-- linux/drivers/media/dvb/frontends/mt352.c | 4 ++-- linux/drivers/media/dvb/frontends/nxt200x.c | 4 ++-- linux/drivers/media/dvb/frontends/zl10353.c | 4 ++-- linux/drivers/media/video/saa7134/saa7134-dvb.c | 8 ++++---- 11 files changed, 26 insertions(+), 26 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index f93a3c78a..630782f88 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -268,7 +268,7 @@ static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe) return 0; } -static int samsung_tdtc9251dh0_pllbuf(struct dvb_frontend* fe, struct dvb_frontend_parameters *params, u8* pllbuf, int buf_len) +static int samsung_tdtc9251dh0_calc_regs(struct dvb_frontend* fe, struct dvb_frontend_parameters *params, u8* pllbuf, int buf_len) { u32 div; unsigned char bs = 0; @@ -427,7 +427,7 @@ int flexcop_frontend_init(struct flexcop_device *fc) /* try the air dvb-t (mt352/Samsung tdtc9251dh0(??)) */ if ((fc->fe = mt352_attach(&samsung_tdtc9251dh0_config, &fc->i2c_adap)) != NULL ) { fc->dev_type = FC_AIR_DVB; - fc->fe->ops->tuner_ops.pllbuf = samsung_tdtc9251dh0_pllbuf; + fc->fe->ops->tuner_ops.calc_regs = samsung_tdtc9251dh0_calc_regs; info("found the mt352 at i2c address: 0x%02x",samsung_tdtc9251dh0_config.demod_address); } else /* try the air atsc 2nd generation (nxt2002) */ diff --git a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c index d40a3703f..c4ffdb375 100644 --- a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -148,7 +148,7 @@ static int thomson_dtt7579_demod_init(struct dvb_frontend* fe) return 0; } -static int thomson_dtt7579_tuner_pllbuf(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf, int buf_len) +static int thomson_dtt7579_tuner_calc_regs(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf, int buf_len) { u32 div; unsigned char bs = 0; @@ -342,7 +342,7 @@ static int advbt771_samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe) return 0; } -static int advbt771_samsung_tdtc9251dh0_tuner_pllbuf(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf, int buf_len) +static int advbt771_samsung_tdtc9251dh0_tuner_calc_regs(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf, int buf_len) { u32 div; unsigned char bs = 0; @@ -513,7 +513,7 @@ static int digitv_alps_tded4_demod_init(struct dvb_frontend* fe) return 0; } -static int digitv_alps_tded4_tuner_pllbuf(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf, int buf_len) +static int digitv_alps_tded4_tuner_calc_regs(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf, int buf_len) { u32 div; struct dvb_ofdm_parameters *op = ¶ms->u.ofdm; @@ -608,7 +608,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) card->i2c_adapter); if (card->fe != NULL) { - card->fe->ops->tuner_ops.pllbuf = thomson_dtt7579_tuner_pllbuf; + card->fe->ops->tuner_ops.calc_regs = thomson_dtt7579_tuner_calc_regs; card->fe->ops->info.frequency_min = 174000000; card->fe->ops->info.frequency_max = 862000000; } @@ -643,7 +643,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) card->fe = mt352_attach(&digitv_alps_tded4_config, card->i2c_adapter); if (card->fe != NULL) { - card->fe->ops->tuner_ops.pllbuf = digitv_alps_tded4_tuner_pllbuf; + card->fe->ops->tuner_ops.calc_regs = digitv_alps_tded4_tuner_calc_regs; dprintk ("dvb_bt8xx: an mt352 was detected on your digitv card\n"); } break; @@ -658,7 +658,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) case BTTV_BOARD_AVDVBT_771: card->fe = mt352_attach(&advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter); if (card->fe != NULL) { - card->fe->ops->tuner_ops.pllbuf = advbt771_samsung_tdtc9251dh0_tuner_pllbuf; + card->fe->ops->tuner_ops.calc_regs = advbt771_samsung_tdtc9251dh0_tuner_calc_regs; card->fe->ops->info.frequency_min = 174000000; card->fe->ops->info.frequency_max = 862000000; } diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index eba635536..e93f7b092 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -414,7 +414,7 @@ static int cxusb_mt352_frontend_attach(struct dvb_usb_device *d) cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, NULL, 0); if ((d->fe = mt352_attach(&cxusb_mt352_config, &d->i2c_adap)) != NULL) { - d->fe->ops->tuner_ops.pllbuf = dvb_usb_tuner_pllbuf; + d->fe->ops->tuner_ops.calc_regs = dvb_usb_tuner_calc_regs; return 0; } @@ -429,7 +429,7 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_device *d) cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, NULL, 0); if ((d->fe = mt352_attach(&cxusb_dee1601_config, &d->i2c_adap)) != NULL) { - d->fe->ops->tuner_ops.pllbuf = dvb_usb_tuner_pllbuf; + d->fe->ops->tuner_ops.calc_regs = dvb_usb_tuner_calc_regs; return 0; } diff --git a/linux/drivers/media/dvb/dvb-usb/digitv.c b/linux/drivers/media/dvb/dvb-usb/digitv.c index eebf0d144..34dbf1a49 100644 --- a/linux/drivers/media/dvb/dvb-usb/digitv.c +++ b/linux/drivers/media/dvb/dvb-usb/digitv.c @@ -118,7 +118,7 @@ static int digitv_nxt6000_tuner_set_params(struct dvb_frontend *fe, struct dvb_f { struct dvb_usb_device *d = fe->dvb->priv; u8 b[5]; - dvb_usb_tuner_pllbuf(fe,fep,b, 5); + dvb_usb_tuner_calc_regs(fe,fep,b, 5); return digitv_ctrl_msg(d,USB_WRITE_TUNER,0,&b[1],4,NULL,0); } @@ -129,7 +129,7 @@ static struct nxt6000_config digitv_nxt6000_config = { static int digitv_frontend_attach(struct dvb_usb_device *d) { if ((d->fe = mt352_attach(&digitv_mt352_config, &d->i2c_adap)) != NULL) { - d->fe->ops->tuner_ops.pllbuf = dvb_usb_tuner_pllbuf; + d->fe->ops->tuner_ops.calc_regs = dvb_usb_tuner_calc_regs; return 0; } if ((d->fe = nxt6000_attach(&digitv_nxt6000_config, &d->i2c_adap)) != NULL) { diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb.h b/linux/drivers/media/dvb/dvb-usb/dvb-usb.h index 2696ee9d8..bf342faa4 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb.h +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb.h @@ -342,7 +342,7 @@ extern int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *, u8[], u32 *, int /* commonly used pll init and set functions */ extern int dvb_usb_tuner_init_i2c(struct dvb_frontend *); -extern int dvb_usb_tuner_pllbuf(struct dvb_frontend *, struct dvb_frontend_parameters *, u8 *buf, int buf_len); +extern int dvb_usb_tuner_calc_regs(struct dvb_frontend *, struct dvb_frontend_parameters *, u8 *buf, int buf_len); extern int dvb_usb_tuner_set_params_i2c(struct dvb_frontend *, struct dvb_frontend_parameters *); /* commonly used firmware download types and function */ diff --git a/linux/drivers/media/dvb/dvb-usb/umt-010.c b/linux/drivers/media/dvb/dvb-usb/umt-010.c index 26efac60e..04a45f2e4 100644 --- a/linux/drivers/media/dvb/dvb-usb/umt-010.c +++ b/linux/drivers/media/dvb/dvb-usb/umt-010.c @@ -57,7 +57,7 @@ static int umt_mt352_frontend_attach(struct dvb_usb_device *d) memset(&umt_config,0,sizeof(struct mt352_config)); umt_config.demod_init = umt_mt352_demod_init; umt_config.demod_address = 0xf; - d->fe->ops->tuner_ops.pllbuf = dvb_usb_tuner_pllbuf; + d->fe->ops->tuner_ops.calc_regs = dvb_usb_tuner_calc_regs; d->fe = mt352_attach(&umt_config, &d->i2c_adap); diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.c b/linux/drivers/media/dvb/frontends/dvb-pll.c index 5f463e8ae..1a92ac8d2 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.c +++ b/linux/drivers/media/dvb/frontends/dvb-pll.c @@ -555,7 +555,7 @@ static int dvb_pll_set_params(struct dvb_frontend *fe, struct dvb_frontend_param return 0; } -static int dvb_pll_pllbuf(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, u8 *buf, int buf_len) +static int dvb_pll_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, u8 *buf, int buf_len) { struct dvb_pll_priv *priv = fe->tuner_priv; int result; @@ -606,7 +606,7 @@ static struct dvb_tuner_ops dvb_pll_tuner_ops = { .release = dvb_pll_release, .sleep = dvb_pll_sleep, .set_params = dvb_pll_set_params, - .pllbuf = dvb_pll_pllbuf, + .calc_regs = dvb_pll_calc_regs, .get_frequency = dvb_pll_get_frequency, .get_bandwidth = dvb_pll_get_bandwidth, }; diff --git a/linux/drivers/media/dvb/frontends/mt352.c b/linux/drivers/media/dvb/frontends/mt352.c index 3c86018e7..ed3bede02 100644 --- a/linux/drivers/media/dvb/frontends/mt352.c +++ b/linux/drivers/media/dvb/frontends/mt352.c @@ -302,8 +302,8 @@ static int mt352_set_parameters(struct dvb_frontend* fe, // retrieve the pllbuf - we do this even if there is no // secondary tuner simply so we have a record of what was sent for // debugging. - if (fe->ops->tuner_ops.pllbuf) { - fe->ops->tuner_ops.pllbuf(fe, param, buf+8, 5); + if (fe->ops->tuner_ops.calc_regs) { + fe->ops->tuner_ops.calc_regs(fe, param, buf+8, 5); buf[8] <<= 1; mt352_write(fe, buf, sizeof(buf)); } diff --git a/linux/drivers/media/dvb/frontends/nxt200x.c b/linux/drivers/media/dvb/frontends/nxt200x.c index 809ffd40f..9b13f14f1 100644 --- a/linux/drivers/media/dvb/frontends/nxt200x.c +++ b/linux/drivers/media/dvb/frontends/nxt200x.c @@ -548,8 +548,8 @@ static int nxt200x_setup_frontend_parameters (struct dvb_frontend* fe, } /* get tuning information */ - if (fe->ops->tuner_ops.pllbuf) { - fe->ops->tuner_ops.pllbuf(fe, p, buf, 5); + if (fe->ops->tuner_ops.calc_regs) { + fe->ops->tuner_ops.calc_regs(fe, p, buf, 5); } /* set additional params */ diff --git a/linux/drivers/media/dvb/frontends/zl10353.c b/linux/drivers/media/dvb/frontends/zl10353.c index f4851907a..1018baae0 100644 --- a/linux/drivers/media/dvb/frontends/zl10353.c +++ b/linux/drivers/media/dvb/frontends/zl10353.c @@ -157,8 +157,8 @@ static int zl10353_set_parameters(struct dvb_frontend *fe, } // if pllbuf is defined, retrieve the settings - if (fe->ops->tuner_ops.pllbuf) { - fe->ops->tuner_ops.pllbuf(fe, param, pllbuf+1, 5); + if (fe->ops->tuner_ops.calc_regs) { + fe->ops->tuner_ops.calc_regs(fe, param, pllbuf+1, 5); pllbuf[1] <<= 1; } else { // fake pllbuf settings diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index cb0ffa50f..51d88c152 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -133,7 +133,7 @@ static int mt352_aver777_init(struct dvb_frontend* fe) return 0; } -static int mt352_pinnacle_tuner_pllbuf(struct dvb_frontend* fe, +static int mt352_pinnacle_tuner_calc_regs(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf, int buf_len) { @@ -168,7 +168,7 @@ static int mt352_pinnacle_tuner_pllbuf(struct dvb_frontend* fe, return 5; } -static int mt352_aver777_tuner_pllbuf(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, u8* pllbuf, int buf_len) +static int mt352_aver777_tuner_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, u8* pllbuf, int buf_len) { if (buf_len < 5) return -EINVAL; @@ -1021,14 +1021,14 @@ static int dvb_init(struct saa7134_dev *dev) printk("%s: pinnacle 300i dvb setup\n",dev->name); dev->dvb.frontend = mt352_attach(&pinnacle_300i, &dev->i2c_adap); - dev->dvb.frontend->ops->tuner_ops.pllbuf = mt352_pinnacle_tuner_pllbuf; + dev->dvb.frontend->ops->tuner_ops.calc_regs = mt352_pinnacle_tuner_calc_regs; break; case SAA7134_BOARD_AVERMEDIA_777: printk("%s: avertv 777 dvb setup\n",dev->name); dev->dvb.frontend = mt352_attach(&avermedia_777, &dev->i2c_adap); - dev->dvb.frontend->ops->tuner_ops.pllbuf = mt352_aver777_tuner_pllbuf; + dev->dvb.frontend->ops->tuner_ops.calc_regs = mt352_aver777_tuner_calc_regs; break; #endif #ifdef HAVE_TDA1004X -- cgit v1.2.3 From 48c7a56c98cf2342c83cedb2e25748b602383d9c Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Wed, 19 Apr 2006 22:31:03 +0100 Subject: Fix tda10046 tuning From: Andrew de Quincey I'd missed the i2c gate ctrl function out of the tda10046, so tuning didn't work Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/dvb-usb/cxusb.c | 4 ++-- linux/drivers/media/dvb/frontends/tda1004x.c | 1 + linux/drivers/media/video/cx88/cx88-dvb.c | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index e93f7b092..c5b5db7bc 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -315,7 +315,7 @@ static int cxusb_mt352_demod_init(struct dvb_frontend* fe) } static int cxusb_lgh064f_tuner_set_params(struct dvb_frontend *fe, - struct dvb_frontend_parameters *fep) + struct dvb_frontend_parameters *fep) { struct dvb_usb_device *d = fe->dvb->priv; return lg_h06xf_pll_set(fe, &d->i2c_adap, fep); @@ -429,7 +429,7 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_device *d) cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, NULL, 0); if ((d->fe = mt352_attach(&cxusb_dee1601_config, &d->i2c_adap)) != NULL) { - d->fe->ops->tuner_ops.calc_regs = dvb_usb_tuner_calc_regs; + d->fe->ops->tuner_ops.calc_regs = dvb_usb_tuner_calc_regs; return 0; } diff --git a/linux/drivers/media/dvb/frontends/tda1004x.c b/linux/drivers/media/dvb/frontends/tda1004x.c index d9abce753..5288b44cf 100644 --- a/linux/drivers/media/dvb/frontends/tda1004x.c +++ b/linux/drivers/media/dvb/frontends/tda1004x.c @@ -1276,6 +1276,7 @@ static struct dvb_frontend_ops tda10046_ops = { .init = tda10046_init, .sleep = tda1004x_sleep, + .i2c_gate_ctrl = tda1004x_i2c_gate_ctrl, .set_frontend = tda1004x_set_fe, .get_frontend = tda1004x_get_fe, diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index 14b12b6c6..fbd8387de 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -390,7 +390,7 @@ static int lgdt3302_tuner_set_params(struct dvb_frontend* fe, } static int lgdt3303_tuner_set_params(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params) + struct dvb_frontend_parameters* params) { struct cx8802_dev *dev= fe->dvb->priv; struct cx88_core *core = dev->core; -- cgit v1.2.3 From fb70cc5ac6a14c86a9573bbb7ec6b95d32e287bb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 19 Apr 2006 23:50:35 +0200 Subject: Add support for Samsung TCPG 6121P30A PAL tuner. From: Hans Verkuil Signed-off-by: Hans Verkuil --- linux/drivers/media/video/tuner-types.c | 21 +++++++++++++++++++++ linux/drivers/media/video/tveeprom.c | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/tuner-types.c b/linux/drivers/media/video/tuner-types.c index 409603c38..897270f56 100644 --- a/linux/drivers/media/video/tuner-types.c +++ b/linux/drivers/media/video/tuner-types.c @@ -1072,6 +1072,22 @@ static struct tuner_params tuner_thomson_fe6600_params[] = { }, }; +/* ------------ TUNER_SAMSUNG_TCPG_6121P30A - Samsung PAL ------------ */ + +static struct tuner_range tuner_samsung_tcpg_6121p30a_pal_ranges[] = { + { 16 * 146.25 /*MHz*/, 0xce, 0x01, }, + { 16 * 428.50 /*MHz*/, 0xce, 0x02, }, + { 16 * 999.99 , 0xce, 0x08, }, +}; + +static struct tuner_params tuner_samsung_tcpg_6121p30a_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_samsung_tcpg_6121p30a_pal_ranges, + .count = ARRAY_SIZE(tuner_samsung_tcpg_6121p30a_pal_ranges), + }, +}; + /* --------------------------------------------------------------------- */ struct tunertype tuners[] = { @@ -1448,6 +1464,11 @@ struct tunertype tuners[] = { .params = tuner_thomson_fe6600_params, .count = ARRAY_SIZE(tuner_thomson_fe6600_params), }, + [TUNER_SAMSUNG_TCPG_6121P30A] = { /* Samsung PAL */ + .name = "Samsung TCPG 6121P30A", + .params = tuner_samsung_tcpg_6121p30a_params, + .count = ARRAY_SIZE(tuner_samsung_tcpg_6121p30a_params), + }, }; unsigned const int tuner_count = ARRAY_SIZE(tuners); diff --git a/linux/drivers/media/video/tveeprom.c b/linux/drivers/media/video/tveeprom.c index 5b9c4446b..635f618ef 100644 --- a/linux/drivers/media/video/tveeprom.c +++ b/linux/drivers/media/video/tveeprom.c @@ -204,7 +204,7 @@ hauppauge_tuner[] = { TUNER_ABSENT, "Philips FQ1286A MK4"}, { TUNER_ABSENT, "Philips FQ1216ME MK5"}, { TUNER_ABSENT, "Philips FQ1236 MK5"}, - { TUNER_ABSENT, "Samsung TCPG_6121P30A"}, + { TUNER_SAMSUNG_TCPG_6121P30A, "Samsung TCPG 6121P30A"}, { TUNER_TCL_2002MB, "TCL 2002MB_3H"}, { TUNER_ABSENT, "TCL 2002MI_3H"}, { TUNER_TCL_2002N, "TCL 2002N 5H"}, -- cgit v1.2.3 From 7975c8e3b6b01e4b1d709b8ea91f5e0b216cdce1 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Wed, 19 Apr 2006 19:40:01 -0400 Subject: cxusb-bluebird: bug-fix: power down corrupts frontend From: Michael Krufky This patch prevents a bug where the frontend is unable to tune after waking from powered down state. Now, the device remains powered on until it is disconnected, just like the windows driver. It seems that the bluebird firmware is unable to successfully handle tuning after a powered down state. This patch fixes all of the FusionHDTV Bluebird USB2 devices. The Medion MD95700 will still behave as before, since it was unaffected by this bug. Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/dvb-usb/cxusb.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index a34068f50..b7ca16774 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -150,6 +150,15 @@ static int cxusb_power_ctrl(struct dvb_usb_device *d, int onoff) return cxusb_ctrl_msg(d, CMD_POWER_OFF, &b, 1, NULL, 0); } +static int cxusb_bluebird_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 b = 0; + if (onoff) + return cxusb_ctrl_msg(d, CMD_POWER_ON, &b, 1, NULL, 0); + else + return 0; +} + static int cxusb_streaming_ctrl(struct dvb_usb_device *d, int onoff) { u8 buf[2] = { 0x03, 0x00 }; @@ -570,7 +579,7 @@ static struct dvb_usb_properties cxusb_bluebird_lgh064f_properties = { .size_of_priv = sizeof(struct cxusb_state), .streaming_ctrl = cxusb_streaming_ctrl, - .power_ctrl = cxusb_power_ctrl, + .power_ctrl = cxusb_bluebird_power_ctrl, .frontend_attach = cxusb_lgdt3303_frontend_attach, .tuner_attach = cxusb_lgh064f_tuner_attach, @@ -615,7 +624,7 @@ static struct dvb_usb_properties cxusb_bluebird_dee1601_properties = { .size_of_priv = sizeof(struct cxusb_state), .streaming_ctrl = cxusb_streaming_ctrl, - .power_ctrl = cxusb_power_ctrl, + .power_ctrl = cxusb_bluebird_power_ctrl, .frontend_attach = cxusb_dee1601_frontend_attach, .tuner_attach = cxusb_dee1601_tuner_attach, @@ -664,7 +673,7 @@ static struct dvb_usb_properties cxusb_bluebird_lgz201_properties = { .size_of_priv = sizeof(struct cxusb_state), .streaming_ctrl = cxusb_streaming_ctrl, - .power_ctrl = cxusb_power_ctrl, + .power_ctrl = cxusb_bluebird_power_ctrl, .frontend_attach = cxusb_mt352_frontend_attach, .tuner_attach = cxusb_lgz201_tuner_attach, @@ -709,7 +718,7 @@ static struct dvb_usb_properties cxusb_bluebird_dtt7579_properties = { .size_of_priv = sizeof(struct cxusb_state), .streaming_ctrl = cxusb_streaming_ctrl, - .power_ctrl = cxusb_power_ctrl, + .power_ctrl = cxusb_bluebird_power_ctrl, .frontend_attach = cxusb_mt352_frontend_attach, .tuner_attach = cxusb_dtt7579_tuner_attach, -- cgit v1.2.3 From 18766ed521f71541ef32188c67e260f66a9b523d Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Wed, 19 Apr 2006 19:49:28 -0400 Subject: drivers/media/dvb/dvb-usb/: possible cleanups From: Adrian Bunk - make the following needlessly global struct static: dvb-usb/cxusb.c: cxusb_mt352_config Signed-off-by: Adrian Bunk Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/dvb-usb/cxusb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index b7ca16774..d40b9dba1 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -373,7 +373,7 @@ static struct mt352_config cxusb_dee1601_config = { .pll_set = dvb_usb_pll_set, }; -struct mt352_config cxusb_mt352_config = { +static struct mt352_config cxusb_mt352_config = { /* used in both lgz201 and th7579 */ .demod_address = 0x0f, .demod_init = cxusb_mt352_demod_init, -- cgit v1.2.3 From a3f7122e105b35188a9e1bc89fdc15758fae8b15 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Wed, 19 Apr 2006 22:53:11 -0500 Subject: Get rid of redundant inclusion of videodev2.h From: Mike Isely Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c | 1 - 1 file changed, 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c index fb83cd2c9..25543e9b1 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c @@ -24,7 +24,6 @@ #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" #include -#include static void set_standard(struct pvr2_hdw *hdw) -- cgit v1.2.3 From bbfdc6d1fdaea36ca996bb4cfdcc493d998a65f6 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Wed, 19 Apr 2006 23:56:17 -0500 Subject: Rework pvrusb2 internal controls implementation From: Mike Isely Rework controls internal architecture. Rework video standard handling. This is a major change. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-audio.c | 10 +- .../drivers/media/video/pvrusb2/pvrusb2-context.c | 2 - linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c | 549 ++++++++ linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h | 115 ++ .../media/video/pvrusb2/pvrusb2-cx2584x-v4l.c | 10 +- .../drivers/media/video/pvrusb2/pvrusb2-encoder.c | 26 +- .../media/video/pvrusb2/pvrusb2-hdw-internal.h | 172 ++- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 1485 ++++++++------------ linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h | 95 +- .../media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c | 67 +- linux/drivers/media/video/pvrusb2/pvrusb2-std.c | 377 +++++ linux/drivers/media/video/pvrusb2/pvrusb2-std.h | 61 + linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c | 315 ++--- linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 258 ++-- .../media/video/pvrusb2/pvrusb2-video-v4l.c | 13 +- linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c | 4 +- 16 files changed, 2100 insertions(+), 1459 deletions(-) create mode 100644 linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c create mode 100644 linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h create mode 100644 linux/drivers/media/video/pvrusb2/pvrusb2-std.c create mode 100644 linux/drivers/media/video/pvrusb2/pvrusb2-std.h (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c index 7d5997bdc..30e3a38f6 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -45,16 +45,16 @@ static void set_stereo(struct pvr2_msp3400_handler *ctxt) pvr2_trace(PVR2_TRACE_CHIPS,"i2c msp3400 v4l2 set_stereo"); - if (hdw->controls[PVR2_CID_INPUT].value == PVR2_CVAL_INPUT_TV) { + if (hdw->input_val == PVR2_CVAL_INPUT_TV) { struct v4l2_tuner vt; memset(&vt,0,sizeof(vt)); - vt.audmode = hdw->controls[PVR2_CID_AUDIOMODE].value; + vt.audmode = hdw->audiomode_val; pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_TUNER,&vt); } route.input = MSP_INPUT_DEFAULT; route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); - switch (hdw->controls[PVR2_CID_INPUT].value) { + switch (hdw->input_val) { case PVR2_CVAL_INPUT_TV: break; case PVR2_CVAL_INPUT_RADIO: @@ -79,8 +79,8 @@ static void set_stereo(struct pvr2_msp3400_handler *ctxt) static int check_stereo(struct pvr2_msp3400_handler *ctxt) { struct pvr2_hdw *hdw = ctxt->hdw; - return ((hdw->controls[PVR2_CID_INPUT].dirty != 0)|| - (hdw->controls[PVR2_CID_AUDIOMODE].dirty != 0)); + return (hdw->input_dirty || + hdw->audiomode_dirty); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-context.c b/linux/drivers/media/video/pvrusb2/pvrusb2-context.c index f3fd61f21..40dc59871 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-context.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-context.c @@ -47,11 +47,9 @@ static void pvr2_context_trigger_poll(struct pvr2_context *mp) static void pvr2_context_poll(struct pvr2_context *mp) { - pvr2_trace(PVR2_TRACE_DEBUG,"pvr2_context_poll BEGIN"); pvr2_context_enter(mp); do { pvr2_hdw_poll(mp->hdw); } while (0); pvr2_context_exit(mp); - pvr2_trace(PVR2_TRACE_DEBUG,"pvr2_context_poll END"); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c new file mode 100644 index 000000000..b2c43a389 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c @@ -0,0 +1,549 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely + * + * 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 "pvrusb2-ctrl.h" +#include "pvrusb2-hdw-internal.h" +#include "compat.h" +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) +#include +#else +#include +#endif + + +/* Set the given control. */ +int pvr2_ctrl_set_value(struct pvr2_ctrl *cptr,int val) +{ + return pvr2_ctrl_set_mask_value(cptr,~0,val); +} + + +/* Set/clear specific bits of the given control. */ +int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val) +{ + int ret = 0; + if (!cptr) return -EINVAL; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->set_value != 0) { + if (cptr->info->type == pvr2_ctl_bitmask) { + mask &= cptr->info->def.type_bitmask.valid_bits; + } else if (cptr->info->type == pvr2_ctl_int) { + if (val < cptr->info->def.type_int.min_value) { + break; + } + if (val > cptr->info->def.type_int.max_value) { + break; + } + } else if (cptr->info->type == pvr2_ctl_enum) { + if (val >= cptr->info->def.type_enum.count) { + break; + } + } + ret = cptr->info->set_value(cptr,mask,val); + } else { + ret = -EPERM; + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Get the current value of the given control. */ +int pvr2_ctrl_get_value(struct pvr2_ctrl *cptr,int *valptr) +{ + int ret = 0; + if (!cptr) return -EINVAL; + LOCK_TAKE(cptr->hdw->big_lock); do { + ret = cptr->info->get_value(cptr,valptr); + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Retrieve control's type */ +enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *cptr) +{ + if (!cptr) return pvr2_ctl_int; + return cptr->info->type; +} + + +/* Retrieve control's maximum value (int type) */ +int pvr2_ctrl_get_max(struct pvr2_ctrl *cptr) +{ + int ret = 0; + if (!cptr) return 0; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->type == pvr2_ctl_int) { + ret = cptr->info->def.type_int.max_value; + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Retrieve control's minimum value (int type) */ +int pvr2_ctrl_get_min(struct pvr2_ctrl *cptr) +{ + int ret = 0; + if (!cptr) return 0; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->type == pvr2_ctl_int) { + ret = cptr->info->def.type_int.min_value; + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Retrieve control's default value (any type) */ +int pvr2_ctrl_get_def(struct pvr2_ctrl *cptr) +{ + int ret = 0; + if (!cptr) return 0; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->type == pvr2_ctl_int) { + ret = cptr->info->default_value; + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Retrieve control's enumeration count (enum only) */ +int pvr2_ctrl_get_cnt(struct pvr2_ctrl *cptr) +{ + int ret = 0; + if (!cptr) return 0; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->type == pvr2_ctl_enum) { + ret = cptr->info->def.type_enum.count; + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Retrieve control's valid mask bits (bit mask only) */ +int pvr2_ctrl_get_mask(struct pvr2_ctrl *cptr) +{ + int ret = 0; + if (!cptr) return 0; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->type == pvr2_ctl_bitmask) { + ret = cptr->info->def.type_bitmask.valid_bits; + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Retrieve the control's name */ +const char *pvr2_ctrl_get_name(struct pvr2_ctrl *cptr) +{ + if (!cptr) return 0; + return cptr->info->name; +} + + +/* Retrieve the control's desc */ +const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *cptr) +{ + if (!cptr) return 0; + return cptr->info->desc; +} + + +/* Retrieve a control enumeration or bit mask value */ +int pvr2_ctrl_get_valname(struct pvr2_ctrl *cptr,int val, + char *bptr,unsigned int bmax, + unsigned int *blen) +{ + int ret = -EINVAL; + if (!cptr) return 0; + *blen = 0; + LOCK_TAKE(cptr->hdw->big_lock); do { + 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 (names[val]) { + *blen = scnprintf( + bptr,bmax,"%s", + names[val]); + } else { + *blen = 0; + } + ret = 0; + } + } else if (cptr->info->type == pvr2_ctl_bitmask) { + const char **names; + unsigned int idx; + int msk; + names = cptr->info->def.type_bitmask.bit_names; + val &= cptr->info->def.type_bitmask.valid_bits; + for (idx = 0, msk = 1; val; idx++, msk <<= 1) { + if (val & msk) { + *blen = scnprintf(bptr,bmax,"%s", + names[idx]); + ret = 0; + break; + } + } + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Return true if control is writable */ +int pvr2_ctrl_is_writable(struct pvr2_ctrl *cptr) +{ + if (!cptr) return 0; + return cptr->info->set_value != 0; +} + + +/* Return true if control has custom symbolic representation */ +int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *cptr) +{ + if (!cptr) return 0; + if (!cptr->info->val_to_sym) return 0; + if (!cptr->info->sym_to_val) return 0; + return !0; +} + + +/* Convert a given mask/val to a custom symbolic value */ +int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *cptr, + int mask,int val, + char *buf,unsigned int maxlen, + unsigned int *len) +{ + if (!cptr) return -EINVAL; + if (!cptr->info->val_to_sym) return -EINVAL; + return cptr->info->val_to_sym(cptr,mask,val,buf,maxlen,len); +} + + +/* Convert a symbolic value to a mask/value pair */ +int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *cptr, + const char *buf,unsigned int len, + int *maskptr,int *valptr) +{ + if (!cptr) return -EINVAL; + if (!cptr->info->sym_to_val) return -EINVAL; + return cptr->info->sym_to_val(cptr,buf,len,maskptr,valptr); +} + + +static unsigned int gen_bitmask_string(int msk,int val,int msk_only, + const char **names, + char *ptr,unsigned int len) +{ + unsigned int idx; + long sm,um; + int spcFl; + unsigned int uc,cnt; + const char *idStr; + + spcFl = 0; + uc = 0; + um = 0; + for (idx = 0, sm = 1; msk; idx++, sm <<= 1) { + if (sm & msk) { + msk &= ~sm; + idStr = names[idx]; + if (idStr) { + cnt = scnprintf(ptr,len,"%s%s%s", + (spcFl ? " " : ""), + (msk_only ? "" : + ((val & sm) ? "+" : "-")), + idStr); + ptr += cnt; len -= cnt; uc += cnt; + spcFl = !0; + } else { + um |= sm; + } + } + } + if (um) { + if (msk_only) { + cnt = scnprintf(ptr,len,"%s0x%lx", + (spcFl ? " " : ""), + um); + ptr += cnt; len -= cnt; uc += cnt; + spcFl = !0; + } else if (um & val) { + cnt = scnprintf(ptr,len,"%s+0x%lx", + (spcFl ? " " : ""), + um & val); + ptr += cnt; len -= cnt; uc += cnt; + spcFl = !0; + } else if (um & ~val) { + cnt = scnprintf(ptr,len,"%s+0x%lx", + (spcFl ? " " : ""), + um & ~val); + ptr += cnt; len -= cnt; uc += cnt; + spcFl = !0; + } + } + return uc; +} + + +static int parse_token(const char *ptr,unsigned int len, + int *valptr, + const char **names,unsigned int namecnt) +{ + char buf[33]; + unsigned int slen; + unsigned int idx; + int negfl; + char *p2; + *valptr = 0; + if (!names) namecnt = 0; + for (idx = 0; idx < namecnt; idx++) { + if (!names[idx]) continue; + slen = strlen(names[idx]); + if (slen != len) continue; + if (memcmp(names[idx],ptr,slen)) continue; + *valptr = idx; + return 0; + } + negfl = 0; + if ((*ptr == '-') || (*ptr == '+')) { + negfl = (*ptr == '-'); + ptr++; len--; + } + if (len >= sizeof(buf)) return -EINVAL; + memcpy(buf,ptr,len); + buf[len] = 0; + *valptr = simple_strtol(buf,&p2,0); + if (negfl) *valptr = -(*valptr); + if (*p2) return -EINVAL; + return 0; +} + + +static int parse_mtoken(const char *ptr,unsigned int len, + int *valptr, + const char **names,int valid_bits) +{ + char buf[33]; + unsigned int slen; + unsigned int idx; + char *p2; + int msk; + *valptr = 0; + for (idx = 0, msk = 1; valid_bits; idx++, msk <<= 1) { + if (!msk & valid_bits) continue; + valid_bits &= ~msk; + if (!names[idx]) continue; + slen = strlen(names[idx]); + if (slen != len) continue; + if (memcmp(names[idx],ptr,slen)) continue; + *valptr = msk; + return 0; + } + if (len >= sizeof(buf)) return -EINVAL; + memcpy(buf,ptr,len); + buf[len] = 0; + *valptr = simple_strtol(buf,&p2,0); + if (*p2) return -EINVAL; + return 0; +} + + +static int parse_tlist(const char *ptr,unsigned int len, + int *maskptr,int *valptr, + const char **names,int valid_bits) +{ + unsigned int cnt; + int mask,val,kv,mode,ret; + mask = 0; + val = 0; + ret = 0; + while (len) { + cnt = 0; + while ((cnt < len) && + ((ptr[cnt] <= 32) || + (ptr[cnt] >= 127))) cnt++; + ptr += cnt; + len -= cnt; + mode = 0; + if ((*ptr == '-') || (*ptr == '+')) { + mode = (*ptr == '-') ? -1 : 1; + ptr++; + len--; + } + cnt = 0; + while (cnt < len) { + if (ptr[cnt] <= 32) break; + if (ptr[cnt] >= 127) break; + cnt++; + } + if (!cnt) break; + if (parse_mtoken(ptr,cnt,&kv,names,valid_bits)) { + ret = -EINVAL; + break; + } + ptr += cnt; + len -= cnt; + switch (mode) { + case 0: + mask = valid_bits; + val |= kv; + break; + case -1: + mask |= kv; + val &= ~kv; + break; + case 1: + mask |= kv; + val |= kv; + break; + default: + break; + } + } + *maskptr = mask; + *valptr = val; + return ret; +} + + +/* Convert a symbolic value to a mask/value pair */ +int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *cptr, + const char *ptr,unsigned int len, + int *maskptr,int *valptr) +{ + int ret = -EINVAL; + unsigned int cnt; + + *maskptr = 0; + *valptr = 0; + + cnt = 0; + while ((cnt < len) && ((ptr[cnt] <= 32) || (ptr[cnt] >= 127))) cnt++; + len -= cnt; ptr += cnt; + cnt = 0; + while ((cnt < len) && ((ptr[len-(cnt+1)] <= 32) || + (ptr[len-(cnt+1)] >= 127))) cnt++; + len -= cnt; + + if (!len) return -EINVAL; + + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->type == pvr2_ctl_int) { + ret = parse_token(ptr,len,valptr,0,0); + if ((ret == 0) && + ((*valptr < cptr->info->def.type_int.min_value) || + (*valptr > cptr->info->def.type_int.max_value))) { + ret = -EINVAL; + } + if (maskptr) *maskptr = ~0; + } else if (cptr->info->type == pvr2_ctl_enum) { + ret = parse_token( + 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 = -EINVAL; + } + if (maskptr) *maskptr = ~0; + } else if (cptr->info->type == pvr2_ctl_bitmask) { + ret = parse_tlist( + ptr,len,maskptr,valptr, + cptr->info->def.type_bitmask.bit_names, + cptr->info->def.type_bitmask.valid_bits); + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Convert a given mask/val to a symbolic value */ +int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *cptr, + int mask,int val, + char *buf,unsigned int maxlen, + unsigned int *len) +{ + int ret = -EINVAL; + + *len = 0; + if (cptr->info->type == pvr2_ctl_int) { + *len = scnprintf(buf,maxlen,"%d",val); + ret = 0; + } else 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 (names[val]) { + *len = scnprintf( + buf,maxlen,"%s", + names[val]); + } else { + *len = 0; + } + ret = 0; + } + } else if (cptr->info->type == pvr2_ctl_bitmask) { + *len = gen_bitmask_string( + mask & cptr->info->def.type_bitmask.valid_bits, + val,0, + cptr->info->def.type_bitmask.bit_names, + buf,maxlen); + } + return ret; +} + + +/* Convert a given mask/val to a symbolic value */ +int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *cptr, + int mask,int val, + char *buf,unsigned int maxlen, + unsigned int *len) +{ + int ret; + LOCK_TAKE(cptr->hdw->big_lock); do { + ret = pvr2_ctrl_value_to_sym_internal(cptr,mask,val, + buf,maxlen,len); + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h new file mode 100644 index 000000000..9d74151a3 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h @@ -0,0 +1,115 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely + * + * 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 + * + */ +#ifndef __PVRUSB2_CTRL_H +#define __PVRUSB2_CTRL_H + +struct pvr2_ctrl; + +enum pvr2_ctl_type { + pvr2_ctl_int = 0, + pvr2_ctl_enum = 1, + pvr2_ctl_bitmask = 2, +}; + + +/* Set the given control. */ +int pvr2_ctrl_set_value(struct pvr2_ctrl *,int val); + +/* Set/clear specific bits of the given control. */ +int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *,int mask,int val); + +/* Get the current value of the given control. */ +int pvr2_ctrl_get_value(struct pvr2_ctrl *,int *valptr); + +/* Retrieve control's type */ +enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *); + +/* Retrieve control's maximum value (int type) */ +int pvr2_ctrl_get_max(struct pvr2_ctrl *); + +/* Retrieve control's minimum value (int type) */ +int pvr2_ctrl_get_min(struct pvr2_ctrl *); + +/* Retrieve control's default value (any type) */ +int pvr2_ctrl_get_def(struct pvr2_ctrl *); + +/* Retrieve control's enumeration count (enum only) */ +int pvr2_ctrl_get_cnt(struct pvr2_ctrl *); + +/* Retrieve control's valid mask bits (bit mask only) */ +int pvr2_ctrl_get_mask(struct pvr2_ctrl *); + +/* Retrieve the control's name */ +const char *pvr2_ctrl_get_name(struct pvr2_ctrl *); + +/* Retrieve the control's desc */ +const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *); + +/* Retrieve a control enumeration or bit mask value */ +int pvr2_ctrl_get_valname(struct pvr2_ctrl *,int,char *,unsigned int, + unsigned int *); + +/* Return true if control is writable */ +int pvr2_ctrl_is_writable(struct pvr2_ctrl *); + +/* Return true if control has custom symbolic representation */ +int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *); + +/* Convert a given mask/val to a custom symbolic value */ +int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *, + int mask,int val, + char *buf,unsigned int maxlen, + unsigned int *len); + +/* Convert a symbolic value to a mask/value pair */ +int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *, + const char *buf,unsigned int len, + int *maskptr,int *valptr); + +/* Convert a given mask/val to a symbolic value */ +int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *, + int mask,int val, + char *buf,unsigned int maxlen, + unsigned int *len); + +/* Convert a symbolic value to a mask/value pair */ +int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *, + const char *buf,unsigned int len, + int *maskptr,int *valptr); + +/* Convert a given mask/val to a symbolic value - must already be + inside of critical region. */ +int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *, + int mask,int val, + char *buf,unsigned int maxlen, + unsigned int *len); + +#endif /* __PVRUSB2_CTRL_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index 3cbce8eba..47e7f5dbd 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -58,7 +58,7 @@ static void set_input(struct pvr2_v4l_cx2584x *ctxt) memset(&route,0,sizeof(route)); - switch(hdw->controls[PVR2_CID_INPUT].value) { + switch(hdw->input_val) { case PVR2_CVAL_INPUT_TV: vid_input = CX25840_COMPOSITE7; aud_input = CX25840_AUDIO8; @@ -91,7 +91,7 @@ static void set_input(struct pvr2_v4l_cx2584x *ctxt) static int check_input(struct pvr2_v4l_cx2584x *ctxt) { struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->controls[PVR2_CID_INPUT].dirty != 0; + return hdw->input_dirty != 0; } @@ -101,8 +101,8 @@ static void set_audio(struct pvr2_v4l_cx2584x *ctxt) struct pvr2_hdw *hdw = ctxt->hdw; pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_audio %d", - hdw->controls[PVR2_CID_SRATE].value); - switch (hdw->controls[PVR2_CID_SRATE].value) { + hdw->srate_val); + switch (hdw->srate_val) { default: case PVR2_CVAL_SRATE_48: val = 48000; @@ -118,7 +118,7 @@ static void set_audio(struct pvr2_v4l_cx2584x *ctxt) static int check_audio(struct pvr2_v4l_cx2584x *ctxt) { struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->controls[PVR2_CID_SRATE].dirty != 0; + return hdw->srate_dirty != 0; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 2a93bcc96..c60786390 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -268,10 +268,10 @@ static int pvr2_write_encoder_vcmd (struct pvr2_hdw *hdw, u8 cmd, int pvr2_encoder_configure(struct pvr2_hdw *hdw) { int ret = 0, audio, i; - int vd_std = hdw->controls[PVR2_CID_STDCUR].value; - int height = hdw->controls[PVR2_CID_VRES].value; - int width = hdw->controls[PVR2_CID_HRES].value; - int height_full = !hdw->controls[PVR2_CID_INTERLACE].value; + v4l2_std_id vd_std = hdw->video_std_cur; + int height = hdw->res_ver_val; + int width = hdw->res_hor_val; + int height_full = !hdw->interlace_val; int is_30fps, is_ntsc; @@ -370,9 +370,9 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw) /* set video bitrate */ ret |= pvr2_write_encoder_vcmd( hdw, CX2341X_ENC_SET_BIT_RATE, 3, - (hdw->controls[PVR2_CID_VBR].value ? 1 : 0), - hdw->controls[PVR2_CID_AVERAGEVIDEOBITRATE].value, - (u32) (hdw->controls[PVR2_CID_PEAKVIDEOBITRATE].value) / 400); + (hdw->vbr_val ? 1 : 0), + hdw->videobitrate_val, + hdw->videopeak_val / 400); /* setup GOP structure (GOP size = 0f or 0c, 3-1 = 2 B-frames) */ ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_GOP_PROPERTIES, 2, is_30fps ? 0x0f : 0x0c, 0x03); @@ -384,13 +384,11 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw) ret |= pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_SET_GOP_CLOSURE,1,0); /* set audio stream properties 0x40b9? 0100 0000 1011 1001 */ - audio = (pvr_tbl_audiobitrate[hdw->controls[ - PVR2_CID_AUDIOBITRATE].value] | - pvr_tbl_srate[hdw->controls[PVR2_CID_SRATE].value] | - hdw->controls[PVR2_CID_AUDIOLAYER].value << 2 | - (hdw->controls[PVR2_CID_AUDIOCRC].value ? 1 << 14 : 0) | - pvr_tbl_emphasis[hdw->controls[ - PVR2_CID_AUDIOEMPHASIS].value]); + audio = (pvr_tbl_audiobitrate[hdw->audiobitrate_val] | + pvr_tbl_srate[hdw->srate_val] | + hdw->audiolayer_val << 2 | + (hdw->audiocrc_val ? 1 << 14 : 0) | + pvr_tbl_emphasis[hdw->audioemphasis_val]); ret |= pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_SET_AUDIO_PROPERTIES,1, audio); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 9c5f0f74c..ad48f0751 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -45,49 +45,9 @@ #include "pvrusb2-hdw.h" #include "pvrusb2-io.h" - -/* Definition of state variables that we can inspect & change. Numbers are - assigned from zero counting up with no gaps. */ -#define PVR2_CID_BRIGHTNESS 0 -#define PVR2_CID_CONTRAST 1 -#define PVR2_CID_SATURATION 2 -#define PVR2_CID_HUE 3 -#define PVR2_CID_VOLUME 4 -#define PVR2_CID_BALANCE 5 -#define PVR2_CID_BASS 6 -#define PVR2_CID_TREBLE 7 -#define PVR2_CID_MUTE 8 -#define PVR2_CID_SRATE 9 -#define PVR2_CID_AUDIOBITRATE 10 -#define PVR2_CID_AUDIOCRC 11 -#define PVR2_CID_AUDIOEMPHASIS 12 -#define PVR2_CID_VBR 13 -#define PVR2_CID_AVERAGEVIDEOBITRATE 14 -#define PVR2_CID_PEAKVIDEOBITRATE 15 -#define PVR2_CID_STDAVAIL 16 // V4L2 video standard bit mask -#define PVR2_CID_INPUT 17 -#define PVR2_CID_AUDIOMODE 18 // V4L2 standard audio mode enum -#define PVR2_CID_FREQUENCY 19 // Units of Hz -#define PVR2_CID_HRES 20 -#define PVR2_CID_VRES 21 -#define PVR2_CID_INTERLACE 22 -#define PVR2_CID_AUDIOLAYER 23 -#define PVR2_CID_CHANNEL 24 -#define PVR2_CID_CHANPROG_ID 25 -#define PVR2_CID_CHANPROG_FREQ 26 -#define PVR2_CID_SIGNAL_PRESENT 27 -#define PVR2_CID_STREAMING_ENABLED 28 -#define PVR2_CID_HSM 29 -#define PVR2_CID_SUBSYS_MASK 30 -#define PVR2_CID_SUBSYS_STREAM_MASK 31 -#define PVR2_CID_STDCUR 32 // V4L2 video standard bit mask -#define PVR2_CID_STDNAME 33 // Enumeration of available standards - /* Legal values for the SRATE state variable */ #define PVR2_CVAL_SRATE_48 0 #define PVR2_CVAL_SRATE_44_1 1 -#define PVR2_CVAL_SRATE_MIN PVR2_CVAL_SRATE_48 -#define PVR2_CVAL_SRATE_MAX PVR2_CVAL_SRATE_44_1 /* Legal values for the AUDIOBITRATE state variable */ #define PVR2_CVAL_AUDIOBITRATE_384 0 @@ -105,22 +65,16 @@ #define PVR2_CVAL_AUDIOBITRATE_48 12 #define PVR2_CVAL_AUDIOBITRATE_32 13 #define PVR2_CVAL_AUDIOBITRATE_VBR 14 -#define PVR2_CVAL_AUDIOBITRATE_MIN PVR2_CVAL_AUDIOBITRATE_384 -#define PVR2_CVAL_AUDIOBITRATE_MAX PVR2_CVAL_AUDIOBITRATE_VBR /* Legal values for the AUDIOEMPHASIS state variable */ #define PVR2_CVAL_AUDIOEMPHASIS_NONE 0 #define PVR2_CVAL_AUDIOEMPHASIS_50_15 1 #define PVR2_CVAL_AUDIOEMPHASIS_CCITT 2 -#define PVR2_CVAL_AUDIOEMPHASIS_MIN PVR2_CVAL_AUDIOEMPHASIS_NONE -#define PVR2_CVAL_AUDIOEMPHASIS_MAX PVR2_CVAL_AUDIOEMPHASIS_CCITT /* Legal values for PVR2_CID_HSM */ #define PVR2_CVAL_HSM_FAIL 0 #define PVR2_CVAL_HSM_FULL 1 #define PVR2_CVAL_HSM_HIGH 2 -#define PVR2_CVAL_HSM_MIN PVR2_CVAL_HSM_FAIL -#define PVR2_CVAL_HSM_MAX PVR2_CVAL_HSM_HIGH #define PVR2_VID_ENDPOINT 0x84 #define PVR2_UNK_ENDPOINT 0x86 /* maybe raw yuv ? */ @@ -135,37 +89,72 @@ struct pvr2_decoder; -struct pvr2_ctl_def; -struct pvr2_ctrl; - -typedef int (*pvr2_ctl_set_func)(struct pvr2_ctrl *,int val); -typedef int (*pvr2_ctl_get_func)(struct pvr2_ctrl *); - -struct pvr2_ctl_def { - int id; +typedef int (*pvr2_ctlf_is_dirty)(struct pvr2_ctrl *); +typedef void (*pvr2_ctlf_clear_dirty)(struct pvr2_ctrl *); +typedef int (*pvr2_ctlf_get_value)(struct pvr2_ctrl *,int *); +typedef int (*pvr2_ctlf_set_value)(struct pvr2_ctrl *,int msk,int val); +typedef int (*pvr2_ctlf_val_to_sym)(struct pvr2_ctrl *,int msk,int val, + char *,unsigned int,unsigned int *); +typedef int (*pvr2_ctlf_sym_to_val)(struct pvr2_ctrl *, + const char *,unsigned int, + int *mskp,int *valp); + +/* This structure describes a specific control. A table of these is set up + in pvrusb2-hdw.c. */ +struct pvr2_ctl_info { + /* Control's name suitable for use as an identifier */ const char *name; + + /* Short description of control */ const char *desc; - pvr2_ctl_set_func set_func; - pvr2_ctl_get_func get_func; - int mask_value; - int max_value; - int min_value; + + /* Control's implementation */ + pvr2_ctlf_get_value get_value; /* Get its value */ + pvr2_ctlf_set_value set_value; /* Set its value */ + pvr2_ctlf_val_to_sym val_to_sym; /* Custom convert value->symbol */ + pvr2_ctlf_sym_to_val sym_to_val; /* Custom convert symbol->value */ + pvr2_ctlf_is_dirty is_dirty; /* Return true if dirty */ + pvr2_ctlf_clear_dirty clear_dirty; /* Clear dirty state */ + + /* Control's type (int, enum, bitmask) */ + enum pvr2_ctl_type type; + + /* Associated V4L control ID, if any */ + int v4l_id; + + /* Associated driver internal ID, if any */ + int internal_id; + + /* Don't implicitly initialize this control's value */ int skip_init; - int default_value; - int is_valid; - const char **value_defs_ptr; - unsigned int value_defs_count; + + /* Starting value for this control */ + long default_value; + + /* Type-specific control information */ + union { + struct { /* Integer control */ + long min_value; /* lower limit */ + long max_value; /* upper limit */ + } type_int; + struct { /* enumerated control */ + unsigned int count; /* enum value count */ + const char **value_names; /* symbol names */ + } type_enum; + struct { /* bitmask control */ + unsigned int valid_bits; /* bits in use */ + const char **bit_names; /* symbol name/bit */ + } type_bitmask; + } def; }; struct pvr2_ctrl { + const struct pvr2_ctl_info *info; struct pvr2_hdw *hdw; - const struct pvr2_ctl_def *ctl_def; - int is_valid; - int value; - int dirty; }; + struct pvr2_audio_stat { void *ctxt; void (*detach)(void *); @@ -247,6 +236,10 @@ struct pvr2_hdw { /* Frequency table */ unsigned int freqTable[FREQTABLE_SIZE]; + unsigned int freqProgSlot; + unsigned int freqSlot; + unsigned int freqVal; + int freqDirty; /* Stuff for handling low level control interaction with device */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) @@ -298,9 +291,14 @@ struct pvr2_hdw { int tuner_updated; v4l2_std_id video_std_avail; v4l2_std_id video_std_cur; - struct pvr2_ctl_def video_std_enum; + int video_std_dirty; + struct pvr2_ctl_info video_std_info_enum; + struct pvr2_ctl_info video_std_info_avail; + struct pvr2_ctl_info video_std_info_cur; struct v4l2_standard *std_defs; - const char **video_std_names; + const char **video_std_enum_names; + const char *video_std_mask_ptrs[32]; + char video_std_mask_names[32][10]; unsigned int std_cnt; int std_id; @@ -321,14 +319,44 @@ struct pvr2_hdw { int flag_bilingual; struct pvr2_audio_stat *audio_stat; - /* Every last bit of controllable state */ + /* Control state */ +#define VCREATE_DATA(lab) int lab##_val; int lab##_dirty + VCREATE_DATA(brightness); + VCREATE_DATA(contrast); + VCREATE_DATA(saturation); + VCREATE_DATA(hue); + VCREATE_DATA(volume); + VCREATE_DATA(balance); + VCREATE_DATA(bass); + VCREATE_DATA(treble); + VCREATE_DATA(mute); + VCREATE_DATA(srate); + VCREATE_DATA(audiobitrate); + VCREATE_DATA(audiocrc); + VCREATE_DATA(audioemphasis); + VCREATE_DATA(vbr); + VCREATE_DATA(videobitrate); + VCREATE_DATA(videopeak); + VCREATE_DATA(input); + VCREATE_DATA(audiomode); + VCREATE_DATA(res_hor); + VCREATE_DATA(res_ver); + VCREATE_DATA(interlace); + VCREATE_DATA(audiolayer); +#undef VCREATE_DATA + struct pvr2_ctrl *controls; }; int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw); -int pvr2_ctrl_internal_set_value(struct pvr2_ctrl *cptr,int val); -int pvr2_ctrl_internal_get_value(struct pvr2_ctrl *cptr); +unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *); + +void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, + unsigned long msk,unsigned long val); +void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw, + unsigned long msk, + unsigned long val); int pvr2_hdw_internal_set_stdenum_cur(struct pvr2_hdw *hdw,int val); void pvr2_hdw_internal_set_std_cur(struct pvr2_hdw *hdw,int msk); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index f0de1e154..9ea68eeef 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -24,9 +24,9 @@ #include #include #include -#include #include #include "pvrusb2.h" +#include "pvrusb2-std.h" #include "pvrusb2-util.h" #include "pvrusb2-hdw.h" #include "pvrusb2-i2c-core.h" @@ -123,39 +123,6 @@ static const char *control_values_audioemphasis[] = { }; -static const char *control_values_videostandard[] = { - "PAL-B", - "PAL-B1", - "PAL-G", - "PAL-H", - - "PAL-I", - "PAL-D", - "PAL-D1", - "PAL-K", - - "PAL-M", - "PAL-N", - "PAL-Nc", - "PAL-60", - - "NTSC-M", - "NTSC-M-JP", - "NTSC-443", - 0, - - "SECAM-B", - "SECAM-D", - "SECAM-G", - "SECAM-H", - - "SECAM-K", - "SECAM-K1", - "SECAM-L", - "SECAM-LC", -}; - - static const char *control_values_input[] = { [PVR2_CVAL_INPUT_TV] = "television", /*xawtv needs this name*/ [PVR2_CVAL_INPUT_RADIO] = "radio", @@ -180,339 +147,515 @@ static const char *control_values_hsm[] = { }; -#define VDEF(x) \ - .value_defs_ptr = x, \ - .value_defs_count = (sizeof(x)/sizeof(x[0])) - -static int pvr2_ctl_set_chanprog_id(struct pvr2_ctrl *cptr,int value); -static int pvr2_ctl_get_chanprog_id(struct pvr2_ctrl *cptr); -static int pvr2_ctl_get_signal(struct pvr2_ctrl *cptr); -static int pvr2_ctl_get_streaming(struct pvr2_ctrl *cptr); -static int pvr2_ctl_get_hsm(struct pvr2_ctrl *cptr); -static int pvr2_ctl_get_subsys_mask(struct pvr2_ctrl *cptr); -static int pvr2_ctl_set_subsys_mask(struct pvr2_ctrl *cptr,int val); -static int pvr2_ctl_get_subsys_stream_mask(struct pvr2_ctrl *cptr); -static int pvr2_ctl_set_subsys_stream_mask(struct pvr2_ctrl *cptr,int val); -static int pvr2_ctl_set_stdcur(struct pvr2_ctrl *cptr,int val); -static int pvr2_ctl_get_stdcur(struct pvr2_ctrl *cptr); -static int pvr2_ctl_get_stdavail(struct pvr2_ctrl *cptr); -static int pvr2_ctl_set_stdenumcur(struct pvr2_ctrl *cptr,int val); -static int pvr2_ctl_get_stdenumcur(struct pvr2_ctrl *cptr); - -static struct pvr2_ctl_def control_defs[] = -{ - [PVR2_CID_BRIGHTNESS] = { - .id = V4L2_CID_BRIGHTNESS, - .is_valid = !0, +// ????? Need to tie these to bit positions +static const char *control_values_subsystem[] = { + "enc_firmware", + "enc_config", + "digitizer_run", + "usbstream_run", + "enc_run", +}; + + +static int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp) +{ + struct pvr2_hdw *hdw = cptr->hdw; + if ((hdw->freqProgSlot > 0) && (hdw->freqProgSlot <= FREQTABLE_SIZE)) { + *vp = hdw->freqTable[hdw->freqProgSlot-1]; + } else { + *vp = 0; + } + return 0; +} + +static int ctrl_channelfreq_set(struct pvr2_ctrl *cptr,int m,int v) +{ + struct pvr2_hdw *hdw = cptr->hdw; + if ((hdw->freqProgSlot > 0) && (hdw->freqProgSlot <= FREQTABLE_SIZE)) { + hdw->freqTable[hdw->freqProgSlot-1] = v; + } + return 0; +} + +static int ctrl_channelprog_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->freqProgSlot; + return 0; +} + +static int ctrl_channelprog_set(struct pvr2_ctrl *cptr,int m,int v) +{ + struct pvr2_hdw *hdw = cptr->hdw; + if ((v >= 0) && (v <= FREQTABLE_SIZE)) { + hdw->freqProgSlot = v; + } + return 0; +} + +static int ctrl_channel_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->freqSlot; + return 0; +} + +static int ctrl_channel_set(struct pvr2_ctrl *cptr,int m,int v) +{ + unsigned freq = 0; + struct pvr2_hdw *hdw = cptr->hdw; + hdw->freqSlot = v; + if ((hdw->freqSlot > 0) && (hdw->freqSlot <= FREQTABLE_SIZE)) { + freq = hdw->freqTable[hdw->freqSlot-1]; + } + if (freq && (freq != hdw->freqVal)) { + hdw->freqVal = freq; + hdw->freqDirty = !0; + } + return 0; +} + +static int ctrl_freq_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->freqVal; + return 0; +} + +static int ctrl_freq_is_dirty(struct pvr2_ctrl *cptr) +{ + return cptr->hdw->freqDirty != 0; +} + +static void ctrl_freq_clear_dirty(struct pvr2_ctrl *cptr) +{ + cptr->hdw->freqDirty = 0; +} + +static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v) +{ + struct pvr2_hdw *hdw = cptr->hdw; + hdw->freqVal = v; + hdw->freqDirty = !0; + hdw->freqSlot = 0; + return 0; +} + +static int ctrl_streamingenabled_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->flag_streaming_enabled; + return 0; +} + +static int ctrl_hsm_get(struct pvr2_ctrl *cptr,int *vp) +{ + int result = pvr2_hdw_is_hsm(cptr->hdw); + *vp = PVR2_CVAL_HSM_FULL; + if (result < 0) *vp = PVR2_CVAL_HSM_FAIL; + if (result) *vp = PVR2_CVAL_HSM_HIGH; + return 0; +} + +static int ctrl_stdavail_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->video_std_avail; + return 0; +} + +static int ctrl_std_val_to_sym(struct pvr2_ctrl *cptr,int msk,int val, + char *bufPtr,unsigned int bufSize, + unsigned int *len) +{ + *len = pvr2_std_id_to_str(bufPtr,bufSize,msk & val); + return 0; +} + +static int ctrl_std_sym_to_val(struct pvr2_ctrl *cptr, + const char *bufPtr,unsigned int bufSize, + int *mskp,int *valp) +{ + int ret; + v4l2_std_id id; + ret = pvr2_std_str_to_id(&id,bufPtr,bufSize); + if (ret < 0) return ret; + if (mskp) *mskp = id; + if (valp) *valp = id; + return 0; +} + +static int ctrl_stdcur_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->video_std_cur; + return 0; +} + +static int ctrl_stdcur_set(struct pvr2_ctrl *cptr,int m,int v) +{ + pvr2_hdw_internal_set_std_cur(cptr->hdw,v); + return 0; +} + +static int ctrl_signal_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = ((pvr2_hdw_get_signal_status_internal(cptr->hdw) & + PVR2_SIGNAL_OK) ? 1 : 0); + return 0; +} + +static int ctrl_subsys_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->subsys_enabled_mask; + return 0; +} + +static int ctrl_subsys_set(struct pvr2_ctrl *cptr,int m,int v) +{ + pvr2_hdw_subsys_bit_chg_no_lock(cptr->hdw,m,v); + return 0; +} + +static int ctrl_subsys_stream_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->subsys_stream_mask; + return 0; +} + +static int ctrl_subsys_stream_set(struct pvr2_ctrl *cptr,int m,int v) +{ + pvr2_hdw_subsys_stream_bit_chg_no_lock(cptr->hdw,m,v); + return 0; +} + +static int ctrl_stdenumcur_set(struct pvr2_ctrl *cptr,int m,int v) +{ + if (v < 0) return -EINVAL; + if (v >= cptr->hdw->std_cnt) return -EINVAL; + cptr->hdw->std_id = v; + pvr2_hdw_internal_set_std_cur(cptr->hdw, + cptr->hdw->std_id); + return 0; +} + + +static int ctrl_stdenumcur_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->std_id; + return 0; +} + + +static int ctrl_stdenumcur_is_dirty(struct pvr2_ctrl *cptr) +{ + return cptr->hdw->video_std_dirty != 0; +} + + +static void ctrl_stdenumcur_clear_dirty(struct pvr2_ctrl *cptr) +{ + cptr->hdw->video_std_dirty = 0; +} + + +#define DEFINT(vmin,vmax) \ + .type = pvr2_ctl_int, \ + .def.type_int.min_value = vmin, \ + .def.type_int.max_value = vmax + +#define DEFENUM(tab) \ + .type = pvr2_ctl_enum, \ + .def.type_enum.count = (sizeof(tab)/sizeof((tab)[0])), \ + .def.type_enum.value_names = tab + +#define DEFMASK(msk,tab) \ + .type = pvr2_ctl_bitmask, \ + .def.type_bitmask.valid_bits = msk, \ + .def.type_bitmask.bit_names = tab + +#define DEFREF(vname) \ + .set_value = ctrl_set_##vname, \ + .get_value = ctrl_get_##vname, \ + .is_dirty = ctrl_isdirty_##vname, \ + .clear_dirty = ctrl_cleardirty_##vname + + +#define VCREATE_FUNCS(vname) \ +static int ctrl_get_##vname(struct pvr2_ctrl *cptr,int *vp) \ +{*vp = cptr->hdw->vname##_val; return 0;} \ +static int ctrl_set_##vname(struct pvr2_ctrl *cptr,int m,int v) \ +{cptr->hdw->vname##_val = v; cptr->hdw->vname##_dirty = !0; return 0;} \ +static int ctrl_isdirty_##vname(struct pvr2_ctrl *cptr) \ +{return cptr->hdw->vname##_dirty != 0;} \ +static void ctrl_cleardirty_##vname(struct pvr2_ctrl *cptr) \ +{cptr->hdw->vname##_dirty = 0;} + +VCREATE_FUNCS(brightness) +VCREATE_FUNCS(contrast) +VCREATE_FUNCS(saturation) +VCREATE_FUNCS(hue) +VCREATE_FUNCS(volume) +VCREATE_FUNCS(balance) +VCREATE_FUNCS(bass) +VCREATE_FUNCS(treble) +VCREATE_FUNCS(mute) +VCREATE_FUNCS(srate) +VCREATE_FUNCS(audiobitrate) +VCREATE_FUNCS(audiocrc) +VCREATE_FUNCS(audioemphasis) +VCREATE_FUNCS(vbr) +VCREATE_FUNCS(videobitrate) +VCREATE_FUNCS(videopeak) +VCREATE_FUNCS(input) +VCREATE_FUNCS(audiomode) +VCREATE_FUNCS(res_hor) +VCREATE_FUNCS(res_ver) +VCREATE_FUNCS(interlace) +VCREATE_FUNCS(audiolayer) + +#define MIN_FREQ 55250000L +#define MAX_FREQ 850000000L + +/* Table definition of all controls which can be manipulated */ +static const struct pvr2_ctl_info control_defs[] = { + { + .v4l_id = V4L2_CID_BRIGHTNESS, .desc = "Brightness", .name = "brightness", - .min_value = 0, - .max_value = 255, .default_value = 128, - }, - [PVR2_CID_CONTRAST] = { - .id = V4L2_CID_CONTRAST, - .is_valid = !0, + DEFREF(brightness), + DEFINT(0,255), + },{ + .v4l_id = V4L2_CID_CONTRAST, .desc = "Contrast", .name = "contrast", - .min_value = 0, - .max_value = 127, .default_value = 68, - }, - [PVR2_CID_SATURATION] = { - .id = V4L2_CID_SATURATION, - .is_valid = !0, + DEFREF(contrast), + DEFINT(0,127), + },{ + .v4l_id = V4L2_CID_SATURATION, .desc = "Saturation", .name = "saturation", - .min_value = 0, - .max_value = 127, .default_value = 64, - }, - [PVR2_CID_HUE] = { - .id = V4L2_CID_HUE, - .is_valid = !0, + DEFREF(saturation), + DEFINT(0,127), + },{ + .v4l_id = V4L2_CID_HUE, .desc = "Hue", .name = "hue", - .min_value = -128, - .max_value = 127, .default_value = 0, - }, - [PVR2_CID_VOLUME] = { - .id = V4L2_CID_AUDIO_VOLUME, - .is_valid = !0, + DEFREF(hue), + DEFINT(-128,127), + },{ + .v4l_id = V4L2_CID_AUDIO_VOLUME, .desc = "Volume", .name = "volume", - .min_value = 0, - .max_value = 65535, .default_value = 65535, - }, - [PVR2_CID_BALANCE] = { - .id = V4L2_CID_AUDIO_BALANCE, - .is_valid = !0, + DEFREF(volume), + DEFINT(0,65535), + },{ + .v4l_id = V4L2_CID_AUDIO_BALANCE, .desc = "Balance", .name = "balance", - .min_value = -32768, - .max_value = 32767, .default_value = 0, - }, - [PVR2_CID_BASS] = { - .id = V4L2_CID_AUDIO_BASS, - .is_valid = !0, + DEFREF(balance), + DEFINT(-32768,32767), + },{ + .v4l_id = V4L2_CID_AUDIO_BASS, .desc = "Bass", .name = "bass", - .min_value = -32768, - .max_value = 32767, .default_value = 0, - }, - [PVR2_CID_TREBLE] = { - .id = V4L2_CID_AUDIO_TREBLE, - .is_valid = !0, + DEFREF(bass), + DEFINT(-32768,32767), + },{ + .v4l_id = V4L2_CID_AUDIO_TREBLE, .desc = "Treble", .name = "treble", - .min_value = -32768, - .max_value = 32767, .default_value = 0, - }, - [PVR2_CID_MUTE] = { - .id = V4L2_CID_AUDIO_MUTE, - .is_valid = !0, + DEFREF(treble), + DEFINT(-32768,32767), + },{ + .v4l_id = V4L2_CID_AUDIO_MUTE, .desc = "Mute", .name = "mute", - .min_value = 0, - .max_value = 1, .default_value = 0, - }, - [PVR2_CID_SRATE] = { - .id = V4L2_CID_PVR_SRATE, - .is_valid = !0, + DEFREF(mute), + DEFINT(0,1), + },{ + .v4l_id = V4L2_CID_PVR_SRATE, .desc = "Sample rate", .name = "srate", - .min_value = PVR2_CVAL_SRATE_MIN, - .max_value = PVR2_CVAL_SRATE_MAX, .default_value = PVR2_CVAL_SRATE_48, - VDEF(control_values_srate), - }, - [PVR2_CID_AUDIOBITRATE] = { - .id = V4L2_CID_PVR_AUDIOBITRATE, - .is_valid = !0, + DEFREF(srate), + DEFENUM(control_values_srate), + },{ + .v4l_id = V4L2_CID_PVR_AUDIOBITRATE, .desc = "Audio Bitrate", .name = "audio_bitrate", - .min_value = PVR2_CVAL_AUDIOBITRATE_MIN, - .max_value = PVR2_CVAL_AUDIOBITRATE_MAX, .default_value = PVR2_CVAL_AUDIOBITRATE_224, - VDEF(control_values_audiobitrate), - }, - [PVR2_CID_AUDIOCRC] = { - .id = V4L2_CID_PVR_AUDIOCRC, - .is_valid = !0, + DEFREF(audiobitrate), + DEFENUM(control_values_audiobitrate), + },{ + .v4l_id = V4L2_CID_PVR_AUDIOCRC, .desc = "Audio CRC", .name = "audio_crc", - .min_value = 0, - .max_value = 1, .default_value = 1, - }, - [PVR2_CID_AUDIOEMPHASIS] = { - .id = V4L2_CID_PVR_AUDIOEMPHASIS, - .is_valid = !0, + DEFREF(audiocrc), + DEFINT(0,1), + },{ + .desc = "Audio Layer", + .name = "audio_layer", + .default_value = 2, + DEFREF(audiolayer), + DEFINT(0,3), + },{ + .v4l_id = V4L2_CID_PVR_AUDIOEMPHASIS, .desc = "Audio Emphasis", .name = "audio_emphasis", - .min_value = PVR2_CVAL_AUDIOEMPHASIS_MIN, - .max_value = PVR2_CVAL_AUDIOEMPHASIS_MAX, .default_value = PVR2_CVAL_AUDIOEMPHASIS_NONE, - VDEF(control_values_audioemphasis), - }, - [PVR2_CID_VBR] = { - .id = V4L2_CID_PVR_VBR, - .is_valid = !0, + DEFREF(audioemphasis), + DEFENUM(control_values_audioemphasis), + },{ + .desc = "Interlace mode", + .name = "interlace", + .internal_id = PVR2_CID_INTERLACE, + .default_value = 0, + DEFREF(interlace), + DEFINT(0,1), + },{ + .v4l_id = V4L2_CID_PVR_VBR, .desc = "Variable video bitrate", .name = "vbr", - .min_value = 0, - .max_value = 1, .default_value = 0, - }, - [PVR2_CID_AVERAGEVIDEOBITRATE] = { - .id = V4L2_CID_PVR_VIDEOBITRATE, - .is_valid = !0, + DEFREF(vbr), + DEFINT(0,1), + },{ + .v4l_id = V4L2_CID_PVR_VIDEOBITRATE, .desc = "Average video bitrate", .name = "video_average_bitrate", - .min_value = 1, - .max_value = 20000000, .default_value = 6000000, - }, - [PVR2_CID_PEAKVIDEOBITRATE] = { - .id = V4L2_CID_PVR_VIDEOPEAK, - .is_valid = !0, + DEFREF(videobitrate), + DEFINT(500000,20000000), + },{ + .v4l_id = V4L2_CID_PVR_VIDEOPEAK, .desc = "Peak video bitrate", .name = "video_peak_bitrate", - .min_value = 1, - .max_value = 20000000, .default_value = 6000000, - }, - [PVR2_CID_STDAVAIL] = { - .is_valid = !0, - .desc = "Video Standards Available Mask", - .name = "video_standard_mask_available", - .min_value = 0, - .max_value = 0, - .default_value = (int)V4L2_STD_UNKNOWN, - .mask_value = (int)V4L2_STD_ALL, - .get_func = pvr2_ctl_get_stdavail, - VDEF(control_values_videostandard), - }, - [PVR2_CID_INPUT] = { - .id = V4L2_CID_PVR_INPUT, - .is_valid = !0, + DEFREF(videopeak), + DEFINT(500000,20000000), + },{ .desc = "Video Source", .name = "input", - .min_value = PVR2_CVAL_INPUT_MIN, - .max_value = PVR2_CVAL_INPUT_MAX, + .internal_id = PVR2_CID_INPUT, .default_value = PVR2_CVAL_INPUT_TV, - VDEF(control_values_input), - }, - [PVR2_CID_AUDIOMODE] = { - .id = V4L2_CID_PVR_AUDIOMODE, - .is_valid = !0, + DEFREF(input), + DEFENUM(control_values_input), + },{ .desc = "Audio Mode", .name = "audio_mode", - .min_value = V4L2_TUNER_MODE_MONO, - .max_value = V4L2_TUNER_MODE_LANG1_LANG2, + .internal_id = PVR2_CID_AUDIOMODE, .default_value = V4L2_TUNER_MODE_STEREO, - VDEF(control_values_audiomode), - }, - [PVR2_CID_FREQUENCY] = { - .id = V4L2_CID_PVR_FREQUENCY, - .is_valid = !0, + DEFREF(audiomode), + DEFENUM(control_values_audiomode), + },{ .desc = "Tuner Frequency (Hz)", .name = "frequency", - .min_value = 55250000L, - .max_value = 850000000L, + .internal_id = PVR2_CID_FREQUENCY, .default_value = 175250000L, - }, - [PVR2_CID_HRES] = { - .id = V4L2_CID_PVR_HRES, - .is_valid = !0, + .set_value = ctrl_freq_set, + .get_value = ctrl_freq_get, + .is_dirty = ctrl_freq_is_dirty, + .clear_dirty = ctrl_freq_clear_dirty, + DEFINT(MIN_FREQ,MAX_FREQ), + },{ + .desc = "Channel", + .name = "channel", + .set_value = ctrl_channel_set, + .get_value = ctrl_channel_get, + DEFINT(0,FREQTABLE_SIZE), + },{ + .desc = "Channel Program Frequency", + .name = "freq_table_value", + .set_value = ctrl_channelfreq_set, + .get_value = ctrl_channelfreq_get, + DEFINT(MIN_FREQ,MAX_FREQ), + },{ + .desc = "Channel Program ID", + .name = "freq_table_channel", + .set_value = ctrl_channelprog_set, + .get_value = ctrl_channelprog_get, + DEFINT(0,FREQTABLE_SIZE), + },{ .desc = "Horizontal capture resolution", .name = "resolution_hor", - .min_value = 320, - .max_value = 720, + .internal_id = PVR2_CID_HRES, .default_value = 720, - }, - [PVR2_CID_VRES] = { - .id = V4L2_CID_PVR_VRES, - .is_valid = !0, + DEFREF(res_hor), + DEFINT(320,720), + },{ .desc = "Vertical capture resolution", .name = "resolution_ver", - .min_value = 200, - .max_value = 625, + .internal_id = PVR2_CID_VRES, .default_value = 480, - }, - [PVR2_CID_INTERLACE] = { - .id = V4L2_CID_PVR_INTERLACE, - .is_valid = !0, - .desc = "Interlace mode", - .name = "interlace", - .min_value = 0, - .max_value = 1, - .default_value = 0, - }, - [PVR2_CID_AUDIOLAYER] = { - .is_valid = !0, - .desc = "Audio Layer", - .name = "audio_layer", - .min_value = 0, /* This is all a wild guess */ - .max_value = 3, - .default_value = 2, /* Appears to be all that is supported */ - }, - [PVR2_CID_CHANNEL] = { - .is_valid = !0, - .desc = "Channel", - .name = "channel", - .min_value = 0, - .max_value = FREQTABLE_SIZE, - .default_value = 0, - }, - [PVR2_CID_CHANPROG_ID] = { - .is_valid = !0, - .desc = "Channel Program ID", - .name = "freq_table_channel", - .min_value = 0, - .max_value = FREQTABLE_SIZE, - .default_value = 0, - }, - [PVR2_CID_CHANPROG_FREQ] = { - .is_valid = !0, - .desc = "Channel Program Frequency", - .name = "freq_table_value", - .min_value = 55250000L, - .max_value = 850000000L, - .skip_init = !0, - .set_func = pvr2_ctl_set_chanprog_id, - .get_func = pvr2_ctl_get_chanprog_id, - }, - [PVR2_CID_SIGNAL_PRESENT] = { - .is_valid = !0, - .desc = "Signal Present", - .name = "signal_present", - .min_value = 0, - .max_value = 1, - .get_func = pvr2_ctl_get_signal, - }, - [PVR2_CID_STREAMING_ENABLED] = { - .is_valid = !0, + DEFREF(res_ver), + DEFINT(200,625), + },{ .desc = "Streaming Enabled", .name = "streaming_enabled", - .min_value = 0, - .max_value = 1, - .get_func = pvr2_ctl_get_streaming, - }, - [PVR2_CID_HSM] = { - .is_valid = !0, + .get_value = ctrl_streamingenabled_get, + DEFINT(0,1), + },{ .desc = "USB Speed", .name = "usb_speed", - .min_value = PVR2_CVAL_HSM_MIN, - .max_value = PVR2_CVAL_HSM_MAX, - .get_func = pvr2_ctl_get_hsm, - VDEF(control_values_hsm), - }, - [PVR2_CID_SUBSYS_MASK] = { - .is_valid = !0, + .get_value = ctrl_hsm_get, + DEFENUM(control_values_hsm), + },{ + .desc = "Signal Present", + .name = "signal_present", + .get_value = ctrl_signal_get, + DEFINT(0,1), + },{ + .desc = "Video Standards Available Mask", + .name = "video_standard_mask_available", + .internal_id = PVR2_CID_STDAVAIL, + .get_value = ctrl_stdavail_get, + .val_to_sym = ctrl_std_val_to_sym, + .sym_to_val = ctrl_std_sym_to_val, + .type = pvr2_ctl_bitmask, + },{ + .desc = "Video Standards In Use Mask", + .name = "video_standard_mask_active", + .internal_id = PVR2_CID_STDCUR, + .skip_init = !0, + .get_value = ctrl_stdcur_get, + .set_value = ctrl_stdcur_set, + .val_to_sym = ctrl_std_val_to_sym, + .sym_to_val = ctrl_std_sym_to_val, + .type = pvr2_ctl_bitmask, + },{ .desc = "Subsystem enabled mask", .name = "debug_subsys_mask", - .min_value = 0, - .max_value = 0x7fffffff, .skip_init = !0, - .get_func = pvr2_ctl_get_subsys_mask, - .set_func = pvr2_ctl_set_subsys_mask, - }, - [PVR2_CID_SUBSYS_STREAM_MASK] = { - .is_valid = !0, + .get_value = ctrl_subsys_get, + .set_value = ctrl_subsys_set, + DEFMASK(PVR2_SUBSYS_ALL,control_values_subsystem), + },{ .desc = "Subsystem stream mask", .name = "debug_subsys_stream_mask", - .min_value = 0, - .max_value = 0x7fffffff, .skip_init = !0, - .get_func = pvr2_ctl_get_subsys_stream_mask, - .set_func = pvr2_ctl_set_subsys_stream_mask, - }, - [PVR2_CID_STDCUR] = { - .id = V4L2_CID_PVR_STDCUR, - .is_valid = !0, - .desc = "Video Standard In Use Mask", - .name = "video_standard_mask_active", - .min_value = 0, - .max_value = 0, - .default_value = (int)V4L2_STD_UNKNOWN, - .mask_value = (int)V4L2_STD_ALL, - .set_func = pvr2_ctl_set_stdcur, - .get_func = pvr2_ctl_get_stdcur, - VDEF(control_values_videostandard), - }, + .get_value = ctrl_subsys_stream_get, + .set_value = ctrl_subsys_stream_set, + DEFMASK(PVR2_SUBSYS_ALL,control_values_subsystem), + },{ + .desc = "Video Standard Name", + .name = "video_standard", + .internal_id = PVR2_CID_STDENUM, + .skip_init = !0, + .get_value = ctrl_stdenumcur_get, + .set_value = ctrl_stdenumcur_set, + .is_dirty = ctrl_stdenumcur_is_dirty, + .clear_dirty = ctrl_stdenumcur_clear_dirty, + .type = pvr2_ctl_enum, + } }; -#undef VDEF +#define CTRL_COUNT (sizeof(control_defs)/sizeof(control_defs[0])) -#define CTRL_DEF_COUNT (sizeof(control_defs)/sizeof(control_defs[0])) -#define CTRL_COUNT CTRL_DEF_COUNT+1 const char *pvr2_config_get_name(enum pvr2_config cfg) { @@ -1066,12 +1209,6 @@ void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw, } -static int pvr2_ctl_get_streaming(struct pvr2_ctrl *cptr) -{ - return cptr->hdw->flag_streaming_enabled != 0; -} - - int pvr2_hdw_set_streaming_no_lock(struct pvr2_hdw *hdw,int enableFl) { if ((!enableFl) == !(hdw->flag_streaming_enabled)) return 0; @@ -1231,10 +1368,9 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) for (idx = 0; idx < CTRL_COUNT; idx++) { cptr = hdw->controls + idx; - if (!pvr2_ctrl_is_valid(cptr)) continue; - if (cptr->ctl_def->skip_init) continue; - pvr2_ctrl_internal_set_value(cptr, - cptr->ctl_def->default_value); + if (cptr->info->skip_init) continue; + if (!cptr->info->set_value) continue; + cptr->info->set_value(cptr,~0,cptr->info->default_value); } // Do not use pvr2_reset_ctl_endpoints() here. It is not @@ -1384,6 +1520,8 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, unsigned int idx,cnt1,cnt2; struct pvr2_hdw *hdw; unsigned int hdw_type; + int valid_std_mask; + struct pvr2_ctrl *cptr; __u8 ifnum; hdw_type = devid - pvr2_device_table; @@ -1400,33 +1538,61 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, if (!hdw) goto fail; memset(hdw,0,sizeof(*hdw)); - // Initialize video standard enum dynamic control - hdw->video_std_enum.name = "video_standard"; - hdw->video_std_enum.desc = "Video Standard Name"; - hdw->video_std_enum.id = 0; // ????? - hdw->video_std_enum.set_func = pvr2_ctl_set_stdenumcur; - hdw->video_std_enum.get_func = pvr2_ctl_get_stdenumcur; - hdw->video_std_enum.mask_value = 0; - hdw->video_std_enum.max_value = 0; - hdw->video_std_enum.min_value = 0; - hdw->video_std_enum.default_value = 0; - hdw->video_std_enum.is_valid = !0; - hdw->controls = kmalloc(sizeof(struct pvr2_ctrl) * CTRL_COUNT, GFP_KERNEL); if (!hdw->controls) goto fail; memset(hdw->controls,0,sizeof(struct pvr2_ctrl) * CTRL_COUNT); hdw->hdw_type = hdw_type; - for (idx = 0; idx < CTRL_DEF_COUNT; idx++) { - hdw->controls[idx].hdw = hdw; - hdw->controls[idx].ctl_def = control_defs + idx; - hdw->controls[idx].is_valid = - hdw->controls[idx].ctl_def->is_valid; + for (idx = 0; idx < 32; idx++) { + hdw->video_std_mask_ptrs[idx] = + hdw->video_std_mask_names[idx]; + } + + for (idx = 0; idx < CTRL_COUNT; idx++) { + cptr = hdw->controls + idx; + cptr->hdw = hdw; + cptr->info = control_defs+idx; + } + + // Initialize video standard enum dynamic control + cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDENUM); + if (cptr) { + memcpy(&hdw->video_std_info_enum,cptr->info, + sizeof(hdw->video_std_info_enum)); + cptr->info = &hdw->video_std_info_enum; + + } + // Initialize control data regarding video standard masks + valid_std_mask = pvr2_std_get_usable(); + for (idx = 0; idx < 32; idx++) { + if (!(valid_std_mask & (1 << idx))) continue; + cnt1 = pvr2_std_id_to_str( + hdw->video_std_mask_names[idx], + sizeof(hdw->video_std_mask_names[idx])-1, + 1 << idx); + hdw->video_std_mask_names[idx][cnt1] = 0; + } + cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDAVAIL); + if (cptr) { + memcpy(&hdw->video_std_info_avail,cptr->info, + sizeof(hdw->video_std_info_avail)); + cptr->info = &hdw->video_std_info_avail; + hdw->video_std_info_avail.def.type_bitmask.bit_names = + hdw->video_std_mask_ptrs; + hdw->video_std_info_avail.def.type_bitmask.valid_bits = + valid_std_mask; + } + cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR); + if (cptr) { + memcpy(&hdw->video_std_info_cur,cptr->info, + sizeof(hdw->video_std_info_cur)); + cptr->info = &hdw->video_std_info_cur; + hdw->video_std_info_cur.def.type_bitmask.bit_names = + hdw->video_std_mask_ptrs; + hdw->video_std_info_avail.def.type_bitmask.valid_bits = + valid_std_mask; } - hdw->controls[PVR2_CID_STDNAME].hdw = hdw; - hdw->controls[PVR2_CID_STDNAME].ctl_def = &hdw->video_std_enum; - hdw->controls[PVR2_CID_STDNAME].is_valid = !0; hdw->eeprom_addr = -1; hdw->unit_number = -1; @@ -1556,7 +1722,7 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) } while (0); up(&pvr2_unit_sem); kfree(hdw->controls); if (hdw->std_defs) kfree(hdw->std_defs); - if (hdw->video_std_names) kfree(hdw->video_std_names); + if (hdw->video_std_enum_names) kfree(hdw->video_std_enum_names); kfree(hdw); } @@ -1585,221 +1751,26 @@ void pvr2_hdw_disconnect(struct pvr2_hdw *hdw) } -static int pvr2_ctl_set_chanprog_id(struct pvr2_ctrl *cptr,int value) -{ - /* This is a special case; the value to store is to an array, and - the element to select is determined by PVR_CID_CHANPROG_ID. */ - struct pvr2_hdw *hdw = cptr->hdw; - int id = hdw->controls[PVR2_CID_CHANPROG_ID].value; - if ((id < 1) || (id > FREQTABLE_SIZE)) return 0; - hdw->freqTable[id-1] = value; - if (hdw->controls[PVR2_CID_CHANNEL].value == id) { - /* If the current channel happens to be the slot we just - set, then act like the current channel just got changed - so we'll update that too. */ - hdw->controls[PVR2_CID_CHANNEL].dirty = !0; - } - return 0; -} - - -static int pvr2_ctl_get_chanprog_id(struct pvr2_ctrl *cptr) -{ - /* This is a special case; the value to return is from an array, - and the element to select is determined by - PVR_CID_CHANPROG_ID. */ - struct pvr2_hdw *hdw = cptr->hdw; - int id = hdw->controls[PVR2_CID_CHANPROG_ID].value; - if ((id < 1) || (id > FREQTABLE_SIZE)) return 0; - return hdw->freqTable[id-1]; -} - -// Template data for possible enumerated video standards -static struct v4l2_standard pvr_standards[] = { - { - .id = V4L2_STD_PAL_BG, - .frameperiod = - { - .numerator = 1, - .denominator= 25 - }, - .framelines = 625, - .reserved = {0,0,0,0} - }, { - .id = V4L2_STD_PAL_I, - .frameperiod = - { - .numerator = 1, - .denominator= 25 - }, - .framelines = 625, - .reserved = {0,0,0,0} - }, { - .id = V4L2_STD_PAL_DK, - .frameperiod = - { - .numerator = 1, - .denominator= 25 - }, - .framelines = 625, - .reserved = {0,0,0,0} - }, { - .id = V4L2_STD_SECAM, - .frameperiod = - { - .numerator = 1, - .denominator= 25 - }, - .framelines = 625, - .reserved = {0,0,0,0} - }, { - .id = V4L2_STD_NTSC_M, - .frameperiod = - { - .numerator = 1001, - .denominator= 30000 - }, - .framelines = 525, - .reserved = {0,0,0,0} - }, { - .id = V4L2_STD_PAL_M, - .frameperiod = - { - .numerator = 1001, - .denominator= 30000 - }, - .framelines = 525, - .reserved = {0,0,0,0} - } -}; - -#define pvr_standards_cnt (sizeof(pvr_standards)/sizeof(pvr_standards[0])) - - -struct name_data { - struct v4l2_standard *std; - unsigned int bcnt; - unsigned int scnt; -}; - -static void name_build(struct name_data *dp,const char *str) -{ - if (!dp->bcnt) { - dp->bcnt = scnprintf(dp->std->name, - sizeof(dp->std->name)-1,"%s",str); - dp->scnt = 0; - return; - } - - dp->bcnt += scnprintf(dp->std->name+dp->bcnt, - sizeof(dp->std->name)-(1+dp->bcnt), - "%s%s", - (dp->scnt ? "/" : "-"),str); - (dp->scnt)++; -} - -// Generate a descriptive name for a given standard -static void name_bucket(struct v4l2_standard *std) -{ - struct name_data nd; - nd.std = std; - nd.bcnt = 0; - if (std->id & (V4L2_STD_PAL_B| - V4L2_STD_PAL_B1| - V4L2_STD_PAL_G| - V4L2_STD_PAL_H| - V4L2_STD_PAL_I| - V4L2_STD_PAL_D| - V4L2_STD_PAL_D1| - V4L2_STD_PAL_K)) { - name_build(&nd,"PAL"); - if (std->id & V4L2_STD_PAL_B) name_build(&nd,"B"); - if (std->id & V4L2_STD_PAL_B1) name_build(&nd,"B1"); - if (std->id & V4L2_STD_PAL_D) name_build(&nd,"D"); - if (std->id & V4L2_STD_PAL_D1) name_build(&nd,"D1"); - if (std->id & V4L2_STD_PAL_G) name_build(&nd,"G"); - if (std->id & V4L2_STD_PAL_H) name_build(&nd,"H"); - if (std->id & V4L2_STD_PAL_I) name_build(&nd,"I"); - if (std->id & V4L2_STD_PAL_K) name_build(&nd,"K"); - if (std->id & V4L2_STD_PAL_M) name_build(&nd,"M"); - if (std->id & V4L2_STD_PAL_N) name_build(&nd,"N"); - if (std->id & V4L2_STD_PAL_Nc) name_build(&nd,"Nc"); - if (std->id & V4L2_STD_PAL_60) name_build(&nd,"60"); - std->name[nd.bcnt] = 0; - return; - } - if (std->id & (V4L2_STD_NTSC_M| - V4L2_STD_NTSC_M_JP| - V4L2_STD_NTSC_443)) { - name_build(&nd,"NTSC"); - if (std->id & V4L2_STD_NTSC_M) name_build(&nd,"M"); - if (std->id & V4L2_STD_NTSC_M_JP) name_build(&nd,"Mjp"); - if (std->id & V4L2_STD_NTSC_443) name_build(&nd,"443"); - std->name[nd.bcnt] = 0; - return; - } - if (std->id & (V4L2_STD_SECAM_B| - V4L2_STD_SECAM_D| - V4L2_STD_SECAM_G| - V4L2_STD_SECAM_H| - V4L2_STD_SECAM_K| - V4L2_STD_SECAM_K1| - V4L2_STD_SECAM_L| - V4L2_STD_SECAM_LC)) { - name_build(&nd,"SECAM"); - if (std->id & V4L2_STD_SECAM_B) name_build(&nd,"B"); - if (std->id & V4L2_STD_SECAM_D) name_build(&nd,"D"); - if (std->id & V4L2_STD_SECAM_G) name_build(&nd,"G"); - if (std->id & V4L2_STD_SECAM_H) name_build(&nd,"H"); - if (std->id & V4L2_STD_SECAM_K) name_build(&nd,"K"); - if (std->id & V4L2_STD_SECAM_K1) name_build(&nd,"K1"); - if (std->id & V4L2_STD_SECAM_L) name_build(&nd,"L"); - if (std->id & V4L2_STD_SECAM_LC) name_build(&nd,"LC"); - std->name[nd.bcnt] = 0; - return; - } - std->name[0] = 0; -} - - // Given a mask of viable video standards to choose from, generate an // appropriate array of v4l2_standard data that corresponds to it and set // up related state in the driver to match. void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw,int arg) { - v4l2_std_id buckets[pvr_standards_cnt]; - unsigned int idx1,idx2,std_cnt; - v4l2_std_id mmsk,amsk; - - amsk = (v4l2_std_id)arg; - - // Figure out which standard groups we can work with - std_cnt = 0; - mmsk = 0; - for (idx1 = 0; idx1 < pvr_standards_cnt; idx1++) { - buckets[idx1] = pvr_standards[idx1].id & amsk; - if (!buckets[idx1]) continue; - mmsk |= buckets[idx1]; - amsk &= ~buckets[idx1]; - std_cnt++; - } + struct v4l2_standard *newstd; + unsigned int std_cnt; + unsigned int idx; - if (amsk) { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "Failed to bucketize the following standards: 0x%llx", - amsk); - } + newstd = pvr2_std_create_enum(&std_cnt,arg); if (hdw->std_defs) { kfree(hdw->std_defs); hdw->std_defs = 0; } - if (hdw->video_std_names) { - kfree(hdw->video_std_names); - hdw->video_std_names = 0; - } hdw->std_cnt = 0; + if (hdw->video_std_enum_names) { + kfree(hdw->video_std_enum_names); + hdw->video_std_enum_names = 0; + } if (!std_cnt) { pvr2_trace( @@ -1808,42 +1779,22 @@ void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw,int arg) hdw->video_std_avail = 0; pvr2_hdw_internal_set_std_cur(hdw,0); return; - } - - if (std_cnt) { - // Allocate new video standard array - hdw->std_defs = kmalloc(sizeof(struct v4l2_standard) * std_cnt, - GFP_KERNEL); - hdw->std_cnt = std_cnt; - memset(hdw->std_defs,0,sizeof(struct v4l2_standard) * std_cnt); - hdw->video_std_names = kmalloc(sizeof(char *) * std_cnt, - GFP_KERNEL); - memset(hdw->video_std_names,0,sizeof(char *) * std_cnt); - idx2 = 0; - - // Initialize video standard array - for (idx1 = 0; idx1 < pvr_standards_cnt; idx1++) { - if (!buckets[idx1]) continue; - memcpy(hdw->std_defs + idx2, - pvr_standards + idx1, - sizeof(struct v4l2_standard)); - hdw->std_defs[idx2].id = buckets[idx1]; - idx2++; - } - - // Generate a name for each known video standard - for (idx1 = 0; idx1 < std_cnt; idx1++) { - name_bucket(hdw->std_defs + idx1); - hdw->video_std_names[idx1] = - hdw->std_defs[idx1].name; + } else { + hdw->video_std_enum_names = kmalloc(sizeof(char *)*std_cnt, + GFP_KERNEL); + for (idx = 0; idx < std_cnt; idx++) { + hdw->video_std_enum_names[idx] = + newstd[idx].name; } - // Set up the dynamic control for this standard - hdw->video_std_enum.value_defs_ptr = hdw->video_std_names; - hdw->video_std_enum.value_defs_count = std_cnt; + hdw->video_std_info_enum.def.type_enum.value_names = + hdw->video_std_enum_names; + hdw->video_std_info_enum.def.type_enum.count = std_cnt; + hdw->std_defs = newstd; + hdw->std_cnt = std_cnt; } - hdw->video_std_avail = mmsk; + hdw->video_std_avail = arg; if (!(hdw->video_std_avail & hdw->video_std_cur)) { // Reselect standard if there isn't one that matches... pvr2_hdw_internal_set_stdenum_cur(hdw,0); @@ -1857,11 +1808,17 @@ unsigned int pvr2_hdw_get_stdenum_count(struct pvr2_hdw *hdw) } -const struct v4l2_standard *pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw, - unsigned int idx) +int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw, + struct v4l2_standard *std, + unsigned int idx) { - if (idx >= hdw->std_cnt) return 0; - return hdw->std_defs + idx; + int ret = -EINVAL; + LOCK_TAKE(hdw->big_lock); do { + if (idx >= hdw->std_cnt) break; + memcpy(std,hdw->std_defs+idx,sizeof(*std)); + ret = 0; + } while (0); LOCK_GIVE(hdw->big_lock); + return ret; } @@ -1895,8 +1852,7 @@ void pvr2_hdw_internal_set_std_cur(struct pvr2_hdw *hdw,int val) // Fix up standard group now hdw->video_std_cur = id; - hdw->controls[PVR2_CID_STDCUR].value = id; - hdw->controls[PVR2_CID_STDCUR].dirty = !0; + hdw->video_std_dirty = !0; for (idx = 0; idx < hdw->std_cnt; idx++) { if (hdw->std_defs[idx].id & id) { hdw->std_id = idx; @@ -1909,42 +1865,6 @@ void pvr2_hdw_internal_set_std_cur(struct pvr2_hdw *hdw,int val) } -static int pvr2_ctl_set_stdcur(struct pvr2_ctrl *cptr,int val) -{ - pvr2_hdw_internal_set_std_cur(cptr->hdw,val); - return 0; -} - - -static int pvr2_ctl_get_stdcur(struct pvr2_ctrl *cptr) -{ - return (int)(cptr->hdw->video_std_cur); -} - - -static int pvr2_ctl_set_stdenumcur(struct pvr2_ctrl *cptr,int val) -{ - if (val < 0) return -EINVAL; - if (val >= cptr->hdw->std_cnt) return -EINVAL; - cptr->hdw->std_id = val; - pvr2_hdw_internal_set_std_cur(cptr->hdw, - cptr->hdw->std_id); - return 0; -} - - -static int pvr2_ctl_get_stdenumcur(struct pvr2_ctrl *cptr) -{ - return cptr->hdw->std_id; -} - - -static int pvr2_ctl_get_stdavail(struct pvr2_ctrl *cptr) -{ - return (int)(cptr->hdw->video_std_avail); -} - - /* Get the number of defined controls */ unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw) { @@ -1956,14 +1876,14 @@ unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw) struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *hdw, unsigned int idx) { - if (idx < 0) return 0; if (idx >= CTRL_COUNT) return 0; return hdw->controls + idx; } -/* Given an ID, retrieve the control structure associated with it. */ -struct pvr2_ctrl *pvr2_hdw_get_ctrl(struct pvr2_hdw *hdw,unsigned int ctl_id) +/* Retrieve a control handle given its index (0..count-1) */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *hdw, + unsigned int ctl_id) { struct pvr2_ctrl *cptr; unsigned int idx; @@ -1972,212 +1892,38 @@ struct pvr2_ctrl *pvr2_hdw_get_ctrl(struct pvr2_hdw *hdw,unsigned int ctl_id) /* This could be made a lot more efficient, but for now... */ for (idx = 0; idx < CTRL_COUNT; idx++) { cptr = hdw->controls + idx; - i = cptr->ctl_def->id; + i = cptr->info->internal_id; if (i && (i == ctl_id)) return cptr; } - - return 0; -} - - -/* Set the current value of a given control. This assumes we are already - inside our critical region. */ -int pvr2_ctrl_internal_set_value(struct pvr2_ctrl *cptr,int value) -{ - const struct pvr2_ctl_def *dptr; - int ret; - if (!cptr) return -EINVAL; - if (!cptr->is_valid) return -EINVAL; - dptr = cptr->ctl_def; - if (!dptr->is_valid) return -EINVAL; - if (value < dptr->min_value) return -EINVAL; - if (value > dptr->max_value) return -EINVAL; - if (dptr->set_func) { - ret = dptr->set_func(cptr,value); - pvr2_i2c_core_check_stale(cptr->hdw); - pvr2_i2c_core_sync(cptr->hdw); - return ret; - } else if (dptr->get_func) { - /* If there's no "set" function yet there is still a "get" - function, then treat this as a read-only value. */ - return -EINVAL; - } - if ((cptr->value != value) || (ctlchg != 0)) { - cptr->value = value; - cptr->dirty = !0; - } return 0; } -/* Get the current value of a given control. This assumes that we are - already inside our critical region. */ -int pvr2_ctrl_internal_get_value(struct pvr2_ctrl *cptr) -{ - const struct pvr2_ctl_def *dptr; - if (!cptr) return 0; - if (!cptr->is_valid) return 0; - dptr = cptr->ctl_def; - if (!dptr->is_valid) return 0; - if (dptr->get_func) { - return dptr->get_func(cptr); - } - - return cptr->value; -} - - -/* Set the current value of the given control. */ -int pvr2_ctrl_set_value(struct pvr2_ctrl *cptr,int val) -{ - int ret; - if (!cptr) return -EINVAL; - LOCK_TAKE(cptr->hdw->big_lock); do { - ret = pvr2_ctrl_internal_set_value(cptr,val); - } while(0); LOCK_GIVE(cptr->hdw->big_lock); - return ret; -} - - -/* Get the current value of the given control. */ -int pvr2_ctrl_get_value(struct pvr2_ctrl *cptr) +/* Given an ID, retrieve the control structure associated with it. */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *hdw,unsigned int ctl_id) { - int ret; - if (!cptr) return -EINVAL; - LOCK_TAKE(cptr->hdw->big_lock); do { - ret = pvr2_ctrl_internal_get_value(cptr); - } while(0); LOCK_GIVE(cptr->hdw->big_lock); - return ret; -} - + struct pvr2_ctrl *cptr; + unsigned int idx; + int i; -/* Return the type of the given control (int, enum, or bit mask). */ -int pvr2_ctrl_get_type(struct pvr2_ctrl *cptr) -{ - const struct pvr2_ctl_def *dptr; - if (!cptr) return PVR2_CTRL_TYPE_INVALID; - dptr = cptr->ctl_def; - if (dptr->mask_value) { - return PVR2_CTRL_TYPE_BITMASK; - } - if (dptr->value_defs_ptr) { - return PVR2_CTRL_TYPE_ENUM; + /* This could be made a lot more efficient, but for now... */ + for (idx = 0; idx < CTRL_COUNT; idx++) { + cptr = hdw->controls + idx; + i = cptr->info->v4l_id; + if (i && (i == ctl_id)) return cptr; } - return PVR2_CTRL_TYPE_INT; -} - - -/* Return the minimum legal value for a given control. This command is - only relevant for int or enum types. */ -int pvr2_ctrl_get_min_value(struct pvr2_ctrl *cptr) -{ - const struct pvr2_ctl_def *dptr; - if (!cptr) return 0; - dptr = cptr->ctl_def; - return dptr->min_value; -} - - -/* Return the maximum legal value for a given control. This command is - only relevant for int or enum types. */ -int pvr2_ctrl_get_max_value(struct pvr2_ctrl *cptr) -{ - const struct pvr2_ctl_def *dptr; - if (!cptr) return 0; - dptr = cptr->ctl_def; - return dptr->max_value; -} - - -/* Return the default value for a given control. */ -int pvr2_ctrl_get_default_value(struct pvr2_ctrl *cptr) -{ - const struct pvr2_ctl_def *dptr; - if (!cptr) return 0; - dptr = cptr->ctl_def; - return dptr->default_value; -} - - -/* Return a mask of which bits are used within the bit mask of a given - control. This command is only relevant for bit mask types. */ -int pvr2_ctrl_get_mask_value(struct pvr2_ctrl *cptr) -{ - const struct pvr2_ctl_def *dptr; - if (!cptr) return 0; - dptr = cptr->ctl_def; - return dptr->mask_value; -} - - -/* Return true if this is a valid control. */ -int pvr2_ctrl_is_valid(struct pvr2_ctrl *cptr) -{ - if (!cptr) return 0; - return cptr->is_valid; -} - - -/* Return true if the control can be set (otherwise it may only be read, - assuming that it is valid). */ -int pvr2_ctrl_is_writeable(struct pvr2_ctrl *cptr) -{ - const struct pvr2_ctl_def *dptr; - if (!cptr) return 0; - dptr = cptr->ctl_def; - if (!dptr->is_valid) return 0; - if (dptr->set_func) return !0; - if (dptr->get_func) return 0; - return !0; -} - - -/* Return the control's name, or null if there isn't a name or the control - isn't otherwise valid. */ -const char *pvr2_ctrl_get_name(struct pvr2_ctrl *cptr) -{ - const struct pvr2_ctl_def *dptr; - if (!cptr) return 0; - dptr = cptr->ctl_def; - return dptr->name; -} - - -/* Return the control's description, or null if there isn't a name or the - control isn't otherwise valid. */ -const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *cptr) -{ - const struct pvr2_ctl_def *dptr; - if (!cptr) return 0; - dptr = cptr->ctl_def; - return dptr->desc; + return 0; } -/* Return the name for an enumeration value or bit mask position for the - given control. If the control is not an enumeration or bit mask type, - then return null. */ -const char *pvr2_ctrl_get_value_name(struct pvr2_ctrl *cptr,int val) +static const char *get_ctrl_typename(enum pvr2_ctl_type tp) { - int msk,idx; - const struct pvr2_ctl_def *dptr; - if (!cptr) return 0; - dptr = cptr->ctl_def; - if (dptr->mask_value) { - for (idx = 0, msk = 1; - (idx < dptr->value_defs_count) && msk; - idx++, msk <<= 1) { - if (val & msk) { - return dptr->value_defs_ptr[idx]; - } - } - } else { - val -= dptr->min_value; - if (val < 0) return 0; - if (val >= dptr->value_defs_count) return 0; - return dptr->value_defs_ptr[val]; + switch (tp) { + case pvr2_ctl_int: return "integer"; + case pvr2_ctl_enum: return "enum"; + case pvr2_ctl_bitmask: return "bitmask"; } - return 0; + return ""; } @@ -2193,64 +1939,33 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) unsigned long saved_subsys_mask = hdw->subsys_enabled_mask; unsigned long stale_subsys_mask = 0; unsigned int idx; - const struct pvr2_ctl_def *dptr; struct pvr2_ctrl *cptr; int value; - const char *ctl_name; - const char *ctl_value; int commit_flag = 0; - - /* Let's see if the channel changed and we have to update the - frequency because of it. This setup means one can tune the - receiver either by just setting the channel (using the frequency - table), or by directly programming the frequency. How do we - resolve the obvious conflict here? The direct frequency takes - priority; if directly set then we commit that value and force - the channel to zero which is interpreted to mean "none". If on - the other hand we see that the channel has been set and it's a - legal value, then we copy that into the frequency. The metaphor - here is similar to when you tune your digital radio: You an - either set a frequency directly or punch up a pre-programmed - station. Either way a frequency is set, and if you do use a - preset, then the radio also shows you which preset it is - until - you override that by directly entering a new frequency. */ - if (hdw->controls[PVR2_CID_FREQUENCY].dirty) { - /* Frequency has been directly set, so clear out the - channel. */ - hdw->controls[PVR2_CID_CHANNEL].value = 0; - } else if (hdw->controls[PVR2_CID_CHANNEL].dirty) { - int id = hdw->controls[PVR2_CID_CHANNEL].value; - if ((id > 0) && (id <= FREQTABLE_SIZE)) { - if (hdw->controls[PVR2_CID_FREQUENCY].value != - hdw->freqTable[id-1]) { - hdw->controls[PVR2_CID_FREQUENCY].value = - hdw->freqTable[id-1]; - hdw->controls[PVR2_CID_FREQUENCY].dirty = !0; - } - } - } + char buf[100]; + unsigned int bcnt,ccnt; for (idx = 0; idx < CTRL_COUNT; idx++) { cptr = hdw->controls + idx; - if (!cptr->dirty) continue; + if (cptr->info->is_dirty == 0) continue; + if (!cptr->info->is_dirty(cptr)) continue; if (!commit_flag) { commit_flag = !0; } - value = cptr->value; - dptr = cptr->ctl_def; - ctl_name = dptr->name; - if (dptr->value_defs_ptr) { - if (value < dptr->value_defs_count) { - ctl_value = dptr->value_defs_ptr[value]; - } else { - ctl_value = ""; - } - } else { - ctl_value = ""; - } + + bcnt = scnprintf(buf,sizeof(buf),"\"%s\" <-- ", + cptr->info->name); + value = 0; + cptr->info->get_value(cptr,&value); + pvr2_ctrl_value_to_sym_internal(cptr,~0,value, + buf+bcnt, + sizeof(buf)-bcnt,&ccnt); + bcnt += ccnt; + bcnt += scnprintf(buf+bcnt,sizeof(buf)-bcnt," <%s>", + get_ctrl_typename(cptr->info->type)); pvr2_trace(PVR2_TRACE_CTL, - "/*--TRACE_COMMIT--*/ \"%s\" <-- %d (%s)", - ctl_name,value,ctl_value); + "/*--TRACE_COMMIT--*/ %.*s", + bcnt,buf); } if (!commit_flag) { @@ -2261,7 +1976,7 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) /* When video standard changes, reset the hres and vres values - but if the user has pending changes there, then let the changes take priority. */ - if (hdw->controls[PVR2_CID_STDCUR].dirty) { + if (hdw->video_std_dirty) { /* Rewrite the vertical resolution to be appropriate to the video standard that has been selected. */ int nvres; @@ -2270,28 +1985,28 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) } else { nvres = 576; } - if (nvres != hdw->controls[PVR2_CID_VRES].value) { - hdw->controls[PVR2_CID_VRES].value = nvres; - hdw->controls[PVR2_CID_VRES].dirty = !0; + if (nvres != hdw->res_ver_val) { + hdw->res_ver_val = nvres; + hdw->res_ver_dirty = !0; } - if (!hdw->controls[PVR2_CID_INTERLACE].value) { - hdw->controls[PVR2_CID_INTERLACE].value = 0; - hdw->controls[PVR2_CID_INTERLACE].dirty = !0; + if (!hdw->interlace_val) { + hdw->interlace_val = 0; + hdw->interlace_dirty = !0; } } - if (hdw->controls[PVR2_CID_STDCUR].dirty || - hdw->controls[PVR2_CID_VRES].dirty || - hdw->controls[PVR2_CID_HRES].dirty || - hdw->controls[PVR2_CID_INTERLACE].dirty || - hdw->controls[PVR2_CID_VBR].dirty || - hdw->controls[PVR2_CID_AVERAGEVIDEOBITRATE].dirty || - hdw->controls[PVR2_CID_PEAKVIDEOBITRATE].dirty || - hdw->controls[PVR2_CID_AUDIOBITRATE].dirty || - hdw->controls[PVR2_CID_SRATE].dirty || - hdw->controls[PVR2_CID_AUDIOLAYER].dirty || - hdw->controls[PVR2_CID_AUDIOCRC].dirty || - hdw->controls[PVR2_CID_AUDIOEMPHASIS].dirty) { + if (hdw->video_std_dirty || + hdw->res_ver_dirty || + hdw->res_hor_dirty || + hdw->interlace_dirty || + hdw->vbr_dirty || + hdw->videobitrate_dirty || + hdw->videopeak_dirty || + hdw->audiobitrate_dirty || + hdw->srate_dirty || + hdw->audiolayer_dirty || + hdw->audiocrc_dirty || + hdw->audioemphasis_dirty) { /* If any of this changes, then the encoder needs to be reconfigured, and we need to reset the stream. */ stale_subsys_mask |= PVR2_SUBSYS_ENC_CFG; @@ -2306,7 +2021,8 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) for (idx = 0; idx < CTRL_COUNT; idx++) { cptr = hdw->controls + idx; - cptr->dirty = 0; + if (!cptr->info->clear_dirty) continue; + cptr->info->clear_dirty(cptr); } /* Now execute i2c core update */ @@ -2374,7 +2090,7 @@ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw) unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *hdw) { unsigned int msk = 0; - switch (hdw->controls[PVR2_CID_INPUT].value) { + switch (hdw->input_val) { case PVR2_CVAL_INPUT_TV: case PVR2_CVAL_INPUT_RADIO: if (hdw->decoder_ctrl && @@ -2398,42 +2114,6 @@ unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *hdw) } -static int pvr2_ctl_get_subsys_mask(struct pvr2_ctrl *cptr) -{ - return cptr->hdw->subsys_enabled_mask; -} - - -static int pvr2_ctl_set_subsys_mask(struct pvr2_ctrl *cptr,int val) -{ - pvr2_hdw_subsys_bit_chg_no_lock(cptr->hdw,~0,val); - return 0; -} - - -static int pvr2_ctl_get_subsys_stream_mask(struct pvr2_ctrl *cptr) -{ - return cptr->hdw->subsys_stream_mask; -} - - -static int pvr2_ctl_set_subsys_stream_mask(struct pvr2_ctrl *cptr, - int val) -{ - pvr2_hdw_subsys_stream_bit_chg_no_lock(cptr->hdw,~0,val); - return 0; -} - - -static int pvr2_ctl_get_hsm(struct pvr2_ctrl *cptr) -{ - int result = pvr2_hdw_is_hsm(cptr->hdw); - if (result < 0) return PVR2_CVAL_HSM_FAIL; - if (result) return PVR2_CVAL_HSM_HIGH; - return PVR2_CVAL_HSM_FULL; -} - - int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw) { int result; @@ -2449,13 +2129,6 @@ int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw) } -static int pvr2_ctl_get_signal(struct pvr2_ctrl *cptr) -{ - return ((pvr2_hdw_get_signal_status_internal(cptr->hdw) & - PVR2_SIGNAL_OK) ? 1 : 0); -} - - /* Return bit mask indicating signal status */ unsigned int pvr2_hdw_get_signal_status(struct pvr2_hdw *hdw) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 4101f4097..51e896956 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -25,13 +25,10 @@ #include #include #include "pvrusb2-io.h" +#include "pvrusb2-ctrl.h" -#define PVR2_CTRL_TYPE_INVALID 0 -#define PVR2_CTRL_TYPE_ENUM 1 -#define PVR2_CTRL_TYPE_INT 2 -#define PVR2_CTRL_TYPE_BITMASK 3 - -/* Private V4L2-compatible controls available in this driver */ +/* Private V4L2-compatible controls available in this driver, look these up + with pvr2_hdw_get_ctrl_v4l(). */ #define V4L2_CID_PVR_SRATE (V4L2_CID_PRIVATE_BASE) #define V4L2_CID_PVR_AUDIOBITRATE (V4L2_CID_PRIVATE_BASE+1) #define V4L2_CID_PVR_AUDIOCRC (V4L2_CID_PRIVATE_BASE+2) @@ -41,25 +38,23 @@ #define V4L2_CID_PVR_VIDEOPEAK (V4L2_CID_PRIVATE_BASE+6) #define V4L2_CID_PVR_VIDEOSTANDARD (V4L2_CID_PRIVATE_BASE+7) -/* Deliberate gap for CIDs we don't want apps to discover */ -#define V4L2_CID_PVR_GAP (V4L2_CID_PRIVATE_BASE+8) - -/* Additional explicit controls needed by V4L2 ioctl implementation */ -#define V4L2_CID_PVR_STDCUR (V4L2_CID_PVR_GAP+1) -#define V4L2_CID_PVR_INPUT (V4L2_CID_PVR_GAP+2) -#define V4L2_CID_PVR_AUDIOMODE (V4L2_CID_PVR_GAP+3) -#define V4L2_CID_PVR_FREQUENCY (V4L2_CID_PVR_GAP+4) -#define V4L2_CID_PVR_HRES (V4L2_CID_PVR_GAP+5) -#define V4L2_CID_PVR_VRES (V4L2_CID_PVR_GAP+6) -#define V4L2_CID_PVR_INTERLACE (V4L2_CID_PVR_GAP+7) +/* Private internal control ids, look these up with + pvr2_hdw_get_ctrl_by_id() - these are NOT visible in V4L */ +#define PVR2_CID_STDENUM 1 +#define PVR2_CID_STDCUR 2 +#define PVR2_CID_STDAVAIL 3 +#define PVR2_CID_INPUT 4 +#define PVR2_CID_AUDIOMODE 5 +#define PVR2_CID_FREQUENCY 6 +#define PVR2_CID_HRES 7 +#define PVR2_CID_VRES 8 +#define PVR2_CID_INTERLACE 9 /* Legal values for the INPUT state variable */ #define PVR2_CVAL_INPUT_TV 0 #define PVR2_CVAL_INPUT_SVIDEO 1 #define PVR2_CVAL_INPUT_COMPOSITE 2 #define PVR2_CVAL_INPUT_RADIO 3 -#define PVR2_CVAL_INPUT_MIN PVR2_CVAL_INPUT_TV -#define PVR2_CVAL_INPUT_MAX PVR2_CVAL_INPUT_RADIO /* Values that pvr2_hdw_get_signal_status() returns */ #define PVR2_SIGNAL_OK 0x0001 @@ -99,8 +94,6 @@ const char *pvr2_config_get_name(enum pvr2_config); struct pvr2_hdw; -struct pvr2_ctrl; - /* Create and return a structure for interacting with the underlying hardware */ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, @@ -155,59 +148,11 @@ unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *); /* Retrieve a control handle given its index (0..count-1) */ struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *,unsigned int); -/* Retrieve a control handle given its well known ID */ -struct pvr2_ctrl *pvr2_hdw_get_ctrl(struct pvr2_hdw *,unsigned int ctl_id); - -/* Set the current value of the given control. */ -int pvr2_ctrl_set_value(struct pvr2_ctrl *,int val); - -/* Get the current value of the given control. */ -int pvr2_ctrl_get_value(struct pvr2_ctrl *); - -/* Return the type of the given control (int, enum, or bit mask). */ -int pvr2_ctrl_get_type(struct pvr2_ctrl *); - -/* Return the minimum legal value for a given control. This command is - only relevant for int or enum types. */ -int pvr2_ctrl_get_min_value(struct pvr2_ctrl *); - -/* Return the maximum legal value for a given control. This command is - only relevant for int or enum types. */ -int pvr2_ctrl_get_max_value(struct pvr2_ctrl *); - -/* Return a mask of which bits are used within the bit mask of a given - control. This command is only relevant for bit mask types. */ -int pvr2_ctrl_get_mask_value(struct pvr2_ctrl *); +/* Retrieve a control handle given its internal ID (if any) */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *,unsigned int); -/* Return the default value for a given control. */ -int pvr2_ctrl_get_default_value(struct pvr2_ctrl *); - -/* Return true if this is a valid control. */ -int pvr2_ctrl_is_valid(struct pvr2_ctrl *); - -/* Return true if the control can be set (otherwise it may only be read, - assuming that it is valid). */ -int pvr2_ctrl_is_writeable(struct pvr2_ctrl *); - -/* Return the control's name, or null if there isn't a name or the control - isn't otherwise valid. */ -const char *pvr2_ctrl_get_name(struct pvr2_ctrl *); - -/* Return the control's description, or null if there isn't a name or the - control isn't otherwise valid. */ -const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *); - -/* Return the name for an enumeration value or bit mask position for the - given control. If the control is not an enumeration or bit mask type, - then return null. */ -const char *pvr2_ctrl_get_value_name(struct pvr2_ctrl *,int val); - -/* Return the number of support standard groups */ -unsigned int pvr2_hdw_get_stdenum_count(struct pvr2_hdw *); - -/* Return a pointer to a v4l2 standard descriptor for a given group */ -const struct v4l2_standard *pvr2_hdw_get_stdenum_value(struct pvr2_hdw *, - unsigned int idx); +/* Retrieve a control handle given its V4L ID (if any) */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *,unsigned int ctl_id); /* Commit all control changes made up to this point */ int pvr2_hdw_commit_ctl(struct pvr2_hdw *); @@ -233,6 +178,10 @@ int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config); /* Get handle to video output stream */ struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *); +/* Emit a video standard struct */ +int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std, + unsigned int idx); + /* Enable / disable various pieces of hardware. Items to change are identified by bit positions within msk, and new state for each item is identified by corresponding bit positions within val. */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c index 25543e9b1..e6b176bf3 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c @@ -29,7 +29,7 @@ static void set_standard(struct pvr2_hdw *hdw) { v4l2_std_id vs; - vs = hdw->controls[PVR2_CID_STDCUR].value; + vs = hdw->video_std_cur; pvr2_trace(PVR2_TRACE_CHIPS, "i2c v4l2 set_standard(0x%llx)",(__u64)vs); @@ -39,7 +39,7 @@ static void set_standard(struct pvr2_hdw *hdw) static int check_standard(struct pvr2_hdw *hdw) { - return hdw->controls[PVR2_CID_STDCUR].dirty != 0; + return hdw->video_std_dirty != 0; } @@ -55,32 +55,30 @@ static void set_bcsh(struct pvr2_hdw *hdw) struct v4l2_control ctrl; pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_bcsh" " b=%d c=%d s=%d h=%d", - hdw->controls[PVR2_CID_BRIGHTNESS].value, - hdw->controls[PVR2_CID_CONTRAST].value, - hdw->controls[PVR2_CID_SATURATION].value, - hdw->controls[PVR2_CID_HUE].value); + hdw->brightness_val,hdw->contrast_val, + hdw->saturation_val,hdw->hue_val); memset(&ctrl,0,sizeof(ctrl)); ctrl.id = V4L2_CID_BRIGHTNESS; - ctrl.value = hdw->controls[PVR2_CID_BRIGHTNESS].value; + ctrl.value = hdw->brightness_val; pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); ctrl.id = V4L2_CID_CONTRAST; - ctrl.value = hdw->controls[PVR2_CID_CONTRAST].value; + ctrl.value = hdw->contrast_val; pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); ctrl.id = V4L2_CID_SATURATION; - ctrl.value = hdw->controls[PVR2_CID_SATURATION].value; + ctrl.value = hdw->saturation_val; pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); ctrl.id = V4L2_CID_HUE; - ctrl.value = hdw->controls[PVR2_CID_HUE].value; + ctrl.value = hdw->hue_val; pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); } static int check_bcsh(struct pvr2_hdw *hdw) { - return (hdw->controls[PVR2_CID_BRIGHTNESS].dirty || - hdw->controls[PVR2_CID_CONTRAST].dirty || - hdw->controls[PVR2_CID_SATURATION].dirty || - hdw->controls[PVR2_CID_HUE].dirty); + return (hdw->brightness_dirty || + hdw->contrast_dirty || + hdw->saturation_dirty || + hdw->hue_dirty); } @@ -97,37 +95,37 @@ static void set_volume(struct pvr2_hdw *hdw) pvr2_trace(PVR2_TRACE_CHIPS, "i2c v4l2 set_volume" "(vol=%d bal=%d bas=%d treb=%d mute=%d)", - hdw->controls[PVR2_CID_VOLUME].value, - hdw->controls[PVR2_CID_BALANCE].value, - hdw->controls[PVR2_CID_BASS].value, - hdw->controls[PVR2_CID_TREBLE].value, - hdw->controls[PVR2_CID_MUTE].value); + hdw->volume_val, + hdw->balance_val, + hdw->bass_val, + hdw->treble_val, + hdw->mute_val); memset(&ctrl,0,sizeof(ctrl)); ctrl.id = V4L2_CID_AUDIO_MUTE; - ctrl.value = hdw->controls[PVR2_CID_MUTE].value ? 1 : 0; + ctrl.value = hdw->mute_val ? 1 : 0; pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); ctrl.id = V4L2_CID_AUDIO_VOLUME; - ctrl.value = hdw->controls[PVR2_CID_VOLUME].value; + ctrl.value = hdw->volume_val; pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); ctrl.id = V4L2_CID_AUDIO_BALANCE; - ctrl.value = hdw->controls[PVR2_CID_BALANCE].value; + ctrl.value = hdw->balance_val; pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); ctrl.id = V4L2_CID_AUDIO_BASS; - ctrl.value = hdw->controls[PVR2_CID_BASS].value; + ctrl.value = hdw->bass_val; pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); ctrl.id = V4L2_CID_AUDIO_TREBLE; - ctrl.value = hdw->controls[PVR2_CID_TREBLE].value; + ctrl.value = hdw->treble_val; pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); } static int check_volume(struct pvr2_hdw *hdw) { - return (hdw->controls[PVR2_CID_VOLUME].dirty || - hdw->controls[PVR2_CID_BALANCE].dirty || - hdw->controls[PVR2_CID_BASS].dirty || - hdw->controls[PVR2_CID_TREBLE].dirty || - hdw->controls[PVR2_CID_MUTE].dirty); + return (hdw->volume_dirty || + hdw->balance_dirty || + hdw->bass_dirty || + hdw->treble_dirty || + hdw->mute_dirty); } @@ -142,7 +140,7 @@ static void set_frequency(struct pvr2_hdw *hdw) { unsigned long fv; struct v4l2_frequency freq; - fv = hdw->controls[PVR2_CID_FREQUENCY].value; + fv = hdw->freqVal; pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_freq(%lu)",fv); memset(&freq,0,sizeof(freq)); freq.frequency = fv / 62500; @@ -154,7 +152,7 @@ static void set_frequency(struct pvr2_hdw *hdw) static int check_frequency(struct pvr2_hdw *hdw) { - return hdw->controls[PVR2_CID_FREQUENCY].dirty != 0; + return hdw->freqDirty != 0; } @@ -172,8 +170,8 @@ static void set_size(struct pvr2_hdw *hdw) memset(&fmt,0,sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt.fmt.pix.width = hdw->controls[PVR2_CID_HRES].value; - fmt.fmt.pix.height = hdw->controls[PVR2_CID_VRES].value; + fmt.fmt.pix.width = hdw->res_hor_val; + fmt.fmt.pix.height = hdw->res_ver_val; pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_size(%dx%d)", fmt.fmt.pix.width,fmt.fmt.pix.height); @@ -184,8 +182,7 @@ static void set_size(struct pvr2_hdw *hdw) static int check_size(struct pvr2_hdw *hdw) { - return (hdw->controls[PVR2_CID_HRES].dirty || - hdw->controls[PVR2_CID_VRES].dirty); + return (hdw->res_hor_dirty || hdw->res_ver_dirty); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-std.c b/linux/drivers/media/video/pvrusb2/pvrusb2-std.c new file mode 100644 index 000000000..857829120 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-std.c @@ -0,0 +1,377 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely + * + * 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 "pvrusb2-std.h" +#include "pvrusb2-debug.h" + +struct std_name { + const char *name; + v4l2_std_id id; +}; + +#define CSTD_PAL \ + (V4L2_STD_PAL_B| \ + V4L2_STD_PAL_B1| \ + V4L2_STD_PAL_G| \ + V4L2_STD_PAL_H| \ + V4L2_STD_PAL_I| \ + V4L2_STD_PAL_D| \ + V4L2_STD_PAL_D1| \ + V4L2_STD_PAL_K| \ + V4L2_STD_PAL_M| \ + V4L2_STD_PAL_N| \ + V4L2_STD_PAL_Nc| \ + V4L2_STD_PAL_60) + +#define CSTD_NTSC \ + (V4L2_STD_NTSC_M| \ + V4L2_STD_NTSC_M_JP| \ + V4L2_STD_NTSC_443) + +#define CSTD_SECAM \ + (V4L2_STD_SECAM_B| \ + V4L2_STD_SECAM_D| \ + V4L2_STD_SECAM_G| \ + V4L2_STD_SECAM_H| \ + V4L2_STD_SECAM_K| \ + V4L2_STD_SECAM_K1| \ + V4L2_STD_SECAM_L| \ + V4L2_STD_SECAM_LC) + +#define TSTD_B (V4L2_STD_PAL_B|V4L2_STD_SECAM_B) +#define TSTD_B1 (V4L2_STD_PAL_B1) +#define TSTD_D (V4L2_STD_PAL_D|V4L2_STD_SECAM_D) +#define TSTD_D1 (V4L2_STD_PAL_D1) +#define TSTD_G (V4L2_STD_PAL_G|V4L2_STD_SECAM_G) +#define TSTD_H (V4L2_STD_PAL_H|V4L2_STD_SECAM_H) +#define TSTD_I (V4L2_STD_PAL_I) +#define TSTD_K (V4L2_STD_PAL_K|V4L2_STD_SECAM_K) +#define TSTD_K1 (V4L2_STD_SECAM_K1) +#define TSTD_L (V4L2_STD_SECAM_L) +#define TSTD_LC (V4L2_STD_SECAM_LC) +#define TSTD_M (V4L2_STD_PAL_M|V4L2_STD_NTSC_M) +#define TSTD_Mj (V4L2_STD_NTSC_M_JP) +#define TSTD_443 (V4L2_STD_NTSC_443) +#define TSTD_N (V4L2_STD_PAL_N) +#define TSTD_Nc (V4L2_STD_PAL_Nc) +#define TSTD_60 (V4L2_STD_PAL_60) + +#define CSTD_ALL (CSTD_PAL|CSTD_NTSC|CSTD_SECAM) + +/* Mapping of standard bits to color system */ +const static struct std_name std_groups[] = { + {"PAL",CSTD_PAL}, + {"NTSC",CSTD_NTSC}, + {"SECAM",CSTD_SECAM}, +}; + +/* Mapping of standard bits to modulation system */ +const static struct std_name std_items[] = { + {"B",TSTD_B}, + {"B1",TSTD_B1}, + {"D",TSTD_D}, + {"D1",TSTD_D1}, + {"G",TSTD_G}, + {"H",TSTD_H}, + {"I",TSTD_I}, + {"K",TSTD_K}, + {"K1",TSTD_K1}, + {"L",TSTD_L}, + {"LC",TSTD_LC}, + {"M",TSTD_M}, + {"Mj",TSTD_Mj}, + {"443",TSTD_443}, + {"N",TSTD_N}, + {"Nc",TSTD_Nc}, + {"60",TSTD_60}, +}; + + +// Search an array of std_name structures and return a pointer to the +// element with the matching name. +static const struct std_name *find_std_name(const struct std_name *arrPtr, + unsigned int arrSize, + const char *bufPtr, + unsigned int bufSize) +{ + unsigned int idx; + const struct std_name *p; + for (idx = 0; idx < arrSize; idx++) { + p = arrPtr + idx; + if (strlen(p->name) != bufSize) continue; + if (!memcmp(bufPtr,p->name,bufSize)) return p; + } + return 0; +} + + +int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr, + unsigned int bufSize) +{ + v4l2_std_id id = 0; + v4l2_std_id cmsk = 0; + v4l2_std_id t; + int mMode = 0; + unsigned int cnt; + char ch; + const struct std_name *sp; + + while (bufSize) { + if (!mMode) { + cnt = 0; + while ((cnt < bufSize) && (bufPtr[cnt] != '-')) cnt++; + if (cnt >= bufSize) return 0; // No more characters + sp = find_std_name( + std_groups, + sizeof(std_groups)/sizeof(std_groups[0]), + bufPtr,cnt); + if (!sp) return 0; // Illegal color system name + cnt++; + bufPtr += cnt; + bufSize -= cnt; + mMode = !0; + cmsk = sp->id; + continue; + } + cnt = 0; + while (cnt < bufSize) { + ch = bufPtr[cnt]; + if (ch == ';') { + mMode = 0; + break; + } + if (ch == '/') break; + cnt++; + } + sp = find_std_name(std_items, + sizeof(std_items)/sizeof(std_items[0]), + bufPtr,cnt); + if (!sp) return 0; // Illegal modulation system ID + t = sp->id & cmsk; + if (!t) return 0; // Specific color + modulation system illegal + id |= t; + if (cnt < bufSize) cnt++; + bufPtr += cnt; + bufSize -= cnt; + } + + if (idPtr) *idPtr = id; + return !0; +} + + +unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize, + v4l2_std_id id) +{ + unsigned int idx1,idx2; + const struct std_name *ip,*gp; + int gfl,cfl; + unsigned int c1,c2; + cfl = 0; + c1 = 0; + for (idx1 = 0; + idx1 < sizeof(std_groups)/sizeof(std_groups[0]); + idx1++) { + gp = std_groups + idx1; + gfl = 0; + for (idx2 = 0; + idx2 < sizeof(std_items)/sizeof(std_items[0]); + idx2++) { + ip = std_items + idx2; + if (!(gp->id & ip->id & id)) continue; + if (!gfl) { + if (cfl) { + c2 = scnprintf(bufPtr,bufSize,";"); + c1 += c2; + bufSize -= c2; + bufPtr += c2; + } + cfl = !0; + c2 = scnprintf(bufPtr,bufSize, + "%s-",gp->name); + gfl = !0; + } else { + c2 = scnprintf(bufPtr,bufSize,"/"); + } + c1 += c2; + bufSize -= c2; + bufPtr += c2; + c2 = scnprintf(bufPtr,bufSize, + ip->name); + c1 += c2; + bufSize -= c2; + bufPtr += c2; + } + } + return c1; +} + + +// Template data for possible enumerated video standards. Here we group +// standards which share common frame rates and resolution. +static struct v4l2_standard generic_standards[] = { + { + .id = (TSTD_B|TSTD_B1| + TSTD_D|TSTD_D1| + TSTD_G| + TSTD_H| + TSTD_I| + TSTD_K|TSTD_K1| + TSTD_L| + TSTD_LC| + TSTD_N|TSTD_Nc), + .frameperiod = + { + .numerator = 1, + .denominator= 25 + }, + .framelines = 625, + .reserved = {0,0,0,0} + }, { + .id = (TSTD_M|TSTD_Mj), + .frameperiod = + { + .numerator = 1001, + .denominator= 30000 + }, + .framelines = 525, + .reserved = {0,0,0,0} + }, { // This is a total wild guess + .id = (TSTD_60), + .frameperiod = + { + .numerator = 1001, + .denominator= 30000 + }, + .framelines = 525, + .reserved = {0,0,0,0} + }, { // This is total wild guess + .id = (TSTD_443), + .frameperiod = + { + .numerator = 1001, + .denominator= 30000 + }, + .framelines = 525, + .reserved = {0,0,0,0} + } +}; + +#define generic_standards_cnt (sizeof(generic_standards)/sizeof(generic_standards[0])) + +static struct v4l2_standard *match_std(v4l2_std_id id) +{ + unsigned int idx; + for (idx = 0; idx < generic_standards_cnt; idx++) { + if (generic_standards[idx].id & id) { + return generic_standards + idx; + } + } + return 0; +} + +struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, + v4l2_std_id id) +{ + unsigned int std_cnt = 0; + unsigned int idx,bcnt; + v4l2_std_id idmsk,cmsk,fmsk; + struct v4l2_standard *stddefs; + struct v4l2_standard *template; + struct v4l2_standard *std; + + if (pvrusb2_debug & PVR2_TRACE_INIT) { + char buf[50]; + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),id); + pvr2_trace( + PVR2_TRACE_INIT,"Mapping standards mask=0x%x (%.*s)", + (int)id,bcnt,buf); + } + + *countptr = 0; + std_cnt = 0; + fmsk = 0; + for (idmsk = 1, cmsk = id; cmsk; idmsk <<= 1) { + if (!(idmsk & cmsk)) continue; + cmsk &= ~idmsk; + if (match_std(idmsk)) { + std_cnt++; + continue; + } + fmsk |= idmsk; + } + + if (fmsk) { + char buf[50]; + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),fmsk); + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "WARNING:" + " Failed to classify the following standard(s): %.*s", + bcnt,buf); + } + + pvr2_trace(PVR2_TRACE_INIT,"Setting up %u unique standard(s)", + std_cnt); + if (!std_cnt) return 0; // paranoia + + stddefs = kmalloc(sizeof(struct v4l2_standard) * std_cnt, + GFP_KERNEL); + memset(stddefs,0,sizeof(struct v4l2_standard) * std_cnt); + idx = 0; + for (idmsk = 1, cmsk = id; cmsk; idmsk <<= 1) { + if (!(idmsk & cmsk)) continue; + cmsk &= ~idmsk; + template = match_std(idmsk); + if (!template) continue; + if (idx >= std_cnt) break; + std = stddefs+idx; + memcpy(std,template,sizeof(*template)); + std->index = idx; + std->id = idmsk; + bcnt = pvr2_std_id_to_str(std->name, + sizeof(std->name)-1, + idmsk); + std->name[bcnt] = 0; + pvr2_trace(PVR2_TRACE_INIT,"Set up standard idx=%u name=%s", + idx,std->name); + idx++; + } + + *countptr = std_cnt; + return stddefs; +} + +v4l2_std_id pvr2_std_get_usable(void) +{ + return CSTD_ALL; +} + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-std.h b/linux/drivers/media/video/pvrusb2/pvrusb2-std.h new file mode 100644 index 000000000..dc9ef5bb3 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-std.h @@ -0,0 +1,61 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely + * + * 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 + * + */ +#ifndef __PVRUSB2_STD_H +#define __PVRUSB2_STD_H + +#include "compat.h" +#include + +// Convert string describing one or more video standards into a mask of V4L +// standard bits. Return true if conversion succeeds otherwise return +// false. String is expected to be of the form: C1-x/y;C2-a/b where C1 and +// C2 are color system names (e.g. "PAL", "NTSC") and x, y, a, and b are +// modulation schemes (e.g. "M", "B", "G", etc). +int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr, + unsigned int bufSize); + +// Convert any arbitrary set of video standard bits into an unambiguous +// readable string. Return value is the number of bytes consumed in the +// buffer. The formatted string is of a form that can be parsed by our +// sibling std_std_to_id() function. +unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize, + v4l2_std_id id); + +// Create an array of suitable v4l2_standard structures given a bit mask of +// video standards to support. The array is allocated from the heap, and +// the number of elements is returned in the first argument. +struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, + v4l2_std_id id); + +// Return mask of which video standard bits are valid +v4l2_std_id pvr2_std_get_usable(void); + +#endif /* __PVRUSB2_STD_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 053c0b24c..3bf8f166c 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -58,11 +58,13 @@ struct pvr2_sysfs_ctl_item { struct class_device_attribute attr_min; struct class_device_attribute attr_max; struct class_device_attribute attr_enum; + struct class_device_attribute attr_bits; struct class_device_attribute attr_val; + struct class_device_attribute attr_custom; struct pvr2_ctrl *cptr; struct pvr2_sysfs *chptr; struct pvr2_sysfs_ctl_item *item_next; - struct attribute *attr_gen[5]; + struct attribute *attr_gen[6]; struct attribute_group grp; char name[80]; }; @@ -80,7 +82,7 @@ static ssize_t show_name(int id,struct class_device *class_dev,char *buf) sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!pvr2_ctrl_is_valid(cptr)) return -EINVAL; + if (!cptr) return -EINVAL; name = pvr2_ctrl_get_desc(cptr); pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s",sfp,id,name); @@ -94,233 +96,171 @@ static ssize_t show_min(int id,struct class_device *class_dev,char *buf) { struct pvr2_ctrl *cptr; struct pvr2_sysfs *sfp; - int val; + long val; sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!pvr2_ctrl_is_valid(cptr)) return -EINVAL; - val = pvr2_ctrl_get_min_value(cptr); - pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %d",sfp,id,val); + if (!cptr) return -EINVAL; + val = pvr2_ctrl_get_min(cptr); - return scnprintf(buf,PAGE_SIZE,"%d\n",val); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld",sfp,id,val); + + return scnprintf(buf,PAGE_SIZE,"%ld\n",val); } static ssize_t show_max(int id,struct class_device *class_dev,char *buf) { struct pvr2_ctrl *cptr; struct pvr2_sysfs *sfp; - int val; + long val; sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!pvr2_ctrl_is_valid(cptr)) return -EINVAL; - val = pvr2_ctrl_get_max_value(cptr); + if (!cptr) return -EINVAL; + val = pvr2_ctrl_get_max(cptr); - pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %d",sfp,id,val); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld",sfp,id,val); - return scnprintf(buf,PAGE_SIZE,"%d\n",val); + return scnprintf(buf,PAGE_SIZE,"%ld\n",val); } -static ssize_t show_val_int(int id,struct class_device *class_dev,char *buf) +static ssize_t show_val_norm(int id,struct class_device *class_dev,char *buf) { struct pvr2_ctrl *cptr; struct pvr2_sysfs *sfp; - int val; + int val,ret; + unsigned int cnt = 0; sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!pvr2_ctrl_is_valid(cptr)) return -EINVAL; - val = pvr2_ctrl_get_value(cptr); + if (!cptr) return -EINVAL; + + ret = pvr2_ctrl_get_value(cptr,&val); + if (ret < 0) return ret; - pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_int(cid=%d) is %d", - sfp,id,val); + ret = pvr2_ctrl_value_to_sym(cptr,~0,val, + buf,PAGE_SIZE-1,&cnt); - return scnprintf(buf,PAGE_SIZE,"%d\n",val); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_norm(cid=%d) is %.*s (%d)", + sfp,id,cnt,buf,val); + buf[cnt] = '\n'; + return cnt+1; } -static ssize_t show_val_enum(int id,struct class_device *class_dev,char *buf) +static ssize_t show_val_custom(int id,struct class_device *class_dev,char *buf) { struct pvr2_ctrl *cptr; struct pvr2_sysfs *sfp; - int val; - const char *name; + int val,ret; + unsigned int cnt = 0; sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!pvr2_ctrl_is_valid(cptr)) return -EINVAL; + if (!cptr) return -EINVAL; - val = pvr2_ctrl_get_value(cptr); - name = pvr2_ctrl_get_value_name(cptr,val); + ret = pvr2_ctrl_get_value(cptr,&val); + if (ret < 0) return ret; - pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_enum(cid=%d) is %s (%d)", - sfp,id,name,val); + ret = pvr2_ctrl_custom_value_to_sym(cptr,~0,val, + buf,PAGE_SIZE-1,&cnt); - return scnprintf(buf,PAGE_SIZE,"%s\n",name); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_custom(cid=%d) is %.*s (%d)", + sfp,id,cnt,buf,val); + buf[cnt] = '\n'; + return cnt+1; } static ssize_t show_enum(int id,struct class_device *class_dev,char *buf) { struct pvr2_ctrl *cptr; struct pvr2_sysfs *sfp; - int minval,maxval,val; - const char *name; - ssize_t cnt = 0; + long val; + unsigned int bcnt,ccnt,ecnt; sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!pvr2_ctrl_is_valid(cptr)) return -EINVAL; - minval = pvr2_ctrl_get_min_value(cptr); - maxval = pvr2_ctrl_get_max_value(cptr); - for (val = minval; val <= maxval; val++) { - name = pvr2_ctrl_get_value_name(cptr,val); - cnt += scnprintf(buf+cnt,PAGE_SIZE-cnt,"%s\n",name); + if (!cptr) return -EINVAL; + ecnt = pvr2_ctrl_get_cnt(cptr); + bcnt = 0; + for (val = 0; val < ecnt; val++) { + pvr2_ctrl_get_valname(cptr,val,buf+bcnt,PAGE_SIZE-bcnt,&ccnt); + bcnt += ccnt; + if (bcnt >= PAGE_SIZE) break; + buf[bcnt] = '\n'; + bcnt++; } pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)",sfp,id); - return cnt; + return bcnt; } -static int store_val_any(int id,struct pvr2_sysfs *sfp, - const char *buf,unsigned int count) +static ssize_t show_bits(int id,struct class_device *class_dev,char *buf) { struct pvr2_ctrl *cptr; - int val,minval,maxval; - int ch,ret; - const char *nv; - unsigned int nl; - int negfl; - - /* Trim leading / trailing whitespace */ - while (count) { - ch = buf[0]; - if ((ch > 32) && (ch < 127)) break; - buf++; - count--; - } - while (count) { - ch = buf[count-1]; - if ((ch > 32) && (ch < 127)) break; - count--; - } + struct pvr2_sysfs *sfp; + int valid_bits,msk; + unsigned int bcnt,ccnt; + sfp = (struct pvr2_sysfs *)class_dev->class_data; + if (!sfp) return -EINVAL; cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!pvr2_ctrl_is_valid(cptr)) return -EINVAL; - - /* Is this an enum? Look for a string value */ - minval = pvr2_ctrl_get_min_value(cptr); - maxval = pvr2_ctrl_get_max_value(cptr); - for (val = minval; val <= maxval; val++) { - nv = pvr2_ctrl_get_value_name(cptr,val); - if ((!nv) && (val == minval)) break; /* Not an enum */ - pvr2_sysfs_trace("pvr2_sysfs(%p) trying ctl_id %d val %d", - sfp,id,val); - if (!nv) { - pvr2_sysfs_trace("pvr2_sysfs(%p) no pointer",sfp); - continue; - } - nl = strlen(nv); - if (nl != count) { - pvr2_sysfs_trace("pvr2_sysfs(%p) count mismatch" - " %d != %d", - sfp,count,nl); - continue; - } - if (memcmp(buf,nv,nl)) { - pvr2_sysfs_trace( - "pvr2_sysfs(%p) name mismatch" - " >>%.*s<< != >>%.*s<<", - sfp,nl,buf,nl,nv); - continue; - } - pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_any(cid=%d)" - " is enum %s", - sfp,id,nv); - ret = pvr2_ctrl_set_value(cptr,val); - pvr2_hdw_commit_ctl(sfp->channel.hdw); - return 0; - } - if (val > minval) { - pvr2_sysfs_trace( - "pvr2_sysfs(%p) store_val_any(cid=%d)" - " unmatched enum >>%.*s<<", - sfp,id,count,buf); - } - - /* Try to parse as a number */ - negfl = 0; - val = 0; - if (count) { - if (buf[0] == '-') { - negfl = !0; - buf++; - count--; - } else if (buf[0] == '+') { - buf++; - count--; - } - } - while (count) { - ch = buf[0]; - if ((ch < '0') || (ch > '9')) break; - val = val * 10; - val += (ch - '0'); - buf++; - count--; + if (!cptr) return -EINVAL; + valid_bits = pvr2_ctrl_get_mask(cptr); + bcnt = 0; + for (msk = 1; valid_bits; msk <<= 1) { + if (!(msk & valid_bits)) continue; + valid_bits &= ~msk; + pvr2_ctrl_get_valname(cptr,msk,buf+bcnt,PAGE_SIZE-bcnt,&ccnt); + bcnt += ccnt; + if (bcnt >= PAGE_SIZE) break; + buf[bcnt] = '\n'; + bcnt++; } - if (!count) { - if (negfl) val = -val; - pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_any(cid=%d)" - " int is %d", - sfp,id,val); - - ret = pvr2_ctrl_set_value(cptr,val); - pvr2_hdw_commit_ctl(sfp->channel.hdw); - return ret; - } - - return -EINVAL; + pvr2_sysfs_trace("pvr2_sysfs(%p) show_bits(cid=%d)",sfp,id); + return bcnt; } -static int store_val_multi(int id,struct pvr2_sysfs *sfp, - const char *buf,unsigned int count) +static int store_val_any(int id,int customfl,struct pvr2_sysfs *sfp, + const char *buf,unsigned int count) { - unsigned int count2; + struct pvr2_ctrl *cptr; int ret; + int mask,val; - while (count) { - count2 = 0; - while ((count2 < count) && (buf[count2] != '\n')) count2++; - ret = store_val_any(id,sfp,buf,count2); - if (ret < 0) return ret; - if (count2 < count) count2++; - buf += count2; - count -= count2; + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); + if (customfl) { + ret = pvr2_ctrl_custom_sym_to_value(cptr,buf,count,&mask,&val); + } else { + ret = pvr2_ctrl_sym_to_value(cptr,buf,count,&mask,&val); } - return 0; + if (ret < 0) return ret; + ret = pvr2_ctrl_set_mask_value(cptr,mask,val); + pvr2_hdw_commit_ctl(sfp->channel.hdw); + return ret; } -static ssize_t store_val_int(int id,struct class_device *class_dev, +static ssize_t store_val_norm(int id,struct class_device *class_dev, const char *buf,size_t count) { struct pvr2_sysfs *sfp; int ret; sfp = (struct pvr2_sysfs *)class_dev->class_data; - ret = store_val_multi(id,sfp,buf,count); + ret = store_val_any(id,0,sfp,buf,count); if (!ret) ret = count; return ret; } -static ssize_t store_val_enum(int id,struct class_device *class_dev, - const char *buf,size_t count) +static ssize_t store_val_custom(int id,struct class_device *class_dev, + const char *buf,size_t count) { struct pvr2_sysfs *sfp; int ret; sfp = (struct pvr2_sysfs *)class_dev->class_data; - ret = store_val_multi(id,sfp,buf,count); + ret = store_val_any(id,1,sfp,buf,count); if (!ret) ret = count; return ret; } @@ -350,11 +290,12 @@ static ssize_t sf_name##_##ctl_id(struct class_device *class_dev,const char *buf CREATE_SHOW_INSTANCE(show_name,ctl_id) \ CREATE_SHOW_INSTANCE(show_min,ctl_id) \ CREATE_SHOW_INSTANCE(show_max,ctl_id) \ -CREATE_SHOW_INSTANCE(show_val_int,ctl_id) \ -CREATE_SHOW_INSTANCE(show_val_enum,ctl_id) \ +CREATE_SHOW_INSTANCE(show_val_norm,ctl_id) \ +CREATE_SHOW_INSTANCE(show_val_custom,ctl_id) \ CREATE_SHOW_INSTANCE(show_enum,ctl_id) \ -CREATE_STORE_INSTANCE(store_val_int,ctl_id) \ -CREATE_STORE_INSTANCE(store_val_enum,ctl_id) +CREATE_SHOW_INSTANCE(show_bits,ctl_id) \ +CREATE_STORE_INSTANCE(store_val_norm,ctl_id) \ +CREATE_STORE_INSTANCE(store_val_custom,ctl_id) \ CREATE_BATCH(0) CREATE_BATCH(1) @@ -396,12 +337,13 @@ struct pvr2_sysfs_func_set { ssize_t (*show_min)(struct class_device *,char *); ssize_t (*show_max)(struct class_device *,char *); ssize_t (*show_enum)(struct class_device *,char *); - ssize_t (*show_val_int)(struct class_device *,char *); - ssize_t (*show_val_enum)(struct class_device *,char *); - ssize_t (*store_val_int)(struct class_device *, - const char *,size_t); - ssize_t (*store_val_enum)(struct class_device *, + ssize_t (*show_bits)(struct class_device *,char *); + ssize_t (*show_val_norm)(struct class_device *,char *); + ssize_t (*store_val_norm)(struct class_device *, const char *,size_t); + ssize_t (*show_val_custom)(struct class_device *,char *); + ssize_t (*store_val_custom)(struct class_device *, + const char *,size_t); }; #define INIT_BATCH(ctl_id) \ @@ -410,10 +352,11 @@ struct pvr2_sysfs_func_set { .show_min = show_min_##ctl_id, \ .show_max = show_max_##ctl_id, \ .show_enum = show_enum_##ctl_id, \ - .show_val_int = show_val_int_##ctl_id, \ - .show_val_enum = show_val_enum_##ctl_id, \ - .store_val_int = store_val_int_##ctl_id, \ - .store_val_enum = store_val_enum_##ctl_id, \ + .show_bits = show_bits_##ctl_id, \ + .show_val_norm = show_val_norm_##ctl_id, \ + .store_val_norm = store_val_norm_##ctl_id, \ + .show_val_custom = show_val_custom_##ctl_id, \ + .store_val_custom = store_val_custom_##ctl_id, \ } \ static struct pvr2_sysfs_func_set funcs[] = { @@ -459,7 +402,7 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) struct pvr2_sysfs_ctl_item *cip; struct pvr2_sysfs_func_set *fp; struct pvr2_ctrl *cptr; - unsigned int cnt; + unsigned int cnt,acnt; if ((ctl_id < 0) || (ctl_id >= (sizeof(funcs)/sizeof(funcs[0])))) { return; @@ -504,28 +447,50 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) cip->attr_val.attr.name = "cur_val"; cip->attr_val.attr.mode = S_IRUGO; + cip->attr_custom.attr.owner = THIS_MODULE; + cip->attr_custom.attr.name = "custom_val"; + cip->attr_custom.attr.mode = S_IRUGO; + cip->attr_enum.attr.owner = THIS_MODULE; cip->attr_enum.attr.name = "enum_val"; cip->attr_enum.attr.mode = S_IRUGO; cip->attr_enum.show = fp->show_enum; - if (pvr2_ctrl_is_writeable(cptr)) { + cip->attr_bits.attr.owner = THIS_MODULE; + cip->attr_bits.attr.name = "bit_val"; + cip->attr_bits.attr.mode = S_IRUGO; + cip->attr_bits.show = fp->show_bits; + + if (pvr2_ctrl_is_writable(cptr)) { cip->attr_val.attr.mode |= S_IWUSR|S_IWGRP; + cip->attr_custom.attr.mode |= S_IWUSR|S_IWGRP; } - cip->attr_gen[0] = &cip->attr_name.attr; - cip->attr_gen[1] = &cip->attr_val.attr; - if (pvr2_ctrl_get_type(cptr) == PVR2_CTRL_TYPE_ENUM) { + acnt = 0; + cip->attr_gen[acnt++] = &cip->attr_name.attr; + cip->attr_gen[acnt++] = &cip->attr_val.attr; + cip->attr_val.show = fp->show_val_norm; + cip->attr_val.store = fp->store_val_norm; + if (pvr2_ctrl_has_custom_symbols(cptr)) { + cip->attr_gen[acnt++] = &cip->attr_custom.attr; + cip->attr_custom.show = fp->show_val_custom; + cip->attr_custom.store = fp->store_val_custom; + } + switch (pvr2_ctrl_get_type(cptr)) { + case pvr2_ctl_enum: // Control is an enumeration - cip->attr_gen[2] = &cip->attr_enum.attr; - cip->attr_val.show = fp->show_val_enum; - cip->attr_val.store = fp->store_val_enum; - } else { + cip->attr_gen[acnt++] = &cip->attr_enum.attr; + break; + case pvr2_ctl_int: // Control is an integer - cip->attr_val.show = fp->show_val_int; - cip->attr_val.store = fp->store_val_int; - cip->attr_gen[2] = &cip->attr_min.attr; - cip->attr_gen[3] = &cip->attr_max.attr; + cip->attr_gen[acnt++] = &cip->attr_min.attr; + cip->attr_gen[acnt++] = &cip->attr_max.attr; + break; + case pvr2_ctl_bitmask: + // Control is an bitmask + cip->attr_gen[acnt++] = &cip->attr_bits.attr; + break; + default: break; } cnt = scnprintf(cip->name,sizeof(cip->name)-1,"ctl_%s", diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 51491e67c..c8f392a7f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -246,46 +246,25 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_ENUMSTD: { struct v4l2_standard *vs = (struct v4l2_standard *)arg; - const struct v4l2_standard *src; int idx = vs->index; - - src = pvr2_hdw_get_stdenum_value(hdw,idx); - if (!src) break; - - memcpy(vs, src, sizeof(struct v4l2_standard)); - vs->index = idx; - - ret = 0; + ret = pvr2_hdw_get_stdenum_value(hdw,vs,idx); break; } case VIDIOC_G_STD: { - struct pvr2_ctrl *cptr; - v4l2_std_id *vs = (v4l2_std_id *)arg; - cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_STDCUR); - if (!pvr2_ctrl_is_valid(cptr)) { - ret = -EINVAL; - break; - } - - *vs = pvr2_ctrl_get_value(cptr); - ret = 0; + int val = 0; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR),&val); + *(v4l2_std_id *)arg = val; break; } case VIDIOC_S_STD: { - struct pvr2_ctrl *cptr; - v4l2_std_id *vs = (v4l2_std_id *)arg; - cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_STDCUR); - if (!pvr2_ctrl_is_valid(cptr)) { - ret = -EINVAL; - break; - } - - pvr2_ctrl_set_value(cptr,*vs); - ret = 0; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR), + *(v4l2_std_id *)arg); break; } @@ -294,16 +273,13 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, struct pvr2_ctrl *cptr; struct v4l2_input *vi = (struct v4l2_input *)arg; struct v4l2_input tmp; + unsigned int cnt; - if ((vi->index > PVR2_CVAL_INPUT_MAX) || - (vi->index < PVR2_CVAL_INPUT_MIN)) { - break; - } - - cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_INPUT); + 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) { case PVR2_CVAL_INPUT_TV: case PVR2_CVAL_INPUT_RADIO: @@ -313,11 +289,16 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case PVR2_CVAL_INPUT_COMPOSITE: tmp.type = V4L2_INPUT_TYPE_CAMERA; break; + default: + ret = -EINVAL; + break; } + if (ret < 0) break; - strlcpy(tmp.name, - pvr2_ctrl_get_value_name(cptr,vi->index), - sizeof(tmp.name)); + cnt = 0; + pvr2_ctrl_get_valname(cptr,vi->index, + tmp.name,sizeof(tmp.name)-1,&cnt); + tmp.name[cnt] = 0; /* Don't bother with audioset, since this driver currently always switches the audio whenever the video is @@ -339,31 +320,20 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, { struct pvr2_ctrl *cptr; struct v4l2_input *vi = (struct v4l2_input *)arg; - cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_INPUT); - if (!pvr2_ctrl_is_valid(cptr)) { - ret = -EINVAL; - break; - } - - vi->index = pvr2_ctrl_get_value(cptr); - ret = 0; + 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; break; } case VIDIOC_S_INPUT: { - struct pvr2_ctrl *cptr; struct v4l2_input *vi = (struct v4l2_input *)arg; - cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_INPUT); - if (!pvr2_ctrl_is_valid(cptr)) { - ret = -EINVAL; - break; - } - - ret = 0; - if (pvr2_ctrl_set_value(cptr,vi->index)) { - ret = -EINVAL; - } + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), + vi->index); break; } @@ -386,9 +356,9 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, } case VIDIOC_G_TUNER: { - struct pvr2_ctrl *cptr; struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; unsigned int status_mask; + int val; if (vt->index !=0) break; status_mask = pvr2_hdw_get_signal_status(hdw); @@ -410,66 +380,44 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, vt->signal = 65535; } - cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_AUDIOMODE); - vt->audmode = pvr2_ctrl_get_value(cptr); - - ret = 0; + val = 0; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_AUDIOMODE), + &val); + vt->audmode = val; break; } case VIDIOC_S_TUNER: { - struct pvr2_ctrl *cptr; struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; if (vt->index != 0) break; - cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_AUDIOMODE); - if (!pvr2_ctrl_is_valid(cptr)) { - ret = -EINVAL; - break; - } - - pvr2_ctrl_set_value(cptr,vt->audmode); - ret = 0; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_AUDIOMODE), + vt->audmode); } case VIDIOC_S_FREQUENCY: { - struct pvr2_ctrl *cptr; const struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; - - cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_FREQUENCY); - if (!pvr2_ctrl_is_valid(cptr)) { - ret = -EINVAL; - break; - } - - pvr2_ctrl_set_value(cptr,vf->frequency * 62500); - - ret = 0; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY), + vf->frequency * 62500); break; } case VIDIOC_G_FREQUENCY: { - struct pvr2_ctrl *cptr; struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; - int val; - - cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_FREQUENCY); - if (!pvr2_ctrl_is_valid(cptr)) { - ret = -EINVAL; - break; - } - - val = pvr2_ctrl_get_value(cptr); - + int val = 0; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY), + &val); val /= 62500; vf->frequency = val; - - ret = 0; break; } @@ -489,24 +437,27 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_G_FMT: { struct v4l2_format *vf = (struct v4l2_format *)arg; - + int val; switch(vf->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: memcpy(vf, &pvr_format[PVR_FORMAT_PIX], sizeof(struct v4l2_format)); - vf->fmt.pix.width = - pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl(hdw, - V4L2_CID_PVR_HRES)); - if (pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl( - hdw,V4L2_CID_PVR_INTERLACE))) { - vf->fmt.pix.width /= 2; - } - vf->fmt.pix.height = - pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl(hdw, - V4L2_CID_PVR_VRES)); + val = 0; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_HRES), + &val); + vf->fmt.pix.width = val; + val = 0; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, + PVR2_CID_INTERLACE), + &val); + if (val) vf->fmt.pix.width /= 2; + val = 0; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_VRES), + &val); + vf->fmt.pix.height = val; ret = 0; break; case V4L2_BUF_TYPE_VBI_CAPTURE: @@ -532,8 +483,10 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, int w = vf->fmt.pix.width; int vd_std, hf, hh; - vd_std = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_STDCUR)); + vd_std = 0; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR), + &vd_std); if (vd_std & V4L2_STD_525_60) { hf=480; } else { @@ -548,18 +501,18 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, vf->fmt.pix.width &= 0xff0; vf->fmt.pix.height = (h > hh) ? hf : hh; - if (cmd == VIDIOC_S_FMT){ + if (cmd == VIDIOC_S_FMT) { pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl(hdw, - V4L2_CID_PVR_HRES), + pvr2_hdw_get_ctrl_by_id(hdw, + PVR2_CID_HRES), vf->fmt.pix.width); pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl(hdw, - V4L2_CID_PVR_VRES), + pvr2_hdw_get_ctrl_by_id(hdw, + PVR2_CID_VRES), vf->fmt.pix.height); pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl( - hdw,V4L2_CID_PVR_INTERLACE), + pvr2_hdw_get_ctrl_by_id( + hdw,PVR2_CID_INTERLACE), vf->fmt.pix.height != hf); } } break; @@ -592,82 +545,61 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, { struct pvr2_ctrl *cptr; struct v4l2_queryctrl *vc = (struct v4l2_queryctrl *)arg; - cptr = pvr2_hdw_get_ctrl(hdw,vc->id); - - if (!pvr2_ctrl_is_valid(cptr)) { + cptr = pvr2_hdw_get_ctrl_v4l(hdw,vc->id); + if (!cptr) { ret = -EINVAL; break; } + strlcpy(vc->name,pvr2_ctrl_get_name(cptr),sizeof(vc->name)); + vc->default_value = pvr2_ctrl_get_def(cptr); switch (pvr2_ctrl_get_type(cptr)) { - case PVR2_CTRL_TYPE_ENUM: + case pvr2_ctl_enum: vc->type = V4L2_CTRL_TYPE_MENU; + vc->minimum = 0; + vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1; + vc->step = 1; break; - case PVR2_CTRL_TYPE_INT: + case pvr2_ctl_int: vc->type = V4L2_CTRL_TYPE_INTEGER; + vc->minimum = pvr2_ctrl_get_min(cptr); + vc->maximum = pvr2_ctrl_get_max(cptr); + vc->step = 1; break; default: ret = -EINVAL; break; } - - strlcpy(vc->name,pvr2_ctrl_get_desc(cptr),sizeof(vc->name)); - vc->minimum = pvr2_ctrl_get_min_value(cptr); - vc->maximum = pvr2_ctrl_get_max_value(cptr); - vc->default_value = pvr2_ctrl_get_default_value(cptr); - vc->step = 1; - ret = 0; break; } case VIDIOC_QUERYMENU: { - struct pvr2_ctrl *cptr; struct v4l2_querymenu *vm = (struct v4l2_querymenu *)arg; - const char *value_name; - cptr = pvr2_hdw_get_ctrl(hdw,vm->id); - if (!pvr2_ctrl_is_valid(cptr)) { - ret = -EINVAL; - break; - } - value_name = pvr2_ctrl_get_value_name(cptr,vm->index); - if (value_name) { - strlcpy(vm->name,value_name,sizeof(vm->name)); - ret = 0; - } else { - ret = -EINVAL; - } - + unsigned int cnt = 0; + ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw,vm->id), + vm->index, + vm->name,sizeof(vm->name)-1, + &cnt); + vm->name[cnt] = 0; break; } case VIDIOC_G_CTRL: { - struct pvr2_ctrl *cptr; struct v4l2_control *vc = (struct v4l2_control *)arg; - - cptr = pvr2_hdw_get_ctrl(hdw,vc->id); - if (!pvr2_ctrl_is_valid(cptr)) { - ret = -EINVAL; - break; - } - ret = 0; - vc->value = pvr2_ctrl_get_value(cptr); + int val = 0; + ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id), + &val); + vc->value = val; break; } case VIDIOC_S_CTRL: { - struct pvr2_ctrl *cptr; struct v4l2_control *vc = (struct v4l2_control *)arg; - - cptr = pvr2_hdw_get_ctrl(hdw,vc->id); - if (!pvr2_ctrl_is_valid(cptr)) { - ret = -EINVAL; - break; - } - - ret = pvr2_ctrl_set_value(cptr,vc->value); + ret = pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id), + vc->value); break; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c index dde031bb8..4127c82f7 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c @@ -54,9 +54,8 @@ static void set_input(struct pvr2_v4l_decoder *ctxt) struct pvr2_hdw *hdw = ctxt->hdw; struct v4l2_routing route; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_input(%d)", - hdw->controls[PVR2_CID_INPUT].value); - switch(hdw->controls[PVR2_CID_INPUT].value){ + pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_input(%d)",hdw->input_val); + switch(hdw->input_val) { case PVR2_CVAL_INPUT_TV: route.input = SAA7115_COMPOSITE4; break; @@ -79,7 +78,7 @@ static void set_input(struct pvr2_v4l_decoder *ctxt) static int check_input(struct pvr2_v4l_decoder *ctxt) { struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->controls[PVR2_CID_INPUT].dirty != 0; + return hdw->input_dirty != 0; } @@ -89,8 +88,8 @@ static void set_audio(struct pvr2_v4l_decoder *ctxt) struct pvr2_hdw *hdw = ctxt->hdw; pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_audio %d", - hdw->controls[PVR2_CID_SRATE].value); - switch (hdw->controls[PVR2_CID_SRATE].value) { + hdw->srate_val); + switch (hdw->srate_val) { default: case PVR2_CVAL_SRATE_48: val = 48000; @@ -106,7 +105,7 @@ static void set_audio(struct pvr2_v4l_decoder *ctxt) static int check_audio(struct pvr2_v4l_decoder *ctxt) { struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->controls[PVR2_CID_SRATE].dirty != 0; + return hdw->srate_dirty != 0; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c index 79334980c..8443ffd52 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c @@ -55,7 +55,7 @@ static void set_input(struct pvr2_v4l_wm8775 *ctxt) memset(&inp,0,sizeof(inp)); pvr2_trace(PVR2_TRACE_CHIPS,"i2c wm8775 set_input(val=%d msk=0x%x)", - hdw->controls[PVR2_CID_INPUT].value,msk); + hdw->input_val,msk); // Always point to input #1 no matter what inp.index = 2; @@ -66,7 +66,7 @@ static void set_input(struct pvr2_v4l_wm8775 *ctxt) static int check_input(struct pvr2_v4l_wm8775 *ctxt) { struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->controls[PVR2_CID_INPUT].dirty != 0; + return hdw->input_dirty != 0; } -- cgit v1.2.3 From f6ff726f1d538206996f33ae3cce23dfaf21d1ad Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Wed, 19 Apr 2006 23:59:11 -0500 Subject: Another rework of pvrusb2 video standard handling From: Mike Isely Clean up logic for handling video standards in the pvrusb2 driver. New implementation should be able to handle all possible V4L defined video standards now, and it should be far easier to maintain this going forward. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c | 4 +- linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c | 2 +- .../drivers/media/video/pvrusb2/pvrusb2-encoder.c | 2 +- .../media/video/pvrusb2/pvrusb2-hdw-internal.h | 38 +-- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 312 ++++++++++++--------- .../media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c | 4 +- linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 2 +- 7 files changed, 208 insertions(+), 156 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c index b2c43a389..e55f43e44 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c @@ -514,8 +514,8 @@ int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *cptr, } } else if (cptr->info->type == pvr2_ctl_bitmask) { *len = gen_bitmask_string( - mask & cptr->info->def.type_bitmask.valid_bits, - val,0, + val & mask & cptr->info->def.type_bitmask.valid_bits, + ~0,!0, cptr->info->def.type_bitmask.bit_names, buf,maxlen); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c index 78bc968c0..1509ed6d6 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c @@ -147,7 +147,7 @@ int pvr2_eeprom_analyze(struct pvr2_hdw *hdw) trace_eeprom("rev_str=%s",tvdata.rev_str); hdw->tuner_type = tvdata.tuner_type; hdw->serial_number = tvdata.serial_number; - pvr2_hdw_internal_set_std_avail(hdw,tvdata.tuner_formats); + hdw->std_mask_eeprom = tvdata.tuner_formats; kfree(eeprom); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c index c60786390..962c88740 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -268,7 +268,7 @@ static int pvr2_write_encoder_vcmd (struct pvr2_hdw *hdw, u8 cmd, int pvr2_encoder_configure(struct pvr2_hdw *hdw) { int ret = 0, audio, i; - v4l2_std_id vd_std = hdw->video_std_cur; + v4l2_std_id vd_std = hdw->std_mask_cur; int height = hdw->res_ver_val; int width = hdw->res_hor_val; int height_full = !hdw->interlace_val; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index ad48f0751..abf2faf10 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -129,7 +129,7 @@ struct pvr2_ctl_info { int skip_init; /* Starting value for this control */ - long default_value; + int default_value; /* Type-specific control information */ union { @@ -238,8 +238,6 @@ struct pvr2_hdw { unsigned int freqTable[FREQTABLE_SIZE]; unsigned int freqProgSlot; unsigned int freqSlot; - unsigned int freqVal; - int freqDirty; /* Stuff for handling low level control interaction with device */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) @@ -289,18 +287,25 @@ struct pvr2_hdw { /* Tuner / frequency control stuff */ unsigned int tuner_type; int tuner_updated; - v4l2_std_id video_std_avail; - v4l2_std_id video_std_cur; - int video_std_dirty; - struct pvr2_ctl_info video_std_info_enum; - struct pvr2_ctl_info video_std_info_avail; - struct pvr2_ctl_info video_std_info_cur; + unsigned int freqVal; + int freqDirty; + + /* Video standard handling */ + v4l2_std_id std_mask_eeprom; // Hardware supported selections + v4l2_std_id std_mask_avail; // Which standards we may select from + v4l2_std_id std_mask_cur; // Currently selected standard(s) + unsigned int std_enum_cnt; // # of enumerated standards + int std_enum_cur; // selected standard enumeration value + int std_dirty; // True if std_mask_cur has changed + struct pvr2_ctl_info std_info_enum; + struct pvr2_ctl_info std_info_avail; + struct pvr2_ctl_info std_info_cur; struct v4l2_standard *std_defs; - const char **video_std_enum_names; - const char *video_std_mask_ptrs[32]; - char video_std_mask_names[32][10]; - unsigned int std_cnt; - int std_id; + const char **std_enum_names; + + // Generated string names, one per actual V4L2 standard + const char *std_mask_ptrs[32]; + char std_mask_names[32][10]; int unit_number; /* ID for driver instance */ unsigned long serial_number; /* ID for hardware itself */ @@ -358,9 +363,8 @@ void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw, unsigned long msk, unsigned long val); -int pvr2_hdw_internal_set_stdenum_cur(struct pvr2_hdw *hdw,int val); -void pvr2_hdw_internal_set_std_cur(struct pvr2_hdw *hdw,int msk); -void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw,int msk); +void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw); +void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw); #endif /* __PVRUSB2_HDW_INTERNAL_H */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 9ea68eeef..4843f2823 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -61,6 +61,7 @@ static int initusbreset = 1; static int procreload = 0; 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; module_param(ctlchg, int, S_IRUGO|S_IWUSR); @@ -74,6 +75,8 @@ MODULE_PARM_DESC(procreload, "Attempt init failure recovery with firmware reload"); module_param_array(tuner, int, NULL, 0444); MODULE_PARM_DESC(tuner,"specify installed tuner type"); +module_param_array(video_std, int, NULL, 0444); +MODULE_PARM_DESC(video_std,"specify initial video standard"); module_param_array(tolerance, int, NULL, 0444); MODULE_PARM_DESC(tolerance,"specify stream error tolerance"); @@ -255,7 +258,20 @@ static int ctrl_hsm_get(struct pvr2_ctrl *cptr,int *vp) static int ctrl_stdavail_get(struct pvr2_ctrl *cptr,int *vp) { - *vp = cptr->hdw->video_std_avail; + *vp = cptr->hdw->std_mask_avail; + return 0; +} + +static int ctrl_stdavail_set(struct pvr2_ctrl *cptr,int m,int v) +{ + struct pvr2_hdw *hdw = cptr->hdw; + v4l2_std_id ns; + ns = hdw->std_mask_avail; + ns = (ns & ~m) | (v & m); + if (ns == hdw->std_mask_avail) return 0; + hdw->std_mask_avail = ns; + pvr2_hdw_internal_set_std_avail(hdw); + pvr2_hdw_internal_find_stdenum(hdw); return 0; } @@ -282,16 +298,33 @@ static int ctrl_std_sym_to_val(struct pvr2_ctrl *cptr, static int ctrl_stdcur_get(struct pvr2_ctrl *cptr,int *vp) { - *vp = cptr->hdw->video_std_cur; + *vp = cptr->hdw->std_mask_cur; return 0; } static int ctrl_stdcur_set(struct pvr2_ctrl *cptr,int m,int v) { - pvr2_hdw_internal_set_std_cur(cptr->hdw,v); + struct pvr2_hdw *hdw = cptr->hdw; + v4l2_std_id ns; + ns = hdw->std_mask_cur; + ns = (ns & ~m) | (v & m); + if (ns == hdw->std_mask_cur) return 0; + hdw->std_mask_cur = ns; + hdw->std_dirty = !0; + pvr2_hdw_internal_find_stdenum(hdw); return 0; } +static int ctrl_stdcur_is_dirty(struct pvr2_ctrl *cptr) +{ + return cptr->hdw->std_dirty != 0; +} + +static void ctrl_stdcur_clear_dirty(struct pvr2_ctrl *cptr) +{ + cptr->hdw->std_dirty = 0; +} + static int ctrl_signal_get(struct pvr2_ctrl *cptr,int *vp) { *vp = ((pvr2_hdw_get_signal_status_internal(cptr->hdw) & @@ -325,31 +358,35 @@ static int ctrl_subsys_stream_set(struct pvr2_ctrl *cptr,int m,int v) static int ctrl_stdenumcur_set(struct pvr2_ctrl *cptr,int m,int v) { + struct pvr2_hdw *hdw = cptr->hdw; if (v < 0) return -EINVAL; - if (v >= cptr->hdw->std_cnt) return -EINVAL; - cptr->hdw->std_id = v; - pvr2_hdw_internal_set_std_cur(cptr->hdw, - cptr->hdw->std_id); + if (v > hdw->std_enum_cnt) return -EINVAL; + hdw->std_enum_cur = v; + if (!v) return 0; + v--; + if (hdw->std_mask_cur == hdw->std_defs[v].id) return 0; + hdw->std_mask_cur = hdw->std_defs[v].id; + hdw->std_dirty = !0; return 0; } static int ctrl_stdenumcur_get(struct pvr2_ctrl *cptr,int *vp) { - *vp = cptr->hdw->std_id; + *vp = cptr->hdw->std_enum_cur; return 0; } static int ctrl_stdenumcur_is_dirty(struct pvr2_ctrl *cptr) { - return cptr->hdw->video_std_dirty != 0; + return cptr->hdw->std_dirty != 0; } static void ctrl_stdenumcur_clear_dirty(struct pvr2_ctrl *cptr) { - cptr->hdw->video_std_dirty = 0; + cptr->hdw->std_dirty = 0; } @@ -613,7 +650,9 @@ static const struct pvr2_ctl_info control_defs[] = { .desc = "Video Standards Available Mask", .name = "video_standard_mask_available", .internal_id = PVR2_CID_STDAVAIL, + .skip_init = !0, .get_value = ctrl_stdavail_get, + .set_value = ctrl_stdavail_set, .val_to_sym = ctrl_std_val_to_sym, .sym_to_val = ctrl_std_sym_to_val, .type = pvr2_ctl_bitmask, @@ -624,6 +663,8 @@ static const struct pvr2_ctl_info control_defs[] = { .skip_init = !0, .get_value = ctrl_stdcur_get, .set_value = ctrl_stdcur_set, + .is_dirty = ctrl_stdcur_is_dirty, + .clear_dirty = ctrl_stdcur_clear_dirty, .val_to_sym = ctrl_std_val_to_sym, .sym_to_val = ctrl_std_sym_to_val, .type = pvr2_ctl_bitmask, @@ -1279,6 +1320,17 @@ static int get_default_tuner_type(struct pvr2_hdw *hdw) } +static v4l2_std_id get_default_standard(struct pvr2_hdw *hdw) +{ + int unit_number = hdw->unit_number; + int tp = 0; + if ((unit_number >= 0) && (unit_number < PVR_NUM)) { + tp = video_std[unit_number]; + } + return tp; +} + + static unsigned int get_default_error_tolerance(struct pvr2_hdw *hdw) { int unit_number = hdw->unit_number; @@ -1316,6 +1368,60 @@ static int pvr2_hdw_check_firmware(struct pvr2_hdw *hdw) } +static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) +{ + char buf[40]; + unsigned int bcnt; + v4l2_std_id std1,std2; + + std1 = get_default_standard(hdw); + + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom); + pvr2_trace(PVR2_TRACE_INIT, + "Supported video standard(s) reported by eeprom: %.*s", + bcnt,buf); + + hdw->std_mask_avail = hdw->std_mask_eeprom; + + std2 = std1 & ~hdw->std_mask_avail; + if (std2) { + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std2); + pvr2_trace(PVR2_TRACE_INIT, + "Expanding supported video standards" + " to include: %.*s", + bcnt,buf); + hdw->std_mask_avail |= std2; + } + + pvr2_hdw_internal_set_std_avail(hdw); + + if (std1) { + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std1); + pvr2_trace(PVR2_TRACE_INIT, + "Initial video standard forced to %.*s", + bcnt,buf); + hdw->std_mask_cur = std1; + hdw->std_dirty = !0; + pvr2_hdw_internal_find_stdenum(hdw); + return; + } + + if (hdw->std_enum_cnt > 1) { + // Autoselect the first listed standard + hdw->std_enum_cur = 1; + hdw->std_mask_cur = hdw->std_defs[hdw->std_enum_cur-1].id; + hdw->std_dirty = !0; + pvr2_trace(PVR2_TRACE_INIT, + "Initial video standard auto-selected to %s", + hdw->std_defs[hdw->std_enum_cur-1].name); + return; + } + + pvr2_trace(PVR2_TRACE_EEPROM, + "Unable to select a viable initial video standard"); +} + + static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) { int ret; @@ -1388,6 +1494,8 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) if (!pvr2_hdw_dev_ok(hdw)) return; } + pvr2_hdw_setup_std(hdw); + if (!get_default_tuner_type(hdw)) { pvr2_trace(PVR2_TRACE_INIT, "pvr2_hdw_setup: Tuner type overridden to %d", @@ -1400,23 +1508,6 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) if (!pvr2_hdw_dev_ok(hdw)) return; - for (idx = 0; idx < hdw->std_cnt; idx++) { - pvr2_trace(PVR2_TRACE_EEPROM, - "Detected video standard %s (from eeprom)", - hdw->std_defs[idx].name); - } - if (hdw->std_cnt) { - pvr2_trace(PVR2_TRACE_EEPROM, - "Initial video standard set to %s" - " (detected from eeprom)", - hdw->std_defs[hdw->std_id].name); - } else { - pvr2_trace(PVR2_TRACE_EEPROM, - "Unable to select a viable video standard"); - } - - if (!pvr2_hdw_dev_ok(hdw)) return; - pvr2_hdw_commit_ctl_internal(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; @@ -1545,8 +1636,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->hdw_type = hdw_type; for (idx = 0; idx < 32; idx++) { - hdw->video_std_mask_ptrs[idx] = - hdw->video_std_mask_names[idx]; + hdw->std_mask_ptrs[idx] = hdw->std_mask_names[idx]; } for (idx = 0; idx < CTRL_COUNT; idx++) { @@ -1558,9 +1648,9 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, // Initialize video standard enum dynamic control cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDENUM); if (cptr) { - memcpy(&hdw->video_std_info_enum,cptr->info, - sizeof(hdw->video_std_info_enum)); - cptr->info = &hdw->video_std_info_enum; + memcpy(&hdw->std_info_enum,cptr->info, + sizeof(hdw->std_info_enum)); + cptr->info = &hdw->std_info_enum; } // Initialize control data regarding video standard masks @@ -1568,29 +1658,29 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, for (idx = 0; idx < 32; idx++) { if (!(valid_std_mask & (1 << idx))) continue; cnt1 = pvr2_std_id_to_str( - hdw->video_std_mask_names[idx], - sizeof(hdw->video_std_mask_names[idx])-1, + hdw->std_mask_names[idx], + sizeof(hdw->std_mask_names[idx])-1, 1 << idx); - hdw->video_std_mask_names[idx][cnt1] = 0; + hdw->std_mask_names[idx][cnt1] = 0; } cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDAVAIL); if (cptr) { - memcpy(&hdw->video_std_info_avail,cptr->info, - sizeof(hdw->video_std_info_avail)); - cptr->info = &hdw->video_std_info_avail; - hdw->video_std_info_avail.def.type_bitmask.bit_names = - hdw->video_std_mask_ptrs; - hdw->video_std_info_avail.def.type_bitmask.valid_bits = + memcpy(&hdw->std_info_avail,cptr->info, + sizeof(hdw->std_info_avail)); + cptr->info = &hdw->std_info_avail; + hdw->std_info_avail.def.type_bitmask.bit_names = + hdw->std_mask_ptrs; + hdw->std_info_avail.def.type_bitmask.valid_bits = valid_std_mask; } cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR); if (cptr) { - memcpy(&hdw->video_std_info_cur,cptr->info, - sizeof(hdw->video_std_info_cur)); - cptr->info = &hdw->video_std_info_cur; - hdw->video_std_info_cur.def.type_bitmask.bit_names = - hdw->video_std_mask_ptrs; - hdw->video_std_info_avail.def.type_bitmask.valid_bits = + memcpy(&hdw->std_info_cur,cptr->info, + sizeof(hdw->std_info_cur)); + cptr->info = &hdw->std_info_cur; + hdw->std_info_cur.def.type_bitmask.bit_names = + hdw->std_mask_ptrs; + hdw->std_info_avail.def.type_bitmask.valid_bits = valid_std_mask; } @@ -1722,7 +1812,7 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) } while (0); up(&pvr2_unit_sem); kfree(hdw->controls); if (hdw->std_defs) kfree(hdw->std_defs); - if (hdw->video_std_enum_names) kfree(hdw->video_std_enum_names); + if (hdw->std_enum_names) kfree(hdw->std_enum_names); kfree(hdw); } @@ -1751,60 +1841,59 @@ void pvr2_hdw_disconnect(struct pvr2_hdw *hdw) } -// Given a mask of viable video standards to choose from, generate an -// appropriate array of v4l2_standard data that corresponds to it and set -// up related state in the driver to match. -void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw,int arg) +// Attempt to autoselect an appropriate value for std_enum_cur given +// whatever is currently in std_mask_cur +void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw) +{ + unsigned int idx; + for (idx = 1; idx < hdw->std_enum_cnt; idx++) { + if (hdw->std_defs[idx-1].id == hdw->std_mask_cur) { + hdw->std_enum_cur = idx; + return; + } + } + hdw->std_enum_cur = 0; +} + + +// Calculate correct set of enumerated standards based on currently known +// set of available standards bits. +void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw) { struct v4l2_standard *newstd; unsigned int std_cnt; unsigned int idx; - newstd = pvr2_std_create_enum(&std_cnt,arg); + newstd = pvr2_std_create_enum(&std_cnt,hdw->std_mask_avail); if (hdw->std_defs) { kfree(hdw->std_defs); hdw->std_defs = 0; } - hdw->std_cnt = 0; - if (hdw->video_std_enum_names) { - kfree(hdw->video_std_enum_names); - hdw->video_std_enum_names = 0; + hdw->std_enum_cnt = 0; + if (hdw->std_enum_names) { + kfree(hdw->std_enum_names); + hdw->std_enum_names = 0; } if (!std_cnt) { pvr2_trace( PVR2_TRACE_ERROR_LEGS, - "Failed to identify any viable standard groups"); - hdw->video_std_avail = 0; - pvr2_hdw_internal_set_std_cur(hdw,0); - return; - } else { - hdw->video_std_enum_names = kmalloc(sizeof(char *)*std_cnt, - GFP_KERNEL); - for (idx = 0; idx < std_cnt; idx++) { - hdw->video_std_enum_names[idx] = - newstd[idx].name; - } - // Set up the dynamic control for this standard - hdw->video_std_info_enum.def.type_enum.value_names = - hdw->video_std_enum_names; - hdw->video_std_info_enum.def.type_enum.count = std_cnt; - hdw->std_defs = newstd; - hdw->std_cnt = std_cnt; + "WARNING: Failed to identify any viable standards"); } - - hdw->video_std_avail = arg; - if (!(hdw->video_std_avail & hdw->video_std_cur)) { - // Reselect standard if there isn't one that matches... - pvr2_hdw_internal_set_stdenum_cur(hdw,0); + hdw->std_enum_names = kmalloc(sizeof(char *)*(std_cnt+1),GFP_KERNEL); + hdw->std_enum_names[0] = "none"; + for (idx = 0; idx < std_cnt; idx++) { + hdw->std_enum_names[idx+1] = + newstd[idx].name; } -} - - -unsigned int pvr2_hdw_get_stdenum_count(struct pvr2_hdw *hdw) -{ - return hdw->std_cnt; + // Set up the dynamic control for this standard + hdw->std_info_enum.def.type_enum.value_names = hdw->std_enum_names; + hdw->std_info_enum.def.type_enum.count = std_cnt+1; + hdw->std_defs = newstd; + hdw->std_enum_cnt = std_cnt+1; + hdw->std_enum_cur = 0; + hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail; } @@ -1813,8 +1902,10 @@ int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw, unsigned int idx) { int ret = -EINVAL; + if (!idx) return ret; + idx--; LOCK_TAKE(hdw->big_lock); do { - if (idx >= hdw->std_cnt) break; + if (idx >= hdw->std_enum_cnt) break; memcpy(std,hdw->std_defs+idx,sizeof(*std)); ret = 0; } while (0); LOCK_GIVE(hdw->big_lock); @@ -1822,49 +1913,6 @@ int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw, } -int pvr2_hdw_internal_set_stdenum_cur(struct pvr2_hdw *hdw,int val) -{ - if (val < 0) return -EINVAL; - if (val >= hdw->std_cnt) return -EINVAL; - pvr2_hdw_internal_set_std_cur(hdw,hdw->std_defs[val].id); - return 0; -} - - -void pvr2_hdw_internal_set_std_cur(struct pvr2_hdw *hdw,int val) -{ - unsigned int idx; - v4l2_std_id msk,id; - - id = (v4l2_std_id)val; - // Only select from available standards - id &= hdw->video_std_avail; - - // Only select a single bit - for (idx = 0, msk = 1; msk; idx++, msk <<= 1) { - if (!(id & msk)) continue; - id = msk; - break; - } - - // Get out if nothing found - if (!msk) return; - - // Fix up standard group now - hdw->video_std_cur = id; - hdw->video_std_dirty = !0; - for (idx = 0; idx < hdw->std_cnt; idx++) { - if (hdw->std_defs[idx].id & id) { - hdw->std_id = idx; - return; - } - } - - // Should never really get here, but just in case... - hdw->std_id = 0; -} - - /* Get the number of defined controls */ unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw) { @@ -1976,11 +2024,11 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) /* When video standard changes, reset the hres and vres values - but if the user has pending changes there, then let the changes take priority. */ - if (hdw->video_std_dirty) { + if (hdw->std_dirty) { /* Rewrite the vertical resolution to be appropriate to the video standard that has been selected. */ int nvres; - if (hdw->video_std_cur & V4L2_STD_525_60) { + if (hdw->std_mask_cur & V4L2_STD_525_60) { nvres = 480; } else { nvres = 576; @@ -1995,7 +2043,7 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) } } - if (hdw->video_std_dirty || + if (hdw->std_dirty || hdw->res_ver_dirty || hdw->res_hor_dirty || hdw->interlace_dirty || diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c index e6b176bf3..9f81aff2b 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c @@ -29,7 +29,7 @@ static void set_standard(struct pvr2_hdw *hdw) { v4l2_std_id vs; - vs = hdw->video_std_cur; + vs = hdw->std_mask_cur; pvr2_trace(PVR2_TRACE_CHIPS, "i2c v4l2 set_standard(0x%llx)",(__u64)vs); @@ -39,7 +39,7 @@ static void set_standard(struct pvr2_hdw *hdw) static int check_standard(struct pvr2_hdw *hdw) { - return hdw->video_std_dirty != 0; + return hdw->std_dirty != 0; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index c8f392a7f..3701d479d 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -247,7 +247,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, { struct v4l2_standard *vs = (struct v4l2_standard *)arg; int idx = vs->index; - ret = pvr2_hdw_get_stdenum_value(hdw,vs,idx); + ret = pvr2_hdw_get_stdenum_value(hdw,vs,idx+1); break; } -- cgit v1.2.3 From 369f3ae0c1361e47201d1cc6cfbd93c0c74fe8bc Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 20 Apr 2006 00:01:13 -0500 Subject: Change pvrusb2 subsystem definition bits for easier handling From: Mike Isely Subsystem bits are defined as ordinal values now instead of bits in a mask. Signed-off-by: Mike Isely --- .../drivers/media/video/pvrusb2/pvrusb2-debugifc.c | 10 ++--- .../drivers/media/video/pvrusb2/pvrusb2-encoder.c | 6 +-- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 49 +++++++++++----------- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h | 20 ++++----- 4 files changed, 42 insertions(+), 43 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index 16e6ea317..586900e36 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -32,11 +32,11 @@ struct debugifc_mask_item { }; static struct debugifc_mask_item mask_items[] = { - {"ENC_FIRMWARE",PVR2_SUBSYS_ENC_FIRMWARE}, - {"ENC_CFG",PVR2_SUBSYS_ENC_CFG}, - {"DIG_RUN",PVR2_SUBSYS_DIGITIZER_RUN}, - {"USB_RUN",PVR2_SUBSYS_USBSTREAM_RUN}, - {"ENC_RUN",PVR2_SUBSYS_ENC_RUN}, + {"ENC_FIRMWARE",(1<subsys_enabled_mask |= PVR2_SUBSYS_ENC_CFG; + hdw->subsys_enabled_mask |= (1<subsys_enabled_mask |= PVR2_SUBSYS_ENC_RUN; + hdw->subsys_enabled_mask |= (1<subsys_enabled_mask &= ~PVR2_SUBSYS_ENC_RUN; + hdw->subsys_enabled_mask &= ~(1<subsys_enabled_mask |= PVR2_SUBSYS_ENC_FIRMWARE; + hdw->subsys_enabled_mask |= (1<subsys_enabled_mask) & hdw->subsys_enabled_mask; if (vmsk) { - if (vmsk & PVR2_SUBSYS_ENC_RUN) { + if (vmsk & (1<subsys_enabled_mask &= - ~PVR2_SUBSYS_DIGITIZER_RUN; + ~(1<subsys_enabled_mask &= @@ -1128,7 +1127,7 @@ void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, } vmsk = (nmsk ^ hdw->subsys_enabled_mask) & nmsk; if (vmsk) { - if (vmsk & PVR2_SUBSYS_ENC_FIRMWARE) { + if (vmsk & (1<subsys_enabled_mask |= - PVR2_SUBSYS_DIGITIZER_RUN; + (1<subsys_stream_mask = PVR2_SUBSYS_RUN_ALL; - hdw->subsys_stream_mask |= PVR2_SUBSYS_ENC_CFG; + hdw->subsys_stream_mask |= (1<subsys_stream_mask); @@ -2057,7 +2056,7 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) hdw->audioemphasis_dirty) { /* If any of this changes, then the encoder needs to be reconfigured, and we need to reset the stream. */ - stale_subsys_mask |= PVR2_SUBSYS_ENC_CFG; + stale_subsys_mask |= (1<subsys_stream_mask; } @@ -2845,8 +2844,8 @@ int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) if (!status) { hdw->subsys_enabled_mask = ((hdw->subsys_enabled_mask & - ~PVR2_SUBSYS_USBSTREAM_RUN) | - (runFl ? PVR2_SUBSYS_USBSTREAM_RUN : 0)); + ~(1< Date: Thu, 20 Apr 2006 00:04:40 -0500 Subject: Make NTSC-Mj available in pvrusb2 when hardware reports NTSC-M availability From: Mike Isely PVR USB2 models that are NTSC capable are also able to handle the Japanese variant (according to the model info on the device's white sticker). However tveeprom doesn't report this. Work around this for now (and consider getting tveeprom fixed later). Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 6538e886c..350db8dcb 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1375,6 +1375,13 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) std1 = get_default_standard(hdw); + // Devices which are NTSC-M capable are also marked as NTSC-Mj + // capable on the device's sticker. Reflect that fact here, + // because tveeprom doesn't seem to report it. + if (hdw->std_mask_eeprom & V4L2_STD_NTSC_M) { + hdw->std_mask_eeprom |= V4L2_STD_NTSC_M_JP; + } + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom); pvr2_trace(PVR2_TRACE_INIT, "Supported video standard(s) reported by eeprom: %.*s", -- cgit v1.2.3 From c860a0d64f8d3a88bb9c0c493ec77e0267562249 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 20 Apr 2006 00:06:19 -0500 Subject: cx23416 fix in pvrusb2 to allow xawtv to function again From: Mike Isely Tweak cx23416 configuration to permit xawtv to work again. This problem was introduced when the model 24xxx hardware support was added. The fix here repairs the problem while and is valid for both old and new hardware. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c index e0cded4be..6c66e258f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -288,9 +288,24 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw) pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure"); - /* set stream output port. */ + /* set stream output port. Some notes here: The ivtv-derived + encoder documentation says that this command only gets a + single argument. However the Windows driver for the model + 29xxx series hardware has been sending 0x01 as a second + argument, while the Windows driver for the model 24xxx + series hardware has been sending 0x02 as a second argument. + Confusing matters further are the observations that 0x01 + for that second argument simply won't work on the 24xxx + hardware, while 0x02 will work on the 29xxx - except that + when we use 0x02 then xawtv breaks due to a loss of + synchronization with the mpeg packet headers. While xawtv + should be fixed to let it resync better (I did try to + contact Gerd about this but he has not answered), it has + also been determined that sending 0x00 as this mystery + second argument seems to work on both hardware models AND + xawtv works again. So we're going to send 0x00. */ ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_OUTPUT_PORT, 2, - 0x01, 0x02); + 0x01, 0x00); /* set the Program Index Information. We want I,P,B frames (max 400) */ ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, -- cgit v1.2.3 From f4549adf17ab1879858c7b8655dfe7ef023e404e Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 20 Apr 2006 00:07:57 -0500 Subject: Include linux/mutex.h not asm/mutex.h in pvrusb2 From: Mike Isely The correct mutex header to grab is linux/mutex.h, and while asm/mutex.h seems to work for x86 architecture, it has been reported not to work for amd64 architecture. So let's just do this right. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-context.h | 3 +-- linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h | 3 +-- linux/drivers/media/video/pvrusb2/pvrusb2-io.c | 3 +-- linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-context.h b/linux/drivers/media/video/pvrusb2/pvrusb2-context.h index 910086ba2..69c1f3a92 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-context.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-context.h @@ -22,8 +22,7 @@ #include "compat.h" #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -#include -#include +#include #else #include #endif diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index abf2faf10..ee0885ef1 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -37,8 +37,7 @@ #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -#include -#include +#include #else #include #endif diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-io.c b/linux/drivers/media/video/pvrusb2/pvrusb2-io.c index a6bc3befe..cb0ca6376 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-io.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-io.c @@ -26,8 +26,7 @@ #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -#include -#include +#include #else #include #endif diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c index f81a0c225..555dbbca8 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c @@ -26,8 +26,7 @@ #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -#include -#include +#include #else #include #endif -- cgit v1.2.3 From f3bab8e1b3ea52801b5b256bf203d6e2c946033d Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 20 Apr 2006 00:12:08 -0500 Subject: Implement I2C transaction filtering in pvrusb2 From: Mike Isely Implement a rudimentary filter mechanism in the I2C adapter within the pvrusb2 driver. It is possible now to interpose a filtering function on a per-target basis. This function can intercept the requested transaction and perform various manipulations on behalf of the client. With this mechanism is also a filter inserted for wm8775 that causes all probe attempts to the chip to always succeed (just return success on probe attempts without touching the hardware). This neatly solves the problem in the PVR USB2 hardware where we can't seem able to probe the chip at all. Signed-off-by: Mike Isely --- .../media/video/pvrusb2/pvrusb2-hdw-internal.h | 8 ++ .../drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 85 +++++++++++++++++++--- 2 files changed, 83 insertions(+), 10 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index ee0885ef1..ec6c35b8d 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -191,6 +191,9 @@ struct pvr2_decoder_ctrl { #define PVR2_HDW_TYPE_24XXX 1 #endif +typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16); +#define PVR2_I2C_FUNC_CNT 128 + /* This structure contains all state data directly needed to manipulate the hardware (as opposed to complying with a kernel interface) */ @@ -221,6 +224,7 @@ struct pvr2_hdw { /* I2C stuff */ struct i2c_adapter i2c_adap; struct i2c_algorithm i2c_algo; + pvr2_i2c_func i2c_func[PVR2_I2C_FUNC_CNT]; int i2c_linked; unsigned int i2c_pend_types; /* Which types of update are needed */ unsigned long i2c_pend_mask; /* Change bits we need to scan */ @@ -365,6 +369,10 @@ void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw, void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw); void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw); +int pvr2_i2c_basic_op(struct pvr2_hdw *,u8 i2c_addr, + u8 *wdata,u16 wlen, + u8 *rdata,u16 rlen); + #endif /* __PVRUSB2_HDW_INTERNAL_H */ /* diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 9a96273f2..e1d3803b3 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -164,6 +164,38 @@ static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */ return ret; } +/* This is the common low level entry point for doing I2C operations to the + hardware. */ +int pvr2_i2c_basic_op(struct pvr2_hdw *hdw, + u8 i2c_addr, + u8 *wdata, + u16 wlen, + u8 *rdata, + u16 rlen) +{ + if (!rdata) rlen = 0; + if (!wdata) wlen = 0; + if (rlen || !wlen) { + return pvr2_i2c_read(hdw,i2c_addr,wdata,wlen,rdata,rlen); + } else { + return pvr2_i2c_write(hdw,i2c_addr,wdata,wlen); + } +} + +/* This is a special entry point that is entered if an I2C operation is + attempted to a wm8775 chip on model 24xxx hardware. Autodetect of this + part doesn't work, but we know it is really there. So let's look for + the autodetect attempt and just return success if we see that. */ +static int i2c_hack_wm8775(struct pvr2_hdw *hdw, + u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) +{ + if (!(rlen || wlen)) { + // This is a probe attempt. Just let it succeed. + return 0; + } + return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen); +} + /* This is a very, very limited I2C adapter implementation. We can only support what we actually know will work on the device... */ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, @@ -171,12 +203,24 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, int num) { int ret = -ENOTSUPP; + pvr2_i2c_func funcp = 0; struct pvr2_hdw *hdw = (struct pvr2_hdw *)(i2c_adap->algo_data); + if (!num) { + ret = -EINVAL; + goto done; + } if ((msgs[0].flags & I2C_M_NOSTART)) { trace_i2c("i2c refusing I2C_M_NOSTART"); goto done; } + if (msgs[0].addr < PVR2_I2C_FUNC_CNT) { + funcp = hdw->i2c_func[msgs[0].addr]; + } + if (!funcp) { + ret = -EIO; + goto done; + } if (num == 1) { if (msgs[0].flags & I2C_M_RD) { @@ -184,8 +228,7 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, u16 tcnt,bcnt,offs; if (!msgs[0].len) { /* Length == 0 read. This is a probe. */ - if (pvr2_i2c_read(hdw,msgs[0].addr, - 0,0,0,0)) { + if (funcp(hdw,msgs[0].addr,0,0,0,0)) { ret = -EIO; goto done; } @@ -202,9 +245,8 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, if (bcnt > sizeof(hdw->cmd_buffer)-1) { bcnt = sizeof(hdw->cmd_buffer)-1; } - if (pvr2_i2c_read(hdw,msgs[0].addr, - 0,0, - msgs[0].buf+offs,bcnt)) { + if (funcp(hdw,msgs[0].addr,0,0, + msgs[0].buf+offs,bcnt)) { ret = -EIO; goto done; } @@ -216,13 +258,19 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, } else { /* Simple write */ ret = 1; - if (pvr2_i2c_write(hdw,msgs[0].addr, - msgs[0].buf,msgs[0].len)) { + if (funcp(hdw,msgs[0].addr, + msgs[0].buf,msgs[0].len,0,0)) { ret = -EIO; } goto done; } } else if (num == 2) { + if (msgs[0].addr != msgs[1].addr) { + trace_i2c("i2c refusing 2 phase transfer with" + " conflicting target addresses"); + ret = -ENOTSUPP; + goto done; + } if ((!((msgs[0].flags & I2C_M_RD))) && (msgs[1].flags & I2C_M_RD)) { u16 tcnt,bcnt,wcnt,offs; @@ -238,9 +286,9 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, if (bcnt > sizeof(hdw->cmd_buffer)-1) { bcnt = sizeof(hdw->cmd_buffer)-1; } - if (pvr2_i2c_read(hdw,msgs[0].addr, - msgs[0].buf,wcnt, - msgs[1].buf+offs,bcnt)) { + if (funcp(hdw,msgs[0].addr, + msgs[0].buf,wcnt, + msgs[1].buf+offs,bcnt)) { ret = -EIO; goto done; } @@ -757,6 +805,23 @@ static void do_i2c_scan(struct pvr2_hdw *hdw) void pvr2_i2c_core_init(struct pvr2_hdw *hdw) { + unsigned int idx; + + // The default action for all possible I2C addresses is just to do + // the transfer normally. + for (idx = 0; idx < PVR2_I2C_FUNC_CNT; idx++) { + hdw->i2c_func[idx] = pvr2_i2c_basic_op; + } + +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX + // If however we're dealing with new hardware, insert some hacks in + // the I2C transfer stack to let things work better. + if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) { + hdw->i2c_func[0x1b] = i2c_hack_wm8775; + } +#endif + + // Configure the adapter and set up everything else related to it. memcpy(&hdw->i2c_adap,&pvr2_i2c_adap_template,sizeof(hdw->i2c_adap)); memcpy(&hdw->i2c_algo,&pvr2_i2c_algo_template,sizeof(hdw->i2c_algo)); strlcpy(hdw->i2c_adap.name,hdw->name,sizeof(hdw->i2c_adap.name)); -- cgit v1.2.3 From ea5b04b69aa97ec244d03f9a4b75685d37d9a378 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 20 Apr 2006 00:13:09 -0500 Subject: Fix pvrusb2 eeprom retrieval bug From: Mike Isely Fix minor problem where wrong I2C address was being used to execute the second phase of an eeprom fetch. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c index 1509ed6d6..4003149a1 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c @@ -83,7 +83,7 @@ static u8 *pvr2_eeprom_fetch(struct pvr2_hdw *hdw) msg[0].flags = 0; msg[0].len = mode16 ? 2 : 1; msg[0].buf = iadd; - msg[1].addr = hdw->eeprom_addr; + msg[1].addr = addr; msg[1].flags = I2C_M_RD; /* We have to do the actual eeprom data fetch ourselves, because -- cgit v1.2.3 From 8bd600f2ff270e5c65b368fd240b41bb33ea57d2 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 20 Apr 2006 00:18:30 -0500 Subject: Implement I2C filter in pvrusb2 for cx25840 From: Mike Isely Insert an I2C transaction filter for the cx25843 chip on new model 24xxx hardware. This filter attempts to stabilize the part by limiting how the part can be accessed. The filter won't allow anyone to touch the chip until an attempt is made to specifically read the chip's revision registers. If that read fails (which can happen if the chip wedges itself), a warning is logged and the driver is rendered useless. If the read succeeds, then the filter deletes itself and normal access to the part takes place. This filter should keep msp3400 away from the chip (a failure scenario which otherwise frequently ends badly for the kernel, due to suspected misbehaviors within the msp3400 module). Signed-off-by: Mike Isely --- .../media/video/pvrusb2/pvrusb2-hdw-internal.h | 1 + .../drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 91 ++++++++++++++++++++++ 2 files changed, 92 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index ec6c35b8d..ff18fc548 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -225,6 +225,7 @@ struct pvr2_hdw { struct i2c_adapter i2c_adap; struct i2c_algorithm i2c_algo; pvr2_i2c_func i2c_func[PVR2_I2C_FUNC_CNT]; + int i2c_cx25840_hack_state; int i2c_linked; unsigned int i2c_pend_types; /* Which types of update are needed */ unsigned long i2c_pend_mask; /* Change bits we need to scan */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index e1d3803b3..6d7c909ce 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -182,6 +182,8 @@ int pvr2_i2c_basic_op(struct pvr2_hdw *hdw, } } +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX + /* This is a special entry point that is entered if an I2C operation is attempted to a wm8775 chip on model 24xxx hardware. Autodetect of this part doesn't work, but we know it is really there. So let's look for @@ -196,6 +198,94 @@ static int i2c_hack_wm8775(struct pvr2_hdw *hdw, return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen); } +/* This is a special entry point that is entered if an I2C operation is + attempted to a cx25840 chip on model 24xxx hardware. This chip can + sometimes wedge itself. Worse still, when this happens msp3400 can + falsely detect this part and then the system gets hosed up after msp3400 + gets confused and dies. What we want to do here is try to keep msp3400 + away and also try to notice if the chip is wedged and send a warning to + the system log. */ +static int i2c_hack_cx25840(struct pvr2_hdw *hdw, + u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) +{ + int ret; + unsigned int subaddr; + u8 wbuf[2]; + int state = hdw->i2c_cx25840_hack_state; + + if (!(rlen || wlen)) { + // Probe attempt - always just succeed and don't bother the + // hardware (this helps to make the state machine further + // down somewhat easier). + return 0; + } + + /* We're looking for the exact pattern where the revision register + is being read. The cx25840 module will always look at the + revision register first. Any other pattern of access therefore + has to be a probe attempt from somebody else so we'll reject it. + Normally we could just let each client just probe the part + anyway, but when the cx25840 is wedged, msp3400 will get a false + positive and that just screws things up... */ + + if (wlen == 0) { + switch (state) { + case 1: subaddr = 0x0100; break; + case 2: subaddr = 0x0101; break; + default: goto fail; + } + } else if (wlen == 2) { + subaddr = (wdata[0] << 8) | wdata[1]; + switch (subaddr) { + case 0x0100: state = 1; break; + case 0x0101: state = 2; break; + default: goto fail; + } + } else { + goto fail; + } + if (!rlen) goto success; + state = 0; + if (rlen != 1) goto fail; + + /* If we get to here then we have a legitimate read for one of the + two revision bytes, so pass it through. */ + wbuf[0] = subaddr >> 8; + wbuf[1] = subaddr; + ret = pvr2_i2c_basic_op(hdw,i2c_addr,wbuf,2,rdata,rlen); + + if ((ret != 0) || (*rdata == 0x04) || (*rdata == 0x0a)) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "WARNING: Detected a wedged cx25840 chip;" + " the device will not work."); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "WARNING: Try power cycling the pvrusb2 device."); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "WARNING: Disabling further access to the device" + " to prevent other foul-ups."); + // This blocks all further communication with the part. + hdw->i2c_func[0x44] = 0; + pvr2_hdw_render_useless(hdw); + goto fail; + } + + /* Success! Now let's just step out of the way and let the cx25840 + module do its work normally from now on... */ + pvr2_trace(PVR2_TRACE_INIT,"cx25840 appears to be OK," + " dropping its I2C filter"); + hdw->i2c_func[0x44] = pvr2_i2c_basic_op; + + success: + hdw->i2c_cx25840_hack_state = state; + return 0; + + fail: + hdw->i2c_cx25840_hack_state = state; + return -EIO; +} + +#endif /* CONFIG_VIDEO_PVRUSB2_24XXX */ + /* This is a very, very limited I2C adapter implementation. We can only support what we actually know will work on the device... */ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, @@ -818,6 +908,7 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) // the I2C transfer stack to let things work better. if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) { hdw->i2c_func[0x1b] = i2c_hack_wm8775; + hdw->i2c_func[0x44] = i2c_hack_cx25840; } #endif -- cgit v1.2.3 From d678f2eed5e7c30fc3976ef3e8293221ff2c0bea Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 20 Apr 2006 00:20:40 -0500 Subject: Change pvrusb2 module request behavior to be more conservative From: Mike Isely Previously when the pvrusb2 driver loaded, it immediately did a request_module() on all possible I2C support modules it might need. But that is overkill, since the actual modules needed is a subset depending on the model type. This change delays module request until the hardware shows up, and then it only requests the modules that make sense for the specific hardware. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 43 +++++++++++++++++++++--- linux/drivers/media/video/pvrusb2/pvrusb2-main.c | 10 ------ 2 files changed, 39 insertions(+), 14 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 350db8dcb..8da06d216 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -53,6 +53,40 @@ static const char *pvr2_device_names[] = { #endif }; +struct pvr2_string_table { + const char **lst; + unsigned int cnt; +}; + +// Names of other client modules to request for 24xxx model hardware +static const char *pvr2_client_24xxx[] = { + "cx25840", + "tuner", + "tda9887", + "wm8775", +}; + +// Names of other client modules to request for 29xxx model hardware +static const char *pvr2_client_29xxx[] = { + "msp3400", + "saa7115", + "tuner", + "tda9887", +}; + +static struct pvr2_string_table pvr2_client_lists[] = { + [PVR2_HDW_TYPE_29XXX] = { + pvr2_client_29xxx, + sizeof(pvr2_client_29xxx)/sizeof(pvr2_client_29xxx[0]), + }, +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX + [PVR2_HDW_TYPE_24XXX] = { + pvr2_client_24xxx, + sizeof(pvr2_client_24xxx)/sizeof(pvr2_client_24xxx[0]), + }, +#endif +}; + static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = 0}; DECLARE_MUTEX(pvr2_unit_sem); @@ -817,10 +851,7 @@ int pvr2_upload_firmware1(struct pvr2_hdw *hdw) "v4l-pvrusb2-24xxx-01.fw", }; #endif - static const struct { - const char **lst; - unsigned int cnt; - } fw_file_defs[] = { + static const struct pvr2_string_table fw_file_defs[] = { [PVR2_HDW_TYPE_29XXX] = { fw_files_29xxx, sizeof(fw_files_29xxx)/sizeof(fw_files_29xxx[0]), @@ -1465,6 +1496,10 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) } if (!pvr2_hdw_dev_ok(hdw)) return; + for (idx = 0; idx < pvr2_client_lists[hdw->hdw_type].cnt; idx++) { + request_module(pvr2_client_lists[hdw->hdw_type].lst[idx]); + } + pvr2_hdw_cmd_powerup(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c index 988f32ad4..245f7a36d 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c @@ -134,16 +134,6 @@ static int __init pvr_init(void) pvr2_trace(PVR2_TRACE_INIT,"pvr_init"); - /* Auto-load various support modules (with which we may - indirectly interact) */ - request_module("msp3400"); - request_module("cx25840"); - request_module("saa7115"); - request_module("tuner"); - request_module("tveeprom"); - request_module("tda9887"); - request_module("wm8775"); - #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS class_ptr = pvr2_sysfs_class_create(); #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ -- cgit v1.2.3 From a38e144fad1d09381623675a1442f2811d7ccc66 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 20 Apr 2006 00:21:36 -0500 Subject: Log a warning message on a too-large I2C read attempt in pvrusb2 From: Mike Isely Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 6d7c909ce..232dea53f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -112,8 +112,22 @@ static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */ #endif if (!data) dlen = 0; - if (dlen > (sizeof(hdw->cmd_buffer) - 4)) return -ENOTSUPP; - if (res && (rlen > (sizeof(hdw->cmd_buffer) - 1))) return -ENOTSUPP; + if (dlen > (sizeof(hdw->cmd_buffer) - 4)) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Killing an I2C read to %u that has wlen too large" + " (desired=%u limit=%u)", + i2c_addr, + dlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 4)); + return -ENOTSUPP; + } + if (res && (rlen > (sizeof(hdw->cmd_buffer) - 1))) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Killing an I2C read to %u that has rlen too large" + " (desired=%u limit=%u)", + i2c_addr, + rlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 1)); + return -ENOTSUPP; + } LOCK_TAKE(hdw->ctl_lock); -- cgit v1.2.3 From 322a72b2cfc8911cfb40781716197551ff3e4504 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 20 Apr 2006 00:24:23 -0500 Subject: Keep the cx25840 I2C filter around in pvrusb2 From: Mike Isely Modify the I2C filter for cx25840 such that it stays around rather than going away after the initial sanity check has passed. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 232dea53f..4f1f4b000 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -234,6 +234,10 @@ static int i2c_hack_cx25840(struct pvr2_hdw *hdw, return 0; } + if (state == 3) { + return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen); + } + /* We're looking for the exact pattern where the revision register is being read. The cx25840 module will always look at the revision register first. Any other pattern of access therefore @@ -283,11 +287,9 @@ static int i2c_hack_cx25840(struct pvr2_hdw *hdw, goto fail; } - /* Success! Now let's just step out of the way and let the cx25840 - module do its work normally from now on... */ - pvr2_trace(PVR2_TRACE_INIT,"cx25840 appears to be OK," - " dropping its I2C filter"); - hdw->i2c_func[0x44] = pvr2_i2c_basic_op; + /* Success! */ + pvr2_trace(PVR2_TRACE_CHIPS,"cx25840 appears to be OK."); + state = 3; success: hdw->i2c_cx25840_hack_state = state; -- cgit v1.2.3 From 2e8bea8ec6176e6319293615bdd4a0fce038d561 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 20 Apr 2006 00:25:13 -0500 Subject: Fix off-by-1 problem in pvrusb2 video standard enumeration From: Mike Isely Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 8da06d216..897f547e5 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1944,9 +1944,9 @@ int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw, { int ret = -EINVAL; if (!idx) return ret; - idx--; LOCK_TAKE(hdw->big_lock); do { if (idx >= hdw->std_enum_cnt) break; + idx--; memcpy(std,hdw->std_defs+idx,sizeof(*std)); ret = 0; } while (0); LOCK_GIVE(hdw->big_lock); -- cgit v1.2.3 From aadd7cd11f4a71c38b1b4d55806a9ad24cad1828 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 20 Apr 2006 00:26:45 -0500 Subject: Fix pvrusb2 bug involving bad return on V4L query control ioctl From: Mike Isely Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 1 + 1 file changed, 1 insertion(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 3701d479d..8a6e91e5a 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -545,6 +545,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, { struct pvr2_ctrl *cptr; struct v4l2_queryctrl *vc = (struct v4l2_queryctrl *)arg; + ret = 0; cptr = pvr2_hdw_get_ctrl_v4l(hdw,vc->id); if (!cptr) { ret = -EINVAL; -- cgit v1.2.3 From 3c70253c62743effde4bb207d05f049bc1c2248b Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 20 Apr 2006 00:28:33 -0500 Subject: Make PAL-N a supported standard if other PAL standards are present in pvrusb2 From: Mike Isely If the pvrusb2 driver detects a PAL-capable device, add PAL-N to the list of supported standards. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 43 ++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 897f547e5..45cacc865 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1397,20 +1397,45 @@ static int pvr2_hdw_check_firmware(struct pvr2_hdw *hdw) return result == 0; } +struct pvr2_std_hack { + v4l2_std_id pat; // Pattern to match + v4l2_std_id msk; // Which bits we care about + v4l2_std_id std; // What additional standards to set +}; + +// This is a mapping of additional standards to support if certain +// standards are already present. +const static struct pvr2_std_hack std_hacks[] = { + { + // Devices which are NTSC-M capable are also marked as + // NTSC-Mj capable on the device's sticker. + .pat = V4L2_STD_NTSC_M, + .std = V4L2_STD_NTSC_M_JP, + },{ + // Enable PAL-N for PAL-capable devices. One user in + // Argentina has encountered this. + .pat = V4L2_STD_PAL_BG|V4L2_STD_PAL_DK|V4L2_STD_PAL_I, + .std = V4L2_STD_PAL_N|V4L2_STD_PAL_Nc + } +}; static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) { char buf[40]; unsigned int bcnt; v4l2_std_id std1,std2; + unsigned int idx; std1 = get_default_standard(hdw); - // Devices which are NTSC-M capable are also marked as NTSC-Mj - // capable on the device's sticker. Reflect that fact here, - // because tveeprom doesn't seem to report it. - if (hdw->std_mask_eeprom & V4L2_STD_NTSC_M) { - hdw->std_mask_eeprom |= V4L2_STD_NTSC_M_JP; + std2 = 0; + for (idx = 0; idx < sizeof(std_hacks)/sizeof(std_hacks[0]); idx++) { + if (std_hacks[idx].msk ? + ((std_hacks[idx].pat ^ hdw->std_mask_eeprom) & + std_hacks[idx].msk) : + (std_hacks[idx].pat & hdw->std_mask_eeprom)) { + std2 |= std_hacks[idx].std; + } } bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom); @@ -1418,6 +1443,14 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) "Supported video standard(s) reported by eeprom: %.*s", bcnt,buf); + if (std2) { + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std2); + pvr2_trace(PVR2_TRACE_INIT, + "Based on eeprom list, also supporting: %.*s", + bcnt,buf); + hdw->std_mask_eeprom |= std2; + } + hdw->std_mask_avail = hdw->std_mask_eeprom; std2 = std1 & ~hdw->std_mask_avail; -- cgit v1.2.3 From e31ae22c2bd0e9a999032518fa4e8047445f39c7 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 20 Apr 2006 00:29:49 -0500 Subject: Bracket a pvrusb2 24xxx-only structure correctly From: Mike Isely Surround a 24xxx-specific data structure in pvrusb2 with the appropriate CONFIG option so that it won't be used if 24xxx support is not enabled. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 45cacc865..07bfb7e3b 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -58,6 +58,7 @@ struct pvr2_string_table { unsigned int cnt; }; +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX // Names of other client modules to request for 24xxx model hardware static const char *pvr2_client_24xxx[] = { "cx25840", @@ -65,6 +66,7 @@ static const char *pvr2_client_24xxx[] = { "tda9887", "wm8775", }; +#endif // Names of other client modules to request for 29xxx model hardware static const char *pvr2_client_29xxx[] = { -- cgit v1.2.3 From e6a398bf0811e2f0cbdac6dd23d58751e65aaa6d Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 20 Apr 2006 00:47:57 -0500 Subject: Need to compile two more sources in pvrusb2 From: Mike Isely Two modules had been added to pvrusb2. We need to compile them... Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/Makefile | 1 + 1 file changed, 1 insertion(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/Makefile b/linux/drivers/media/video/pvrusb2/Makefile index ce78cd00f..fed603ad0 100644 --- a/linux/drivers/media/video/pvrusb2/Makefile +++ b/linux/drivers/media/video/pvrusb2/Makefile @@ -10,6 +10,7 @@ pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ pvrusb2-encoder.o pvrusb2-video-v4l.o \ pvrusb2-eeprom.o pvrusb2-tuner.o pvrusb2-demod.o \ pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \ + pvrusb2-ctrl.o pvrusb2-std.o \ pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \ $(obj-pvrusb2-24xxx-y) \ $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y) -- cgit v1.2.3 From 7210151a9c6d56753594fb042a7b7a1a6afd78a4 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 20 Apr 2006 00:48:47 -0500 Subject: Update operation of wm8775 in pvrusb2 to use new routing API From: Mike Isely Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c index 8443ffd52..9999bc93e 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c @@ -48,21 +48,20 @@ struct pvr2_v4l_wm8775 { static void set_input(struct pvr2_v4l_wm8775 *ctxt) { - struct v4l2_audio inp; + struct v4l2_routing route; struct pvr2_hdw *hdw = ctxt->hdw; int msk = 0; - memset(&inp,0,sizeof(inp)); + memset(&route,0,sizeof(route)); pvr2_trace(PVR2_TRACE_CHIPS,"i2c wm8775 set_input(val=%d msk=0x%x)", hdw->input_val,msk); // Always point to input #1 no matter what - inp.index = 2; - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_AUDIO,&inp); + route.input = 2; + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUIO_ROUTING,&route); } - static int check_input(struct pvr2_v4l_wm8775 *ctxt) { struct pvr2_hdw *hdw = ctxt->hdw; -- cgit v1.2.3 From b8a74ce829faef87a74cddd9d2b37406c0fda9c7 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 20 Apr 2006 01:32:22 -0500 Subject: Fix critical typo in wm8775 support within pvrusb2 From: Mike Isely Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c index 9999bc93e..fcad346e3 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c @@ -59,7 +59,7 @@ static void set_input(struct pvr2_v4l_wm8775 *ctxt) // Always point to input #1 no matter what route.input = 2; - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUIO_ROUTING,&route); + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route); } static int check_input(struct pvr2_v4l_wm8775 *ctxt) -- cgit v1.2.3 From 8541186fb1657556a4298819391bec45d26d6393 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Thu, 20 Apr 2006 16:01:47 +0100 Subject: Additional frontend_init safety checks From: Andrew de Quincey Don't try and set the tuner ops if the demod was not detected. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/ttpci/av7110.c | 24 +++-- linux/drivers/media/dvb/ttpci/budget-av.c | 4 + linux/drivers/media/video/saa7134/saa7134-dvb.c | 116 +++++++++++++++--------- 3 files changed, 96 insertions(+), 48 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/ttpci/av7110.c b/linux/drivers/media/dvb/ttpci/av7110.c index 9ede95193..9567e03a1 100644 --- a/linux/drivers/media/dvb/ttpci/av7110.c +++ b/linux/drivers/media/dvb/ttpci/av7110.c @@ -2083,7 +2083,9 @@ static int frontend_init(struct av7110 *av7110) case 0x0000: // Fujitsu/Siemens DVB-Cable (ves1820/Philips CD1516(??)) av7110->fe = ves1820_attach(&philips_cd1516_config, &av7110->i2c_adap, read_pwm(av7110)); - av7110->fe->ops->tuner_ops.set_params = philips_cd1516_tuner_set_params; + if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = philips_cd1516_tuner_set_params; + } break; } @@ -2134,13 +2136,17 @@ static int frontend_init(struct av7110 *av7110) /* Siemens DVB-C (full-length card) VES1820/Philips CD1516 */ av7110->fe = ves1820_attach(&philips_cd1516_config, &av7110->i2c_adap, read_pwm(av7110)); - av7110->fe->ops->tuner_ops.set_params = philips_cd1516_tuner_set_params; + if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = philips_cd1516_tuner_set_params; + } break; case 0x0003: /* Hauppauge DVB-C 2.1 VES1820/ALPS TDBE2 */ av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); - av7110->fe->ops->tuner_ops.set_params = alps_tdbe2_tuner_set_params; + if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = alps_tdbe2_tuner_set_params; + } break; } break; @@ -2149,13 +2155,17 @@ static int frontend_init(struct av7110 *av7110) // ALPS TDLB7 av7110->fe = sp8870_attach(&alps_tdlb7_config, &av7110->i2c_adap); - av7110->fe->ops->tuner_ops.set_params = alps_tdlb7_tuner_set_params; + if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = alps_tdlb7_tuner_set_params; + } break; case 0x0002: // Hauppauge/TT DVB-C premium rev2.X av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); - av7110->fe->ops->tuner_ops.set_params = alps_tdbe2_tuner_set_params; + if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = alps_tdbe2_tuner_set_params; + } break; case 0x0004: // Galaxis DVB-S rev1.3 @@ -2185,7 +2195,9 @@ static int frontend_init(struct av7110 *av7110) case 0x0008: // Hauppauge/TT DVB-T av7110->fe = l64781_attach(&grundig_29504_401_config, &av7110->i2c_adap); - av7110->fe->ops->tuner_ops.set_params = grundig_29504_401_tuner_set_params; + if (av7110->fe) { + av7110->fe->ops->tuner_ops.set_params = grundig_29504_401_tuner_set_params; + } break; case 0x000A: // Hauppauge/TT Nexus-CA rev1.X diff --git a/linux/drivers/media/dvb/ttpci/budget-av.c b/linux/drivers/media/dvb/ttpci/budget-av.c index 9bd069e4e..680041878 100644 --- a/linux/drivers/media/dvb/ttpci/budget-av.c +++ b/linux/drivers/media/dvb/ttpci/budget-av.c @@ -1113,6 +1113,10 @@ static void frontend_init(struct budget_av *budget_av) case SUBID_DVBT_CINERGY1200: fe = tda10046_attach(&philips_tu1216_config, &budget_av->budget.i2c_adap); + if (fe) { + fe->ops->tuner_ops.init = philips_tu1216_tuner_init; + fe->ops->tuner_ops.set_params = philips_tu1216_tuner_set_params; + } break; } diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index 51d88c152..b659b96e5 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -1021,112 +1021,144 @@ static int dvb_init(struct saa7134_dev *dev) printk("%s: pinnacle 300i dvb setup\n",dev->name); dev->dvb.frontend = mt352_attach(&pinnacle_300i, &dev->i2c_adap); - dev->dvb.frontend->ops->tuner_ops.calc_regs = mt352_pinnacle_tuner_calc_regs; + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.calc_regs = mt352_pinnacle_tuner_calc_regs; + } break; case SAA7134_BOARD_AVERMEDIA_777: printk("%s: avertv 777 dvb setup\n",dev->name); dev->dvb.frontend = mt352_attach(&avermedia_777, &dev->i2c_adap); - dev->dvb.frontend->ops->tuner_ops.calc_regs = mt352_aver777_tuner_calc_regs; + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.calc_regs = mt352_aver777_tuner_calc_regs; + } break; #endif #ifdef HAVE_TDA1004X case SAA7134_BOARD_MD7134: dev->dvb.frontend = tda10046_attach(&medion_cardbus, &dev->i2c_adap); - dev->dvb.frontend->ops->tuner_ops.init = philips_fmd1216_tuner_init; - dev->dvb.frontend->ops->tuner_ops.sleep = philips_fmd1216_tuner_sleep; - dev->dvb.frontend->ops->tuner_ops.set_params = philips_fmd1216_tuner_set_params; + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = philips_fmd1216_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_fmd1216_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_fmd1216_tuner_set_params; + } break; case SAA7134_BOARD_PHILIPS_TOUGH: dev->dvb.frontend = tda10046_attach(&philips_tu1216_60_config, &dev->i2c_adap); - dev->dvb.frontend->ops->tuner_ops.init = philips_tu1216_tuner_60_init; - dev->dvb.frontend->ops->tuner_ops.set_params = philips_tu1216_tuner_60_set_params; + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = philips_tu1216_tuner_60_init; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tu1216_tuner_60_set_params; + } break; case SAA7134_BOARD_FLYDVBTDUO: dev->dvb.frontend = tda10046_attach(&tda827x_lifeview_config, &dev->i2c_adap); - dev->dvb.frontend->ops->tuner_ops.init = philips_tda827x_tuner_init; - dev->dvb.frontend->ops->tuner_ops.sleep = philips_tda827x_tuner_sleep; - dev->dvb.frontend->ops->tuner_ops.set_params = philips_tda827x_tuner_set_params; + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = philips_tda827x_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_tda827x_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tda827x_tuner_set_params; + } break; case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS: dev->dvb.frontend = tda10046_attach(&tda827x_lifeview_config, &dev->i2c_adap); - dev->dvb.frontend->ops->tuner_ops.init = philips_tda827x_tuner_init; - dev->dvb.frontend->ops->tuner_ops.sleep = philips_tda827x_tuner_sleep; - dev->dvb.frontend->ops->tuner_ops.set_params = philips_tda827x_tuner_set_params; + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = philips_tda827x_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_tda827x_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tda827x_tuner_set_params; + } break; case SAA7134_BOARD_PHILIPS_EUROPA: dev->dvb.frontend = tda10046_attach(&philips_europa_config, &dev->i2c_adap); - dev->original_demod_sleep = dev->dvb.frontend->ops->sleep; - dev->dvb.frontend->ops->sleep = philips_europa_demod_sleep; - dev->dvb.frontend->ops->tuner_ops.init = philips_europa_tuner_init; - dev->dvb.frontend->ops->tuner_ops.sleep = philips_europa_tuner_sleep; - dev->dvb.frontend->ops->tuner_ops.set_params = philips_td1316_tuner_set_params; + if (dev->dvb.frontend) { + dev->original_demod_sleep = dev->dvb.frontend->ops->sleep; + dev->dvb.frontend->ops->sleep = philips_europa_demod_sleep; + dev->dvb.frontend->ops->tuner_ops.init = philips_europa_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_europa_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_td1316_tuner_set_params; + } break; case SAA7134_BOARD_VIDEOMATE_DVBT_300: dev->dvb.frontend = tda10046_attach(&philips_europa_config, &dev->i2c_adap); - dev->dvb.frontend->ops->tuner_ops.init = philips_europa_tuner_init; - dev->dvb.frontend->ops->tuner_ops.sleep = philips_europa_tuner_sleep; - dev->dvb.frontend->ops->tuner_ops.set_params = philips_td1316_tuner_set_params; + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = philips_europa_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_europa_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_td1316_tuner_set_params; + } break; case SAA7134_BOARD_VIDEOMATE_DVBT_200: dev->dvb.frontend = tda10046_attach(&philips_tu1216_61_config, &dev->i2c_adap); - dev->dvb.frontend->ops->tuner_ops.init = philips_tu1216_tuner_61_init; - dev->dvb.frontend->ops->tuner_ops.set_params = philips_tu1216_tuner_61_set_params; + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = philips_tu1216_tuner_61_init; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tu1216_tuner_61_set_params; + } break; case SAA7134_BOARD_PHILIPS_TIGER: dev->dvb.frontend = tda10046_attach(&philips_tiger_config, &dev->i2c_adap); - dev->dvb.frontend->ops->tuner_ops.init = philips_tiger_tuner_init; - dev->dvb.frontend->ops->tuner_ops.sleep = philips_tiger_tuner_sleep; - dev->dvb.frontend->ops->tuner_ops.set_params = philips_tiger_tuner_set_params; + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = philips_tiger_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_tiger_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tiger_tuner_set_params; + } break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: dev->dvb.frontend = tda10046_attach(&philips_tiger_config, &dev->i2c_adap); - dev->dvb.frontend->ops->tuner_ops.init = philips_tiger_tuner_init; - dev->dvb.frontend->ops->tuner_ops.sleep = philips_tiger_tuner_sleep; - dev->dvb.frontend->ops->tuner_ops.set_params = philips_tiger_tuner_set_params; + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = philips_tiger_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_tiger_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tiger_tuner_set_params; + } break; case SAA7134_BOARD_FLYDVBT_LR301: dev->dvb.frontend = tda10046_attach(&tda827x_lifeview_config, &dev->i2c_adap); - dev->dvb.frontend->ops->tuner_ops.init = philips_tda827x_tuner_init; - dev->dvb.frontend->ops->tuner_ops.sleep = philips_tda827x_tuner_sleep; - dev->dvb.frontend->ops->tuner_ops.set_params = philips_tda827x_tuner_set_params; + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = philips_tda827x_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = philips_tda827x_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = philips_tda827x_tuner_set_params; + } break; case SAA7134_BOARD_FLYDVB_TRIO: dev->dvb.frontend = tda10046_attach(&lifeview_trio_config, &dev->i2c_adap); - dev->dvb.frontend->ops->tuner_ops.sleep = lifeview_trio_tuner_sleep; - dev->dvb.frontend->ops->tuner_ops.set_params = lifeview_trio_tuner_set_params; + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.sleep = lifeview_trio_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = lifeview_trio_tuner_set_params; + } break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: dev->dvb.frontend = tda10046_attach(&ads_tech_duo_config, &dev->i2c_adap); - dev->dvb.frontend->ops->tuner_ops.init = ads_duo_tuner_init; - dev->dvb.frontend->ops->tuner_ops.sleep = ads_duo_tuner_sleep; - dev->dvb.frontend->ops->tuner_ops.set_params = ads_duo_tuner_set_params; + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = ads_duo_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = ads_duo_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = ads_duo_tuner_set_params; + } break; case SAA7134_BOARD_TEVION_DVBT_220RF: dev->dvb.frontend = tda10046_attach(&tevion_dvbt220rf_config, &dev->i2c_adap); - dev->dvb.frontend->ops->tuner_ops.sleep = tevion_dvb220rf_tuner_sleep; - dev->dvb.frontend->ops->tuner_ops.set_params = tevion_dvb220rf_tuner_set_params; + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.sleep = tevion_dvb220rf_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = tevion_dvb220rf_tuner_set_params; + } break; case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: dev->dvb.frontend = tda10046_attach(&ads_tech_duo_config, &dev->i2c_adap); - dev->dvb.frontend->ops->tuner_ops.init = ads_duo_tuner_init; - dev->dvb.frontend->ops->tuner_ops.sleep = ads_duo_tuner_sleep; - dev->dvb.frontend->ops->tuner_ops.set_params = ads_duo_tuner_set_params; + if (dev->dvb.frontend) { + dev->dvb.frontend->ops->tuner_ops.init = ads_duo_tuner_init; + dev->dvb.frontend->ops->tuner_ops.sleep = ads_duo_tuner_sleep; + dev->dvb.frontend->ops->tuner_ops.set_params = ads_duo_tuner_set_params; + } break; #endif #ifdef HAVE_NXT200X -- cgit v1.2.3 From f4b7afc8372d52c7f20b5c021fed02150ddeefda Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Thu, 20 Apr 2006 12:18:50 -0400 Subject: saa7134: Missing 'break' in Terratec Cinergy 400 TV initialization From: Mikhail Gusarov There is a missing break in card initialization function. Might screw up initialization of Terratec Cinergy 400 TV. Signed-off-by: Mikhail Gusarov Signed-off-by: Michael Krufky --- linux/drivers/media/video/saa7134/saa7134-cards.c | 1 + 1 file changed, 1 insertion(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index 3629aee32..04812f5f8 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -3581,6 +3581,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) /* power-up tuner chip */ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x00040000, 0x00040000); saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00040000, 0x00000000); + break; case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL: /* this turns the remote control chip off to work around a bug in it */ saa_writeb(SAA7134_GPIO_GPMODE1, 0x80); -- cgit v1.2.3 From 196aa5c354858ff740aedc08e450d0b666c493a6 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Thu, 20 Apr 2006 21:56:25 -0400 Subject: cx88-blackbird: clean up the buffers when closing the MPEG stream From: Valentin Zagura This patch cleans up the buffer queue when the MPEG stream is closed, preventing the message, 'cx8802_timeout' Signed-off-by: Valentin Zagura Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/cx88-blackbird.c | 1 + 1 file changed, 1 insertion(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index 73580db8b..f34a76511 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -1602,6 +1602,7 @@ static int mpeg_release(struct inode *inode, struct file *file) BLACKBIRD_RAW_BITS_NONE ); + cx8802_cancel_buffers(fh->dev); /* stop mpeg capture */ if (fh->mpegq.streaming) videobuf_streamoff(&fh->mpegq); -- cgit v1.2.3 From 89ea8b07bd06b642101d489dbe88cbc18d707a17 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 22 Apr 2006 15:22:46 +0200 Subject: Add support for the cx25836/7 video decoder. From: Hans Verkuil Signed-off-by: Scott Alfter Signed-off-by: Hans Verkuil --- linux/drivers/media/video/cx25840/cx25840-audio.c | 24 ++- linux/drivers/media/video/cx25840/cx25840-core.c | 181 +++++++++++++++------- linux/drivers/media/video/cx25840/cx25840-core.h | 5 +- 3 files changed, 154 insertions(+), 56 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx25840/cx25840-audio.c b/linux/drivers/media/video/cx25840/cx25840-audio.c index 9b9133804..c836feeb5 100644 --- a/linux/drivers/media/video/cx25840/cx25840-audio.c +++ b/linux/drivers/media/video/cx25840/cx25840-audio.c @@ -35,7 +35,8 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) return -EINVAL; /* assert soft reset */ - cx25840_and_or(client, 0x810, ~0x1, 0x01); + if (!state->is_cx25836) + cx25840_and_or(client, 0x810, ~0x1, 0x01); /* common for all inputs and rates */ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */ @@ -50,6 +51,9 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) /* AUX_PLL_FRAC */ cx25840_write4(client, 0x110, 0xee39bb01); + if (state->is_cx25836) + break; + /* src3/4/6_ctl = 0x0801f77f */ cx25840_write4(client, 0x900, 0x7ff70108); cx25840_write4(client, 0x904, 0x7ff70108); @@ -63,6 +67,9 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) /* AUX_PLL_FRAC */ cx25840_write4(client, 0x110, 0xd66bec00); + if (state->is_cx25836) + break; + /* src3/4/6_ctl = 0x08016d59 */ cx25840_write4(client, 0x900, 0x596d0108); cx25840_write4(client, 0x904, 0x596d0108); @@ -76,6 +83,9 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) /* AUX_PLL_FRAC */ cx25840_write4(client, 0x110, 0xe5d69800); + if (state->is_cx25836) + break; + /* src3/4/6_ctl = 0x08014faa */ cx25840_write4(client, 0x900, 0xaa4f0108); cx25840_write4(client, 0x904, 0xaa4f0108); @@ -91,6 +101,9 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) /* AUX_PLL_FRAC */ cx25840_write4(client, 0x110, 0x69082a01); + if (state->is_cx25836) + break; + /* src1_ctl = 0x08010000 */ cx25840_write4(client, 0x8f8, 0x00000108); @@ -110,6 +123,9 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) /* AUX_PLL_FRAC */ cx25840_write4(client, 0x110, 0xd66bec00); + if (state->is_cx25836) + break; + /* src1_ctl = 0x08010000 */ cx25840_write4(client, 0x8f8, 0xcd600108); @@ -126,6 +142,9 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) /* AUX_PLL_FRAC */ cx25840_write4(client, 0x110, 0xe5d69800); + if (state->is_cx25836) + break; + /* src1_ctl = 0x08010000 */ cx25840_write4(client, 0x8f8, 0x00800108); @@ -138,7 +157,8 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) } /* deassert soft reset */ - cx25840_and_or(client, 0x810, ~0x1, 0x00); + if (!state->is_cx25836) + cx25840_and_or(client, 0x810, ~0x1, 0x00); state->audclk_freq = freq; diff --git a/linux/drivers/media/video/cx25840/cx25840-core.c b/linux/drivers/media/video/cx25840/cx25840-core.c index eba2b15b0..a9cfd098d 100644 --- a/linux/drivers/media/video/cx25840/cx25840-core.c +++ b/linux/drivers/media/video/cx25840/cx25840-core.c @@ -116,7 +116,7 @@ u32 cx25840_read4(struct i2c_client * client, u16 addr) (buffer[2] << 8) | buffer[3]; } -int cx25840_and_or(struct i2c_client *client, u16 addr, u8 and_mask, +int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask, u8 or_value) { return cx25840_write(client, addr, @@ -128,7 +128,8 @@ int cx25840_and_or(struct i2c_client *client, u16 addr, u8 and_mask, static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, enum cx25840_audio_input aud_input); -static void log_status(struct i2c_client *client); +static void log_audio_status(struct i2c_client *client); +static void log_video_status(struct i2c_client *client); /* ----------------------------------------------------------------------- */ @@ -158,6 +159,33 @@ static void init_dll2(struct i2c_client *client) cx25840_write(client, 0x15d, 0xe1); } +static void cx25836_initialize(struct i2c_client *client) +{ + /* reset configuration is described on page 3-77 of the CX25836 datasheet */ + /* 2. */ + cx25840_and_or(client, 0x000, ~0x01, 0x01); + cx25840_and_or(client, 0x000, ~0x01, 0x00); + /* 3a. */ + cx25840_and_or(client, 0x15a, ~0x70, 0x00); + /* 3b. */ + cx25840_and_or(client, 0x15b, ~0x1e, 0x06); + /* 3c. */ + cx25840_and_or(client, 0x159, ~0x02, 0x02); + /* 3d. */ + /* There should be a 10-us delay here, but since the + i2c bus already has a 10-us delay we don't need to do + anything */ + /* 3e. */ + cx25840_and_or(client, 0x159, ~0x02, 0x00); + /* 3f. */ + cx25840_and_or(client, 0x159, ~0xc0, 0xc0); + /* 3g. */ + cx25840_and_or(client, 0x159, ~0x01, 0x00); + cx25840_and_or(client, 0x159, ~0x01, 0x01); + /* 3h. */ + cx25840_and_or(client, 0x15b, ~0x1e, 0x10); +} + static void cx25840_initialize(struct i2c_client *client, int loadfw) { struct cx25840_state *state = i2c_get_clientdata(client); @@ -330,8 +358,10 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp state->vid_input = vid_input; state->aud_input = aud_input; - cx25840_audio_set_path(client); - input_change(client); + if (!state->is_cx25836) { + cx25840_audio_set_path(client); + input_change(client); + } return 0; } @@ -381,6 +411,7 @@ static int set_v4lstd(struct i2c_client *client, v4l2_std_id std) v4l2_std_id cx25840_get_v4lstd(struct i2c_client * client) { + struct cx25840_state *state = i2c_get_clientdata(client); /* check VID_FMT_SEL first */ u8 fmt = cx25840_read(client, 0x400) & 0xf; @@ -394,7 +425,7 @@ v4l2_std_id cx25840_get_v4lstd(struct i2c_client * client) { /* if the audio std is A2-M, then this is the South Korean NTSC standard */ - if (cx25840_read(client, 0x805) == 2) + if (!state->is_cx25836 && cx25840_read(client, 0x805) == 2) return V4L2_STD_NTSC_M_KR; return V4L2_STD_NTSC_M; } @@ -467,6 +498,8 @@ static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_MUTE: + if (state->is_cx25836) + return -EINVAL; return cx25840_audio(client, VIDIOC_S_CTRL, ctrl); default: @@ -501,6 +534,8 @@ static int get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_MUTE: + if (state->is_cx25836) + return -EINVAL; return cx25840_audio(client, VIDIOC_G_CTRL, ctrl); default: return -EINVAL; @@ -590,7 +625,7 @@ static int set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) /* ----------------------------------------------------------------------- */ -static struct v4l2_queryctrl cx25840_qctrl[] = { +static struct v4l2_queryctrl cx25836_qctrl[] = { { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, @@ -627,7 +662,11 @@ static struct v4l2_queryctrl cx25840_qctrl[] = { .step = 1, .default_value = 0, .flags = 0, - }, { + }, +}; + +static struct v4l2_queryctrl cx25840_qctrl[] = { + { .id = V4L2_CID_AUDIO_VOLUME, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Volume", @@ -717,8 +756,8 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, case VIDIOC_STREAMON: v4l_dbg(1, cx25840_debug, client, "enable output\n"); - cx25840_write(client, 0x115, 0x8c); - cx25840_write(client, 0x116, 0x07); + cx25840_write(client, 0x115, state->is_cx25836 ? 0x0c : 0x8c); + cx25840_write(client, 0x116, state->is_cx25836 ? 0x04 : 0x07); break; case VIDIOC_STREAMOFF: @@ -728,7 +767,9 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, break; case VIDIOC_LOG_STATUS: - log_status(client); + log_video_status(client); + if (!state->is_cx25836) + log_audio_status(client); break; case VIDIOC_G_CTRL: @@ -742,6 +783,14 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, struct v4l2_queryctrl *qc = arg; int i; + for (i = 0; i < ARRAY_SIZE(cx25836_qctrl); i++) + if (qc->id && qc->id == cx25836_qctrl[i].id) { + memcpy(qc, &cx25836_qctrl[i], sizeof(*qc)); + return 0; + } + if (state->is_cx25836) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(cx25840_qctrl); i++) if (qc->id && qc->id == cx25840_qctrl[i].id) { memcpy(qc, &cx25840_qctrl[i], sizeof(*qc)); @@ -771,31 +820,41 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, return set_input(client, route->input, state->aud_input); case VIDIOC_INT_G_AUDIO_ROUTING: + if (state->is_cx25836) + return -EINVAL; route->input = state->aud_input; route->output = 0; break; case VIDIOC_INT_S_AUDIO_ROUTING: + if (state->is_cx25836) + return -EINVAL; return set_input(client, state->vid_input, route->input); case VIDIOC_S_FREQUENCY: - input_change(client); + if (!state->is_cx25836) { + input_change(client); + } break; case VIDIOC_G_TUNER: { - u8 mode = cx25840_read(client, 0x804); - u8 vpres = cx25840_read(client, 0x80a) & 0x10; + u8 vpres = cx25840_read(client, 0x40e) & 0x20; + u8 mode; int val = 0; if (state->radio) break; + vt->signal = vpres ? 0xffff : 0x0; + if (state->is_cx25836) + break; + vt->capability |= V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; - vt->signal = vpres ? 0xffff : 0x0; + mode = cx25840_read(client, 0x804); /* get rxsubchans and audmode */ if ((mode & 0xf) == 1) @@ -815,7 +874,7 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, } case VIDIOC_S_TUNER: - if (state->radio) + if (state->radio || state->is_cx25836) break; switch (vt->audmode) { @@ -857,12 +916,14 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, return set_v4lfmt(client, (struct v4l2_format *)arg); case VIDIOC_INT_RESET: - cx25840_initialize(client, 0); + if (state->is_cx25836) + cx25836_initialize(client); + else + cx25840_initialize(client, 0); break; case VIDIOC_INT_G_CHIP_IDENT: - *(enum v4l2_chip_ident *)arg = - V4L2_IDENT_CX25840 + ((cx25840_read(client, 0x100) >> 4) & 0xf); + *(enum v4l2_chip_ident *)arg = state->id; break; default: @@ -884,6 +945,7 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address, { struct i2c_client *client; struct cx25840_state *state; + enum v4l2_chip_ident id; u16 device_id; /* Check if the adapter supports the needed features @@ -896,10 +958,11 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address, #endif return 0; - client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (client == 0) + state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL); + if (state == 0) return -ENOMEM; + client = &state->c; client->addr = address; client->adapter = adapter; client->driver = &i2c_driver_cx25840; @@ -914,10 +977,18 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address, device_id |= cx25840_read(client, 0x100); /* The high byte of the device ID should be - * 0x84 if chip is present */ - if ((device_id & 0xff00) != 0x8400) { + * 0x83 for the cx2583x and 0x84 for the cx2584x */ + if ((device_id & 0xff00) == 0x8300) { + id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; + state->is_cx25836 = 1; + } + else if ((device_id & 0xff00) == 0x8400) { + id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf); + state->is_cx25836 = 0; + } + else { v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n"); - kfree(client); + kfree(state); return 0; } @@ -926,21 +997,18 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address, (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : 3, address << 1, adapter->name); - state = kmalloc(sizeof(struct cx25840_state), GFP_KERNEL); - if (state == NULL) { - kfree(client); - return -ENOMEM; - } - i2c_set_clientdata(client, state); - memset(state, 0, sizeof(struct cx25840_state)); state->vid_input = CX25840_COMPOSITE7; state->aud_input = CX25840_AUDIO8; state->audclk_freq = 48000; state->pvr150_workaround = 0; state->audmode = V4L2_TUNER_MODE_LANG1; + state->id = id; - cx25840_initialize(client, 1); + if (state->is_cx25836) + cx25836_initialize(client); + else + cx25840_initialize(client, 1); i2c_attach_client(client); @@ -972,7 +1040,6 @@ static int cx25840_detach_client(struct i2c_client *client) } kfree(state); - kfree(client); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) MOD_DEC_USE_COUNT; @@ -1019,7 +1086,7 @@ module_exit(m__exit); /* ----------------------------------------------------------------------- */ -static void log_status(struct i2c_client *client) +static void log_video_status(struct i2c_client *client) { static const char *const fmt_strs[] = { "0x0", @@ -1031,9 +1098,36 @@ static void log_status(struct i2c_client *client) }; struct cx25840_state *state = i2c_get_clientdata(client); - u8 microctrl_vidfmt = cx25840_read(client, 0x80a); u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf; u8 gen_stat1 = cx25840_read(client, 0x40d); + u8 gen_stat2 = cx25840_read(client, 0x40e); + int vid_input = state->vid_input; + + v4l_info(client, "Video signal: %spresent\n", + (gen_stat2 & 0x20) ? "" : "not "); + v4l_info(client, "Detected format: %s\n", + fmt_strs[gen_stat1 & 0xf]); + + v4l_info(client, "Specified standard: %s\n", + vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection"); + + if (vid_input >= CX25840_COMPOSITE1 && + vid_input <= CX25840_COMPOSITE8) { + v4l_info(client, "Specified video input: Composite %d\n", + vid_input - CX25840_COMPOSITE1 + 1); + } else { + v4l_info(client, "Specified video input: S-Video (Luma In%d, Chroma In%d)\n", + (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8); + } + + v4l_info(client, "Specified audioclock freq: %d Hz\n", state->audclk_freq); +} + +/* ----------------------------------------------------------------------- */ + +static void log_audio_status(struct i2c_client *client) +{ + struct cx25840_state *state = i2c_get_clientdata(client); u8 download_ctl = cx25840_read(client, 0x803); u8 mod_det_stat0 = cx25840_read(client, 0x804); u8 mod_det_stat1 = cx25840_read(client, 0x805); @@ -1041,15 +1135,9 @@ static void log_status(struct i2c_client *client) u8 pref_mode = cx25840_read(client, 0x809); u8 afc0 = cx25840_read(client, 0x80b); u8 mute_ctl = cx25840_read(client, 0x8d3); - int vid_input = state->vid_input; int aud_input = state->aud_input; char *p; - v4l_info(client, "Video signal: %spresent\n", - (microctrl_vidfmt & 0x10) ? "" : "not "); - v4l_info(client, "Detected format: %s\n", - fmt_strs[gen_stat1 & 0xf]); - switch (mod_det_stat0) { case 0x00: p = "mono"; break; case 0x01: p = "stereo"; break; @@ -1149,25 +1237,12 @@ static void log_status(struct i2c_client *client) v4l_info(client, "Configured audio system: %s\n", p); } - v4l_info(client, "Specified standard: %s\n", - vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection"); - - if (vid_input >= CX25840_COMPOSITE1 && - vid_input <= CX25840_COMPOSITE8) { - v4l_info(client, "Specified video input: Composite %d\n", - vid_input - CX25840_COMPOSITE1 + 1); - } else { - v4l_info(client, "Specified video input: S-Video (Luma In%d, Chroma In%d)\n", - (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8); - } if (aud_input) { v4l_info(client, "Specified audio input: Tuner (In%d)\n", aud_input); } else { v4l_info(client, "Specified audio input: External\n"); } - v4l_info(client, "Specified audioclock freq: %d Hz\n", state->audclk_freq); - switch (pref_mode & 0xf) { case 0: p = "mono/language A"; break; case 1: p = "language B"; break; diff --git a/linux/drivers/media/video/cx25840/cx25840-core.h b/linux/drivers/media/video/cx25840/cx25840-core.h index b4bdc8e8e..fe3d1ece1 100644 --- a/linux/drivers/media/video/cx25840/cx25840-core.h +++ b/linux/drivers/media/video/cx25840/cx25840-core.h @@ -34,12 +34,15 @@ #define CX25840_CID_ENABLE_PVR150_WORKAROUND (V4L2_CID_PRIVATE_BASE+0) struct cx25840_state { + struct i2c_client c; int pvr150_workaround; int radio; enum cx25840_video_input vid_input; enum cx25840_audio_input aud_input; u32 audclk_freq; int audmode; + enum v4l2_chip_ident id; + int is_cx25836; }; /* ----------------------------------------------------------------------- */ @@ -48,7 +51,7 @@ int cx25840_write(struct i2c_client *client, u16 addr, u8 value); int cx25840_write4(struct i2c_client *client, u16 addr, u32 value); u8 cx25840_read(struct i2c_client *client, u16 addr); u32 cx25840_read4(struct i2c_client *client, u16 addr); -int cx25840_and_or(struct i2c_client *client, u16 addr, u8 mask, u8 value); +int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value); v4l2_std_id cx25840_get_v4lstd(struct i2c_client *client); /* ----------------------------------------------------------------------- */ -- cgit v1.2.3 From 43b81c829e515e8e949cab05782c68c9b2dd5c64 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 22 Apr 2006 15:15:07 -0400 Subject: fix frequency values in the ranges structures of the LG TDVS H06xF tuners From: Rusty Scott Frequency range values in the current driver for the LG TDVS H06xF tuners appear to have been a transposing of the 5 in the mid range 160-455 instead of 165-450. This patch corrects the pll programming for these tuners as per the datasheet. Signed-off-by: Rusty Scott Signed-off-by: Mac Michaels Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/frontends/dvb-pll.c | 4 ++-- linux/drivers/media/video/tuner-types.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.c b/linux/drivers/media/dvb/frontends/dvb-pll.c index b6e2c387a..791706ec1 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.c +++ b/linux/drivers/media/dvb/frontends/dvb-pll.c @@ -235,8 +235,8 @@ struct dvb_pll_desc dvb_pll_tdvs_tua6034 = { .max = 863000000, .count = 3, .entries = { - { 160000000, 44000000, 62500, 0xce, 0x01 }, - { 455000000, 44000000, 62500, 0xce, 0x02 }, + { 165000000, 44000000, 62500, 0xce, 0x01 }, + { 450000000, 44000000, 62500, 0xce, 0x02 }, { 999999999, 44000000, 62500, 0xce, 0x04 }, }, }; diff --git a/linux/drivers/media/video/tuner-types.c b/linux/drivers/media/video/tuner-types.c index 560879ce9..cf2e5756b 100644 --- a/linux/drivers/media/video/tuner-types.c +++ b/linux/drivers/media/video/tuner-types.c @@ -904,8 +904,8 @@ static struct tuner_params tuner_philips_fmd1216me_mk3_params[] = { /* ------------ TUNER_LG_TDVS_H062F - INFINEON ATSC ------------ */ static struct tuner_range tuner_tua6034_ntsc_ranges[] = { - { 16 * 160.00 /*MHz*/, 0x8e, 0x01 }, - { 16 * 455.00 /*MHz*/, 0x8e, 0x02 }, + { 16 * 165.00 /*MHz*/, 0x8e, 0x01 }, + { 16 * 450.00 /*MHz*/, 0x8e, 0x02 }, { 16 * 999.99 , 0x8e, 0x04 }, }; -- cgit v1.2.3 From a3443fe26243f9e8a2d5c4f74624990d9be848c5 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 22 Apr 2006 15:15:11 -0400 Subject: fix display name for LG TDVS-H06xF From: Michael Krufky This patch fixes the display name for LG TDVS-H06xF tuners in both tuner and dvb-pll modules. Changing the names of the actual pll_desc struct and tuner definitions has been held back until after the dvb tuner refactoring gets merged. Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/frontends/dvb-pll.c | 4 ++-- linux/drivers/media/video/tuner-types.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.c b/linux/drivers/media/dvb/frontends/dvb-pll.c index 791706ec1..50a79eea8 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.c +++ b/linux/drivers/media/dvb/frontends/dvb-pll.c @@ -227,10 +227,10 @@ struct dvb_pll_desc dvb_pll_tua6034 = { EXPORT_SYMBOL(dvb_pll_tua6034); /* Infineon TUA6034 - * used in LG TDVS H061F and LG TDVS H062F + * used in LG TDVS-H061F, LG TDVS-H062F and LG TDVS-H064F */ struct dvb_pll_desc dvb_pll_tdvs_tua6034 = { - .name = "LG/Infineon TUA6034", + .name = "LG TDVS-H06xF", .min = 54000000, .max = 863000000, .count = 3, diff --git a/linux/drivers/media/video/tuner-types.c b/linux/drivers/media/video/tuner-types.c index cf2e5756b..c00742f25 100644 --- a/linux/drivers/media/video/tuner-types.c +++ b/linux/drivers/media/video/tuner-types.c @@ -1404,7 +1404,7 @@ struct tunertype tuners[] = { .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_params), }, [TUNER_LG_TDVS_H062F] = { /* LGINNOTEK ATSC */ - .name = "LG TDVS-H062F/TUA6034", + .name = "LG TDVS-H06xF", /* H061F, H062F & H064F */ .params = tuner_tua6034_params, .count = ARRAY_SIZE(tuner_tua6034_params), }, -- cgit v1.2.3 From 2ba58137b1ebe360fd85cba358245d2136659d8b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 23 Apr 2006 10:54:56 +0200 Subject: Remove broken 'fast firmware load' from cx25840. From: Hans Verkuil The fast firmware load hack in cx25840 uses private data. In fact, it breaks pvrusb2 and doesn't work at all with ivtv. It is a unsafe implementation and so it is removed. Signed-off-by: Hans Verkuil --- .../drivers/media/video/cx25840/cx25840-firmware.c | 50 +--------------------- 1 file changed, 2 insertions(+), 48 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx25840/cx25840-firmware.c b/linux/drivers/media/video/cx25840/cx25840-firmware.c index 539de43dd..e14e0bc1f 100644 --- a/linux/drivers/media/video/cx25840/cx25840-firmware.c +++ b/linux/drivers/media/video/cx25840/cx25840-firmware.c @@ -44,34 +44,16 @@ #define FWDEV(x) (x)->name #endif -static int fastfw = 1; static char *firmware = FWFILE; #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) -module_param(fastfw, bool, 0444); module_param(firmware, charp, 0444); #else -MODULE_PARM(fastfw, "i"); MODULE_PARM(firmware, "s"); #endif -MODULE_PARM_DESC(fastfw, "Load firmware fast [0=100MHz 1=333MHz (default)]"); MODULE_PARM_DESC(firmware, "Firmware image [default: " FWFILE "]"); -static void set_i2c_delay(struct i2c_client *client, int delay) -{ - struct i2c_algo_bit_data *algod = client->adapter->algo_data; - - /* We aren't guaranteed to be using algo_bit, - * so avoid the null pointer dereference - * and disable the 'fast firmware load' */ - if (algod) { - algod->udelay = delay; - } else { - fastfw = 0; - } -} - static void start_fw_load(struct i2c_client *client) { /* DL_ADDR_LB=0 DL_ADDR_HB=0 */ @@ -81,16 +63,10 @@ static void start_fw_load(struct i2c_client *client) cx25840_write(client, 0x803, 0x0b); /* AUTO_INC_DIS=1 */ cx25840_write(client, 0x000, 0x20); - - if (fastfw) - set_i2c_delay(client, 3); } static void end_fw_load(struct i2c_client *client) { - if (fastfw) - set_i2c_delay(client, 10); - /* AUTO_INC_DIS=0 */ cx25840_write(client, 0x000, 0x00); /* DL_ENABLE=0 */ @@ -117,30 +93,8 @@ static int fw_write(struct i2c_client *client, u8 * data, int size) int sent; if ((sent = i2c_master_send(client, data, size)) < size) { - - if (fastfw) { - v4l_err(client, "333MHz i2c firmware load failed\n"); - fastfw = 0; - set_i2c_delay(client, 10); - - if (sent > 2) { - u16 dl_addr = cx25840_read(client, 0x801) << 8; - dl_addr |= cx25840_read(client, 0x800); - dl_addr -= sent - 2; - cx25840_write(client, 0x801, dl_addr >> 8); - cx25840_write(client, 0x800, dl_addr & 0xff); - } - - if (i2c_master_send(client, data, size) < size) { - v4l_err(client, "100MHz i2c firmware load failed\n"); - return -ENOSYS; - } - - } else { - v4l_err(client, "firmware load i2c failure\n"); - return -ENOSYS; - } - + v4l_err(client, "firmware load i2c failure\n"); + return -ENOSYS; } return 0; -- cgit v1.2.3 From 6f290f63b3a7cd010a4bedcd36932b18617b34bb Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Apr 2006 10:29:46 -0300 Subject: [PATCH] update pwc driver From: Luc Saillard Add v4l2 compatibility Include the decompressor (legal problem has been resolv by Alan Cox) Faster decoder and easier to maintain, optimize, ... Can export to userland compressed stream Support more cameras, lot of bugs are fixed. Signed-off-by: Luc Saillard Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/pwc/Kconfig | 13 +- linux/drivers/media/video/pwc/Makefile | 11 +- linux/drivers/media/video/pwc/pwc-ctrl.c | 718 +++++++------ linux/drivers/media/video/pwc/pwc-if.c | 1374 +++++++++++------------- linux/drivers/media/video/pwc/pwc-kiara.c | 575 +++++++++- linux/drivers/media/video/pwc/pwc-kiara.h | 8 +- linux/drivers/media/video/pwc/pwc-misc.c | 67 +- linux/drivers/media/video/pwc/pwc-timon.c | 1132 ++++++++++++++++++- linux/drivers/media/video/pwc/pwc-timon.h | 8 +- linux/drivers/media/video/pwc/pwc-uncompress.c | 154 ++- linux/drivers/media/video/pwc/pwc-uncompress.h | 4 +- linux/drivers/media/video/pwc/pwc.h | 177 ++- 12 files changed, 2983 insertions(+), 1258 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pwc/Kconfig b/linux/drivers/media/video/pwc/Kconfig index 53cbc950f..697145e0b 100644 --- a/linux/drivers/media/video/pwc/Kconfig +++ b/linux/drivers/media/video/pwc/Kconfig @@ -7,6 +7,7 @@ config USB_PWC * Philips PCA645, PCA646 * Philips PCVC675, PCVC680, PCVC690 * Philips PCVC720/40, PCVC730, PCVC740, PCVC750 + * Philips SPC900NC * Askey VC010 * Logitech QuickCam Pro 3000, 4000, 'Zoom', 'Notebook Pro' and 'Orbit'/'Sphere' @@ -19,10 +20,18 @@ config USB_PWC and never will be, but the 665 and 720/20 are supported by other drivers. - See for more information and - installation instructions. + Some newer logitech webcams are not handled by this driver but by the + Usb Video Class driver (linux-uvc). The built-in microphone is enabled by selecting USB Audio support. To compile this driver as a module, choose M here: the module will be called pwc. + +config USB_PWC_DEBUG + bool "USB Philips Cameras verbose debug" + depends USB_PWC + help + Say Y here in order to have the pwc driver generate verbose debugging + messages. + A special module options 'trace' is used to control the verbosity. diff --git a/linux/drivers/media/video/pwc/Makefile b/linux/drivers/media/video/pwc/Makefile index 33d60126c..9db2260d1 100644 --- a/linux/drivers/media/video/pwc/Makefile +++ b/linux/drivers/media/video/pwc/Makefile @@ -1,3 +1,12 @@ -pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o pwc-timon.o pwc-kiara.o +pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-v4l.o pwc-uncompress.o +pwc-objs += pwc-dec1.o pwc-dec23.o pwc-kiara.o pwc-timon.o obj-$(CONFIG_USB_PWC) += pwc.o + +ifeq ($(CONFIG_USB_PWC_DEBUG),y) +EXTRA_CFLAGS += -DCONFIG_PWC_DEBUG=1 +else +EXTRA_CFLAGS += -DCONFIG_PWC_DEBUG=0 +endif + + diff --git a/linux/drivers/media/video/pwc/pwc-ctrl.c b/linux/drivers/media/video/pwc/pwc-ctrl.c index 4ba549bfa..c64f498f1 100644 --- a/linux/drivers/media/video/pwc/pwc-ctrl.c +++ b/linux/drivers/media/video/pwc/pwc-ctrl.c @@ -2,7 +2,7 @@ Functions that send various control messages to the webcam, including video modes. (C) 1999-2003 Nemosoft Unv. - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -41,12 +41,14 @@ #include #endif #include +#include #include "pwc.h" -#include "pwc-ioctl.h" #include "pwc-uncompress.h" #include "pwc-kiara.h" #include "pwc-timon.h" +#include "pwc-dec1.h" +#include "pwc-dec23.h" /* Request types: video */ #define SET_LUM_CTL 0x01 @@ -57,6 +59,10 @@ #define GET_STATUS_CTL 0x06 #define SET_EP_STREAM_CTL 0x07 #define GET_EP_STREAM_CTL 0x08 +#define GET_XX_CTL 0x09 +#define SET_XX_CTL 0x0A +#define GET_XY_CTL 0x0B +#define SET_XY_CTL 0x0C #define SET_MPT_CTL 0x0D #define GET_MPT_CTL 0x0E @@ -93,12 +99,20 @@ #define READ_SHUTTER_FORMATTER 0x0600 #define READ_RED_GAIN_FORMATTER 0x0700 #define READ_BLUE_GAIN_FORMATTER 0x0800 +#define GET_STATUS_B00 0x0B00 #define SENSOR_TYPE_FORMATTER1 0x0C00 +#define GET_STATUS_3000 0x3000 #define READ_RAW_Y_MEAN_FORMATTER 0x3100 #define SET_POWER_SAVE_MODE_FORMATTER 0x3200 #define MIRROR_IMAGE_FORMATTER 0x3300 #define LED_FORMATTER 0x3400 +#define LOWLIGHT 0x3500 +#define GET_STATUS_3600 0x3600 #define SENSOR_TYPE_FORMATTER2 0x3700 +#define GET_STATUS_3800 0x3800 +#define GET_STATUS_4000 0x4000 +#define GET_STATUS_4100 0x4100 /* Get */ +#define CTL_STATUS_4200 0x4200 /* [GS] 1 */ /* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */ #define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100 @@ -138,6 +152,7 @@ static struct Nala_table_entry Nala_table[PSZ_MAX][8] = #include "pwc-nala.h" }; +static void pwc_set_image_buffer_size(struct pwc_device *pdev); /****************************************************************************/ @@ -159,31 +174,7 @@ static struct Nala_table_entry Nala_table[PSZ_MAX][8] = &buf, buflen, 500) -#if PWC_DEBUG -void pwc_hexdump(void *p, int len) -{ - int i; - unsigned char *s; - char buf[100], *d; - - s = (unsigned char *)p; - d = buf; - *d = '\0'; - Debug("Doing hexdump @ %p, %d bytes.\n", p, len); - for (i = 0; i < len; i++) { - d += sprintf(d, "%02X ", *s++); - if ((i & 0xF) == 0xF) { - Debug("%s\n", buf); - d = buf; - *d = '\0'; - } - } - if ((i & 0xF) != 0) - Debug("%s\n", buf); -} -#endif - -static inline int send_video_command(struct usb_device *udev, int index, void *buf, int buflen) +static int send_video_command(struct usb_device *udev, int index, void *buf, int buflen) { return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), @@ -196,7 +187,7 @@ static inline int send_video_command(struct usb_device *udev, int index, void *b -static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames) +static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames) { unsigned char buf[3]; int ret, fps; @@ -229,34 +220,14 @@ static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int fra if (pEntry->alternate == 0) return -EINVAL; - if (pEntry->compressed) - return -ENOENT; /* Not supported. */ - memcpy(buf, pEntry->mode, 3); ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 3); if (ret < 0) { - Debug("Failed to send video command... %d\n", ret); + PWC_DEBUG_MODULE("Failed to send video command... %d\n", ret); return ret; } if (pEntry->compressed && pdev->vpalette != VIDEO_PALETTE_RAW) - { - switch(pdev->type) { - case 645: - case 646: -/* pwc_dec1_init(pdev->type, pdev->release, buf, pdev->decompress_data); */ - break; - - case 675: - case 680: - case 690: - case 720: - case 730: - case 740: - case 750: -/* pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); */ - break; - } - } + pwc_dec1_init(pdev->type, pdev->release, buf, pdev->decompress_data); pdev->cmd_len = 3; memcpy(pdev->cmd_buf, buf, 3); @@ -283,7 +254,7 @@ static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int fra } -static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) +static int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) { unsigned char buf[13]; const struct Timon_table_entry *pChoose; @@ -315,8 +286,8 @@ static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int fr if (ret < 0) return ret; -/* if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) - pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); */ + if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) + pwc_dec23_init(pdev, pdev->type, buf); pdev->cmd_len = 13; memcpy(pdev->cmd_buf, buf, 13); @@ -336,7 +307,7 @@ static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int fr } -static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) +static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) { const struct Kiara_table_entry *pChoose = NULL; int fps, ret; @@ -350,21 +321,14 @@ static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int fr fps = (frames / 5) - 1; /* special case: VGA @ 5 fps and snapshot is raw bayer mode */ - if (size == PSZ_VGA && frames == 5 && snapshot) + if (size == PSZ_VGA && frames == 5 && snapshot && pdev->vpalette == VIDEO_PALETTE_RAW) { /* Only available in case the raw palette is selected or we have the decompressor available. This mode is only available in compressed form */ - if (pdev->vpalette == VIDEO_PALETTE_RAW) - { - Info("Choosing VGA/5 BAYER mode (%d).\n", pdev->vpalette); - pChoose = &RawEntry; - } - else - { - Info("VGA/5 BAYER mode _must_ have a decompressor available, or use RAW palette.\n"); - } + PWC_DEBUG_SIZE("Choosing VGA/5 BAYER mode.\n"); + pChoose = &RawEntry; } else { @@ -372,6 +336,7 @@ static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int fr if the preferred ratio is not available. Skip this step when using RAW modes. */ + snapshot = 0; while (compression <= 3) { pChoose = &Kiara_table[size][fps][compression]; if (pChoose->alternate != 0) @@ -382,7 +347,7 @@ static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int fr if (pChoose == NULL || pChoose->alternate == 0) return -ENOENT; /* Not supported. */ - Debug("Using alternate setting %d.\n", pChoose->alternate); + PWC_TRACE("Using alternate setting %d.\n", pChoose->alternate); /* usb_control_msg won't take staticly allocated arrays as argument?? */ memcpy(buf, pChoose->mode, 12); @@ -394,8 +359,8 @@ static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int fr if (ret < 0) return ret; -/* if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) - pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); */ + if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) + pwc_dec23_init(pdev, pdev->type, buf); pdev->cmd_len = 12; memcpy(pdev->cmd_buf, buf, 12); @@ -410,49 +375,13 @@ static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int fr pdev->frame_size = (pdev->vbandlength * pdev->image.y) / 4; else pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; + PWC_TRACE("frame_size=%d, vframes=%d, vsize=%d, vsnapshot=%d, vbandlength=%d\n", + pdev->frame_size,pdev->vframes,pdev->vsize,pdev->vsnapshot,pdev->vbandlength); return 0; } -static void pwc_set_image_buffer_size(struct pwc_device *pdev) -{ - int i, factor = 0, filler = 0; - - /* for PALETTE_YUV420P */ - switch(pdev->vpalette) - { - case VIDEO_PALETTE_YUV420P: - factor = 6; - filler = 128; - break; - case VIDEO_PALETTE_RAW: - factor = 6; /* can be uncompressed YUV420P */ - filler = 0; - break; - } - - /* Set sizes in bytes */ - pdev->image.size = pdev->image.x * pdev->image.y * factor / 4; - pdev->view.size = pdev->view.x * pdev->view.y * factor / 4; - - /* Align offset, or you'll get some very weird results in - YUV420 mode... x must be multiple of 4 (to get the Y's in - place), and y even (or you'll mixup U & V). This is less of a - problem for YUV420P. - */ - pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC; - pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE; - - /* Fill buffers with gray or black */ - for (i = 0; i < MAX_IMAGES; i++) { - if (pdev->image_ptr[i] != NULL) - memset(pdev->image_ptr[i], filler, pdev->view.size); - } -} - - - /** @pdev: device structure @width: viewport width @@ -465,50 +394,78 @@ int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frame { int ret, size; - Trace(TRACE_FLOW, "set_video_mode(%dx%d @ %d, palette %d).\n", width, height, frames, pdev->vpalette); + PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, palette %d).\n", width, height, frames, pdev->vpalette); size = pwc_decode_size(pdev, width, height); if (size < 0) { - Debug("Could not find suitable size.\n"); + PWC_DEBUG_MODULE("Could not find suitable size.\n"); return -ERANGE; } - Debug("decode_size = %d.\n", size); + PWC_TRACE("decode_size = %d.\n", size); - ret = -EINVAL; - switch(pdev->type) { - case 645: - case 646: + if (DEVICE_USE_CODEC1(pdev->type)) { ret = set_video_mode_Nala(pdev, size, frames); - break; - case 675: - case 680: - case 690: - ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot); - break; - - case 720: - case 730: - case 740: - case 750: + } else if (DEVICE_USE_CODEC3(pdev->type)) { ret = set_video_mode_Kiara(pdev, size, frames, compression, snapshot); - break; + + } else { + ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot); } if (ret < 0) { - if (ret == -ENOENT) - Info("Video mode %s@%d fps is only supported with the decompressor module (pwcx).\n", size2name[size], frames); - else { - Err("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); - } + PWC_ERROR("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); return ret; } pdev->view.x = width; pdev->view.y = height; pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; pwc_set_image_buffer_size(pdev); - Trace(TRACE_SIZE, "Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y); + PWC_DEBUG_SIZE("Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y); return 0; } +#define BLACK_Y 0 +#define BLACK_U 128 +#define BLACK_V 128 + +static void pwc_set_image_buffer_size(struct pwc_device *pdev) +{ + int i, factor = 0; + + /* for PALETTE_YUV420P */ + switch(pdev->vpalette) + { + case VIDEO_PALETTE_YUV420P: + factor = 6; + break; + case VIDEO_PALETTE_RAW: + factor = 6; /* can be uncompressed YUV420P */ + break; + } + + /* Set sizes in bytes */ + pdev->image.size = pdev->image.x * pdev->image.y * factor / 4; + pdev->view.size = pdev->view.x * pdev->view.y * factor / 4; + + /* Align offset, or you'll get some very weird results in + YUV420 mode... x must be multiple of 4 (to get the Y's in + place), and y even (or you'll mixup U & V). This is less of a + problem for YUV420P. + */ + pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC; + pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE; + + /* Fill buffers with black colors */ + for (i = 0; i < pwc_mbufs; i++) { + unsigned char *p = pdev->image_data + pdev->images[i].offset; + memset(p, BLACK_Y, pdev->view.x * pdev->view.y); + p += pdev->view.x * pdev->view.y; + memset(p, BLACK_U, pdev->view.x * pdev->view.y/4); + p += pdev->view.x * pdev->view.y/4; + memset(p, BLACK_V, pdev->view.x * pdev->view.y/4); + } +} + + /* BRIGHTNESS */ @@ -520,7 +477,7 @@ int pwc_get_brightness(struct pwc_device *pdev) ret = RecvControlMsg(GET_LUM_CTL, BRIGHTNESS_FORMATTER, 1); if (ret < 0) return ret; - return buf << 9; + return buf; } int pwc_set_brightness(struct pwc_device *pdev, int value) @@ -545,7 +502,7 @@ int pwc_get_contrast(struct pwc_device *pdev) ret = RecvControlMsg(GET_LUM_CTL, CONTRAST_FORMATTER, 1); if (ret < 0) return ret; - return buf << 10; + return buf; } int pwc_set_contrast(struct pwc_device *pdev, int value) @@ -570,7 +527,7 @@ int pwc_get_gamma(struct pwc_device *pdev) ret = RecvControlMsg(GET_LUM_CTL, GAMMA_FORMATTER, 1); if (ret < 0) return ret; - return buf << 11; + return buf; } int pwc_set_gamma(struct pwc_device *pdev, int value) @@ -588,37 +545,47 @@ int pwc_set_gamma(struct pwc_device *pdev, int value) /* SATURATION */ -int pwc_get_saturation(struct pwc_device *pdev) +/* return a value between [-100 , 100] */ +int pwc_get_saturation(struct pwc_device *pdev, int *value) { char buf; - int ret; + int ret, saturation_register; if (pdev->type < 675) - return -1; - ret = RecvControlMsg(GET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1); + return -EINVAL; + if (pdev->type < 730) + saturation_register = SATURATION_MODE_FORMATTER2; + else + saturation_register = SATURATION_MODE_FORMATTER1; + ret = RecvControlMsg(GET_CHROM_CTL, saturation_register, 1); if (ret < 0) return ret; - return 32768 + buf * 327; + *value = (signed)buf; + return 0; } +/* @param value saturation color between [-100 , 100] */ int pwc_set_saturation(struct pwc_device *pdev, int value) { char buf; + int saturation_register; if (pdev->type < 675) return -EINVAL; - if (value < 0) - value = 0; - if (value > 0xffff) - value = 0xffff; - /* saturation ranges from -100 to +100 */ - buf = (value - 32768) / 327; - return SendControlMsg(SET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1); + if (value < -100) + value = -100; + if (value > 100) + value = 100; + if (pdev->type < 730) + saturation_register = SATURATION_MODE_FORMATTER2; + else + saturation_register = SATURATION_MODE_FORMATTER1; + return SendControlMsg(SET_CHROM_CTL, saturation_register, 1); } /* AGC */ -static inline int pwc_set_agc(struct pwc_device *pdev, int mode, int value) +int pwc_set_agc(struct pwc_device *pdev, int mode, int value) { char buf; int ret; @@ -643,7 +610,7 @@ static inline int pwc_set_agc(struct pwc_device *pdev, int mode, int value) return 0; } -static inline int pwc_get_agc(struct pwc_device *pdev, int *value) +int pwc_get_agc(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; @@ -673,7 +640,7 @@ static inline int pwc_get_agc(struct pwc_device *pdev, int *value) return 0; } -static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value) +int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value) { char buf[2]; int speed, ret; @@ -691,23 +658,16 @@ static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int v value = 0; if (value > 0xffff) value = 0xffff; - switch(pdev->type) { - case 675: - case 680: - case 690: + + if (DEVICE_USE_CODEC2(pdev->type)) { /* speed ranges from 0x0 to 0x290 (656) */ speed = (value / 100); buf[1] = speed >> 8; buf[0] = speed & 0xff; - break; - case 720: - case 730: - case 740: - case 750: + } else if (DEVICE_USE_CODEC3(pdev->type)) { /* speed seems to range from 0x0 to 0xff */ buf[1] = 0; buf[0] = value >> 8; - break; } ret = SendControlMsg(SET_LUM_CTL, PRESET_SHUTTER_FORMATTER, 2); @@ -715,6 +675,25 @@ static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int v return ret; } +/* This function is not exported to v4l1, so output values between 0 -> 256 */ +int pwc_get_shutter_speed(struct pwc_device *pdev, int *value) +{ + unsigned char buf[2]; + int ret; + + ret = RecvControlMsg(GET_STATUS_CTL, READ_SHUTTER_FORMATTER, 2); + if (ret < 0) + return ret; + *value = buf[0] + (buf[1] << 8); + if (DEVICE_USE_CODEC2(pdev->type)) { + /* speed ranges from 0x0 to 0x290 (656) */ + *value *= 256/656; + } else if (DEVICE_USE_CODEC3(pdev->type)) { + /* speed seems to range from 0x0 to 0xff */ + } + return 0; +} + /* POWER */ @@ -736,19 +715,19 @@ int pwc_camera_power(struct pwc_device *pdev, int power) /* private calls */ -static inline int pwc_restore_user(struct pwc_device *pdev) +int pwc_restore_user(struct pwc_device *pdev) { char buf; /* dummy */ return SendControlMsg(SET_STATUS_CTL, RESTORE_USER_DEFAULTS_FORMATTER, 0); } -static inline int pwc_save_user(struct pwc_device *pdev) +int pwc_save_user(struct pwc_device *pdev) { char buf; /* dummy */ return SendControlMsg(SET_STATUS_CTL, SAVE_USER_DEFAULTS_FORMATTER, 0); } -static inline int pwc_restore_factory(struct pwc_device *pdev) +int pwc_restore_factory(struct pwc_device *pdev) { char buf; /* dummy */ return SendControlMsg(SET_STATUS_CTL, RESTORE_FACTORY_DEFAULTS_FORMATTER, 0); @@ -766,7 +745,7 @@ static inline int pwc_restore_factory(struct pwc_device *pdev) * 03: manual * 04: auto */ -static inline int pwc_set_awb(struct pwc_device *pdev, int mode) +int pwc_set_awb(struct pwc_device *pdev, int mode) { char buf; int ret; @@ -786,7 +765,7 @@ static inline int pwc_set_awb(struct pwc_device *pdev, int mode) return 0; } -static inline int pwc_get_awb(struct pwc_device *pdev) +int pwc_get_awb(struct pwc_device *pdev) { unsigned char buf; int ret; @@ -798,7 +777,7 @@ static inline int pwc_get_awb(struct pwc_device *pdev) return buf; } -static inline int pwc_set_red_gain(struct pwc_device *pdev, int value) +int pwc_set_red_gain(struct pwc_device *pdev, int value) { unsigned char buf; @@ -811,7 +790,7 @@ static inline int pwc_set_red_gain(struct pwc_device *pdev, int value) return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1); } -static inline int pwc_get_red_gain(struct pwc_device *pdev, int *value) +int pwc_get_red_gain(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; @@ -824,7 +803,7 @@ static inline int pwc_get_red_gain(struct pwc_device *pdev, int *value) } -static inline int pwc_set_blue_gain(struct pwc_device *pdev, int value) +int pwc_set_blue_gain(struct pwc_device *pdev, int value) { unsigned char buf; @@ -837,7 +816,7 @@ static inline int pwc_set_blue_gain(struct pwc_device *pdev, int value) return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1); } -static inline int pwc_get_blue_gain(struct pwc_device *pdev, int *value) +int pwc_get_blue_gain(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; @@ -854,7 +833,7 @@ static inline int pwc_get_blue_gain(struct pwc_device *pdev, int *value) internal red/blue gains, which may be different from the manual gains set or read above. */ -static inline int pwc_read_red_gain(struct pwc_device *pdev, int *value) +static int pwc_read_red_gain(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; @@ -866,7 +845,7 @@ static inline int pwc_read_red_gain(struct pwc_device *pdev, int *value) return 0; } -static inline int pwc_read_blue_gain(struct pwc_device *pdev, int *value) +static int pwc_read_blue_gain(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; @@ -879,7 +858,7 @@ static inline int pwc_read_blue_gain(struct pwc_device *pdev, int *value) } -static inline int pwc_set_wb_speed(struct pwc_device *pdev, int speed) +static int pwc_set_wb_speed(struct pwc_device *pdev, int speed) { unsigned char buf; @@ -888,7 +867,7 @@ static inline int pwc_set_wb_speed(struct pwc_device *pdev, int speed) return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1); } -static inline int pwc_get_wb_speed(struct pwc_device *pdev, int *value) +static int pwc_get_wb_speed(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; @@ -901,7 +880,7 @@ static inline int pwc_get_wb_speed(struct pwc_device *pdev, int *value) } -static inline int pwc_set_wb_delay(struct pwc_device *pdev, int delay) +static int pwc_set_wb_delay(struct pwc_device *pdev, int delay) { unsigned char buf; @@ -910,7 +889,7 @@ static inline int pwc_set_wb_delay(struct pwc_device *pdev, int delay) return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1); } -static inline int pwc_get_wb_delay(struct pwc_device *pdev, int *value) +static int pwc_get_wb_delay(struct pwc_device *pdev, int *value) { unsigned char buf; int ret; @@ -946,7 +925,7 @@ int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) return SendControlMsg(SET_STATUS_CTL, LED_FORMATTER, 2); } -static int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) +int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) { unsigned char buf[2]; int ret; @@ -965,7 +944,7 @@ static int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) return 0; } -static inline int pwc_set_contour(struct pwc_device *pdev, int contour) +int pwc_set_contour(struct pwc_device *pdev, int contour) { unsigned char buf; int ret; @@ -990,7 +969,7 @@ static inline int pwc_set_contour(struct pwc_device *pdev, int contour) return 0; } -static inline int pwc_get_contour(struct pwc_device *pdev, int *contour) +int pwc_get_contour(struct pwc_device *pdev, int *contour) { unsigned char buf; int ret; @@ -1012,7 +991,7 @@ static inline int pwc_get_contour(struct pwc_device *pdev, int *contour) } -static inline int pwc_set_backlight(struct pwc_device *pdev, int backlight) +int pwc_set_backlight(struct pwc_device *pdev, int backlight) { unsigned char buf; @@ -1023,7 +1002,7 @@ static inline int pwc_set_backlight(struct pwc_device *pdev, int backlight) return SendControlMsg(SET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); } -static inline int pwc_get_backlight(struct pwc_device *pdev, int *backlight) +int pwc_get_backlight(struct pwc_device *pdev, int *backlight) { int ret; unsigned char buf; @@ -1031,12 +1010,35 @@ static inline int pwc_get_backlight(struct pwc_device *pdev, int *backlight) ret = RecvControlMsg(GET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); if (ret < 0) return ret; - *backlight = buf; + *backlight = !!buf; + return 0; +} + +int pwc_set_colour_mode(struct pwc_device *pdev, int colour) +{ + unsigned char buf; + + if (colour) + buf = 0xff; + else + buf = 0x0; + return SendControlMsg(SET_CHROM_CTL, COLOUR_MODE_FORMATTER, 1); +} + +int pwc_get_colour_mode(struct pwc_device *pdev, int *colour) +{ + int ret; + unsigned char buf; + + ret = RecvControlMsg(GET_CHROM_CTL, COLOUR_MODE_FORMATTER, 1); + if (ret < 0) + return ret; + *colour = !!buf; return 0; } -static inline int pwc_set_flicker(struct pwc_device *pdev, int flicker) +int pwc_set_flicker(struct pwc_device *pdev, int flicker) { unsigned char buf; @@ -1047,7 +1049,7 @@ static inline int pwc_set_flicker(struct pwc_device *pdev, int flicker) return SendControlMsg(SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); } -static inline int pwc_get_flicker(struct pwc_device *pdev, int *flicker) +int pwc_get_flicker(struct pwc_device *pdev, int *flicker) { int ret; unsigned char buf; @@ -1055,12 +1057,11 @@ static inline int pwc_get_flicker(struct pwc_device *pdev, int *flicker) ret = RecvControlMsg(GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); if (ret < 0) return ret; - *flicker = buf; + *flicker = !!buf; return 0; } - -static inline int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise) +int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise) { unsigned char buf; @@ -1072,7 +1073,7 @@ static inline int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise) return SendControlMsg(SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); } -static inline int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise) +int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise) { int ret; unsigned char buf; @@ -1084,7 +1085,7 @@ static inline int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise) return 0; } -static int pwc_mpt_reset(struct pwc_device *pdev, int flags) +static int _pwc_mpt_reset(struct pwc_device *pdev, int flags) { unsigned char buf; @@ -1092,7 +1093,18 @@ static int pwc_mpt_reset(struct pwc_device *pdev, int flags) return SendControlMsg(SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, 1); } -static inline int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) +int pwc_mpt_reset(struct pwc_device *pdev, int flags) +{ + int ret; + ret = _pwc_mpt_reset(pdev, flags); + if (ret >= 0) { + pdev->pan_angle = 0; + pdev->tilt_angle = 0; + } + return ret; +} + +static int _pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) { unsigned char buf[4]; @@ -1110,7 +1122,35 @@ static inline int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) return SendControlMsg(SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, 4); } -static inline int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status) +int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) +{ + int ret; + + /* check absolute ranges */ + if (pan < pdev->angle_range.pan_min || + pan > pdev->angle_range.pan_max || + tilt < pdev->angle_range.tilt_min || + tilt > pdev->angle_range.tilt_max) + return -ERANGE; + + /* go to relative range, check again */ + pan -= pdev->pan_angle; + tilt -= pdev->tilt_angle; + /* angles are specified in degrees * 100, thus the limit = 36000 */ + if (pan < -36000 || pan > 36000 || tilt < -36000 || tilt > 36000) + return -ERANGE; + + ret = _pwc_mpt_set_angle(pdev, pan, tilt); + if (ret >= 0) { + pdev->pan_angle += pan; + pdev->tilt_angle += tilt; + } + if (ret == -EPIPE) /* stall -> out of range */ + ret = -ERANGE; + return ret; +} + +static int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status) { int ret; unsigned char buf[5]; @@ -1151,6 +1191,45 @@ int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) /* End of Add-Ons */ /* ************************************************* */ +/* Linux 2.5.something and 2.6 pass direct pointers to arguments of + ioctl() calls. With 2.4, you have to do tedious copy_from_user() + and copy_to_user() calls. With these macros we circumvent this, + and let me maintain only one source file. The functionality is + exactly the same otherwise. + */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + +/* define local variable for arg */ +#define ARG_DEF(ARG_type, ARG_name)\ + ARG_type *ARG_name = arg; +/* copy arg to local variable */ +#define ARG_IN(ARG_name) /* nothing */ +/* argument itself (referenced) */ +#define ARGR(ARG_name) (*ARG_name) +/* argument address */ +#define ARGA(ARG_name) ARG_name +/* copy local variable to arg */ +#define ARG_OUT(ARG_name) /* nothing */ + +#else + +#define ARG_DEF(ARG_type, ARG_name)\ + ARG_type ARG_name; +#define ARG_IN(ARG_name)\ + if (copy_from_user(&ARG_name, arg, sizeof(ARG_name))) {\ + ret = -EFAULT;\ + break;\ + } +#define ARGR(ARG_name) ARG_name +#define ARGA(ARG_name) &ARG_name +#define ARG_OUT(ARG_name)\ + if (copy_to_user(arg, &ARG_name, sizeof(ARG_name))) {\ + ret = -EFAULT;\ + break;\ + } + +#endif int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) { @@ -1180,206 +1259,243 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) case VIDIOCPWCSCQUAL: { - int *qual = arg; + ARG_DEF(int, qual) - if (*qual < 0 || *qual > 3) + ARG_IN(qual) + if (ARGR(qual) < 0 || ARGR(qual) > 3) ret = -EINVAL; else - ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, *qual, pdev->vsnapshot); + ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, ARGR(qual), pdev->vsnapshot); if (ret >= 0) - pdev->vcompression = *qual; + pdev->vcompression = ARGR(qual); break; } case VIDIOCPWCGCQUAL: { - int *qual = arg; - *qual = pdev->vcompression; + ARG_DEF(int, qual) + + ARGR(qual) = pdev->vcompression; + ARG_OUT(qual) break; } case VIDIOCPWCPROBE: { - struct pwc_probe *probe = arg; - strcpy(probe->name, pdev->vdev->name); - probe->type = pdev->type; + ARG_DEF(struct pwc_probe, probe) + + strcpy(ARGR(probe).name, pdev->vdev->name); + ARGR(probe).type = pdev->type; + ARG_OUT(probe) break; } case VIDIOCPWCGSERIAL: { - struct pwc_serial *serial = arg; - strcpy(serial->serial, pdev->serial); + ARG_DEF(struct pwc_serial, serial) + + strcpy(ARGR(serial).serial, pdev->serial); + ARG_OUT(serial) break; } case VIDIOCPWCSAGC: { - int *agc = arg; - if (pwc_set_agc(pdev, *agc < 0 ? 1 : 0, *agc)) + ARG_DEF(int, agc) + + ARG_IN(agc) + if (pwc_set_agc(pdev, ARGR(agc) < 0 ? 1 : 0, ARGR(agc))) ret = -EINVAL; break; } case VIDIOCPWCGAGC: { - int *agc = arg; + ARG_DEF(int, agc) - if (pwc_get_agc(pdev, agc)) + if (pwc_get_agc(pdev, ARGA(agc))) ret = -EINVAL; + ARG_OUT(agc) break; } case VIDIOCPWCSSHUTTER: { - int *shutter_speed = arg; - ret = pwc_set_shutter_speed(pdev, *shutter_speed < 0 ? 1 : 0, *shutter_speed); + ARG_DEF(int, shutter_speed) + + ARG_IN(shutter_speed) + ret = pwc_set_shutter_speed(pdev, ARGR(shutter_speed) < 0 ? 1 : 0, ARGR(shutter_speed)); break; } case VIDIOCPWCSAWB: { - struct pwc_whitebalance *wb = arg; + ARG_DEF(struct pwc_whitebalance, wb) - ret = pwc_set_awb(pdev, wb->mode); - if (ret >= 0 && wb->mode == PWC_WB_MANUAL) { - pwc_set_red_gain(pdev, wb->manual_red); - pwc_set_blue_gain(pdev, wb->manual_blue); + ARG_IN(wb) + ret = pwc_set_awb(pdev, ARGR(wb).mode); + if (ret >= 0 && ARGR(wb).mode == PWC_WB_MANUAL) { + pwc_set_red_gain(pdev, ARGR(wb).manual_red); + pwc_set_blue_gain(pdev, ARGR(wb).manual_blue); } break; } case VIDIOCPWCGAWB: { - struct pwc_whitebalance *wb = arg; + ARG_DEF(struct pwc_whitebalance, wb) - memset(wb, 0, sizeof(struct pwc_whitebalance)); - wb->mode = pwc_get_awb(pdev); - if (wb->mode < 0) + memset(ARGA(wb), 0, sizeof(struct pwc_whitebalance)); + ARGR(wb).mode = pwc_get_awb(pdev); + if (ARGR(wb).mode < 0) ret = -EINVAL; else { - if (wb->mode == PWC_WB_MANUAL) { - ret = pwc_get_red_gain(pdev, &wb->manual_red); + if (ARGR(wb).mode == PWC_WB_MANUAL) { + ret = pwc_get_red_gain(pdev, &ARGR(wb).manual_red); if (ret < 0) break; - ret = pwc_get_blue_gain(pdev, &wb->manual_blue); + ret = pwc_get_blue_gain(pdev, &ARGR(wb).manual_blue); if (ret < 0) break; } - if (wb->mode == PWC_WB_AUTO) { - ret = pwc_read_red_gain(pdev, &wb->read_red); + if (ARGR(wb).mode == PWC_WB_AUTO) { + ret = pwc_read_red_gain(pdev, &ARGR(wb).read_red); if (ret < 0) break; - ret = pwc_read_blue_gain(pdev, &wb->read_blue); + ret =pwc_read_blue_gain(pdev, &ARGR(wb).read_blue); if (ret < 0) break; } } + ARG_OUT(wb) break; } case VIDIOCPWCSAWBSPEED: { - struct pwc_wb_speed *wbs = arg; + ARG_DEF(struct pwc_wb_speed, wbs) - if (wbs->control_speed > 0) { - ret = pwc_set_wb_speed(pdev, wbs->control_speed); + if (ARGR(wbs).control_speed > 0) { + ret = pwc_set_wb_speed(pdev, ARGR(wbs).control_speed); } - if (wbs->control_delay > 0) { - ret = pwc_set_wb_delay(pdev, wbs->control_delay); + if (ARGR(wbs).control_delay > 0) { + ret = pwc_set_wb_delay(pdev, ARGR(wbs).control_delay); } break; } case VIDIOCPWCGAWBSPEED: { - struct pwc_wb_speed *wbs = arg; + ARG_DEF(struct pwc_wb_speed, wbs) - ret = pwc_get_wb_speed(pdev, &wbs->control_speed); + ret = pwc_get_wb_speed(pdev, &ARGR(wbs).control_speed); if (ret < 0) break; - ret = pwc_get_wb_delay(pdev, &wbs->control_delay); + ret = pwc_get_wb_delay(pdev, &ARGR(wbs).control_delay); if (ret < 0) break; + ARG_OUT(wbs) break; } case VIDIOCPWCSLED: { - struct pwc_leds *leds = arg; - ret = pwc_set_leds(pdev, leds->led_on, leds->led_off); + ARG_DEF(struct pwc_leds, leds) + + ARG_IN(leds) + ret = pwc_set_leds(pdev, ARGR(leds).led_on, ARGR(leds).led_off); break; } case VIDIOCPWCGLED: { - struct pwc_leds *leds = arg; - ret = pwc_get_leds(pdev, &leds->led_on, &leds->led_off); + ARG_DEF(struct pwc_leds, leds) + + ret = pwc_get_leds(pdev, &ARGR(leds).led_on, &ARGR(leds).led_off); + ARG_OUT(leds) break; } case VIDIOCPWCSCONTOUR: { - int *contour = arg; - ret = pwc_set_contour(pdev, *contour); + ARG_DEF(int, contour) + + ARG_IN(contour) + ret = pwc_set_contour(pdev, ARGR(contour)); break; } case VIDIOCPWCGCONTOUR: { - int *contour = arg; - ret = pwc_get_contour(pdev, contour); + ARG_DEF(int, contour) + + ret = pwc_get_contour(pdev, ARGA(contour)); + ARG_OUT(contour) break; } case VIDIOCPWCSBACKLIGHT: { - int *backlight = arg; - ret = pwc_set_backlight(pdev, *backlight); + ARG_DEF(int, backlight) + + ARG_IN(backlight) + ret = pwc_set_backlight(pdev, ARGR(backlight)); break; } case VIDIOCPWCGBACKLIGHT: { - int *backlight = arg; - ret = pwc_get_backlight(pdev, backlight); + ARG_DEF(int, backlight) + + ret = pwc_get_backlight(pdev, ARGA(backlight)); + ARG_OUT(backlight) break; } case VIDIOCPWCSFLICKER: { - int *flicker = arg; - ret = pwc_set_flicker(pdev, *flicker); + ARG_DEF(int, flicker) + + ARG_IN(flicker) + ret = pwc_set_flicker(pdev, ARGR(flicker)); break; } case VIDIOCPWCGFLICKER: { - int *flicker = arg; - ret = pwc_get_flicker(pdev, flicker); + ARG_DEF(int, flicker) + + ret = pwc_get_flicker(pdev, ARGA(flicker)); + ARG_OUT(flicker) break; } case VIDIOCPWCSDYNNOISE: { - int *dynnoise = arg; - ret = pwc_set_dynamic_noise(pdev, *dynnoise); + ARG_DEF(int, dynnoise) + + ARG_IN(dynnoise) + ret = pwc_set_dynamic_noise(pdev, ARGR(dynnoise)); break; } case VIDIOCPWCGDYNNOISE: { - int *dynnoise = arg; - ret = pwc_get_dynamic_noise(pdev, dynnoise); + ARG_DEF(int, dynnoise) + + ret = pwc_get_dynamic_noise(pdev, ARGA(dynnoise)); + ARG_OUT(dynnoise); break; } case VIDIOCPWCGREALSIZE: { - struct pwc_imagesize *size = arg; - size->width = pdev->image.x; - size->height = pdev->image.y; + ARG_DEF(struct pwc_imagesize, size) + + ARGR(size).width = pdev->image.x; + ARGR(size).height = pdev->image.y; + ARG_OUT(size) break; } @@ -1387,14 +1503,10 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) { if (pdev->features & FEATURE_MOTOR_PANTILT) { - int *flags = arg; + ARG_DEF(int, flags) - ret = pwc_mpt_reset(pdev, *flags); - if (ret >= 0) - { - pdev->pan_angle = 0; - pdev->tilt_angle = 0; - } + ARG_IN(flags) + ret = pwc_mpt_reset(pdev, ARGR(flags)); } else { @@ -1407,8 +1519,10 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) { if (pdev->features & FEATURE_MOTOR_PANTILT) { - struct pwc_mpt_range *range = arg; - *range = pdev->angle_range; + ARG_DEF(struct pwc_mpt_range, range) + + ARGR(range) = pdev->angle_range; + ARG_OUT(range) } else { @@ -1423,48 +1537,23 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) if (pdev->features & FEATURE_MOTOR_PANTILT) { - struct pwc_mpt_angles *angles = arg; + ARG_DEF(struct pwc_mpt_angles, angles) + + ARG_IN(angles) /* The camera can only set relative angles, so do some calculations when getting an absolute angle . */ - if (angles->absolute) + if (ARGR(angles).absolute) { - new_pan = angles->pan; - new_tilt = angles->tilt; + new_pan = ARGR(angles).pan; + new_tilt = ARGR(angles).tilt; } else { - new_pan = pdev->pan_angle + angles->pan; - new_tilt = pdev->tilt_angle + angles->tilt; - } - /* check absolute ranges */ - if (new_pan < pdev->angle_range.pan_min || - new_pan > pdev->angle_range.pan_max || - new_tilt < pdev->angle_range.tilt_min || - new_tilt > pdev->angle_range.tilt_max) - { - ret = -ERANGE; - } - else - { - /* go to relative range, check again */ - new_pan -= pdev->pan_angle; - new_tilt -= pdev->tilt_angle; - /* angles are specified in degrees * 100, thus the limit = 36000 */ - if (new_pan < -36000 || new_pan > 36000 || new_tilt < -36000 || new_tilt > 36000) - ret = -ERANGE; - } - if (ret == 0) /* no errors so far */ - { - ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt); - if (ret >= 0) - { - pdev->pan_angle += new_pan; - pdev->tilt_angle += new_tilt; - } - if (ret == -EPIPE) /* stall -> out of range */ - ret = -ERANGE; + new_pan = pdev->pan_angle + ARGR(angles).pan; + new_tilt = pdev->tilt_angle + ARGR(angles).tilt; } + ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt); } else { @@ -1478,11 +1567,12 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) if (pdev->features & FEATURE_MOTOR_PANTILT) { - struct pwc_mpt_angles *angles = arg; + ARG_DEF(struct pwc_mpt_angles, angles) - angles->absolute = 1; - angles->pan = pdev->pan_angle; - angles->tilt = pdev->tilt_angle; + ARGR(angles).absolute = 1; + ARGR(angles).pan = pdev->pan_angle; + ARGR(angles).tilt = pdev->tilt_angle; + ARG_OUT(angles) } else { @@ -1495,8 +1585,10 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) { if (pdev->features & FEATURE_MOTOR_PANTILT) { - struct pwc_mpt_status *status = arg; - ret = pwc_mpt_get_status(pdev, status); + ARG_DEF(struct pwc_mpt_status, status) + + ret = pwc_mpt_get_status(pdev, ARGA(status)); + ARG_OUT(status) } else { @@ -1507,22 +1599,24 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) case VIDIOCPWCGVIDCMD: { - struct pwc_video_command *cmd = arg; - - cmd->type = pdev->type; - cmd->release = pdev->release; - cmd->command_len = pdev->cmd_len; - memcpy(&cmd->command_buf, pdev->cmd_buf, pdev->cmd_len); - cmd->bandlength = pdev->vbandlength; - cmd->frame_size = pdev->frame_size; + ARG_DEF(struct pwc_video_command, cmd); + + ARGR(cmd).type = pdev->type; + ARGR(cmd).release = pdev->release; + ARGR(cmd).command_len = pdev->cmd_len; + memcpy(&ARGR(cmd).command_buf, pdev->cmd_buf, pdev->cmd_len); + ARGR(cmd).bandlength = pdev->vbandlength; + ARGR(cmd).frame_size = pdev->frame_size; + ARG_OUT(cmd) break; } /* case VIDIOCPWCGVIDTABLE: { - struct pwc_table_init_buffer *table = arg; - table->len = pdev->cmd_len; - memcpy(&table->buffer, pdev->decompress_data, pdev->decompressor->table_size); + ARG_DEF(struct pwc_table_init_buffer, table); + ARGR(table).len = pdev->cmd_len; + memcpy(&ARGR(table).buffer, pdev->decompress_data, pdev->decompressor->table_size); + ARG_OUT(table) break; } */ @@ -1538,4 +1632,4 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) } - +/* vim: set cinoptions= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ diff --git a/linux/drivers/media/video/pwc/pwc-if.c b/linux/drivers/media/video/pwc/pwc-if.c index 7c97270d3..fbe7ae2a9 100644 --- a/linux/drivers/media/video/pwc/pwc-if.c +++ b/linux/drivers/media/video/pwc/pwc-if.c @@ -1,7 +1,7 @@ /* Linux driver for Philips webcam USB and Video4Linux interface part. (C) 1999-2004 Nemosoft Unv. - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -62,18 +62,21 @@ #include #include #include +#include #include +#include #include "pwc.h" -#include "pwc-ioctl.h" #include "pwc-kiara.h" #include "pwc-timon.h" +#include "pwc-dec23.h" +#include "pwc-dec1.h" #include "pwc-uncompress.h" /* Function prototypes and driver templates */ /* hotplug device table support */ -static struct usb_device_id pwc_device_table [] = { +static const struct usb_device_id pwc_device_table [] = { { USB_DEVICE(0x0471, 0x0302) }, /* Philips models */ { USB_DEVICE(0x0471, 0x0303) }, { USB_DEVICE(0x0471, 0x0304) }, @@ -81,9 +84,10 @@ static struct usb_device_id pwc_device_table [] = { { USB_DEVICE(0x0471, 0x0308) }, { USB_DEVICE(0x0471, 0x030C) }, { USB_DEVICE(0x0471, 0x0310) }, - { USB_DEVICE(0x0471, 0x0311) }, + { USB_DEVICE(0x0471, 0x0311) }, /* Philips ToUcam PRO II */ { USB_DEVICE(0x0471, 0x0312) }, { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */ + { USB_DEVICE(0x0471, 0x0329) }, /* Philips SPC 900NC PC Camera */ { USB_DEVICE(0x069A, 0x0001) }, /* Askey */ { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */ { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */ @@ -94,8 +98,9 @@ static struct usb_device_id pwc_device_table [] = { { USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */ { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */ { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */ - { USB_DEVICE(0x055D, 0x9000) }, /* Samsung */ - { USB_DEVICE(0x055D, 0x9001) }, + { USB_DEVICE(0x055D, 0x9000) }, /* Samsung MPC-C10 */ + { USB_DEVICE(0x055D, 0x9001) }, /* Samsung MPC-C30 */ + { USB_DEVICE(0x055D, 0x9002) }, /* Samsung SNC-35E (Ver3.0) */ { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */ { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */ { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */ @@ -122,11 +127,13 @@ static struct usb_driver pwc_driver = { static int default_size = PSZ_QCIF; static int default_fps = 10; static int default_fbufs = 3; /* Default number of frame buffers */ -static int default_mbufs = 2; /* Default number of mmap() buffers */ - int pwc_trace = TRACE_MODULE | TRACE_FLOW | TRACE_PWCX; + int pwc_mbufs = 2; /* Default number of mmap() buffers */ +#if CONFIG_PWC_DEBUG + int pwc_trace = PWC_DEBUG_LEVEL; +#endif static int power_save = 0; static int led_on = 100, led_off = 0; /* defaults to LED that is on while in use */ -static int pwc_preferred_compression = 2; /* 0..3 = uncompressed..high */ + int pwc_preferred_compression = 1; /* 0..3 = uncompressed..high */ static struct { int type; char serial_number[30]; @@ -138,7 +145,7 @@ static struct { static int pwc_video_open(struct inode *inode, struct file *file); static int pwc_video_close(struct inode *inode, struct file *file); -static ssize_t pwc_video_read(struct file *file, char __user * buf, +static ssize_t pwc_video_read(struct file *file, char __user *buf, size_t count, loff_t *ppos); static unsigned int pwc_video_poll(struct file *file, poll_table *wait); static int pwc_video_ioctl(struct inode *inode, struct file *file, @@ -153,7 +160,6 @@ static struct file_operations pwc_fops = { .poll = pwc_video_poll, .mmap = pwc_video_mmap, .ioctl = pwc_video_ioctl, - .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; static struct video_device pwc_template = { @@ -203,7 +209,8 @@ static struct video_device pwc_template = { /* Here we want the physical address of the memory. * This is used when initializing the contents of the area. */ -static inline unsigned long kvirt_to_pa(unsigned long adr) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) +static unsigned long kvirt_to_pa(unsigned long adr) { unsigned long kva, ret; @@ -212,43 +219,59 @@ static inline unsigned long kvirt_to_pa(unsigned long adr) ret = __pa(kva); return ret; } +#endif + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) +/** + * kzalloc - allocate memory. The memory is set to zero. + * @size: how many bytes of memory are required. + * @flags: the type of memory to allocate. + */ +void *kzalloc(size_t size, int flags) +{ + void *ret = kmalloc(size, flags); + if (ret) + memset(ret, 0, size); + return ret; +} +#endif -static void * rvmalloc(unsigned long size) +static void *pwc_rvmalloc(unsigned long size) { void * mem; unsigned long adr; - size=PAGE_ALIGN(size); mem=vmalloc_32(size); - if (mem) - { - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr=(unsigned long) mem; - while (size > 0) - { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr+=PAGE_SIZE; - size-=PAGE_SIZE; - } - } + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr=(unsigned long) mem; + while (size > 0) + { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } return mem; } -static void rvfree(void * mem, unsigned long size) +static void pwc_rvfree(void * mem, unsigned long size) { unsigned long adr; - if (mem) - { - adr=(unsigned long) mem; - while ((long) size > 0) - { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr+=PAGE_SIZE; - size-=PAGE_SIZE; - } - vfree(mem); - } + if (!mem) + return; + + adr=(unsigned long) mem; + while ((long) size > 0) + { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); } @@ -256,100 +279,83 @@ static void rvfree(void * mem, unsigned long size) static int pwc_allocate_buffers(struct pwc_device *pdev) { - int i; + int i, err; void *kbuf; - Trace(TRACE_MEMORY, ">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev); + PWC_DEBUG_MEMORY(">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev); if (pdev == NULL) return -ENXIO; -#ifdef PWC_MAGIC - if (pdev->magic != PWC_MAGIC) { - Err("allocate_buffers(): magic failed.\n"); - return -ENXIO; - } -#endif - /* Allocate Isochronous pipe buffers */ + /* Allocate Isochronuous pipe buffers */ for (i = 0; i < MAX_ISO_BUFS; i++) { if (pdev->sbuf[i].data == NULL) { - kbuf = kmalloc(ISO_BUFFER_SIZE, GFP_KERNEL); + kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL); if (kbuf == NULL) { - Err("Failed to allocate iso buffer %d.\n", i); + PWC_ERROR("Failed to allocate iso buffer %d.\n", i); return -ENOMEM; } - Trace(TRACE_MEMORY, "Allocated iso buffer at %p.\n", kbuf); + PWC_DEBUG_MEMORY("Allocated iso buffer at %p.\n", kbuf); pdev->sbuf[i].data = kbuf; - memset(kbuf, 0, ISO_BUFFER_SIZE); } } /* Allocate frame buffer structure */ if (pdev->fbuf == NULL) { - kbuf = kmalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL); + kbuf = kzalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL); if (kbuf == NULL) { - Err("Failed to allocate frame buffer structure.\n"); + PWC_ERROR("Failed to allocate frame buffer structure.\n"); return -ENOMEM; } - Trace(TRACE_MEMORY, "Allocated frame buffer structure at %p.\n", kbuf); + PWC_DEBUG_MEMORY("Allocated frame buffer structure at %p.\n", kbuf); pdev->fbuf = kbuf; - memset(kbuf, 0, default_fbufs * sizeof(struct pwc_frame_buf)); } + /* create frame buffers, and make circular ring */ for (i = 0; i < default_fbufs; i++) { if (pdev->fbuf[i].data == NULL) { kbuf = vmalloc(PWC_FRAME_SIZE); /* need vmalloc since frame buffer > 128K */ if (kbuf == NULL) { - Err("Failed to allocate frame buffer %d.\n", i); + PWC_ERROR("Failed to allocate frame buffer %d.\n", i); return -ENOMEM; } - Trace(TRACE_MEMORY, "Allocated frame buffer %d at %p.\n", i, kbuf); + PWC_DEBUG_MEMORY("Allocated frame buffer %d at %p.\n", i, kbuf); pdev->fbuf[i].data = kbuf; - memset(kbuf, 128, PWC_FRAME_SIZE); + memset(kbuf, 0, PWC_FRAME_SIZE); } } /* Allocate decompressor table space */ - kbuf = NULL; - switch (pdev->type) - { - case 675: - case 680: - case 690: - case 720: - case 730: - case 740: - case 750: -#if 0 /* keep */ - Trace(TRACE_MEMORY,"private_data(%zu)\n",sizeof(struct pwc_dec23_private)); - kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); /* Timon & Kiara */ - break; - case 645: - case 646: - /* TODO & FIXME */ - kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); - break; -#endif - ; - } - pdev->decompress_data = kbuf; + if (DEVICE_USE_CODEC1(pdev->type)) + err = pwc_dec1_alloc(pdev); + else + err = pwc_dec23_alloc(pdev); + + if (err) { + PWC_ERROR("Failed to allocate decompress table.\n"); + return err; + } /* Allocate image buffer; double buffer for mmap() */ - kbuf = rvmalloc(default_mbufs * pdev->len_per_image); + kbuf = pwc_rvmalloc(pwc_mbufs * pdev->len_per_image); if (kbuf == NULL) { - Err("Failed to allocate image buffer(s). needed (%d)\n",default_mbufs * pdev->len_per_image); + PWC_ERROR("Failed to allocate image buffer(s). needed (%d)\n", + pwc_mbufs * pdev->len_per_image); return -ENOMEM; } - Trace(TRACE_MEMORY, "Allocated image buffer at %p.\n", kbuf); + PWC_DEBUG_MEMORY("Allocated image buffer at %p.\n", kbuf); pdev->image_data = kbuf; - for (i = 0; i < default_mbufs; i++) - pdev->image_ptr[i] = kbuf + i * pdev->len_per_image; - for (; i < MAX_IMAGES; i++) - pdev->image_ptr[i] = NULL; + for (i = 0; i < pwc_mbufs; i++) { + pdev->images[i].offset = i * pdev->len_per_image; + pdev->images[i].vma_use_count = 0; + } + for (; i < MAX_IMAGES; i++) { + pdev->images[i].offset = 0; + } kbuf = NULL; - Trace(TRACE_MEMORY, "<< pwc_allocate_buffers()\n"); + PWC_DEBUG_MEMORY("<< pwc_allocate_buffers()\n"); return 0; } @@ -357,21 +363,14 @@ static void pwc_free_buffers(struct pwc_device *pdev) { int i; - Trace(TRACE_MEMORY, "Entering free_buffers(%p).\n", pdev); + PWC_DEBUG_MEMORY("Entering free_buffers(%p).\n", pdev); if (pdev == NULL) return; -#ifdef PWC_MAGIC - if (pdev->magic != PWC_MAGIC) { - Err("free_buffers(): magic failed.\n"); - return; - } -#endif - /* Release Iso-pipe buffers */ for (i = 0; i < MAX_ISO_BUFS; i++) if (pdev->sbuf[i].data != NULL) { - Trace(TRACE_MEMORY, "Freeing ISO buffer at %p.\n", pdev->sbuf[i].data); + PWC_DEBUG_MEMORY("Freeing ISO buffer at %p.\n", pdev->sbuf[i].data); kfree(pdev->sbuf[i].data); pdev->sbuf[i].data = NULL; } @@ -380,7 +379,7 @@ static void pwc_free_buffers(struct pwc_device *pdev) if (pdev->fbuf != NULL) { for (i = 0; i < default_fbufs; i++) { if (pdev->fbuf[i].data != NULL) { - Trace(TRACE_MEMORY, "Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data); + PWC_DEBUG_MEMORY("Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data); vfree(pdev->fbuf[i].data); pdev->fbuf[i].data = NULL; } @@ -391,20 +390,19 @@ static void pwc_free_buffers(struct pwc_device *pdev) /* Intermediate decompression buffer & tables */ if (pdev->decompress_data != NULL) { - Trace(TRACE_MEMORY, "Freeing decompression buffer at %p.\n", pdev->decompress_data); + PWC_DEBUG_MEMORY("Freeing decompression buffer at %p.\n", pdev->decompress_data); kfree(pdev->decompress_data); pdev->decompress_data = NULL; } - pdev->decompressor = NULL; /* Release image buffers */ if (pdev->image_data != NULL) { - Trace(TRACE_MEMORY, "Freeing image buffer at %p.\n", pdev->image_data); - rvfree(pdev->image_data, default_mbufs * pdev->len_per_image); + PWC_DEBUG_MEMORY("Freeing image buffer at %p.\n", pdev->image_data); + pwc_rvfree(pdev->image_data, pwc_mbufs * pdev->len_per_image); } pdev->image_data = NULL; - Trace(TRACE_MEMORY, "Leaving free_buffers().\n"); + PWC_DEBUG_MEMORY("Leaving free_buffers().\n"); } /* The frame & image buffer mess. @@ -464,7 +462,7 @@ static void pwc_free_buffers(struct pwc_device *pdev) /** \brief Find next frame buffer to fill. Take from empty or full list, whichever comes first. */ -static inline int pwc_next_fill_frame(struct pwc_device *pdev) +static int pwc_next_fill_frame(struct pwc_device *pdev) { int ret; unsigned long flags; @@ -489,23 +487,17 @@ static inline int pwc_next_fill_frame(struct pwc_device *pdev) } else { /* Hmm. Take it from the full list */ -#if PWC_DEBUG /* sanity check */ if (pdev->full_frames == NULL) { - Err("Neither empty or full frames available!\n"); + PWC_ERROR("Neither empty or full frames available!\n"); spin_unlock_irqrestore(&pdev->ptrlock, flags); return -EINVAL; } -#endif pdev->fill_frame = pdev->full_frames; pdev->full_frames = pdev->full_frames->next; ret = 1; } pdev->fill_frame->next = NULL; -#if PWC_DEBUG - Trace(TRACE_SEQUENCE, "Assigning sequence number %d.\n", pdev->sequence); - pdev->fill_frame->sequence = pdev->sequence++; -#endif spin_unlock_irqrestore(&pdev->ptrlock, flags); return ret; } @@ -521,6 +513,8 @@ static void pwc_reset_buffers(struct pwc_device *pdev) int i; unsigned long flags; + PWC_DEBUG_MEMORY(">> %s __enter__\n", __FUNCTION__); + spin_lock_irqsave(&pdev->ptrlock, flags); pdev->full_frames = NULL; pdev->full_frames_tail = NULL; @@ -540,13 +534,15 @@ static void pwc_reset_buffers(struct pwc_device *pdev) pdev->image_read_pos = 0; pdev->fill_image = 0; spin_unlock_irqrestore(&pdev->ptrlock, flags); + + PWC_DEBUG_MEMORY("<< %s __leaving__\n", __FUNCTION__); } /** \brief Do all the handling for getting one frame: get pointer, decompress, advance pointers. */ -static int pwc_handle_frame(struct pwc_device *pdev) +int pwc_handle_frame(struct pwc_device *pdev) { int ret = 0; unsigned long flags; @@ -556,41 +552,40 @@ static int pwc_handle_frame(struct pwc_device *pdev) we can release the lock after this without problems */ if (pdev->read_frame != NULL) { /* This can't theoretically happen */ - Err("Huh? Read frame still in use?\n"); + PWC_ERROR("Huh? Read frame still in use?\n"); + spin_unlock_irqrestore(&pdev->ptrlock, flags); + return ret; + } + + + if (pdev->full_frames == NULL) { + PWC_ERROR("Woops. No frames ready.\n"); } else { - if (pdev->full_frames == NULL) { - Err("Woops. No frames ready.\n"); + pdev->read_frame = pdev->full_frames; + pdev->full_frames = pdev->full_frames->next; + pdev->read_frame->next = NULL; + } + + if (pdev->read_frame != NULL) { + /* Decompression is a lenghty process, so it's outside of the lock. + This gives the isoc_handler the opportunity to fill more frames + in the mean time. + */ + spin_unlock_irqrestore(&pdev->ptrlock, flags); + ret = pwc_decompress(pdev); + spin_lock_irqsave(&pdev->ptrlock, flags); + + /* We're done with read_buffer, tack it to the end of the empty buffer list */ + if (pdev->empty_frames == NULL) { + pdev->empty_frames = pdev->read_frame; + pdev->empty_frames_tail = pdev->empty_frames; } else { - pdev->read_frame = pdev->full_frames; - pdev->full_frames = pdev->full_frames->next; - pdev->read_frame->next = NULL; - } - - if (pdev->read_frame != NULL) { -#if PWC_DEBUG - Trace(TRACE_SEQUENCE, "Decompressing frame %d\n", pdev->read_frame->sequence); -#endif - /* Decompression is a lenghty process, so it's outside of the lock. - This gives the isoc_handler the opportunity to fill more frames - in the mean time. - */ - spin_unlock_irqrestore(&pdev->ptrlock, flags); - ret = pwc_decompress(pdev); - spin_lock_irqsave(&pdev->ptrlock, flags); - - /* We're done with read_buffer, tack it to the end of the empty buffer list */ - if (pdev->empty_frames == NULL) { - pdev->empty_frames = pdev->read_frame; - pdev->empty_frames_tail = pdev->empty_frames; - } - else { - pdev->empty_frames_tail->next = pdev->read_frame; - pdev->empty_frames_tail = pdev->read_frame; - } - pdev->read_frame = NULL; + pdev->empty_frames_tail->next = pdev->read_frame; + pdev->empty_frames_tail = pdev->read_frame; } + pdev->read_frame = NULL; } spin_unlock_irqrestore(&pdev->ptrlock, flags); return ret; @@ -599,12 +594,114 @@ static int pwc_handle_frame(struct pwc_device *pdev) /** \brief Advance pointers of image buffer (after each user request) */ -static inline void pwc_next_image(struct pwc_device *pdev) +void pwc_next_image(struct pwc_device *pdev) { pdev->image_used[pdev->fill_image] = 0; - pdev->fill_image = (pdev->fill_image + 1) % default_mbufs; + pdev->fill_image = (pdev->fill_image + 1) % pwc_mbufs; +} + +/** + * Print debug information when a frame is discarded because all of our buffer + * is full + */ +static void pwc_frame_dumped(struct pwc_device *pdev) +{ + pdev->vframes_dumped++; + if (pdev->vframe_count < FRAME_LOWMARK) + return; + + if (pdev->vframes_dumped < 20) + PWC_DEBUG_FLOW("Dumping frame %d\n", pdev->vframe_count); + else if (pdev->vframes_dumped == 20) + PWC_DEBUG_FLOW("Dumping frame %d (last message)\n", + pdev->vframe_count); } +static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_buf *fbuf) +{ + int awake = 0; + + /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus + frames on the USB wire after an exposure change. This conditition is + however detected in the cam and a bit is set in the header. + */ + if (pdev->type == 730) { + unsigned char *ptr = (unsigned char *)fbuf->data; + + if (ptr[1] == 1 && ptr[0] & 0x10) { + PWC_TRACE("Hyundai CMOS sensor bug. Dropping frame.\n"); + pdev->drop_frames += 2; + pdev->vframes_error++; + } + if ((ptr[0] ^ pdev->vmirror) & 0x01) { + if (ptr[0] & 0x01) { + pdev->snapshot_button_status = 1; + PWC_TRACE("Snapshot button pressed.\n"); + } + else { + PWC_TRACE("Snapshot button released.\n"); + } + } + if ((ptr[0] ^ pdev->vmirror) & 0x02) { + if (ptr[0] & 0x02) + PWC_TRACE("Image is mirrored.\n"); + else + PWC_TRACE("Image is normal.\n"); + } + pdev->vmirror = ptr[0] & 0x03; + /* Sometimes the trailer of the 730 is still sent as a 4 byte packet + after a short frame; this condition is filtered out specifically. A 4 byte + frame doesn't make sense anyway. + So we get either this sequence: + drop_bit set -> 4 byte frame -> short frame -> good frame + Or this one: + drop_bit set -> short frame -> good frame + So we drop either 3 or 2 frames in all! + */ + if (fbuf->filled == 4) + pdev->drop_frames++; + } + else if (pdev->type == 740 || pdev->type == 720) { + unsigned char *ptr = (unsigned char *)fbuf->data; + if ((ptr[0] ^ pdev->vmirror) & 0x01) { + if (ptr[0] & 0x01) { + pdev->snapshot_button_status = 1; + PWC_TRACE("Snapshot button pressed.\n"); + } + else + PWC_TRACE("Snapshot button released.\n"); + } + pdev->vmirror = ptr[0] & 0x03; + } + + /* In case we were instructed to drop the frame, do so silently. + The buffer pointers are not updated either (but the counters are reset below). + */ + if (pdev->drop_frames > 0) + pdev->drop_frames--; + else { + /* Check for underflow first */ + if (fbuf->filled < pdev->frame_total_size) { + PWC_DEBUG_FLOW("Frame buffer underflow (%d bytes);" + " discarded.\n", fbuf->filled); + pdev->vframes_error++; + } + else { + /* Send only once per EOF */ + awake = 1; /* delay wake_ups */ + + /* Find our next frame to fill. This will always succeed, since we + * nick a frame from either empty or full list, but if we had to + * take it from the full list, it means a frame got dropped. + */ + if (pwc_next_fill_frame(pdev)) + pwc_frame_dumped(pdev); + + } + } /* !drop_frames */ + pdev->vframe_count++; + return awake; +} /* This gets called for the Isochronous pipe (video). This is done in * interrupt time, so it has to be fast, not crash, and not stall. Neat. @@ -620,17 +717,12 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs) awake = 0; pdev = (struct pwc_device *)urb->context; if (pdev == NULL) { - Err("isoc_handler() called with NULL device?!\n"); - return; - } -#ifdef PWC_MAGIC - if (pdev->magic != PWC_MAGIC) { - Err("isoc_handler() called with bad magic!\n"); + PWC_ERROR("isoc_handler() called with NULL device?!\n"); return; } -#endif + if (urb->status == -ENOENT || urb->status == -ECONNRESET) { - Trace(TRACE_OPEN, "pwc_isoc_handler(): URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a"); + PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a"); return; } if (urb->status != -EINPROGRESS && urb->status != 0) { @@ -645,13 +737,13 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs) case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break; case -ETIMEDOUT: errmsg = "NAK (device does not respond)"; break; } - Trace(TRACE_FLOW, "pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg); + PWC_DEBUG_FLOW("pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg); /* Give up after a number of contiguous errors on the USB bus. Appearantly something is wrong so we simulate an unplug event. */ if (++pdev->visoc_errors > MAX_ISOC_ERRORS) { - Info("Too many ISOC errors, bailing out.\n"); + PWC_INFO("Too many ISOC errors, bailing out.\n"); pdev->error_status = EIO; awake = 1; wake_up_interruptible(&pdev->frameq); @@ -661,7 +753,7 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs) fbuf = pdev->fill_frame; if (fbuf == NULL) { - Err("pwc_isoc_handler without valid fill frame.\n"); + PWC_ERROR("pwc_isoc_handler without valid fill frame.\n"); awake = 1; goto handler_end; } @@ -688,7 +780,7 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs) /* ...copy data to frame buffer, if possible */ if (flen + fbuf->filled > pdev->frame_total_size) { - Trace(TRACE_FLOW, "Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, pdev->frame_total_size); + PWC_DEBUG_FLOW("Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, pdev->frame_total_size); pdev->vsync = 0; /* Hmm, let's wait for an EOF (end-of-frame) */ pdev->vframes_error++; } @@ -704,96 +796,28 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs) /* Shorter packet... We probably have the end of an image-frame; wake up read() process and let select()/poll() do something. Decompression is done in user time over there. - */ + */ if (pdev->vsync == 2) { - /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus - frames on the USB wire after an exposure change. This conditition is - however detected in the cam and a bit is set in the header. - */ - if (pdev->type == 730) { - unsigned char *ptr = (unsigned char *)fbuf->data; - - if (ptr[1] == 1 && ptr[0] & 0x10) { -#if PWC_DEBUG - Debug("Hyundai CMOS sensor bug. Dropping frame %d.\n", fbuf->sequence); -#endif - pdev->drop_frames += 2; - pdev->vframes_error++; - } - if ((ptr[0] ^ pdev->vmirror) & 0x01) { - if (ptr[0] & 0x01) - Info("Snapshot button pressed.\n"); - else - Info("Snapshot button released.\n"); - } - if ((ptr[0] ^ pdev->vmirror) & 0x02) { - if (ptr[0] & 0x02) - Info("Image is mirrored.\n"); - else - Info("Image is normal.\n"); - } - pdev->vmirror = ptr[0] & 0x03; - /* Sometimes the trailer of the 730 is still sent as a 4 byte packet - after a short frame; this condition is filtered out specifically. A 4 byte - frame doesn't make sense anyway. - So we get either this sequence: - drop_bit set -> 4 byte frame -> short frame -> good frame - Or this one: - drop_bit set -> short frame -> good frame - So we drop either 3 or 2 frames in all! - */ - if (fbuf->filled == 4) - pdev->drop_frames++; + if (pwc_rcv_short_packet(pdev, fbuf)) { + awake = 1; + fbuf = pdev->fill_frame; } - - /* In case we were instructed to drop the frame, do so silently. - The buffer pointers are not updated either (but the counters are reset below). - */ - if (pdev->drop_frames > 0) - pdev->drop_frames--; - else { - /* Check for underflow first */ - if (fbuf->filled < pdev->frame_total_size) { - Trace(TRACE_FLOW, "Frame buffer underflow (%d bytes); discarded.\n", fbuf->filled); - pdev->vframes_error++; - } - else { - /* Send only once per EOF */ - awake = 1; /* delay wake_ups */ - - /* Find our next frame to fill. This will always succeed, since we - * nick a frame from either empty or full list, but if we had to - * take it from the full list, it means a frame got dropped. - */ - if (pwc_next_fill_frame(pdev)) { - pdev->vframes_dumped++; - if ((pdev->vframe_count > FRAME_LOWMARK) && (pwc_trace & TRACE_FLOW)) { - if (pdev->vframes_dumped < 20) - Trace(TRACE_FLOW, "Dumping frame %d.\n", pdev->vframe_count); - if (pdev->vframes_dumped == 20) - Trace(TRACE_FLOW, "Dumping frame %d (last message).\n", pdev->vframe_count); - } - } - fbuf = pdev->fill_frame; - } - } /* !drop_frames */ - pdev->vframe_count++; } fbuf->filled = 0; fillptr = fbuf->data; pdev->vsync = 1; - } /* .. flen < last_packet_size */ + } + pdev->vlast_packet_size = flen; } /* ..status == 0 */ -#if PWC_DEBUG - /* This is normally not interesting to the user, unless you are really debugging something */ else { + /* This is normally not interesting to the user, unless + * you are really debugging something */ static int iso_error = 0; iso_error++; if (iso_error < 20) - Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst); + PWC_DEBUG_FLOW("Iso frame %d of USB has error %d\n", i, fst); } -#endif } handler_end: @@ -803,11 +827,11 @@ handler_end: urb->dev = pdev->udev; i = usb_submit_urb(urb, GFP_ATOMIC); if (i != 0) - Err("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i); + PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i); } -static int pwc_isoc_init(struct pwc_device *pdev) +int pwc_isoc_init(struct pwc_device *pdev) { struct usb_device *udev; struct urb *urb; @@ -826,30 +850,38 @@ static int pwc_isoc_init(struct pwc_device *pdev) /* Get the current alternate interface, adjust packet size */ if (!udev->actconfig) return -EFAULT; - +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5) + idesc = &udev->actconfig->interface[0]->altsetting[pdev->valternate]; +#else intf = usb_ifnum_to_if(udev, 0); if (intf) idesc = usb_altnum_to_altsetting(intf, pdev->valternate); +#endif if (!idesc) return -EFAULT; /* Search video endpoint */ pdev->vmax_packet_size = -1; - for (i = 0; i < idesc->desc.bNumEndpoints; i++) + for (i = 0; i < idesc->desc.bNumEndpoints; i++) { if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) + pdev->vmax_packet_size = idesc->endpoint[i].desc.wMaxPacketSize; +#else pdev->vmax_packet_size = le16_to_cpu(idesc->endpoint[i].desc.wMaxPacketSize); +#endif break; } + } if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) { - Err("Failed to find packet size for video endpoint in current alternate setting.\n"); + PWC_ERROR("Failed to find packet size for video endpoint in current alternate setting.\n"); return -ENFILE; /* Odd error, that should be noticeable */ } /* Set alternate interface */ ret = 0; - Trace(TRACE_OPEN, "Setting alternate interface %d\n", pdev->valternate); + PWC_DEBUG_OPEN("Setting alternate interface %d\n", pdev->valternate); ret = usb_set_interface(pdev->udev, 0, pdev->valternate); if (ret < 0) return ret; @@ -857,12 +889,12 @@ static int pwc_isoc_init(struct pwc_device *pdev) for (i = 0; i < MAX_ISO_BUFS; i++) { urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); if (urb == NULL) { - Err("Failed to allocate urb %d\n", i); + PWC_ERROR("Failed to allocate urb %d\n", i); ret = -ENOMEM; break; } pdev->sbuf[i].urb = urb; - Trace(TRACE_MEMORY, "Allocated URB at 0x%p\n", urb); + PWC_DEBUG_MEMORY("Allocated URB at 0x%p\n", urb); } if (ret) { /* De-allocate in reverse order */ @@ -899,24 +931,26 @@ static int pwc_isoc_init(struct pwc_device *pdev) for (i = 0; i < MAX_ISO_BUFS; i++) { ret = usb_submit_urb(pdev->sbuf[i].urb, GFP_KERNEL); if (ret) - Err("isoc_init() submit_urb %d failed with error %d\n", i, ret); + PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret); else - Trace(TRACE_MEMORY, "URB 0x%p submitted.\n", pdev->sbuf[i].urb); + PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->sbuf[i].urb); } /* All is done... */ pdev->iso_init = 1; - Trace(TRACE_OPEN, "<< pwc_isoc_init()\n"); + PWC_DEBUG_OPEN("<< pwc_isoc_init()\n"); return 0; } -static void pwc_isoc_cleanup(struct pwc_device *pdev) +void pwc_isoc_cleanup(struct pwc_device *pdev) { int i; - Trace(TRACE_OPEN, ">> pwc_isoc_cleanup()\n"); + PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n"); if (pdev == NULL) return; + if (pdev->iso_init == 0) + return; /* Unlinking ISOC buffers one by one */ for (i = 0; i < MAX_ISO_BUFS; i++) { @@ -925,10 +959,10 @@ static void pwc_isoc_cleanup(struct pwc_device *pdev) urb = pdev->sbuf[i].urb; if (urb != 0) { if (pdev->iso_init) { - Trace(TRACE_MEMORY, "Unlinking URB %p\n", urb); + PWC_DEBUG_MEMORY("Unlinking URB %p\n", urb); usb_kill_urb(urb); } - Trace(TRACE_MEMORY, "Freeing URB\n"); + PWC_DEBUG_MEMORY("Freeing URB\n"); usb_free_urb(urb); pdev->sbuf[i].urb = NULL; } @@ -938,12 +972,12 @@ static void pwc_isoc_cleanup(struct pwc_device *pdev) is signalled by EPIPE) */ if (pdev->error_status && pdev->error_status != EPIPE) { - Trace(TRACE_OPEN, "Setting alternate interface 0.\n"); + PWC_DEBUG_OPEN("Setting alternate interface 0.\n"); usb_set_interface(pdev->udev, 0, 0); } pdev->iso_init = 0; - Trace(TRACE_OPEN, "<< pwc_isoc_cleanup()\n"); + PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n"); } int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot) @@ -957,18 +991,18 @@ int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_f /* Try to set video mode... */ start = ret = pwc_set_video_mode(pdev, width, height, new_fps, new_compression, new_snapshot); if (ret) { - Trace(TRACE_FLOW, "pwc_set_video_mode attempt 1 failed.\n"); + PWC_DEBUG_FLOW("pwc_set_video_mode attempt 1 failed.\n"); /* That failed... restore old mode (we know that worked) */ start = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); if (start) { - Trace(TRACE_FLOW, "pwc_set_video_mode attempt 2 failed.\n"); + PWC_DEBUG_FLOW("pwc_set_video_mode attempt 2 failed.\n"); } } if (start == 0) { if (pwc_isoc_init(pdev) < 0) { - Info("Failed to restart ISOC transfers in pwc_try_video_mode.\n"); + PWC_WARNING("Failed to restart ISOC transfers in pwc_try_video_mode.\n"); ret = -EAGAIN; /* let's try again, who knows if it works a second time */ } } @@ -976,54 +1010,129 @@ int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_f return ret; /* Return original error code */ } +/********* + * sysfs + *********/ +static struct pwc_device *cd_to_pwc(struct class_device *cd) +{ + struct video_device *vdev = to_video_device(cd); + return video_get_drvdata(vdev); +} + +static ssize_t show_pan_tilt(struct class_device *class_dev, char *buf) +{ + struct pwc_device *pdev = cd_to_pwc(class_dev); + return sprintf(buf, "%d %d\n", pdev->pan_angle, pdev->tilt_angle); +} + +static ssize_t store_pan_tilt(struct class_device *class_dev, const char *buf, + size_t count) +{ + struct pwc_device *pdev = cd_to_pwc(class_dev); + int pan, tilt; + int ret = -EINVAL; + + if (strncmp(buf, "reset", 5) == 0) + ret = pwc_mpt_reset(pdev, 0x3); + + else if (sscanf(buf, "%d %d", &pan, &tilt) > 0) + ret = pwc_mpt_set_angle(pdev, pan, tilt); + + if (ret < 0) + return ret; + return strlen(buf); +} +static CLASS_DEVICE_ATTR(pan_tilt, S_IRUGO | S_IWUSR, show_pan_tilt, + store_pan_tilt); + +static ssize_t show_snapshot_button_status(struct class_device *class_dev, char *buf) +{ + struct pwc_device *pdev = cd_to_pwc(class_dev); + int status = pdev->snapshot_button_status; + pdev->snapshot_button_status = 0; + return sprintf(buf, "%d\n", status); +} + +static CLASS_DEVICE_ATTR(button, S_IRUGO | S_IWUSR, show_snapshot_button_status, + NULL); + +static void pwc_create_sysfs_files(struct video_device *vdev) +{ + struct pwc_device *pdev = video_get_drvdata(vdev); + if (pdev->features & FEATURE_MOTOR_PANTILT) + video_device_create_file(vdev, &class_device_attr_pan_tilt); + video_device_create_file(vdev, &class_device_attr_button); +} + +static void pwc_remove_sysfs_files(struct video_device *vdev) +{ + struct pwc_device *pdev = video_get_drvdata(vdev); + if (pdev->features & FEATURE_MOTOR_PANTILT) + video_device_remove_file(vdev, &class_device_attr_pan_tilt); + video_device_remove_file(vdev, &class_device_attr_button); +} + +#if CONFIG_PWC_DEBUG +static const char *pwc_sensor_type_to_string(unsigned int sensor_type) +{ + switch(sensor_type) { + case 0x00: + return "Hyundai CMOS sensor"; + case 0x20: + return "Sony CCD sensor + TDA8787"; + case 0x2E: + return "Sony CCD sensor + Exas 98L59"; + case 0x2F: + return "Sony CCD sensor + ADI 9804"; + case 0x30: + return "Sharp CCD sensor + TDA8787"; + case 0x3E: + return "Sharp CCD sensor + Exas 98L59"; + case 0x3F: + return "Sharp CCD sensor + ADI 9804"; + case 0x40: + return "UPA 1021 sensor"; + case 0x100: + return "VGA sensor"; + case 0x101: + return "PAL MR sensor"; + default: + return "unknown type of sensor"; + } +} +#endif /***************************************************************************/ /* Video4Linux functions */ static int pwc_video_open(struct inode *inode, struct file *file) { - int i; + int i, ret; struct video_device *vdev = video_devdata(file); struct pwc_device *pdev; - Trace(TRACE_OPEN, ">> video_open called(vdev = 0x%p).\n", vdev); + PWC_DEBUG_OPEN(">> video_open called(vdev = 0x%p).\n", vdev); pdev = (struct pwc_device *)vdev->priv; if (pdev == NULL) BUG(); - if (pdev->vopen) + if (pdev->vopen) { + PWC_DEBUG_OPEN("I'm busy, someone is using the device.\n"); return -EBUSY; + } down(&pdev->modlock); if (!pdev->usb_init) { - Trace(TRACE_OPEN, "Doing first time initialization.\n"); + PWC_DEBUG_OPEN("Doing first time initialization.\n"); pdev->usb_init = 1; - if (pwc_trace & TRACE_OPEN) + /* Query sensor type */ + ret = pwc_get_cmos_sensor(pdev, &i); + if (ret >= 0) { - /* Query sensor type */ - const char *sensor_type = NULL; - int ret; - - ret = pwc_get_cmos_sensor(pdev, &i); - if (ret >= 0) - { - switch(i) { - case 0x00: sensor_type = "Hyundai CMOS sensor"; break; - case 0x20: sensor_type = "Sony CCD sensor + TDA8787"; break; - case 0x2E: sensor_type = "Sony CCD sensor + Exas 98L59"; break; - case 0x2F: sensor_type = "Sony CCD sensor + ADI 9804"; break; - case 0x30: sensor_type = "Sharp CCD sensor + TDA8787"; break; - case 0x3E: sensor_type = "Sharp CCD sensor + Exas 98L59"; break; - case 0x3F: sensor_type = "Sharp CCD sensor + ADI 9804"; break; - case 0x40: sensor_type = "UPA 1021 sensor"; break; - case 0x100: sensor_type = "VGA sensor"; break; - case 0x101: sensor_type = "PAL MR sensor"; break; - default: sensor_type = "unknown type of sensor"; break; - } - } - if (sensor_type != NULL) - Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev->name, sensor_type, i); + PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n", + pdev->vdev->name, + pwc_sensor_type_to_string(i), i); } } @@ -1031,34 +1140,32 @@ static int pwc_video_open(struct inode *inode, struct file *file) if (power_save) { i = pwc_camera_power(pdev, 1); if (i < 0) - Info("Failed to restore power to the camera! (%d)\n", i); + PWC_DEBUG_OPEN("Failed to restore power to the camera! (%d)\n", i); } /* Set LED on/off time */ if (pwc_set_leds(pdev, led_on, led_off) < 0) - Info("Failed to set LED on/off time.\n"); + PWC_DEBUG_OPEN("Failed to set LED on/off time.\n"); pwc_construct(pdev); /* set min/max sizes correct */ /* So far, so good. Allocate memory. */ i = pwc_allocate_buffers(pdev); if (i < 0) { - Trace(TRACE_OPEN, "Failed to allocate buffer memory.\n"); + PWC_DEBUG_OPEN("Failed to allocate buffers memory.\n"); + pwc_free_buffers(pdev); up(&pdev->modlock); return i; } /* Reset buffers & parameters */ pwc_reset_buffers(pdev); - for (i = 0; i < default_mbufs; i++) + for (i = 0; i < pwc_mbufs; i++) pdev->image_used[i] = 0; pdev->vframe_count = 0; pdev->vframes_dumped = 0; pdev->vframes_error = 0; pdev->visoc_errors = 0; pdev->error_status = 0; -#if PWC_DEBUG - pdev->sequence = 0; -#endif pwc_construct(pdev); /* set min/max sizes correct */ /* Set some defaults */ @@ -1070,29 +1177,44 @@ static int pwc_video_open(struct inode *inode, struct file *file) */ i = pwc_set_video_mode(pdev, pwc_image_sizes[pdev->vsize].x, pwc_image_sizes[pdev->vsize].y, pdev->vframes, pdev->vcompression, 0); if (i) { - Trace(TRACE_OPEN, "First attempt at set_video_mode failed.\n"); - if (pdev->type == 730 || pdev->type == 740 || pdev->type == 750) - i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QSIF].x, pwc_image_sizes[PSZ_QSIF].y, 10, pdev->vcompression, 0); + unsigned int default_resolution; + PWC_DEBUG_OPEN("First attempt at set_video_mode failed.\n"); + if (pdev->type>= 730) + default_resolution = PSZ_QSIF; else - i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QCIF].x, pwc_image_sizes[PSZ_QCIF].y, 10, pdev->vcompression, 0); + default_resolution = PSZ_QCIF; + + i = pwc_set_video_mode(pdev, + pwc_image_sizes[default_resolution].x, + pwc_image_sizes[default_resolution].y, + 10, + pdev->vcompression, + 0); } if (i) { - Trace(TRACE_OPEN, "Second attempt at set_video_mode failed.\n"); + PWC_DEBUG_OPEN("Second attempt at set_video_mode failed.\n"); + pwc_free_buffers(pdev); up(&pdev->modlock); return i; } i = pwc_isoc_init(pdev); if (i) { - Trace(TRACE_OPEN, "Failed to init ISOC stuff = %d.\n", i); + PWC_DEBUG_OPEN("Failed to init ISOC stuff = %d.\n", i); + pwc_isoc_cleanup(pdev); + pwc_free_buffers(pdev); up(&pdev->modlock); return i; } + /* Initialize the webcam to sane value */ + pwc_set_brightness(pdev, 0x7fff); + pwc_set_agc(pdev, 1, 0); + pdev->vopen++; file->private_data = vdev; up(&pdev->modlock); - Trace(TRACE_OPEN, "<< video_open() returns 0.\n"); + PWC_DEBUG_OPEN("<< video_open() returns 0.\n"); return 0; } @@ -1103,35 +1225,23 @@ static int pwc_video_close(struct inode *inode, struct file *file) struct pwc_device *pdev; int i; - Trace(TRACE_OPEN, ">> video_close called(vdev = 0x%p).\n", vdev); + PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev); pdev = (struct pwc_device *)vdev->priv; if (pdev->vopen == 0) - Info("video_close() called on closed device?\n"); + PWC_DEBUG_MODULE("video_close() called on closed device?\n"); /* Dump statistics, but only if a reasonable amount of frames were processed (to prevent endless log-entries in case of snap-shot programs) */ if (pdev->vframe_count > 20) - Info("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error); + PWC_DEBUG_MODULE("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error); - switch (pdev->type) - { - case 675: - case 680: - case 690: - case 720: - case 730: - case 740: - case 750: -/* pwc_dec23_exit(); *//* Timon & Kiara */ - break; - case 645: - case 646: -/* pwc_dec1_exit(); */ - break; - } + if (DEVICE_USE_CODEC1(pdev->type)) + pwc_dec1_exit(); + else + pwc_dec23_exit(); pwc_isoc_cleanup(pdev); pwc_free_buffers(pdev); @@ -1140,15 +1250,15 @@ static int pwc_video_close(struct inode *inode, struct file *file) if (pdev->error_status != EPIPE) { /* Turn LEDs off */ if (pwc_set_leds(pdev, 0, 0) < 0) - Info("Failed to set LED on/off time.\n"); + PWC_DEBUG_MODULE("Failed to set LED on/off time.\n"); if (power_save) { i = pwc_camera_power(pdev, 0); if (i < 0) - Err("Failed to power down camera (%d)\n", i); + PWC_ERROR("Failed to power down camera (%d)\n", i); } } - pdev->vopen = 0; - Trace(TRACE_OPEN, "<< video_close()\n"); + pdev->vopen--; + PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", pdev->vopen); return 0; } @@ -1164,7 +1274,7 @@ static int pwc_video_close(struct inode *inode, struct file *file) device is tricky anyhow. */ -static ssize_t pwc_video_read(struct file *file, char __user * buf, +static ssize_t pwc_video_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct video_device *vdev = file->private_data; @@ -1172,8 +1282,10 @@ static ssize_t pwc_video_read(struct file *file, char __user * buf, int noblock = file->f_flags & O_NONBLOCK; DECLARE_WAITQUEUE(wait, current); int bytes_to_read; + void *image_buffer_addr; - Trace(TRACE_READ, "video_read(0x%p, %p, %zu) called.\n", vdev, buf, count); + PWC_DEBUG_READ("pwc_video_read(vdev=0x%p, buf=%p, count=%zd) called.\n", + vdev, buf, count); if (vdev == NULL) return -EFAULT; pdev = vdev->priv; @@ -1214,16 +1326,19 @@ static ssize_t pwc_video_read(struct file *file, char __user * buf, return -EFAULT; } - Trace(TRACE_READ, "Copying data to user space.\n"); + PWC_DEBUG_READ("Copying data to user space.\n"); if (pdev->vpalette == VIDEO_PALETTE_RAW) - bytes_to_read = pdev->frame_size; + bytes_to_read = pdev->frame_size + sizeof(struct pwc_raw_frame); else bytes_to_read = pdev->view.size; /* copy bytes to user space; we allow for partial reads */ if (count + pdev->image_read_pos > bytes_to_read) count = bytes_to_read - pdev->image_read_pos; - if (copy_to_user(buf, pdev->image_ptr[pdev->fill_image] + pdev->image_read_pos, count)) + image_buffer_addr = pdev->image_data; + image_buffer_addr += pdev->images[pdev->fill_image].offset; + image_buffer_addr += pdev->image_read_pos; + if (copy_to_user(buf, image_buffer_addr, count)) return -EFAULT; pdev->image_read_pos += count; if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */ @@ -1253,370 +1368,62 @@ static unsigned int pwc_video_poll(struct file *file, poll_table *wait) return 0; } -static int pwc_video_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) -{ - struct video_device *vdev = file->private_data; - struct pwc_device *pdev; - DECLARE_WAITQUEUE(wait, current); - - if (vdev == NULL) - return -EFAULT; - pdev = vdev->priv; - if (pdev == NULL) - return -EFAULT; - - switch (cmd) { - /* Query cabapilities */ - case VIDIOCGCAP: - { - struct video_capability *caps = arg; - - strcpy(caps->name, vdev->name); - caps->type = VID_TYPE_CAPTURE; - caps->channels = 1; - caps->audios = 1; - caps->minwidth = pdev->view_min.x; - caps->minheight = pdev->view_min.y; - caps->maxwidth = pdev->view_max.x; - caps->maxheight = pdev->view_max.y; - break; - } - - /* Channel functions (simulate 1 channel) */ - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - - if (v->channel != 0) - return -EINVAL; - v->flags = 0; - v->tuners = 0; - v->type = VIDEO_TYPE_CAMERA; - strcpy(v->name, "Webcam"); - return 0; - } - - case VIDIOCSCHAN: - { - /* The spec says the argument is an integer, but - the bttv driver uses a video_channel arg, which - makes sense becasue it also has the norm flag. - */ - struct video_channel *v = arg; - if (v->channel != 0) - return -EINVAL; - return 0; - } - - - /* Picture functions; contrast etc. */ - case VIDIOCGPICT: - { - struct video_picture *p = arg; - int val; - - val = pwc_get_brightness(pdev); - if (val >= 0) - p->brightness = val; - else - p->brightness = 0xffff; - val = pwc_get_contrast(pdev); - if (val >= 0) - p->contrast = val; - else - p->contrast = 0xffff; - /* Gamma, Whiteness, what's the difference? :) */ - val = pwc_get_gamma(pdev); - if (val >= 0) - p->whiteness = val; - else - p->whiteness = 0xffff; - val = pwc_get_saturation(pdev); - if (val >= 0) - p->colour = val; - else - p->colour = 0xffff; - p->depth = 24; - p->palette = pdev->vpalette; - p->hue = 0xFFFF; /* N/A */ - break; - } - - case VIDIOCSPICT: - { - struct video_picture *p = arg; - /* - * FIXME: Suppose we are mid read - ANSWER: No problem: the firmware of the camera - can handle brightness/contrast/etc - changes at _any_ time, and the palette - is used exactly once in the uncompress - routine. - */ - pwc_set_brightness(pdev, p->brightness); - pwc_set_contrast(pdev, p->contrast); - pwc_set_gamma(pdev, p->whiteness); - pwc_set_saturation(pdev, p->colour); - if (p->palette && p->palette != pdev->vpalette) { - switch (p->palette) { - case VIDEO_PALETTE_YUV420P: - case VIDEO_PALETTE_RAW: - pdev->vpalette = p->palette; - return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); - break; - default: - return -EINVAL; - break; - } - } - break; - } - - /* Window/size parameters */ - case VIDIOCGWIN: - { - struct video_window *vw = arg; - - vw->x = 0; - vw->y = 0; - vw->width = pdev->view.x; - vw->height = pdev->view.y; - vw->chromakey = 0; - vw->flags = (pdev->vframes << PWC_FPS_SHIFT) | - (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0); - break; - } - - case VIDIOCSWIN: - { - struct video_window *vw = arg; - int fps, snapshot, ret; - - fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT; - snapshot = vw->flags & PWC_FPS_SNAPSHOT; - if (fps == 0) - fps = pdev->vframes; - if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot) - return 0; - ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot); - if (ret) - return ret; - break; - } - - /* We don't have overlay support (yet) */ - case VIDIOCGFBUF: - { - struct video_buffer *vb = arg; - - memset(vb,0,sizeof(*vb)); - break; - } - - /* mmap() functions */ - case VIDIOCGMBUF: - { - /* Tell the user program how much memory is needed for a mmap() */ - struct video_mbuf *vm = arg; - int i; - - memset(vm, 0, sizeof(*vm)); - vm->size = default_mbufs * pdev->len_per_image; - vm->frames = default_mbufs; /* double buffering should be enough for most applications */ - for (i = 0; i < default_mbufs; i++) - vm->offsets[i] = i * pdev->len_per_image; - break; - } - - case VIDIOCMCAPTURE: - { - /* Start capture into a given image buffer (called 'frame' in video_mmap structure) */ - struct video_mmap *vm = arg; - - Trace(TRACE_READ, "VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format); - if (vm->frame < 0 || vm->frame >= default_mbufs) - return -EINVAL; - - /* xawtv is nasty. It probes the available palettes - by setting a very small image size and trying - various palettes... The driver doesn't support - such small images, so I'm working around it. - */ - if (vm->format) - { - switch (vm->format) - { - case VIDEO_PALETTE_YUV420P: - case VIDEO_PALETTE_RAW: - break; - default: - return -EINVAL; - break; - } - } - - if ((vm->width != pdev->view.x || vm->height != pdev->view.y) && - (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) { - int ret; - - Trace(TRACE_OPEN, "VIDIOCMCAPTURE: changing size to please xawtv :-(.\n"); - ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot); - if (ret) - return ret; - } /* ... size mismatch */ - - /* FIXME: should we lock here? */ - if (pdev->image_used[vm->frame]) - return -EBUSY; /* buffer wasn't available. Bummer */ - pdev->image_used[vm->frame] = 1; - - /* Okay, we're done here. In the SYNC call we wait until a - frame comes available, then expand image into the given - buffer. - In contrast to the CPiA cam the Philips cams deliver a - constant stream, almost like a grabber card. Also, - we have separate buffers for the rawdata and the image, - meaning we can nearly always expand into the requested buffer. - */ - Trace(TRACE_READ, "VIDIOCMCAPTURE done.\n"); - break; - } - - case VIDIOCSYNC: - { - /* The doc says: "Whenever a buffer is used it should - call VIDIOCSYNC to free this frame up and continue." - - The only odd thing about this whole procedure is - that MCAPTURE flags the buffer as "in use", and - SYNC immediately unmarks it, while it isn't - after SYNC that you know that the buffer actually - got filled! So you better not start a CAPTURE in - the same frame immediately (use double buffering). - This is not a problem for this cam, since it has - extra intermediate buffers, but a hardware - grabber card will then overwrite the buffer - you're working on. - */ - int *mbuf = arg; - int ret; - - Trace(TRACE_READ, "VIDIOCSYNC called (%d).\n", *mbuf); - - /* bounds check */ - if (*mbuf < 0 || *mbuf >= default_mbufs) - return -EINVAL; - /* check if this buffer was requested anyway */ - if (pdev->image_used[*mbuf] == 0) - return -EINVAL; - - /* Add ourselves to the frame wait-queue. - - FIXME: needs auditing for safety. - QUESTION: In what respect? I think that using the - frameq is safe now. - */ - add_wait_queue(&pdev->frameq, &wait); - while (pdev->full_frames == NULL) { - if (pdev->error_status) { - remove_wait_queue(&pdev->frameq, &wait); - set_current_state(TASK_RUNNING); - return -pdev->error_status; - } - - if (signal_pending(current)) { - remove_wait_queue(&pdev->frameq, &wait); - set_current_state(TASK_RUNNING); - return -ERESTARTSYS; - } - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - } - remove_wait_queue(&pdev->frameq, &wait); - set_current_state(TASK_RUNNING); - - /* The frame is ready. Expand in the image buffer - requested by the user. I don't care if you - mmap() 5 buffers and request data in this order: - buffer 4 2 3 0 1 2 3 0 4 3 1 . . . - Grabber hardware may not be so forgiving. - */ - Trace(TRACE_READ, "VIDIOCSYNC: frame ready.\n"); - pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */ - /* Decompress, etc */ - ret = pwc_handle_frame(pdev); - pdev->image_used[*mbuf] = 0; - if (ret) - return -EFAULT; - break; - } - - case VIDIOCGAUDIO: - { - struct video_audio *v = arg; - - strcpy(v->name, "Microphone"); - v->audio = -1; /* unknown audio minor */ - v->flags = 0; - v->mode = VIDEO_SOUND_MONO; - v->volume = 0; - v->bass = 0; - v->treble = 0; - v->balance = 0x8000; - v->step = 1; - break; - } - - case VIDIOCSAUDIO: - { - /* Dummy: nothing can be set */ - break; - } - - case VIDIOCGUNIT: - { - struct video_unit *vu = arg; - - vu->video = pdev->vdev->minor & 0x3F; - vu->audio = -1; /* not known yet */ - vu->vbi = -1; - vu->radio = -1; - vu->teletext = -1; - break; - } - default: - return pwc_ioctl(pdev, cmd, arg); - } /* ..switch */ - return 0; -} - static int pwc_video_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { return video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl); } - static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) { struct video_device *vdev = file->private_data; struct pwc_device *pdev; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end-vma->vm_start; - unsigned long page, pos; + unsigned long start; + unsigned long size; + unsigned long page, pos = 0; + int index; - Trace(TRACE_MEMORY, "mmap(0x%p, 0x%lx, %lu) called.\n", vdev, start, size); + PWC_DEBUG_MEMORY(">> %s\n", __FUNCTION__); pdev = vdev->priv; + size = vma->vm_end - vma->vm_start; + start = vma->vm_start; + + /* Find the idx buffer for this mapping */ + for (index = 0; index < pwc_mbufs; index++) { + pos = pdev->images[index].offset; + if ((pos>>PAGE_SHIFT) == vma->vm_pgoff) + break; + } + if (index == MAX_IMAGES) + return -EINVAL; + if (index == 0) { + /* + * Special case for v4l1. In v4l1, we map only one big buffer, + * but in v4l2 each buffer is mapped + */ + unsigned long total_size; + total_size = pwc_mbufs * pdev->len_per_image; + if (size != pdev->len_per_image && size != total_size) { + PWC_ERROR("Wrong size (%lu) needed to be len_per_image=%d or total_size=%lu\n", + size, pdev->len_per_image, total_size); + return -EINVAL; + } + } else if (size > pdev->len_per_image) + return -EINVAL; - vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_IO; /* from 2.6.9-acX */ - pos = (unsigned long)pdev->image_data; + pos += (unsigned long)pdev->image_data; while (size > 0) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) + page = kvirt_to_pa(pos); + if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; +#else page = vmalloc_to_pfn((void *)pos); if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) return -EAGAIN; - +#endif start += PAGE_SIZE; pos += PAGE_SIZE; if (size > PAGE_SIZE) @@ -1624,7 +1431,6 @@ static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) else size = 0; } - return 0; } @@ -1645,10 +1451,17 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id int video_nr = -1; /* default: use next available device */ char serial_number[30], *name; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) + vendor_id = udev->descriptor.idVendor; + product_id = udev->descriptor.idProduct; +#else + vendor_id = le16_to_cpu(udev->descriptor.idVendor); + product_id = le16_to_cpu(udev->descriptor.idProduct); +#endif + /* Check if we can handle this device */ - Trace(TRACE_PROBE, "probe() called [%04X %04X], if %d\n", - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct), + PWC_DEBUG_PROBE("probe() called [%04X %04X], if %d\n", + vendor_id, product_id, intf->altsetting->desc.bInterfaceNumber); /* the interfaces are probed one by one. We are only interested in the @@ -1658,61 +1471,63 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id if (intf->altsetting->desc.bInterfaceNumber > 0) return -ENODEV; - vendor_id = le16_to_cpu(udev->descriptor.idVendor); - product_id = le16_to_cpu(udev->descriptor.idProduct); - if (vendor_id == 0x0471) { switch (product_id) { case 0x0302: - Info("Philips PCA645VC USB webcam detected.\n"); + PWC_INFO("Philips PCA645VC USB webcam detected.\n"); name = "Philips 645 webcam"; type_id = 645; break; case 0x0303: - Info("Philips PCA646VC USB webcam detected.\n"); + PWC_INFO("Philips PCA646VC USB webcam detected.\n"); name = "Philips 646 webcam"; type_id = 646; break; case 0x0304: - Info("Askey VC010 type 2 USB webcam detected.\n"); + PWC_INFO("Askey VC010 type 2 USB webcam detected.\n"); name = "Askey VC010 webcam"; type_id = 646; break; case 0x0307: - Info("Philips PCVC675K (Vesta) USB webcam detected.\n"); + PWC_INFO("Philips PCVC675K (Vesta) USB webcam detected.\n"); name = "Philips 675 webcam"; type_id = 675; break; case 0x0308: - Info("Philips PCVC680K (Vesta Pro) USB webcam detected.\n"); + PWC_INFO("Philips PCVC680K (Vesta Pro) USB webcam detected.\n"); name = "Philips 680 webcam"; type_id = 680; break; case 0x030C: - Info("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n"); + PWC_INFO("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n"); name = "Philips 690 webcam"; type_id = 690; break; case 0x0310: - Info("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n"); + PWC_INFO("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n"); name = "Philips 730 webcam"; type_id = 730; break; case 0x0311: - Info("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n"); + PWC_INFO("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n"); name = "Philips 740 webcam"; type_id = 740; break; case 0x0312: - Info("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n"); + PWC_INFO("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n"); name = "Philips 750 webcam"; type_id = 750; break; case 0x0313: - Info("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n"); + PWC_INFO("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n"); name = "Philips 720K/40 webcam"; type_id = 720; break; + case 0x0329: + PWC_INFO("Philips SPC 900NC USB webcam detected.\n"); + name = "Philips SPC 900NC webcam"; + type_id = 720; + break; default: return -ENODEV; break; @@ -1721,7 +1536,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id else if (vendor_id == 0x069A) { switch(product_id) { case 0x0001: - Info("Askey VC010 type 1 USB webcam detected.\n"); + PWC_INFO("Askey VC010 type 1 USB webcam detected.\n"); name = "Askey VC010 webcam"; type_id = 645; break; @@ -1733,32 +1548,33 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id else if (vendor_id == 0x046d) { switch(product_id) { case 0x08b0: - Info("Logitech QuickCam Pro 3000 USB webcam detected.\n"); + PWC_INFO("Logitech QuickCam Pro 3000 USB webcam detected.\n"); name = "Logitech QuickCam Pro 3000"; type_id = 740; /* CCD sensor */ break; case 0x08b1: - Info("Logitech QuickCam Notebook Pro USB webcam detected.\n"); + PWC_INFO("Logitech QuickCam Notebook Pro USB webcam detected.\n"); name = "Logitech QuickCam Notebook Pro"; type_id = 740; /* CCD sensor */ break; case 0x08b2: - Info("Logitech QuickCam 4000 Pro USB webcam detected.\n"); + PWC_INFO("Logitech QuickCam 4000 Pro USB webcam detected.\n"); name = "Logitech QuickCam Pro 4000"; type_id = 740; /* CCD sensor */ break; case 0x08b3: - Info("Logitech QuickCam Zoom USB webcam detected.\n"); + PWC_INFO("Logitech QuickCam Zoom USB webcam detected.\n"); name = "Logitech QuickCam Zoom"; type_id = 740; /* CCD sensor */ break; case 0x08B4: - Info("Logitech QuickCam Zoom (new model) USB webcam detected.\n"); + PWC_INFO("Logitech QuickCam Zoom (new model) USB webcam detected.\n"); name = "Logitech QuickCam Zoom"; type_id = 740; /* CCD sensor */ + power_save = 1; break; case 0x08b5: - Info("Logitech QuickCam Orbit/Sphere USB webcam detected.\n"); + PWC_INFO("Logitech QuickCam Orbit/Sphere USB webcam detected.\n"); name = "Logitech QuickCam Orbit"; type_id = 740; /* CCD sensor */ features |= FEATURE_MOTOR_PANTILT; @@ -1766,7 +1582,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id case 0x08b6: case 0x08b7: case 0x08b8: - Info("Logitech QuickCam detected (reserved ID).\n"); + PWC_INFO("Logitech QuickCam detected (reserved ID).\n"); name = "Logitech QuickCam (res.)"; type_id = 730; /* Assuming CMOS */ break; @@ -1782,15 +1598,20 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id */ switch(product_id) { case 0x9000: - Info("Samsung MPC-C10 USB webcam detected.\n"); + PWC_INFO("Samsung MPC-C10 USB webcam detected.\n"); name = "Samsung MPC-C10"; type_id = 675; break; case 0x9001: - Info("Samsung MPC-C30 USB webcam detected.\n"); + PWC_INFO("Samsung MPC-C30 USB webcam detected.\n"); name = "Samsung MPC-C30"; type_id = 675; break; + case 0x9002: + PWC_INFO("Samsung SNC-35E (v3.0) USB webcam detected.\n"); + name = "Samsung MPC-C30"; + type_id = 740; + break; default: return -ENODEV; break; @@ -1799,12 +1620,12 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id else if (vendor_id == 0x041e) { switch(product_id) { case 0x400c: - Info("Creative Labs Webcam 5 detected.\n"); + PWC_INFO("Creative Labs Webcam 5 detected.\n"); name = "Creative Labs Webcam 5"; type_id = 730; break; case 0x4011: - Info("Creative Labs Webcam Pro Ex detected.\n"); + PWC_INFO("Creative Labs Webcam Pro Ex detected.\n"); name = "Creative Labs Webcam Pro Ex"; type_id = 740; break; @@ -1816,7 +1637,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id else if (vendor_id == 0x04cc) { switch(product_id) { case 0x8116: - Info("Sotec Afina Eye USB webcam detected.\n"); + PWC_INFO("Sotec Afina Eye USB webcam detected.\n"); name = "Sotec Afina Eye"; type_id = 730; break; @@ -1829,7 +1650,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id switch(product_id) { case 0x8116: /* This is essentially the same cam as the Sotec Afina Eye */ - Info("AME Co. Afina Eye USB webcam detected.\n"); + PWC_INFO("AME Co. Afina Eye USB webcam detected.\n"); name = "AME Co. Afina Eye"; type_id = 750; break; @@ -1842,12 +1663,12 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id else if (vendor_id == 0x0d81) { switch(product_id) { case 0x1900: - Info("Visionite VCS-UC300 USB webcam detected.\n"); + PWC_INFO("Visionite VCS-UC300 USB webcam detected.\n"); name = "Visionite VCS-UC300"; type_id = 740; /* CCD sensor */ break; case 0x1910: - Info("Visionite VCS-UM100 USB webcam detected.\n"); + PWC_INFO("Visionite VCS-UM100 USB webcam detected.\n"); name = "Visionite VCS-UM100"; type_id = 730; /* CMOS sensor */ break; @@ -1861,15 +1682,15 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id memset(serial_number, 0, 30); usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29); - Trace(TRACE_PROBE, "Device serial number is %s\n", serial_number); + PWC_DEBUG_PROBE("Device serial number is %s\n", serial_number); if (udev->descriptor.bNumConfigurations > 1) - Info("Warning: more than 1 configuration available.\n"); + PWC_WARNING("Warning: more than 1 configuration available.\n"); /* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */ pdev = kzalloc(sizeof(struct pwc_device), GFP_KERNEL); if (pdev == NULL) { - Err("Oops, could not allocate memory for pwc_device.\n"); + PWC_ERROR("Oops, could not allocate memory for pwc_device.\n"); return -ENOMEM; } pdev->type = type_id; @@ -1900,17 +1721,22 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->vdev = video_device_alloc(); if (pdev->vdev == 0) { - Err("Err, cannot allocate video_device struture. Failing probe."); + PWC_ERROR("Err, cannot allocate video_device struture. Failing probe."); kfree(pdev); return -ENOMEM; } memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template)); + pdev->vdev->dev = &(udev->dev); strcpy(pdev->vdev->name, name); pdev->vdev->owner = THIS_MODULE; video_set_drvdata(pdev->vdev, pdev); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) + pdev->release = udev->descriptor.bcdDevice; +#else pdev->release = le16_to_cpu(udev->descriptor.bcdDevice); - Trace(TRACE_PROBE, "Release: %04x\n", pdev->release); +#endif + PWC_DEBUG_PROBE("Release: %04x\n", pdev->release); /* Now search device_hint[] table for a match, so we can hint a node number. */ for (hint = 0; hint < MAX_DEV_HINTS; hint++) { @@ -1920,7 +1746,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id if ((device_hint[hint].serial_number[0] == '*') || !strcmp(device_hint[hint].serial_number, serial_number)) { /* match! */ video_nr = device_hint[hint].device_node; - Trace(TRACE_PROBE, "Found hint, will try to register as /dev/video%d\n", video_nr); + PWC_DEBUG_PROBE("Found hint, will try to register as /dev/video%d\n", video_nr); break; } } @@ -1929,21 +1755,27 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->vdev->release = video_device_release; i = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr); if (i < 0) { - Err("Failed to register as video device (%d).\n", i); + PWC_ERROR("Failed to register as video device (%d).\n", i); video_device_release(pdev->vdev); /* Drip... drip... drip... */ kfree(pdev); /* Oops, no memory leaks please */ return -EIO; } else { - Info("Registered as /dev/video%d.\n", pdev->vdev->minor & 0x3F); + PWC_INFO("Registered as /dev/video%d.\n", pdev->vdev->minor & 0x3F); } /* occupy slot */ if (hint < MAX_DEV_HINTS) device_hint[hint].pdev = pdev; - Trace(TRACE_PROBE, "probe() function returning struct at 0x%p.\n", pdev); + PWC_DEBUG_PROBE("probe() function returning struct at 0x%p.\n", pdev); usb_set_intfdata (intf, pdev); + pwc_create_sysfs_files(pdev->vdev); + + /* Set the leds off */ + pwc_set_leds(pdev, 0, 0); + pwc_camera_power(pdev, 0); + return 0; } @@ -1957,27 +1789,21 @@ static void usb_pwc_disconnect(struct usb_interface *intf) pdev = usb_get_intfdata (intf); usb_set_intfdata (intf, NULL); if (pdev == NULL) { - Err("pwc_disconnect() Called without private pointer.\n"); + PWC_ERROR("pwc_disconnect() Called without private pointer.\n"); goto disconnect_out; } if (pdev->udev == NULL) { - Err("pwc_disconnect() already called for %p\n", pdev); + PWC_ERROR("pwc_disconnect() already called for %p\n", pdev); goto disconnect_out; } if (pdev->udev != interface_to_usbdev(intf)) { - Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n"); - goto disconnect_out; - } -#ifdef PWC_MAGIC - if (pdev->magic != PWC_MAGIC) { - Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n"); + PWC_ERROR("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n"); goto disconnect_out; } -#endif /* We got unplugged; this is signalled by an EPIPE error code */ if (pdev->vopen) { - Info("Disconnected while webcam is in use!\n"); + PWC_INFO("Disconnected while webcam is in use!\n"); pdev->error_status = EPIPE; } @@ -1987,7 +1813,8 @@ static void usb_pwc_disconnect(struct usb_interface *intf) while (pdev->vopen) schedule(); /* Device is now closed, so we can safely unregister it */ - Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n"); + PWC_DEBUG_PROBE("Unregistering video device in disconnect().\n"); + pwc_remove_sysfs_files(pdev->vdev); video_unregister_device(pdev->vdev); /* Free memory (don't set pdev to 0 just yet) */ @@ -2021,58 +1848,69 @@ static int pwc_atoi(const char *s) * Initialization code & module stuff */ -static char size[10]; -static int fps = 0; -static int fbufs = 0; -static int mbufs = 0; -static int trace = -1; +static char *size; +static int fps; +static int fbufs; +static int mbufs; static int compression = -1; static int leds[2] = { -1, -1 }; -static char *dev_hint[MAX_DEV_HINTS] = { }; +static int leds_nargs; +static char *dev_hint[MAX_DEV_HINTS]; +static int dev_hint_nargs; + +module_param(size, charp, 0444); +module_param(fps, int, 0444); +module_param(fbufs, int, 0444); +module_param(mbufs, int, 0444); +#if CONFIG_PWC_DEBUG +module_param_named(trace, pwc_trace, int, 0644); +#endif +module_param(power_save, int, 0444); +module_param(compression, int, 0444); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) +module_param_array(leds, int, leds_nargs, 0444); +module_param_array(dev_hint, charp, dev_hint_nargs, 0444); +#else +module_param_array(leds, int, &leds_nargs, 0444); +module_param_array(dev_hint, charp, &dev_hint_nargs, 0444); +#endif -module_param_string(size, size, sizeof(size), 0); MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif, vga"); -module_param(fps, int, 0000); MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30"); -module_param(fbufs, int, 0000); MODULE_PARM_DESC(fbufs, "Number of internal frame buffers to reserve"); -module_param(mbufs, int, 0000); MODULE_PARM_DESC(mbufs, "Number of external (mmap()ed) image buffers"); -module_param(trace, int, 0000); MODULE_PARM_DESC(trace, "For debugging purposes"); -module_param(power_save, bool, 0000); MODULE_PARM_DESC(power_save, "Turn power save feature in camera on or off"); -module_param(compression, int, 0000); MODULE_PARM_DESC(compression, "Preferred compression quality. Range 0 (uncompressed) to 3 (high compression)"); -module_param_array(leds, int, NULL, 0000); MODULE_PARM_DESC(leds, "LED on,off time in milliseconds"); -module_param_array(dev_hint, charp, NULL, 0000); MODULE_PARM_DESC(dev_hint, "Device node hints"); MODULE_DESCRIPTION("Philips & OEM USB webcam driver"); MODULE_AUTHOR("Luc Saillard "); MODULE_LICENSE("GPL"); +MODULE_ALIAS("pwcx"); +MODULE_VERSION( PWC_VERSION ); static int __init usb_pwc_init(void) { int i, sz; char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" }; - Info("Philips webcam module version " PWC_VERSION " loaded.\n"); - Info("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n"); - Info("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n"); - Info("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n"); + PWC_INFO("Philips webcam module version " PWC_VERSION " loaded.\n"); + PWC_INFO("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n"); + PWC_INFO("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n"); + PWC_INFO("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n"); if (fps) { if (fps < 4 || fps > 30) { - Err("Framerate out of bounds (4-30).\n"); + PWC_ERROR("Framerate out of bounds (4-30).\n"); return -EINVAL; } default_fps = fps; - Info("Default framerate set to %d.\n", default_fps); + PWC_DEBUG_MODULE("Default framerate set to %d.\n", default_fps); } - if (size[0]) { + if (size) { /* string; try matching with array */ for (sz = 0; sz < PSZ_MAX; sz++) { if (!strcmp(sizenames[sz], size)) { /* Found! */ @@ -2081,41 +1919,42 @@ static int __init usb_pwc_init(void) } } if (sz == PSZ_MAX) { - Err("Size not recognized; try size=[sqcif | qsif | qcif | sif | cif | vga].\n"); + PWC_ERROR("Size not recognized; try size=[sqcif | qsif | qcif | sif | cif | vga].\n"); return -EINVAL; } - Info("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y); + PWC_DEBUG_MODULE("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y); } if (mbufs) { if (mbufs < 1 || mbufs > MAX_IMAGES) { - Err("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES); + PWC_ERROR("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES); return -EINVAL; } - default_mbufs = mbufs; - Info("Number of image buffers set to %d.\n", default_mbufs); + pwc_mbufs = mbufs; + PWC_DEBUG_MODULE("Number of image buffers set to %d.\n", pwc_mbufs); } if (fbufs) { if (fbufs < 2 || fbufs > MAX_FRAMES) { - Err("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES); + PWC_ERROR("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES); return -EINVAL; } default_fbufs = fbufs; - Info("Number of frame buffers set to %d.\n", default_fbufs); + PWC_DEBUG_MODULE("Number of frame buffers set to %d.\n", default_fbufs); } - if (trace >= 0) { - Info("Trace options: 0x%04x\n", trace); - pwc_trace = trace; +#if CONFIG_PWC_DEBUG + if (pwc_trace >= 0) { + PWC_DEBUG_MODULE("Trace options: 0x%04x\n", pwc_trace); } +#endif if (compression >= 0) { if (compression > 3) { - Err("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n"); + PWC_ERROR("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n"); return -EINVAL; } pwc_preferred_compression = compression; - Info("Preferred compression set to %d.\n", pwc_preferred_compression); + PWC_DEBUG_MODULE("Preferred compression set to %d.\n", pwc_preferred_compression); } if (power_save) - Info("Enabling power save on open/close.\n"); + PWC_DEBUG_MODULE("Enabling power save on open/close.\n"); if (leds[0] >= 0) led_on = leds[0]; if (leds[1] >= 0) @@ -2146,14 +1985,14 @@ static int __init usb_pwc_init(void) dot++; /* Few sanity checks */ if (*dot != '\0' && dot > colon) { - Err("Malformed camera hint: the colon must be after the dot.\n"); + PWC_ERROR("Malformed camera hint: the colon must be after the dot.\n"); return -EINVAL; } if (*colon == '\0') { /* No colon */ if (*dot != '\0') { - Err("Malformed camera hint: no colon + device node given.\n"); + PWC_ERROR("Malformed camera hint: no colon + device node given.\n"); return -EINVAL; } else { @@ -2178,28 +2017,27 @@ static int __init usb_pwc_init(void) device_hint[i].serial_number[k] = '\0'; } } -#if PWC_DEBUG - Debug("device_hint[%d]:\n", i); - Debug(" type : %d\n", device_hint[i].type); - Debug(" serial# : %s\n", device_hint[i].serial_number); - Debug(" node : %d\n", device_hint[i].device_node); -#endif + PWC_TRACE("device_hint[%d]:\n", i); + PWC_TRACE(" type : %d\n", device_hint[i].type); + PWC_TRACE(" serial# : %s\n", device_hint[i].serial_number); + PWC_TRACE(" node : %d\n", device_hint[i].device_node); } else device_hint[i].type = 0; /* not filled */ } /* ..for MAX_DEV_HINTS */ - Trace(TRACE_PROBE, "Registering driver at address 0x%p.\n", &pwc_driver); + PWC_DEBUG_PROBE("Registering driver at address 0x%p.\n", &pwc_driver); return usb_register(&pwc_driver); } static void __exit usb_pwc_exit(void) { - Trace(TRACE_MODULE, "Deregistering driver.\n"); + PWC_DEBUG_MODULE("Deregistering driver.\n"); usb_deregister(&pwc_driver); - Info("Philips webcam module removed.\n"); + PWC_INFO("Philips webcam module removed.\n"); } module_init(usb_pwc_init); module_exit(usb_pwc_exit); +/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ diff --git a/linux/drivers/media/video/pwc/pwc-kiara.c b/linux/drivers/media/video/pwc/pwc-kiara.c index 4c96037f7..fec39cc5a 100644 --- a/linux/drivers/media/video/pwc/pwc-kiara.c +++ b/linux/drivers/media/video/pwc/pwc-kiara.c @@ -1,5 +1,5 @@ /* Linux driver for Philips webcam - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -316,3 +316,576 @@ const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4] = }, }; + +/* + * Rom table for kiara chips + * + * 32 roms tables (one for each resolution ?) + * 2 tables per roms (one for each passes) (Y, and U&V) + * 128 bytes per passes + */ + +const unsigned int KiaraRomTable [8][2][16][8] = +{ + { /* version 0 */ + { /* version 0, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x0000124a,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009292,0x00009292,0x00009493,0x000124db}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x0000a493,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x000124db,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 0, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000001,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000049,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009252,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009292,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009292, + 0x00009492,0x00009493,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009252,0x00009493, + 0x000126dc,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 1 */ + { /* version 1, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009252,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009252, + 0x00009492,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 1, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000049,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000000}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000049,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x0000924a,0x0000924a, + 0x00009492,0x00009493,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 2 */ + { /* version 2, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x0000a49b}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x0001b724,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 2, passes 1 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x0000a49b,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x0001249b,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 3 */ + { /* version 3, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0001b925,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 3, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 4 */ + { /* version 4, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009252,0x00009493, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 4, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000049,0x00000049,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00000249,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 5 */ + { /* version 5, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 5, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x00009252,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 6 */ + { /* version 6, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000126db, + 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 6, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 7 */ + { /* version 7, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x000124db}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x00024924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 7, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x00009492,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + } +}; + diff --git a/linux/drivers/media/video/pwc/pwc-kiara.h b/linux/drivers/media/video/pwc/pwc-kiara.h index 12929abbb..0bdb22547 100644 --- a/linux/drivers/media/video/pwc/pwc-kiara.h +++ b/linux/drivers/media/video/pwc/pwc-kiara.h @@ -1,5 +1,5 @@ /* Linux driver for Philips webcam - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -27,7 +27,7 @@ #ifndef PWC_KIARA_H #define PWC_KIARA_H -#include "pwc-ioctl.h" +#include struct Kiara_table_entry { @@ -37,8 +37,8 @@ struct Kiara_table_entry unsigned char mode[12]; /* precomputed mode settings for cam */ }; -const extern struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4]; -const extern unsigned int KiaraRomTable[8][2][16][8]; +extern const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4]; +extern const unsigned int KiaraRomTable[8][2][16][8]; #endif diff --git a/linux/drivers/media/video/pwc/pwc-misc.c b/linux/drivers/media/video/pwc/pwc-misc.c index 58fe79747..589c68743 100644 --- a/linux/drivers/media/video/pwc/pwc-misc.c +++ b/linux/drivers/media/video/pwc/pwc-misc.c @@ -1,7 +1,7 @@ /* Linux driver for Philips webcam Various miscellaneous functions and tables. (C) 1999-2003 Nemosoft Unv. - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -24,18 +24,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include "pwc.h" -struct pwc_coord pwc_image_sizes[PSZ_MAX] = +const struct pwc_coord pwc_image_sizes[PSZ_MAX] = { - { 128, 96, 0 }, - { 160, 120, 0 }, - { 176, 144, 0 }, - { 320, 240, 0 }, - { 352, 288, 0 }, - { 640, 480, 0 }, + { 128, 96, 0 }, /* sqcif */ + { 160, 120, 0 }, /* qsif */ + { 176, 144, 0 }, /* qcif */ + { 320, 240, 0 }, /* sif */ + { 352, 288, 0 }, /* cif */ + { 640, 480, 0 }, /* vga */ }; /* x,y -> PSZ_ */ @@ -52,7 +51,7 @@ int pwc_decode_size(struct pwc_device *pdev, int width, int height) { if (width > pdev->abs_max.x || height > pdev->abs_max.y) { - Debug("VIDEO_PALETTE_RAW: going beyond abs_max.\n"); + PWC_DEBUG_SIZE("VIDEO_PALETTE_RAW: going beyond abs_max.\n"); return -1; } } @@ -60,7 +59,7 @@ int pwc_decode_size(struct pwc_device *pdev, int width, int height) { if (width > pdev->view_max.x || height > pdev->view_max.y) { - Debug("VIDEO_PALETTE_ not RAW: going beyond view_max.\n"); + PWC_DEBUG_SIZE("VIDEO_PALETTE_not RAW: going beyond view_max.\n"); return -1; } } @@ -81,9 +80,8 @@ int pwc_decode_size(struct pwc_device *pdev, int width, int height) /* initialize variables depending on type and decompressor*/ void pwc_construct(struct pwc_device *pdev) { - switch(pdev->type) { - case 645: - case 646: + if (DEVICE_USE_CODEC1(pdev->type)) { + pdev->view_min.x = 128; pdev->view_min.y = 96; pdev->view_max.x = 352; @@ -95,10 +93,23 @@ void pwc_construct(struct pwc_device *pdev) pdev->vendpoint = 4; pdev->frame_header_size = 0; pdev->frame_trailer_size = 0; - break; - case 675: - case 680: - case 690: + + } else if (DEVICE_USE_CODEC3(pdev->type)) { + + pdev->view_min.x = 160; + pdev->view_min.y = 120; + pdev->view_max.x = 640; + pdev->view_max.y = 480; + pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA; + pdev->abs_max.x = 640; + pdev->abs_max.y = 480; + pdev->vcinterface = 3; + pdev->vendpoint = 5; + pdev->frame_header_size = TOUCAM_HEADER_SIZE; + pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE; + + } else /* if (DEVICE_USE_CODEC2(pdev->type)) */ { + pdev->view_min.x = 128; pdev->view_min.y = 96; /* Anthill bug #38: PWC always reports max size, even without PWCX */ @@ -111,30 +122,12 @@ void pwc_construct(struct pwc_device *pdev) pdev->vendpoint = 4; pdev->frame_header_size = 0; pdev->frame_trailer_size = 0; - break; - case 720: - case 730: - case 740: - case 750: - pdev->view_min.x = 160; - pdev->view_min.y = 120; - pdev->view_max.x = 640; - pdev->view_max.y = 480; - pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA; - pdev->abs_max.x = 640; - pdev->abs_max.y = 480; - pdev->vcinterface = 3; - pdev->vendpoint = 5; - pdev->frame_header_size = TOUCAM_HEADER_SIZE; - pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE; - break; } - Debug("type = %d\n",pdev->type); pdev->vpalette = VIDEO_PALETTE_YUV420P; /* default */ pdev->view_min.size = pdev->view_min.x * pdev->view_min.y; pdev->view_max.size = pdev->view_max.x * pdev->view_max.y; /* length of image, in YUV format; always allocate enough memory. */ - pdev->len_per_image = (pdev->abs_max.x * pdev->abs_max.y * 3) / 2; + pdev->len_per_image = PAGE_ALIGN((pdev->abs_max.x * pdev->abs_max.y * 3) / 2); } diff --git a/linux/drivers/media/video/pwc/pwc-timon.c b/linux/drivers/media/video/pwc/pwc-timon.c index 175250d08..be65bdcd1 100644 --- a/linux/drivers/media/video/pwc/pwc-timon.c +++ b/linux/drivers/media/video/pwc/pwc-timon.c @@ -1,5 +1,5 @@ /* Linux driver for Philips webcam - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -314,3 +314,1133 @@ const struct Timon_table_entry Timon_table[PSZ_MAX][6][4] = }, }; +/* + * 16 versions: + * 2 tables (one for Y, and one for U&V) + * 16 levels of details per tables + * 8 blocs + */ + +const unsigned int TimonRomTable [16][2][16][8] = +{ + { /* version 0 */ + { /* version 0, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000001,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000001, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000009,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x0000124a,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 0, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000001,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000001, + 0x00000001,0x00000009,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000009,0x00000049,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000009,0x00000049,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 1 */ + { /* version 1, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000009,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 1, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000001,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000049,0x00000249,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009252,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 2 */ + { /* version 2, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009252,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009252, + 0x00009492,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 2, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000049,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000000}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000049,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x0000924a,0x0000924a, + 0x00009492,0x00009493,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 3 */ + { /* version 3, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009252, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009292,0x0000a49b,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 3, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00000049,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009252,0x00009292,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009292, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 4 */ + { /* version 4, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x0000a49b}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x0001b724,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 4, passes 1 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x0000a49b,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x0001249b,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 5 */ + { /* version 5, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000126dc,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 5, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x00009493,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x000124db,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009493,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 6 */ + { /* version 6, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0001b925,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 6, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 7 */ + { /* version 7, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x0002496e}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x0001c96e,0x0002496e}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x0002496d,0x00025bb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 7, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000136e4,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00012492,0x000126db, + 0x0001b724,0x0001b925,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 8 */ + { /* version 8, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000126dc,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x00024b76,0x00024b77}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x00024b76,0x00025bbf}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0001c92d,0x00024b76,0x00025bbf}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x0001b724,0x00024b6d,0x0002ddb6,0x0002efff}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 8, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x0001b724,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x0002496d,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 9 */ + { /* version 9, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009252,0x00009493, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 9, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000049,0x00000049,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00000249,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 10 */ + { /* version 10, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000126dc,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0002496d,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 10, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00000249,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x00009493,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009493,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x00009492,0x00009493,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009493,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 11 */ + { /* version 11, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 11, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x00009252,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 12 */ + { /* version 12, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000126db, + 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 12, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 13 */ + { /* version 13, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x000124db}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x00024924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 13, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x00009492,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 14 */ + { /* version 14, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000136e4,0x0001b725,0x000124db}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000126dc,0x0001b724,0x0001b92d,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x0002496d,0x00024b76,0x00024b77}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x00024924,0x0002db6d,0x00036db6,0x0002efff}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 14, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000136e4,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 15 */ + { /* version 15, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x0002496d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x00024b6d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x00024924,0x0002db6d,0x00036db6,0x0002efff}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 15, passes 1 */ + {0x00000000,0x00000000,0x0000924a,0x0000924a, + 0x00009292,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000124db,0x0001b724,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x0001b724,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x0001b724,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000136db, + 0x0001b724,0x0001b724,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001c924,0x0001b724,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001c924,0x0001b724,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b724,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x0001c924,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x00024924,0x0002496d,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + } +}; diff --git a/linux/drivers/media/video/pwc/pwc-timon.h b/linux/drivers/media/video/pwc/pwc-timon.h index a86b3782a..eef9e2cd4 100644 --- a/linux/drivers/media/video/pwc/pwc-timon.h +++ b/linux/drivers/media/video/pwc/pwc-timon.h @@ -1,5 +1,5 @@ /* Linux driver for Philips webcam - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -42,7 +42,7 @@ #ifndef PWC_TIMON_H #define PWC_TIMON_H -#include "pwc-ioctl.h" +#include struct Timon_table_entry { @@ -52,8 +52,8 @@ struct Timon_table_entry unsigned char mode[13]; /* precomputed mode settings for cam */ }; -const extern struct Timon_table_entry Timon_table[PSZ_MAX][6][4]; -const extern unsigned int TimonRomTable [16][2][16][8]; +extern const struct Timon_table_entry Timon_table[PSZ_MAX][6][4]; +extern const unsigned int TimonRomTable [16][2][16][8]; #endif diff --git a/linux/drivers/media/video/pwc/pwc-uncompress.c b/linux/drivers/media/video/pwc/pwc-uncompress.c index eec2c3c45..5d82028ef 100644 --- a/linux/drivers/media/video/pwc/pwc-uncompress.c +++ b/linux/drivers/media/video/pwc/pwc-uncompress.c @@ -1,7 +1,7 @@ /* Linux driver for Philips webcam Decompression frontend. (C) 1999-2003 Nemosoft Unv. - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -22,6 +22,8 @@ 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 + + vim: set ts=8: */ #include @@ -29,6 +31,8 @@ #include "pwc.h" #include "pwc-uncompress.h" +#include "pwc-dec1.h" +#include "pwc-dec23.h" int pwc_decompress(struct pwc_device *pdev) { @@ -40,107 +44,95 @@ int pwc_decompress(struct pwc_device *pdev) if (pdev == NULL) return -EFAULT; -#if defined(__KERNEL__) && defined(PWC_MAGIC) - if (pdev->magic != PWC_MAGIC) { - Err("pwc_decompress(): magic failed.\n"); - return -EFAULT; - } -#endif fbuf = pdev->read_frame; if (fbuf == NULL) return -EFAULT; - image = pdev->image_ptr[pdev->fill_image]; - if (!image) - return -EFAULT; + image = pdev->image_data; + image += pdev->images[pdev->fill_image].offset; yuv = fbuf->data + pdev->frame_header_size; /* Skip header */ /* Raw format; that's easy... */ if (pdev->vpalette == VIDEO_PALETTE_RAW) { - memcpy(image, yuv, pdev->frame_size); + struct pwc_raw_frame *raw_frame = image; + raw_frame->type = cpu_to_le16(pdev->type); + raw_frame->vbandlength = cpu_to_le16(pdev->vbandlength); + /* cmd_buf is always 4 bytes, but sometimes, only the + * first 3 bytes is filled (Nala case). We can + * determine this using the type of the webcam */ + memcpy(raw_frame->cmd, pdev->cmd_buf, 4); + memcpy(raw_frame+1, yuv, pdev->frame_size); return 0; } if (pdev->vbandlength == 0) { - /* Uncompressed mode. We copy the data into the output buffer, - using the viewport size (which may be larger than the image - size). Unfortunately we have to do a bit of byte stuffing - to get the desired output format/size. + /* Uncompressed mode. + * We copy the data into the output buffer, using the viewport + * size (which may be larger than the image size). + * Unfortunately we have to do a bit of byte stuffing to get + * the desired output format/size. + * + * We do some byte shuffling here to go from the + * native format to YUV420P. */ - /* - * We do some byte shuffling here to go from the - * native format to YUV420P. - */ - src = (u16 *)yuv; - n = pdev->view.x * pdev->view.y; - - /* offset in Y plane */ - stride = pdev->view.x * pdev->offset.y + pdev->offset.x; - dsty = (u16 *)(image + stride); - - /* offsets in U/V planes */ - stride = pdev->view.x * pdev->offset.y / 4 + pdev->offset.x / 2; - dstu = (u16 *)(image + n + stride); - dstv = (u16 *)(image + n + n / 4 + stride); - - /* increment after each line */ - stride = (pdev->view.x - pdev->image.x) / 2; /* u16 is 2 bytes */ - - for (line = 0; line < pdev->image.y; line++) { - for (col = 0; col < pdev->image.x; col += 4) { - *dsty++ = *src++; - *dsty++ = *src++; - if (line & 1) - *dstv++ = *src++; - else - *dstu++ = *src++; - } - dsty += stride; + src = (u16 *)yuv; + n = pdev->view.x * pdev->view.y; + + /* offset in Y plane */ + stride = pdev->view.x * pdev->offset.y + pdev->offset.x; + dsty = (u16 *)(image + stride); + + /* offsets in U/V planes */ + stride = pdev->view.x * pdev->offset.y / 4 + pdev->offset.x / 2; + dstu = (u16 *)(image + n + stride); + dstv = (u16 *)(image + n + n / 4 + stride); + + /* increment after each line */ + stride = (pdev->view.x - pdev->image.x) / 2; /* u16 is 2 bytes */ + + for (line = 0; line < pdev->image.y; line++) { + for (col = 0; col < pdev->image.x; col += 4) { + *dsty++ = *src++; + *dsty++ = *src++; if (line & 1) - dstv += (stride >> 1); + *dstv++ = *src++; else - dstu += (stride >> 1); + *dstu++ = *src++; } + dsty += stride; + if (line & 1) + dstv += (stride >> 1); + else + dstu += (stride >> 1); + } + + return 0; } - else { - /* Compressed; the decompressor routines will write the data - in planar format immediately. - */ - int flags; - - flags = PWCX_FLAG_PLANAR; - if (pdev->vsize == PSZ_VGA && pdev->vframes == 5 && pdev->vsnapshot) - { - printk(KERN_ERR "pwc: Mode Bayer is not supported for now\n"); - flags |= PWCX_FLAG_BAYER; - return -ENXIO; /* No such device or address: missing decompressor */ - } - -#if 0 /* keep */ - switch (pdev->type) - { - case 675: - case 680: - case 690: - case 720: - case 730: - case 740: - case 750: - pwc_dec23_decompress(&pdev->image, &pdev->view, - &pdev->offset, yuv, image, flags, - pdev->decompress_data, pdev->vbandlength); - break; - case 645: - case 646: - /* TODO & FIXME */ - return -ENXIO; /* Missing decompressor */ - break; - } -#endif + + /* + * Compressed; + * the decompressor routines will write the data in planar format + * immediately. + */ + if (pdev->vsize == PSZ_VGA && pdev->vframes == 5 && pdev->vsnapshot) { + PWC_ERROR("Mode Bayer is not supported for now\n"); + /* flags |= PWCX_FLAG_BAYER; */ + return -ENXIO; /* No such device or address: missing decompressor */ + } + + if (DEVICE_USE_CODEC1(pdev->type)) { + + /* TODO & FIXME */ + PWC_ERROR("This chipset is not supported for now\n"); + return -ENXIO; /* No such device or address: missing decompressor */ + + } else { + pwc_dec23_decompress(pdev, yuv, image, PWCX_FLAG_PLANAR); } return 0; } +/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ diff --git a/linux/drivers/media/video/pwc/pwc-uncompress.h b/linux/drivers/media/video/pwc/pwc-uncompress.h index f75e1b6cb..041227f65 100644 --- a/linux/drivers/media/video/pwc/pwc-uncompress.h +++ b/linux/drivers/media/video/pwc/pwc-uncompress.h @@ -1,5 +1,5 @@ /* (C) 1999-2003 Nemosoft Unv. - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -32,7 +32,7 @@ #include -#include "pwc-ioctl.h" +#include /* from pwc-dec.h */ #define PWCX_FLAG_PLANAR 0x0001 diff --git a/linux/drivers/media/video/pwc/pwc.h b/linux/drivers/media/video/pwc/pwc.h index 74d003695..1fd8c34d1 100644 --- a/linux/drivers/media/video/pwc/pwc.h +++ b/linux/drivers/media/video/pwc/pwc.h @@ -1,5 +1,5 @@ /* (C) 1999-2003 Nemosoft Unv. - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -29,52 +29,87 @@ #include #include #include -#include "compat.h" -#include #include #include +#include #include #include +#include +#include #include "pwc-uncompress.h" -#include "pwc-ioctl.h" - -/* Defines and structures for the Philips webcam */ -/* Used for checking memory corruption/pointer validation */ -#define PWC_MAGIC 0x89DC10ABUL -#undef PWC_MAGIC +#include /* Turn some debugging options on/off */ -#define PWC_DEBUG 0 +#ifndef CONFIG_PWC_DEBUG +#define CONFIG_PWC_DEBUG 1 +#endif + +/* Version block */ +#define PWC_MAJOR 10 +#define PWC_MINOR 0 +#define PWC_EXTRAMINOR 12 +#define PWC_VERSION_CODE KERNEL_VERSION(PWC_MAJOR,PWC_MINOR,PWC_EXTRAMINOR) +#define PWC_VERSION "10.0.12" +#define PWC_NAME "pwc" +#define PFX PWC_NAME ": " + /* Trace certain actions in the driver */ -#define TRACE_MODULE 0x0001 -#define TRACE_PROBE 0x0002 -#define TRACE_OPEN 0x0004 -#define TRACE_READ 0x0008 -#define TRACE_MEMORY 0x0010 -#define TRACE_FLOW 0x0020 -#define TRACE_SIZE 0x0040 -#define TRACE_PWCX 0x0080 -#define TRACE_SEQUENCE 0x1000 - -#define Trace(R, A...) if (pwc_trace & R) printk(KERN_DEBUG PWC_NAME " " A) -#define Debug(A...) printk(KERN_DEBUG PWC_NAME " " A) -#define Info(A...) printk(KERN_INFO PWC_NAME " " A) -#define Err(A...) printk(KERN_ERR PWC_NAME " " A) +#define PWC_DEBUG_LEVEL_MODULE (1<<0) +#define PWC_DEBUG_LEVEL_PROBE (1<<1) +#define PWC_DEBUG_LEVEL_OPEN (1<<2) +#define PWC_DEBUG_LEVEL_READ (1<<3) +#define PWC_DEBUG_LEVEL_MEMORY (1<<4) +#define PWC_DEBUG_LEVEL_FLOW (1<<5) +#define PWC_DEBUG_LEVEL_SIZE (1<<6) +#define PWC_DEBUG_LEVEL_IOCTL (1<<7) +#define PWC_DEBUG_LEVEL_TRACE (1<<8) + +#define PWC_DEBUG_MODULE(fmt, args...) PWC_DEBUG(MODULE, fmt, ##args) +#define PWC_DEBUG_PROBE(fmt, args...) PWC_DEBUG(PROBE, fmt, ##args) +#define PWC_DEBUG_OPEN(fmt, args...) PWC_DEBUG(OPEN, fmt, ##args) +#define PWC_DEBUG_READ(fmt, args...) PWC_DEBUG(READ, fmt, ##args) +#define PWC_DEBUG_MEMORY(fmt, args...) PWC_DEBUG(MEMORY, fmt, ##args) +#define PWC_DEBUG_FLOW(fmt, args...) PWC_DEBUG(FLOW, fmt, ##args) +#define PWC_DEBUG_SIZE(fmt, args...) PWC_DEBUG(SIZE, fmt, ##args) +#define PWC_DEBUG_IOCTL(fmt, args...) PWC_DEBUG(IOCTL, fmt, ##args) +#define PWC_DEBUG_TRACE(fmt, args...) PWC_DEBUG(TRACE, fmt, ##args) + + +#if CONFIG_PWC_DEBUG + +#define PWC_DEBUG_LEVEL (PWC_DEBUG_LEVEL_MODULE) + +#define PWC_DEBUG(level, fmt, args...) do {\ + if ((PWC_DEBUG_LEVEL_ ##level) & pwc_trace) \ + printk(KERN_DEBUG PFX fmt, ##args); \ + } while(0) + +#define PWC_ERROR(fmt, args...) printk(KERN_ERR PFX fmt, ##args) +#define PWC_WARNING(fmt, args...) printk(KERN_WARNING PFX fmt, ##args) +#define PWC_INFO(fmt, args...) printk(KERN_INFO PFX fmt, ##args) +#define PWC_TRACE(fmt, args...) PWC_DEBUG(TRACE, fmt, ##args) + +#else /* if ! CONFIG_PWC_DEBUG */ + +#define PWC_ERROR(fmt, args...) printk(KERN_ERR PFX fmt, ##args) +#define PWC_WARNING(fmt, args...) printk(KERN_WARNING PFX fmt, ##args) +#define PWC_INFO(fmt, args...) printk(KERN_INFO PFX fmt, ##args) +#define PWC_TRACE(fmt, args...) do { } while(0) +#define PWC_DEBUG(level, fmt, args...) do { } while(0) + +#define pwc_trace 0 +#endif /* Defines for ToUCam cameras */ #define TOUCAM_HEADER_SIZE 8 #define TOUCAM_TRAILER_SIZE 4 #define FEATURE_MOTOR_PANTILT 0x0001 - -/* Version block */ -#define PWC_MAJOR 9 -#define PWC_MINOR 0 -#define PWC_VERSION "9.0.2-unofficial" -#define PWC_NAME "pwc" +#define FEATURE_CODEC1 0x0002 +#define FEATURE_CODEC2 0x0004 /* Turn certain features on/off */ #define PWC_INT_PIPE 0 @@ -96,6 +131,18 @@ /* Absolute maximum number of buffers available for mmap() */ #define MAX_IMAGES 10 +/* Some macros to quickly find the type of a webcam */ +#define DEVICE_USE_CODEC1(x) ((x)<675) +#define DEVICE_USE_CODEC2(x) ((x)>=675 && (x)<700) +#define DEVICE_USE_CODEC3(x) ((x)>=700) +#define DEVICE_USE_CODEC23(x) ((x)>=675) + + +#ifndef V4L2_PIX_FMT_PWC1 +#define V4L2_PIX_FMT_PWC1 v4l2_fourcc('P','W','C','1') +#define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P','W','C','2') +#endif + /* The following structures were based on cpia.h. Why reinvent the wheel? :-) */ struct pwc_iso_buf { @@ -111,17 +158,19 @@ struct pwc_frame_buf void *data; volatile int filled; /* number of bytes filled */ struct pwc_frame_buf *next; /* list */ -#if PWC_DEBUG - int sequence; /* Sequence number */ -#endif +}; + +/* additionnal informations used when dealing image between kernel and userland */ +struct pwc_imgbuf +{ + unsigned long offset; /* offset of this buffer in the big array of image_data */ + int vma_use_count; /* count the number of time this memory is mapped */ }; struct pwc_device { struct video_device *vdev; -#ifdef PWC_MAGIC - int magic; -#endif + /* Pointer to our usb_device */ struct usb_device *udev; @@ -178,12 +227,8 @@ struct pwc_device int frame_size; int frame_total_size; /* including header & trailer */ int drop_frames; -#if PWC_DEBUG - int sequence; /* Debugging aid */ -#endif /* 3: decompression */ - struct pwc_decompressor *decompressor; /* function block with decompression routines */ void *decompress_data; /* private data for decompression engine */ /* 4: image */ @@ -199,7 +244,7 @@ struct pwc_device struct pwc_coord offset; /* offset within the viewport */ void *image_data; /* total buffer, which is subdivided into ... */ - void *image_ptr[MAX_IMAGES]; /* ...several images... */ + struct pwc_imgbuf images[MAX_IMAGES];/* ...several images... */ int fill_image; /* ...which are rotated. */ int len_per_image; /* length per image */ int image_read_pos; /* In case we read data in pieces, keep track of were we are in the imagebuffer */ @@ -212,6 +257,7 @@ struct pwc_device struct pwc_mpt_range angle_range; int pan_angle; /* in degrees * 100 */ int tilt_angle; /* absolute angle; 0,0 is home position */ + int snapshot_button_status; /* set to 1 when the user push the button, reset to 0 when this value is read */ /*** Misc. data ***/ wait_queue_head_t frameq; /* When waiting for a frame to finish... */ @@ -220,20 +266,27 @@ struct pwc_device #endif }; - #ifdef __cplusplus extern "C" { #endif -/* Global variable */ +/* Global variables */ +#if CONFIG_PWC_DEBUG extern int pwc_trace; +#endif +extern int pwc_preferred_compression; +extern int pwc_mbufs; /** functions in pwc-if.c */ int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot); +int pwc_handle_frame(struct pwc_device *pdev); +void pwc_next_image(struct pwc_device *pdev); +int pwc_isoc_init(struct pwc_device *pdev); +void pwc_isoc_cleanup(struct pwc_device *pdev); /** Functions in pwc-misc.c */ /* sizes in pixels */ -extern struct pwc_coord pwc_image_sizes[PSZ_MAX]; +extern const struct pwc_coord pwc_image_sizes[PSZ_MAX]; int pwc_decode_size(struct pwc_device *pdev, int width, int height); void pwc_construct(struct pwc_device *pdev); @@ -241,6 +294,9 @@ void pwc_construct(struct pwc_device *pdev); /** Functions in pwc-ctrl.c */ /* Request a certain video mode. Returns < 0 if not possible */ extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot); +/* Calculate the number of bytes per image (not frame) */ +extern int pwc_mpt_reset(struct pwc_device *pdev, int flags); +extern int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt); /* Various controls; should be obvious. Value 0..65535, or < 0 on error */ extern int pwc_get_brightness(struct pwc_device *pdev); @@ -249,10 +305,37 @@ extern int pwc_get_contrast(struct pwc_device *pdev); extern int pwc_set_contrast(struct pwc_device *pdev, int value); extern int pwc_get_gamma(struct pwc_device *pdev); extern int pwc_set_gamma(struct pwc_device *pdev, int value); -extern int pwc_get_saturation(struct pwc_device *pdev); +extern int pwc_get_saturation(struct pwc_device *pdev, int *value); extern int pwc_set_saturation(struct pwc_device *pdev, int value); extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value); +extern int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value); extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor); +extern int pwc_restore_user(struct pwc_device *pdev); +extern int pwc_save_user(struct pwc_device *pdev); +extern int pwc_restore_factory(struct pwc_device *pdev); + +/* exported for use by v4l2 controls */ +extern int pwc_get_red_gain(struct pwc_device *pdev, int *value); +extern int pwc_set_red_gain(struct pwc_device *pdev, int value); +extern int pwc_get_blue_gain(struct pwc_device *pdev, int *value); +extern int pwc_set_blue_gain(struct pwc_device *pdev, int value); +extern int pwc_get_awb(struct pwc_device *pdev); +extern int pwc_set_awb(struct pwc_device *pdev, int mode); +extern int pwc_set_agc(struct pwc_device *pdev, int mode, int value); +extern int pwc_get_agc(struct pwc_device *pdev, int *value); +extern int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value); +extern int pwc_get_shutter_speed(struct pwc_device *pdev, int *value); + +extern int pwc_set_colour_mode(struct pwc_device *pdev, int colour); +extern int pwc_get_colour_mode(struct pwc_device *pdev, int *colour); +extern int pwc_set_contour(struct pwc_device *pdev, int contour); +extern int pwc_get_contour(struct pwc_device *pdev, int *contour); +extern int pwc_set_backlight(struct pwc_device *pdev, int backlight); +extern int pwc_get_backlight(struct pwc_device *pdev, int *backlight); +extern int pwc_set_flicker(struct pwc_device *pdev, int flicker); +extern int pwc_get_flicker(struct pwc_device *pdev, int *flicker); +extern int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise); +extern int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise); /* Power down or up the camera; not supported by all models */ extern int pwc_camera_power(struct pwc_device *pdev, int power); @@ -260,6 +343,9 @@ extern int pwc_camera_power(struct pwc_device *pdev, int power); /* Private ioctl()s; see pwc-ioctl.h */ extern int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg); +/** Functions in pwc-v4l.c */ +extern int pwc_video_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg); /** pwc-uncompress.c */ /* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */ @@ -271,3 +357,4 @@ extern int pwc_decompress(struct pwc_device *pdev); #endif +/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ -- cgit v1.2.3 From 00ae040d86fe4250f09667bacd62bad2bd970090 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Apr 2006 11:09:17 -0300 Subject: Fix compilation for sn9c102 on older kernels From: reinhard schwab Signed-off-by: Reinhard Schwab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/sn9c102/sn9c102.h | 6 ++++++ linux/drivers/media/video/sn9c102/sn9c102_core.c | 6 ++++++ linux/drivers/media/video/usbvideo/usbvideo.h | 10 ++++++++++ linux/drivers/media/video/usbvideo/vicam.c | 6 ++++++ 4 files changed, 28 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/sn9c102/sn9c102.h b/linux/drivers/media/video/sn9c102/sn9c102.h index 3f644c63f..1fcb7e73d 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102.h +++ b/linux/drivers/media/video/sn9c102/sn9c102.h @@ -34,7 +34,9 @@ #include #include #include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#endif #include #include @@ -146,7 +148,11 @@ struct sn9c102_device { enum sn9c102_dev_state state; u8 users; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex dev_mutex, fileop_mutex; +#else + struct semaphore dev_mutex, fileop_mutex; +#endif spinlock_t queue_lock; wait_queue_head_t open, wait_frame, wait_stream; }; diff --git a/linux/drivers/media/video/sn9c102/sn9c102_core.c b/linux/drivers/media/video/sn9c102/sn9c102_core.c index ea4394dc9..3add1c79f 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_core.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_core.c @@ -1769,7 +1769,13 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) pos = cam->frame[i].bufmem; while (size > 0) { /* size is page-aligned */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15) + unsigned long page = vmalloc_to_pfn(pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, + vma->vm_page_prot)) { +#else if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { +#endif mutex_unlock(&cam->fileop_mutex); return -EAGAIN; } diff --git a/linux/drivers/media/video/usbvideo/usbvideo.h b/linux/drivers/media/video/usbvideo/usbvideo.h index 0d2066b7d..de266e78a 100644 --- a/linux/drivers/media/video/usbvideo/usbvideo.h +++ b/linux/drivers/media/video/usbvideo/usbvideo.h @@ -20,7 +20,9 @@ #include "compat.h" #include #include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#endif /* Most helpful debugging aid */ #define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__)))) @@ -215,7 +217,11 @@ struct uvd { unsigned long flags; /* FLAGS_USBVIDEO_xxx */ unsigned long paletteBits; /* Which palettes we accept? */ unsigned short defaultPalette; /* What palette to use for read() */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex lock; +#else + struct semaphore lock; +#endif int user; /* user count for exclusive use */ videosize_t videosize; /* Current setting */ @@ -274,7 +280,11 @@ struct usbvideo { int num_cameras; /* As allocated */ struct usb_driver usbdrv; /* Interface to the USB stack */ char drvName[80]; /* Driver name */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex lock; /* Mutex protecting camera structures */ +#else + struct semaphore lock; +#endif struct usbvideo_cb cb; /* Table of callbacks (virtual methods) */ struct video_device vdt; /* Video device template */ struct uvd *cam; /* Array of camera structures */ diff --git a/linux/drivers/media/video/usbvideo/vicam.c b/linux/drivers/media/video/usbvideo/vicam.c index 98cb58e89..0ae33d432 100644 --- a/linux/drivers/media/video/usbvideo/vicam.c +++ b/linux/drivers/media/video/usbvideo/vicam.c @@ -43,7 +43,9 @@ #include #include #include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include +#endif #include "usbvideo.h" // #define VICAM_DEBUG @@ -409,7 +411,11 @@ struct vicam_camera { struct usb_device *udev; // usb device /* guard against simultaneous accesses to the camera */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) struct mutex cam_lock; +#else + struct semaphore cam_lock; +#endif int is_initialized; u8 open_count; -- cgit v1.2.3 From 795652e2cf756c5cfa9b4cecb3f066b8ea01eb08 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Apr 2006 11:26:16 -0300 Subject: Added missing pwc files from previous commit From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/pwc/pwc-dec1.c | 50 + linux/drivers/media/video/pwc/pwc-dec1.h | 43 + linux/drivers/media/video/pwc/pwc-dec23.c | 941 +++++++++++++++++ linux/drivers/media/video/pwc/pwc-dec23.h | 67 ++ linux/drivers/media/video/pwc/pwc-v4l.c | 1233 ++++++++++++++++++++++ linux/drivers/media/video/sn9c102/sn9c102.h | 4 +- linux/drivers/media/video/sn9c102/sn9c102_core.c | 2 +- 7 files changed, 2337 insertions(+), 3 deletions(-) create mode 100644 linux/drivers/media/video/pwc/pwc-dec1.c create mode 100644 linux/drivers/media/video/pwc/pwc-dec1.h create mode 100644 linux/drivers/media/video/pwc/pwc-dec23.c create mode 100644 linux/drivers/media/video/pwc/pwc-dec23.h create mode 100644 linux/drivers/media/video/pwc/pwc-v4l.c (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pwc/pwc-dec1.c b/linux/drivers/media/video/pwc/pwc-dec1.c new file mode 100644 index 000000000..c29593f58 --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-dec1.c @@ -0,0 +1,50 @@ +/* Linux driver for Philips webcam + Decompression for chipset version 1 + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + + +#include "pwc-dec1.h" + + +void pwc_dec1_init(int type, int release, void *buffer, void *table) +{ + +} + +void pwc_dec1_exit(void) +{ + + + +} + +int pwc_dec1_alloc(struct pwc_device *pwc) +{ + pwc->decompress_data = kmalloc(sizeof(struct pwc_dec1_private), GFP_KERNEL); + if (pwc->decompress_data == NULL) + return -ENOMEM; + return 0; +} + diff --git a/linux/drivers/media/video/pwc/pwc-dec1.h b/linux/drivers/media/video/pwc/pwc-dec1.h new file mode 100644 index 000000000..8b62ddcc5 --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-dec1.h @@ -0,0 +1,43 @@ +/* Linux driver for Philips webcam + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + + +#ifndef PWC_DEC1_H +#define PWC_DEC1_H + +#include "pwc.h" + +struct pwc_dec1_private +{ + int version; + +}; + +int pwc_dec1_alloc(struct pwc_device *pwc); +void pwc_dec1_init(int type, int release, void *buffer, void *private_data); +void pwc_dec1_exit(void); + +#endif + diff --git a/linux/drivers/media/video/pwc/pwc-dec23.c b/linux/drivers/media/video/pwc/pwc-dec23.c new file mode 100644 index 000000000..f9096c72c --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-dec23.c @@ -0,0 +1,941 @@ +/* Linux driver for Philips webcam + Decompression for chipset version 2 et 3 + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "pwc-timon.h" +#include "pwc-kiara.h" +#include "pwc-dec23.h" +#include + +#include + +/* + * USE_LOOKUP_TABLE_TO_CLAMP + * 0: use a C version of this tests: { a<0?0:(a>255?255:a) } + * 1: use a faster lookup table for cpu with a big cache (intel) + */ +#define USE_LOOKUP_TABLE_TO_CLAMP 1 +/* + * UNROLL_LOOP_FOR_COPYING_BLOCK + * 0: use a loop for a smaller code (but little slower) + * 1: when unrolling the loop, gcc produces some faster code (perhaps only + * valid for intel processor class). Activating this option, automaticaly + * activate USE_LOOKUP_TABLE_TO_CLAMP + */ +#define UNROLL_LOOP_FOR_COPY 1 +#if UNROLL_LOOP_FOR_COPY +# undef USE_LOOKUP_TABLE_TO_CLAMP +# define USE_LOOKUP_TABLE_TO_CLAMP 1 +#endif + +/* + * ENABLE_BAYER_DECODER + * 0: bayer decoder is not build (save some space) + * 1: bayer decoder is build and can be used + */ +#define ENABLE_BAYER_DECODER 0 + +static void build_subblock_pattern(struct pwc_dec23_private *pdec) +{ + static const unsigned int initial_values[12] = { + -0x526500, -0x221200, 0x221200, 0x526500, + -0x3de200, 0x3de200, + -0x6db480, -0x2d5d00, 0x2d5d00, 0x6db480, + -0x12c200, 0x12c200 + + }; + static const unsigned int values_derivated[12] = { + 0xa4ca, 0x4424, -0x4424, -0xa4ca, + 0x7bc4, -0x7bc4, + 0xdb69, 0x5aba, -0x5aba, -0xdb69, + 0x2584, -0x2584 + }; + unsigned int temp_values[12]; + int i, j; + + memcpy(temp_values, initial_values, sizeof(initial_values)); + for (i = 0; i < 256; i++) { + for (j = 0; j < 12; j++) { + pdec->table_subblock[i][j] = temp_values[j]; + temp_values[j] += values_derivated[j]; + } + } +} + +static void build_bit_powermask_table(struct pwc_dec23_private *pdec) +{ + unsigned char *p; + unsigned int bit, byte, mask, val; + unsigned int bitpower = 1; + + for (bit = 0; bit < 8; bit++) { + mask = bitpower - 1; + p = pdec->table_bitpowermask[bit]; + for (byte = 0; byte < 256; byte++) { + val = (byte & mask); + if (byte & bitpower) + val = -val; + *p++ = val; + } + bitpower<<=1; + } +} + + +static void build_table_color(const unsigned int romtable[16][8], + unsigned char p0004[16][1024], + unsigned char p8004[16][256]) +{ + int compression_mode, j, k, bit, pw; + unsigned char *p0, *p8; + const unsigned int *r; + + /* We have 16 compressions tables */ + for (compression_mode = 0; compression_mode < 16; compression_mode++) { + p0 = p0004[compression_mode]; + p8 = p8004[compression_mode]; + r = romtable[compression_mode]; + + for (j = 0; j < 8; j++, r++, p0 += 128) { + + for (k = 0; k < 16; k++) { + if (k == 0) + bit = 1; + else if (k >= 1 && k < 3) + bit = (r[0] >> 15) & 7; + else if (k >= 3 && k < 6) + bit = (r[0] >> 12) & 7; + else if (k >= 6 && k < 10) + bit = (r[0] >> 9) & 7; + else if (k >= 10 && k < 13) + bit = (r[0] >> 6) & 7; + else if (k >= 13 && k < 15) + bit = (r[0] >> 3) & 7; + else + bit = (r[0]) & 7; + if (k == 0) + *p8++ = 8; + else + *p8++ = j - bit; + *p8++ = bit; + + pw = 1 << bit; + p0[k + 0x00] = (1 * pw) + 0x80; + p0[k + 0x10] = (2 * pw) + 0x80; + p0[k + 0x20] = (3 * pw) + 0x80; + p0[k + 0x30] = (4 * pw) + 0x80; + p0[k + 0x40] = (-1 * pw) + 0x80; + p0[k + 0x50] = (-2 * pw) + 0x80; + p0[k + 0x60] = (-3 * pw) + 0x80; + p0[k + 0x70] = (-4 * pw) + 0x80; + } /* end of for (k=0; k<16; k++, p8++) */ + } /* end of for (j=0; j<8; j++ , table++) */ + } /* end of foreach compression_mode */ +} + +/* + * + */ +static void fill_table_dc00_d800(struct pwc_dec23_private *pdec) +{ +#define SCALEBITS 15 +#define ONE_HALF (1UL << (SCALEBITS - 1)) + int i; + unsigned int offset1 = ONE_HALF; + unsigned int offset2 = 0x0000; + + for (i=0; i<256; i++) { + pdec->table_dc00[i] = offset1 & ~(ONE_HALF); + pdec->table_d800[i] = offset2; + + offset1 += 0x7bc4; + offset2 += 0x7bc4; + } +} + +/* + * To decode the stream: + * if look_bits(2) == 0: # op == 2 in the lookup table + * skip_bits(2) + * end of the stream + * elif look_bits(3) == 7: # op == 1 in the lookup table + * skip_bits(3) + * yyyy = get_bits(4) + * xxxx = get_bits(8) + * else: # op == 0 in the lookup table + * skip_bits(x) + * + * For speedup processing, we build a lookup table and we takes the first 6 bits. + * + * struct { + * unsigned char op; // operation to execute + * unsigned char bits; // bits use to perform operation + * unsigned char offset1; // offset to add to access in the table_0004 % 16 + * unsigned char offset2; // offset to add to access in the table_0004 + * } + * + * How to build this table ? + * op == 2 when (i%4)==0 + * op == 1 when (i%8)==7 + * op == 0 otherwise + * + */ +static const unsigned char hash_table_ops[64*4] = { + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x10, + 0x00, 0x06, 0x01, 0x30, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x01, 0x20, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x50, + 0x00, 0x05, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x03, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x10, + 0x00, 0x06, 0x02, 0x10, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x01, 0x60, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x50, + 0x00, 0x05, 0x02, 0x40, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x03, 0x40, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x10, + 0x00, 0x06, 0x01, 0x70, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x01, 0x20, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x50, + 0x00, 0x05, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x03, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x10, + 0x00, 0x06, 0x02, 0x50, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x01, 0x60, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x00, + 0x00, 0x04, 0x01, 0x50, + 0x00, 0x05, 0x02, 0x40, + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x40, + 0x00, 0x05, 0x03, 0x40, + 0x01, 0x00, 0x00, 0x00 +}; + +/* + * + */ +static const unsigned int MulIdx[16][16] = { + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}, + {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3,}, + {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,}, + {4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4,}, + {6, 7, 8, 9, 7, 10, 11, 8, 8, 11, 10, 7, 9, 8, 7, 6,}, + {4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4,}, + {1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2,}, + {0, 3, 3, 0, 1, 2, 2, 1, 2, 1, 1, 2, 3, 0, 0, 3,}, + {0, 1, 2, 3, 3, 2, 1, 0, 3, 2, 1, 0, 0, 1, 2, 3,}, + {1, 1, 1, 1, 3, 3, 3, 3, 0, 0, 0, 0, 2, 2, 2, 2,}, + {7, 10, 11, 8, 9, 8, 7, 6, 6, 7, 8, 9, 8, 11, 10, 7,}, + {4, 5, 5, 4, 5, 4, 4, 5, 5, 4, 4, 5, 4, 5, 5, 4,}, + {7, 9, 6, 8, 10, 8, 7, 11, 11, 7, 8, 10, 8, 6, 9, 7,}, + {1, 3, 0, 2, 2, 0, 3, 1, 2, 0, 3, 1, 1, 3, 0, 2,}, + {1, 2, 2, 1, 3, 0, 0, 3, 0, 3, 3, 0, 2, 1, 1, 2,}, + {10, 8, 7, 11, 8, 6, 9, 7, 7, 9, 6, 8, 11, 7, 8, 10} +}; + +#if USE_LOOKUP_TABLE_TO_CLAMP +#define MAX_OUTER_CROP_VALUE (512) +static unsigned char pwc_crop_table[256 + 2*MAX_OUTER_CROP_VALUE]; +#define CLAMP(x) (pwc_crop_table[MAX_OUTER_CROP_VALUE+(x)]) +#else +#define CLAMP(x) ((x)>255?255:((x)<0?0:x)) +#endif + + +/* If the type or the command change, we rebuild the lookup table */ +int pwc_dec23_init(struct pwc_device *pwc, int type, unsigned char *cmd) +{ + int flags, version, shift, i; + struct pwc_dec23_private *pdec; + + if (pwc->decompress_data == NULL) { + pdec = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); + if (pdec == NULL) + return -ENOMEM; + pwc->decompress_data = pdec; + } + pdec = pwc->decompress_data; + + if (DEVICE_USE_CODEC3(type)) { + flags = cmd[2] & 0x18; + if (flags == 8) + pdec->nbits = 7; /* More bits, mean more bits to encode the stream, but better quality */ + else if (flags == 0x10) + pdec->nbits = 8; + else + pdec->nbits = 6; + + version = cmd[2] >> 5; + build_table_color(KiaraRomTable[version][0], pdec->table_0004_pass1, pdec->table_8004_pass1); + build_table_color(KiaraRomTable[version][1], pdec->table_0004_pass2, pdec->table_8004_pass2); + + } else { + + flags = cmd[2] & 6; + if (flags == 2) + pdec->nbits = 7; + else if (flags == 4) + pdec->nbits = 8; + else + pdec->nbits = 6; + + version = cmd[2] >> 3; + build_table_color(TimonRomTable[version][0], pdec->table_0004_pass1, pdec->table_8004_pass1); + build_table_color(TimonRomTable[version][1], pdec->table_0004_pass2, pdec->table_8004_pass2); + } + + /* Informations can be coded on a variable number of bits but never less than 8 */ + shift = 8 - pdec->nbits; + pdec->scalebits = SCALEBITS - shift; + pdec->nbitsmask = 0xFF >> shift; + + fill_table_dc00_d800(pdec); + build_subblock_pattern(pdec); + build_bit_powermask_table(pdec); + +#if USE_LOOKUP_TABLE_TO_CLAMP + /* Build the static table to clamp value [0-255] */ + for (i=0;i> scalebits]; + *d++ = cm[c[1] >> scalebits]; + *d++ = cm[c[2] >> scalebits]; + *d++ = cm[c[3] >> scalebits]; + + d = dst + bytes_per_line; + *d++ = cm[c[4] >> scalebits]; + *d++ = cm[c[5] >> scalebits]; + *d++ = cm[c[6] >> scalebits]; + *d++ = cm[c[7] >> scalebits]; + + d = dst + bytes_per_line*2; + *d++ = cm[c[8] >> scalebits]; + *d++ = cm[c[9] >> scalebits]; + *d++ = cm[c[10] >> scalebits]; + *d++ = cm[c[11] >> scalebits]; + + d = dst + bytes_per_line*3; + *d++ = cm[c[12] >> scalebits]; + *d++ = cm[c[13] >> scalebits]; + *d++ = cm[c[14] >> scalebits]; + *d++ = cm[c[15] >> scalebits]; +#else + int i; + const int *c = src; + unsigned char *d = dst; + for (i = 0; i < 4; i++, c++) + *d++ = CLAMP((*c) >> scalebits); + + d = dst + bytes_per_line; + for (i = 0; i < 4; i++, c++) + *d++ = CLAMP((*c) >> scalebits); + + d = dst + bytes_per_line*2; + for (i = 0; i < 4; i++, c++) + *d++ = CLAMP((*c) >> scalebits); + + d = dst + bytes_per_line*3; + for (i = 0; i < 4; i++, c++) + *d++ = CLAMP((*c) >> scalebits); +#endif +} + +/* + * Copy the 4x4 image block to a CrCb plane buffer + * + */ +static void copy_image_block_CrCb(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits) +{ +#if UNROLL_LOOP_FOR_COPY + /* Unroll all loops */ + const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE; + const int *c = src; + unsigned char *d = dst; + + *d++ = cm[c[0] >> scalebits]; + *d++ = cm[c[4] >> scalebits]; + *d++ = cm[c[1] >> scalebits]; + *d++ = cm[c[5] >> scalebits]; + *d++ = cm[c[2] >> scalebits]; + *d++ = cm[c[6] >> scalebits]; + *d++ = cm[c[3] >> scalebits]; + *d++ = cm[c[7] >> scalebits]; + + d = dst + bytes_per_line; + *d++ = cm[c[12] >> scalebits]; + *d++ = cm[c[8] >> scalebits]; + *d++ = cm[c[13] >> scalebits]; + *d++ = cm[c[9] >> scalebits]; + *d++ = cm[c[14] >> scalebits]; + *d++ = cm[c[10] >> scalebits]; + *d++ = cm[c[15] >> scalebits]; + *d++ = cm[c[11] >> scalebits]; +#else + int i; + const int *c1 = src; + const int *c2 = src + 4; + unsigned char *d = dst; + + for (i = 0; i < 4; i++, c1++, c2++) { + *d++ = CLAMP((*c1) >> scalebits); + *d++ = CLAMP((*c2) >> scalebits); + } + c1 = src + 12; + d = dst + bytes_per_line; + for (i = 0; i < 4; i++, c1++, c2++) { + *d++ = CLAMP((*c1) >> scalebits); + *d++ = CLAMP((*c2) >> scalebits); + } +#endif +} + +#if ENABLE_BAYER_DECODER +/* + * Format: 8x2 pixels + * . G . G . G . G . G . G . G + * . . . . . . . . . . . . . . + * . G . G . G . G . G . G . G + * . . . . . . . . . . . . . . + * or + * . . . . . . . . . . . . . . + * G . G . G . G . G . G . G . + * . . . . . . . . . . . . . . + * G . G . G . G . G . G . G . +*/ +static void copy_image_block_Green(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits) +{ +#if UNROLL_LOOP_FOR_COPY + /* Unroll all loops */ + const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE; + unsigned char *d = dst; + const int *c = src; + + d[0] = cm[c[0] >> scalebits]; + d[2] = cm[c[1] >> scalebits]; + d[4] = cm[c[2] >> scalebits]; + d[6] = cm[c[3] >> scalebits]; + d[8] = cm[c[4] >> scalebits]; + d[10] = cm[c[5] >> scalebits]; + d[12] = cm[c[6] >> scalebits]; + d[14] = cm[c[7] >> scalebits]; + + d = dst + bytes_per_line; + d[0] = cm[c[8] >> scalebits]; + d[2] = cm[c[9] >> scalebits]; + d[4] = cm[c[10] >> scalebits]; + d[6] = cm[c[11] >> scalebits]; + d[8] = cm[c[12] >> scalebits]; + d[10] = cm[c[13] >> scalebits]; + d[12] = cm[c[14] >> scalebits]; + d[14] = cm[c[15] >> scalebits]; +#else + int i; + unsigned char *d; + const int *c = src; + + d = dst; + for (i = 0; i < 8; i++, c++) + d[i*2] = CLAMP((*c) >> scalebits); + + d = dst + bytes_per_line; + for (i = 0; i < 8; i++, c++) + d[i*2] = CLAMP((*c) >> scalebits); +#endif +} +#endif + +#if ENABLE_BAYER_DECODER +/* + * Format: 4x4 pixels + * R . R . R . R + * . B . B . B . + * R . R . R . R + * . B . B . B . + */ +static void copy_image_block_RedBlue(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits) +{ +#if UNROLL_LOOP_FOR_COPY + /* Unroll all loops */ + const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE; + unsigned char *d = dst; + const int *c = src; + + d[0] = cm[c[0] >> scalebits]; + d[2] = cm[c[1] >> scalebits]; + d[4] = cm[c[2] >> scalebits]; + d[6] = cm[c[3] >> scalebits]; + + d = dst + bytes_per_line; + d[1] = cm[c[4] >> scalebits]; + d[3] = cm[c[5] >> scalebits]; + d[5] = cm[c[6] >> scalebits]; + d[7] = cm[c[7] >> scalebits]; + + d = dst + bytes_per_line*2; + d[0] = cm[c[8] >> scalebits]; + d[2] = cm[c[9] >> scalebits]; + d[4] = cm[c[10] >> scalebits]; + d[6] = cm[c[11] >> scalebits]; + + d = dst + bytes_per_line*3; + d[1] = cm[c[12] >> scalebits]; + d[3] = cm[c[13] >> scalebits]; + d[5] = cm[c[14] >> scalebits]; + d[7] = cm[c[15] >> scalebits]; +#else + int i; + unsigned char *d; + const int *c = src; + + d = dst; + for (i = 0; i < 4; i++, c++) + d[i*2] = CLAMP((*c) >> scalebits); + + d = dst + bytes_per_line; + for (i = 0; i < 4; i++, c++) + d[i*2+1] = CLAMP((*c) >> scalebits); + + d = dst + bytes_per_line*2; + for (i = 0; i < 4; i++, c++) + d[i*2] = CLAMP((*c) >> scalebits); + + d = dst + bytes_per_line*3; + for (i = 0; i < 4; i++, c++) + d[i*2+1] = CLAMP((*c) >> scalebits); +#endif +} +#endif + +/* + * To manage the stream, we keep bits in a 32 bits register. + * fill_nbits(n): fill the reservoir with at least n bits + * skip_bits(n): discard n bits from the reservoir + * get_bits(n): fill the reservoir, returns the first n bits and discard the + * bits from the reservoir. + * __get_nbits(n): faster version of get_bits(n), but asumes that the reservoir + * contains at least n bits. bits returned is discarded. + */ +#define fill_nbits(pdec, nbits_wanted) do { \ + while (pdec->nbits_in_reservoir<(nbits_wanted)) \ + { \ + pdec->reservoir |= (*(pdec->stream)++) << (pdec->nbits_in_reservoir); \ + pdec->nbits_in_reservoir += 8; \ + } \ +} while(0); + +#define skip_nbits(pdec, nbits_to_skip) do { \ + pdec->reservoir >>= (nbits_to_skip); \ + pdec->nbits_in_reservoir -= (nbits_to_skip); \ +} while(0); + +#define get_nbits(pdec, nbits_wanted, result) do { \ + fill_nbits(pdec, nbits_wanted); \ + result = (pdec->reservoir) & ((1U<<(nbits_wanted))-1); \ + skip_nbits(pdec, nbits_wanted); \ +} while(0); + +#define __get_nbits(pdec, nbits_wanted, result) do { \ + result = (pdec->reservoir) & ((1U<<(nbits_wanted))-1); \ + skip_nbits(pdec, nbits_wanted); \ +} while(0); + +#define look_nbits(pdec, nbits_wanted) \ + ((pdec->reservoir) & ((1U<<(nbits_wanted))-1)) + +/* + * Decode a 4x4 pixel block + */ +static void decode_block(struct pwc_dec23_private *pdec, + const unsigned char *ptable0004, + const unsigned char *ptable8004) +{ + unsigned int primary_color; + unsigned int channel_v, offset1, op; + int i; + + fill_nbits(pdec, 16); + __get_nbits(pdec, pdec->nbits, primary_color); + + if (look_nbits(pdec,2) == 0) { + skip_nbits(pdec, 2); + /* Very simple, the color is the same for all pixels of the square */ + for (i = 0; i < 16; i++) + pdec->temp_colors[i] = pdec->table_dc00[primary_color]; + + return; + } + + /* This block is encoded with small pattern */ + for (i = 0; i < 16; i++) + pdec->temp_colors[i] = pdec->table_d800[primary_color]; + + __get_nbits(pdec, 3, channel_v); + channel_v = ((channel_v & 1) << 2) | (channel_v & 2) | ((channel_v & 4) >> 2); + + ptable0004 += (channel_v * 128); + ptable8004 += (channel_v * 32); + + offset1 = 0; + do + { + unsigned int htable_idx, rows = 0; + const unsigned int *block; + + /* [ zzzz y x x ] + * xx == 00 :=> end of the block def, remove the two bits from the stream + * yxx == 111 + * yxx == any other value + * + */ + fill_nbits(pdec, 16); + htable_idx = look_nbits(pdec, 6); + op = hash_table_ops[htable_idx * 4]; + + if (op == 2) { + skip_nbits(pdec, 2); + + } else if (op == 1) { + /* 15bits [ xxxx xxxx yyyy 111 ] + * yyy => offset in the table8004 + * xxx => offset in the tabled004 (tree) + */ + unsigned int mask, shift; + unsigned int nbits, col1; + unsigned int yyyy; + + skip_nbits(pdec, 3); + /* offset1 += yyyy */ + __get_nbits(pdec, 4, yyyy); + offset1 += 1 + yyyy; + offset1 &= 0x0F; + nbits = ptable8004[offset1 * 2]; + + /* col1 = xxxx xxxx */ + __get_nbits(pdec, nbits+1, col1); + + /* Bit mask table */ + mask = pdec->table_bitpowermask[nbits][col1]; + shift = ptable8004[offset1 * 2 + 1]; + rows = ((mask << shift) + 0x80) & 0xFF; + + block = pdec->table_subblock[rows]; + for (i = 0; i < 16; i++) + pdec->temp_colors[i] += block[MulIdx[offset1][i]]; + + } else { + /* op == 0 + * offset1 is coded on 3 bits + */ + unsigned int shift; + + offset1 += hash_table_ops [htable_idx * 4 + 2]; + offset1 &= 0x0F; + + rows = ptable0004[offset1 + hash_table_ops [htable_idx * 4 + 3]]; + block = pdec->table_subblock[rows]; + for (i = 0; i < 16; i++) + pdec->temp_colors[i] += block[MulIdx[offset1][i]]; + + shift = hash_table_ops[htable_idx * 4 + 1]; + skip_nbits(pdec, shift); + } + + } while (op != 2); + +} + +static void DecompressBand23(struct pwc_dec23_private *pdec, + const unsigned char *rawyuv, + unsigned char *planar_y, + unsigned char *planar_u, + unsigned char *planar_v, + unsigned int compressed_image_width, + unsigned int real_image_width) +{ + int compression_index, nblocks; + const unsigned char *ptable0004; + const unsigned char *ptable8004; + + pdec->reservoir = 0; + pdec->nbits_in_reservoir = 0; + pdec->stream = rawyuv + 1; /* The first byte of the stream is skipped */ + + get_nbits(pdec, 4, compression_index); + + /* pass 1: uncompress Y component */ + nblocks = compressed_image_width / 4; + + ptable0004 = pdec->table_0004_pass1[compression_index]; + ptable8004 = pdec->table_8004_pass1[compression_index]; + + /* Each block decode a square of 4x4 */ + while (nblocks) { + decode_block(pdec, ptable0004, ptable8004); + copy_image_block_Y(pdec->temp_colors, planar_y, real_image_width, pdec->scalebits); + planar_y += 4; + nblocks--; + } + + /* pass 2: uncompress UV component */ + nblocks = compressed_image_width / 8; + + ptable0004 = pdec->table_0004_pass2[compression_index]; + ptable8004 = pdec->table_8004_pass2[compression_index]; + + /* Each block decode a square of 4x4 */ + while (nblocks) { + decode_block(pdec, ptable0004, ptable8004); + copy_image_block_CrCb(pdec->temp_colors, planar_u, real_image_width/2, pdec->scalebits); + + decode_block(pdec, ptable0004, ptable8004); + copy_image_block_CrCb(pdec->temp_colors, planar_v, real_image_width/2, pdec->scalebits); + + planar_v += 8; + planar_u += 8; + nblocks -= 2; + } + +} + +#if ENABLE_BAYER_DECODER +/* + * Size need to be a multiple of 8 in width + * + * Return a block of four line encoded like this: + * + * G R G R G R G R G R G R G R G R + * B G B G B G B G B G B G B G B G + * G R G R G R G R G R G R G R G R + * B G B G B G B G B G B G B G B G + * + */ +static void DecompressBandBayer(struct pwc_dec23_private *pdec, + const unsigned char *rawyuv, + unsigned char *rgbbayer, + unsigned int compressed_image_width, + unsigned int real_image_width) +{ + int compression_index, nblocks; + const unsigned char *ptable0004; + const unsigned char *ptable8004; + unsigned char *dest; + + pdec->reservoir = 0; + pdec->nbits_in_reservoir = 0; + pdec->stream = rawyuv + 1; /* The first byte of the stream is skipped */ + + get_nbits(pdec, 4, compression_index); + + /* pass 1: uncompress RB component */ + nblocks = compressed_image_width / 4; + + ptable0004 = pdec->table_0004_pass1[compression_index]; + ptable8004 = pdec->table_8004_pass1[compression_index]; + dest = rgbbayer; + + /* Each block decode a square of 4x4 */ + while (nblocks) { + decode_block(pdec, ptable0004, ptable8004); + copy_image_block_RedBlue(pdec->temp_colors, rgbbayer, real_image_width, pdec->scalebits); + dest += 8; + nblocks--; + } + + /* pass 2: uncompress G component */ + nblocks = compressed_image_width / 8; + + ptable0004 = pdec->table_0004_pass2[compression_index]; + ptable8004 = pdec->table_8004_pass2[compression_index]; + + /* Each block decode a square of 4x4 */ + while (nblocks) { + decode_block(pdec, ptable0004, ptable8004); + copy_image_block_Green(pdec->temp_colors, rgbbayer+1, real_image_width, pdec->scalebits); + + decode_block(pdec, ptable0004, ptable8004); + copy_image_block_Green(pdec->temp_colors, rgbbayer+real_image_width, real_image_width, pdec->scalebits); + + rgbbayer += 16; + nblocks -= 2; + } +} +#endif + + +/** + * + * Uncompress a pwc23 buffer. + * + * pwc.view: size of the image wanted + * pwc.image: size of the image returned by the camera + * pwc.offset: (x,y) to displayer image in the view + * + * src: raw data + * dst: image output + * flags: PWCX_FLAG_PLANAR or PWCX_FLAG_BAYER + */ +void pwc_dec23_decompress(const struct pwc_device *pwc, + const void *src, + void *dst, + int flags) +{ + int bandlines_left, stride, bytes_per_block; + + bandlines_left = pwc->image.y / 4; + bytes_per_block = pwc->view.x * 4; + + if (flags & PWCX_FLAG_BAYER) { +#if ENABLE_BAYER_DECODER + /* RGB Bayer format */ + unsigned char *rgbout; + + stride = pwc->view.x * pwc->offset.y; + rgbout = dst + stride + pwc->offset.x; + + + while (bandlines_left--) { + + DecompressBandBayer(pwc->decompress_data, + src, + rgbout, + pwc->image.x, pwc->view.x); + + src += pwc->vbandlength; + rgbout += bytes_per_block; + + } +#else + memcpy(dst, 0, pwc->view.x * pwc->view.y); +#endif + + } else { + /* YUV420P image format */ + unsigned char *pout_planar_y; + unsigned char *pout_planar_u; + unsigned char *pout_planar_v; + unsigned int plane_size; + + plane_size = pwc->view.x * pwc->view.y; + + /* offset in Y plane */ + stride = pwc->view.x * pwc->offset.y; + pout_planar_y = dst + stride + pwc->offset.x; + + /* offsets in U/V planes */ + stride = (pwc->view.x * pwc->offset.y) / 4 + pwc->offset.x / 2; + pout_planar_u = dst + plane_size + stride; + pout_planar_v = dst + plane_size + plane_size / 4 + stride; + + while (bandlines_left--) { + + DecompressBand23(pwc->decompress_data, + src, + pout_planar_y, pout_planar_u, pout_planar_v, + pwc->image.x, pwc->view.x); + src += pwc->vbandlength; + pout_planar_y += bytes_per_block; + pout_planar_u += pwc->view.x; + pout_planar_v += pwc->view.x; + + } + + } + +} + +void pwc_dec23_exit(void) +{ + /* Do nothing */ + +} + +/** + * Allocate a private structure used by lookup table. + * You must call kfree() to free the memory allocated. + */ +int pwc_dec23_alloc(struct pwc_device *pwc) +{ + pwc->decompress_data = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); + if (pwc->decompress_data == NULL) + return -ENOMEM; + return 0; +} + +/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ diff --git a/linux/drivers/media/video/pwc/pwc-dec23.h b/linux/drivers/media/video/pwc/pwc-dec23.h new file mode 100644 index 000000000..1c55298ad --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-dec23.h @@ -0,0 +1,67 @@ +/* Linux driver for Philips webcam + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef PWC_DEC23_H +#define PWC_DEC23_H + +#include "pwc.h" + +struct pwc_dec23_private +{ + unsigned int scalebits; + unsigned int nbitsmask, nbits; /* Number of bits of a color in the compressed stream */ + + unsigned int reservoir; + unsigned int nbits_in_reservoir; + const unsigned char *stream; + int temp_colors[16]; + + unsigned char table_0004_pass1[16][1024]; + unsigned char table_0004_pass2[16][1024]; + unsigned char table_8004_pass1[16][256]; + unsigned char table_8004_pass2[16][256]; + unsigned int table_subblock[256][12]; + + unsigned char table_bitpowermask[8][256]; + unsigned int table_d800[256]; + unsigned int table_dc00[256]; + +}; + + +int pwc_dec23_alloc(struct pwc_device *pwc); +int pwc_dec23_init(struct pwc_device *pwc, int type, unsigned char *cmd); +void pwc_dec23_exit(void); +void pwc_dec23_decompress(const struct pwc_device *pwc, + const void *src, + void *dst, + int flags); + + + +#endif + + +/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ + diff --git a/linux/drivers/media/video/pwc/pwc-v4l.c b/linux/drivers/media/video/pwc/pwc-v4l.c new file mode 100644 index 000000000..961ce2ddf --- /dev/null +++ b/linux/drivers/media/video/pwc/pwc-v4l.c @@ -0,0 +1,1233 @@ +/* Linux driver for Philips webcam + USB and Video4Linux interface part. + (C) 1999-2004 Nemosoft Unv. + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pwc.h" + +static struct v4l2_queryctrl pwc_controls[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 128, + .step = 1, + .default_value = 64, + }, + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 64, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = -100, + .maximum = 100, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma", + .minimum = 0, + .maximum = 32, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Gain", + .minimum = 0, + .maximum = 256, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Gain", + .minimum = 0, + .maximum = 256, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto White Balance", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Shutter Speed (Exposure)", + .minimum = 0, + .maximum = 256, + .step = 1, + .default_value = 200, + }, + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain Enabled", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain Level", + .minimum = 0, + .maximum = 256, + .step = 1, + .default_value = 0, + }, +#if XAWTV_HAS_BEEN_FIXED + { + .id = V4L2_CID_PRIVATE_SAVE_USER, + .type = V4L2_CTRL_TYPE_BUTTON, + .name = "Save User Settings", + .minimum = 0, + .maximum = 0, + .step = 0, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_RESTORE_USER, + .type = V4L2_CTRL_TYPE_BUTTON, + .name = "Restore User Settings", + .minimum = 0, + .maximum = 0, + .step = 0, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_RESTORE_FACTORY, + .type = V4L2_CTRL_TYPE_BUTTON, + .name = "Restore Factory Settings", + .minimum = 0, + .maximum = 0, + .step = 0, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_COLOUR_MODE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Colour mode", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_AUTOCONTOUR, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto contour", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_CONTOUR, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contour", + .minimum = 0, + .maximum = 63, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_BACKLIGHT, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Backlight compensation", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_FLICKERLESS, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flickerless", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_PRIVATE_NOISE_REDUCTION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Noise reduction", + .minimum = 0, + .maximum = 3, + .step = 1, + .default_value = 0, + }, +#endif +}; + +#if CONFIG_PWC_DEBUG +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) +static const char *v4l1_ioctls[] = { + "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT", + "CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ", + "SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT", + "GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", + "SMICROCODE", "GVBIFMT", "SVBIFMT" }; +#define V4L1_IOCTLS ARRAY_SIZE(v4l1_ioctls) +#else +/* In 2.6.16-rc1 v4l_printk_ioctl is not defined but exported */ +extern void v4l_printk_ioctl(unsigned int cmd); +#endif +#endif + +static void pwc_vidioc_fill_fmt(const struct pwc_device *pdev, struct v4l2_format *f) +{ + memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); + f->fmt.pix.width = pdev->view.x; + f->fmt.pix.height = pdev->view.y; + f->fmt.pix.field = V4L2_FIELD_NONE; + if (pdev->vpalette == VIDEO_PALETTE_YUV420P) { + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; + f->fmt.pix.bytesperline = (f->fmt.pix.width * 3)/2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + } else { + /* vbandlength contains 4 lines ... */ + f->fmt.pix.bytesperline = pdev->vbandlength/4; + f->fmt.pix.sizeimage = pdev->frame_size + sizeof(struct pwc_raw_frame); + if (DEVICE_USE_CODEC1(pdev->type)) + f->fmt.pix.pixelformat = V4L2_PIX_FMT_PWC1; + else + f->fmt.pix.pixelformat = V4L2_PIX_FMT_PWC2; + } + PWC_DEBUG_IOCTL("pwc_vidioc_fill_fmt() " + "width=%d, height=%d, bytesperline=%d, sizeimage=%d, pixelformat=%c%c%c%c\n", + f->fmt.pix.width, + f->fmt.pix.height, + f->fmt.pix.bytesperline, + f->fmt.pix.sizeimage, + (f->fmt.pix.pixelformat)&255, + (f->fmt.pix.pixelformat>>8)&255, + (f->fmt.pix.pixelformat>>16)&255, + (f->fmt.pix.pixelformat>>24)&255); +} + +/* ioctl(VIDIOC_TRY_FMT) */ +static int pwc_vidioc_try_fmt(struct pwc_device *pdev, struct v4l2_format *f) +{ + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + PWC_DEBUG_IOCTL("Bad video type must be V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); + return -EINVAL; + } + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YUV420: + break; + case V4L2_PIX_FMT_PWC1: + if (DEVICE_USE_CODEC23(pdev->type)) { + PWC_DEBUG_IOCTL("codec1 is only supported for old pwc webcam\n"); + return -EINVAL; + } + break; + case V4L2_PIX_FMT_PWC2: + if (DEVICE_USE_CODEC1(pdev->type)) { + PWC_DEBUG_IOCTL("codec23 is only supported for new pwc webcam\n"); + return -EINVAL; + } + break; + default: + PWC_DEBUG_IOCTL("Unsupported pixel format\n"); + return -EINVAL; + + } + + if (f->fmt.pix.width > pdev->view_max.x) + f->fmt.pix.width = pdev->view_max.x; + else if (f->fmt.pix.width < pdev->view_min.x) + f->fmt.pix.width = pdev->view_min.x; + + if (f->fmt.pix.height > pdev->view_max.y) + f->fmt.pix.height = pdev->view_max.y; + else if (f->fmt.pix.height < pdev->view_min.y) + f->fmt.pix.height = pdev->view_min.y; + + return 0; +} + +/* ioctl(VIDIOC_SET_FMT) */ +static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f) +{ + int ret, fps, snapshot, compression, pixelformat; + + ret = pwc_vidioc_try_fmt(pdev, f); + if (ret<0) + return ret; + + pixelformat = f->fmt.pix.pixelformat; + compression = pdev->vcompression; + snapshot = 0; + fps = pdev->vframes; + if (f->fmt.pix.priv) { + compression = (f->fmt.pix.priv & PWC_QLT_MASK) >> PWC_QLT_SHIFT; + snapshot = !!(f->fmt.pix.priv & PWC_FPS_SNAPSHOT); + fps = (f->fmt.pix.priv & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT; + if (fps == 0) + fps = pdev->vframes; + } + + if (pixelformat == V4L2_PIX_FMT_YUV420) + pdev->vpalette = VIDEO_PALETTE_YUV420P; + else + pdev->vpalette = VIDEO_PALETTE_RAW; + + PWC_DEBUG_IOCTL("Try to change format to: width=%d height=%d fps=%d " + "compression=%d snapshot=%d format=%c%c%c%c\n", + f->fmt.pix.width, f->fmt.pix.height, fps, + compression, snapshot, + (pixelformat)&255, + (pixelformat>>8)&255, + (pixelformat>>16)&255, + (pixelformat>>24)&255); + + ret = pwc_try_video_mode(pdev, + f->fmt.pix.width, + f->fmt.pix.height, + fps, + compression, + snapshot); + + PWC_DEBUG_IOCTL("pwc_try_video_mode(), return=%d\n", ret); + + if (ret) + return ret; + + pwc_vidioc_fill_fmt(pdev, f); + + return 0; + +} + +int pwc_video_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct pwc_device *pdev; + DECLARE_WAITQUEUE(wait, current); + + if (vdev == NULL) + return -EFAULT; + pdev = vdev->priv; + if (pdev == NULL) + return -EFAULT; + +#if CONFIG_PWC_DEBUG +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) + switch (_IOC_TYPE(cmd)) { + case 'v': + PWC_DEBUG_IOCTL("ioctl 0x%x (v4l1, VIDIOC%s)\n", cmd, + (_IOC_NR(cmd) < V4L1_IOCTLS) ? v4l1_ioctls[_IOC_NR(cmd)] : "???"); + break; + case 'V': + PWC_DEBUG_IOCTL("ioctl 0x%x (v4l2, %s)\n", cmd, + v4l2_ioctl_names[_IOC_NR(cmd)]); + break; + default: + PWC_DEBUG_IOCTL("ioctl 0x%x (unknown)\n", cmd); + } +#else + if (PWC_DEBUG_LEVEL_IOCTL & pwc_trace) + v4l_printk_ioctl(cmd); +#endif +#endif + + + switch (cmd) { + /* Query cabapilities */ + case VIDIOCGCAP: + { + struct video_capability *caps = arg; + + strcpy(caps->name, vdev->name); + caps->type = VID_TYPE_CAPTURE; + caps->channels = 1; + caps->audios = 1; + caps->minwidth = pdev->view_min.x; + caps->minheight = pdev->view_min.y; + caps->maxwidth = pdev->view_max.x; + caps->maxheight = pdev->view_max.y; + break; + } + + /* Channel functions (simulate 1 channel) */ + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + + if (v->channel != 0) + return -EINVAL; + v->flags = 0; + v->tuners = 0; + v->type = VIDEO_TYPE_CAMERA; + strcpy(v->name, "Webcam"); + return 0; + } + + case VIDIOCSCHAN: + { + /* The spec says the argument is an integer, but + the bttv driver uses a video_channel arg, which + makes sense becasue it also has the norm flag. + */ + struct video_channel *v = arg; + if (v->channel != 0) + return -EINVAL; + return 0; + } + + + /* Picture functions; contrast etc. */ + case VIDIOCGPICT: + { + struct video_picture *p = arg; + int val; + + val = pwc_get_brightness(pdev); + if (val >= 0) + p->brightness = (val<<9); + else + p->brightness = 0xffff; + val = pwc_get_contrast(pdev); + if (val >= 0) + p->contrast = (val<<10); + else + p->contrast = 0xffff; + /* Gamma, Whiteness, what's the difference? :) */ + val = pwc_get_gamma(pdev); + if (val >= 0) + p->whiteness = (val<<11); + else + p->whiteness = 0xffff; + if (pwc_get_saturation(pdev, &val)<0) + p->colour = 0xffff; + else + p->colour = 32768 + val * 327; + p->depth = 24; + p->palette = pdev->vpalette; + p->hue = 0xFFFF; /* N/A */ + break; + } + + case VIDIOCSPICT: + { + struct video_picture *p = arg; + /* + * FIXME: Suppose we are mid read + ANSWER: No problem: the firmware of the camera + can handle brightness/contrast/etc + changes at _any_ time, and the palette + is used exactly once in the uncompress + routine. + */ + pwc_set_brightness(pdev, p->brightness); + pwc_set_contrast(pdev, p->contrast); + pwc_set_gamma(pdev, p->whiteness); + pwc_set_saturation(pdev, (p->colour-32768)/327); + if (p->palette && p->palette != pdev->vpalette) { + switch (p->palette) { + case VIDEO_PALETTE_YUV420P: + case VIDEO_PALETTE_RAW: + pdev->vpalette = p->palette; + return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); + break; + default: + return -EINVAL; + break; + } + } + break; + } + + /* Window/size parameters */ + case VIDIOCGWIN: + { + struct video_window *vw = arg; + + vw->x = 0; + vw->y = 0; + vw->width = pdev->view.x; + vw->height = pdev->view.y; + vw->chromakey = 0; + vw->flags = (pdev->vframes << PWC_FPS_SHIFT) | + (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0); + break; + } + + case VIDIOCSWIN: + { + struct video_window *vw = arg; + int fps, snapshot, ret; + + fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT; + snapshot = vw->flags & PWC_FPS_SNAPSHOT; + if (fps == 0) + fps = pdev->vframes; + if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot) + return 0; + ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot); + if (ret) + return ret; + break; + } + + /* We don't have overlay support (yet) */ + case VIDIOCGFBUF: + { + struct video_buffer *vb = arg; + + memset(vb,0,sizeof(*vb)); + break; + } + + /* mmap() functions */ + case VIDIOCGMBUF: + { + /* Tell the user program how much memory is needed for a mmap() */ + struct video_mbuf *vm = arg; + int i; + + memset(vm, 0, sizeof(*vm)); + vm->size = pwc_mbufs * pdev->len_per_image; + vm->frames = pwc_mbufs; /* double buffering should be enough for most applications */ + for (i = 0; i < pwc_mbufs; i++) + vm->offsets[i] = i * pdev->len_per_image; + break; + } + + case VIDIOCMCAPTURE: + { + /* Start capture into a given image buffer (called 'frame' in video_mmap structure) */ + struct video_mmap *vm = arg; + + PWC_DEBUG_READ("VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format); + if (vm->frame < 0 || vm->frame >= pwc_mbufs) + return -EINVAL; + + /* xawtv is nasty. It probes the available palettes + by setting a very small image size and trying + various palettes... The driver doesn't support + such small images, so I'm working around it. + */ + if (vm->format) + { + switch (vm->format) + { + case VIDEO_PALETTE_YUV420P: + case VIDEO_PALETTE_RAW: + break; + default: + return -EINVAL; + break; + } + } + + if ((vm->width != pdev->view.x || vm->height != pdev->view.y) && + (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) { + int ret; + + PWC_DEBUG_OPEN("VIDIOCMCAPTURE: changing size to please xawtv :-(.\n"); + ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot); + if (ret) + return ret; + } /* ... size mismatch */ + + /* FIXME: should we lock here? */ + if (pdev->image_used[vm->frame]) + return -EBUSY; /* buffer wasn't available. Bummer */ + pdev->image_used[vm->frame] = 1; + + /* Okay, we're done here. In the SYNC call we wait until a + frame comes available, then expand image into the given + buffer. + In contrast to the CPiA cam the Philips cams deliver a + constant stream, almost like a grabber card. Also, + we have separate buffers for the rawdata and the image, + meaning we can nearly always expand into the requested buffer. + */ + PWC_DEBUG_READ("VIDIOCMCAPTURE done.\n"); + break; + } + + case VIDIOCSYNC: + { + /* The doc says: "Whenever a buffer is used it should + call VIDIOCSYNC to free this frame up and continue." + + The only odd thing about this whole procedure is + that MCAPTURE flags the buffer as "in use", and + SYNC immediately unmarks it, while it isn't + after SYNC that you know that the buffer actually + got filled! So you better not start a CAPTURE in + the same frame immediately (use double buffering). + This is not a problem for this cam, since it has + extra intermediate buffers, but a hardware + grabber card will then overwrite the buffer + you're working on. + */ + int *mbuf = arg; + int ret; + + PWC_DEBUG_READ("VIDIOCSYNC called (%d).\n", *mbuf); + + /* bounds check */ + if (*mbuf < 0 || *mbuf >= pwc_mbufs) + return -EINVAL; + /* check if this buffer was requested anyway */ + if (pdev->image_used[*mbuf] == 0) + return -EINVAL; + + /* Add ourselves to the frame wait-queue. + + FIXME: needs auditing for safety. + QUESTION: In what respect? I think that using the + frameq is safe now. + */ + add_wait_queue(&pdev->frameq, &wait); + while (pdev->full_frames == NULL) { + /* Check for unplugged/etc. here */ + if (pdev->error_status) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -pdev->error_status; + } + + if (signal_pending(current)) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -ERESTARTSYS; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + + /* The frame is ready. Expand in the image buffer + requested by the user. I don't care if you + mmap() 5 buffers and request data in this order: + buffer 4 2 3 0 1 2 3 0 4 3 1 . . . + Grabber hardware may not be so forgiving. + */ + PWC_DEBUG_READ("VIDIOCSYNC: frame ready.\n"); + pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */ + /* Decompress, etc */ + ret = pwc_handle_frame(pdev); + pdev->image_used[*mbuf] = 0; + if (ret) + return -EFAULT; + break; + } + + case VIDIOCGAUDIO: + { + struct video_audio *v = arg; + + strcpy(v->name, "Microphone"); + v->audio = -1; /* unknown audio minor */ + v->flags = 0; + v->mode = VIDEO_SOUND_MONO; + v->volume = 0; + v->bass = 0; + v->treble = 0; + v->balance = 0x8000; + v->step = 1; + break; + } + + case VIDIOCSAUDIO: + { + /* Dummy: nothing can be set */ + break; + } + + case VIDIOCGUNIT: + { + struct video_unit *vu = arg; + + vu->video = pdev->vdev->minor & 0x3F; + vu->audio = -1; /* not known yet */ + vu->vbi = -1; + vu->radio = -1; + vu->teletext = -1; + break; + } + + /* V4L2 Layer */ + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = arg; + + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCAP) This application "\ + "try to use the v4l2 layer\n"); + strcpy(cap->driver,PWC_NAME); + strlcpy(cap->card, vdev->name, sizeof(cap->card)); + usb_make_path(pdev->udev,cap->bus_info,sizeof(cap->bus_info)); + cap->version = PWC_VERSION_CODE; + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + return 0; + } + + case VIDIOC_ENUMINPUT: + { + struct v4l2_input *i = arg; + + if ( i->index ) /* Only one INPUT is supported */ + return -EINVAL; + + memset(i, 0, sizeof(struct v4l2_input)); + strcpy(i->name, "usb"); + return 0; + } + + case VIDIOC_G_INPUT: + { + int *i = arg; + *i = 0; /* Only one INPUT is supported */ + return 0; + } + case VIDIOC_S_INPUT: + { + int *i = arg; + + if ( *i ) { /* Only one INPUT is supported */ + PWC_DEBUG_IOCTL("Only one input source is"\ + " supported with this webcam.\n"); + return -EINVAL; + } + return 0; + } + + /* TODO: */ + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *c = arg; + int i; + + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) query id=%d\n", c->id); + for (i=0; iid) { + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) found\n"); + memcpy(c,&pwc_controls[i],sizeof(struct v4l2_queryctrl)); + return 0; + } + } + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) not found\n"); + + return -EINVAL; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *c = arg; + int ret; + + switch (c->id) + { + case V4L2_CID_BRIGHTNESS: + c->value = pwc_get_brightness(pdev); + if (c->value<0) + return -EINVAL; + return 0; + case V4L2_CID_CONTRAST: + c->value = pwc_get_contrast(pdev); + if (c->value<0) + return -EINVAL; + return 0; + case V4L2_CID_SATURATION: + ret = pwc_get_saturation(pdev, &c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_GAMMA: + c->value = pwc_get_gamma(pdev); + if (c->value<0) + return -EINVAL; + return 0; + case V4L2_CID_RED_BALANCE: + ret = pwc_get_red_gain(pdev, &c->value); + if (ret<0) + return -EINVAL; + c->value >>= 8; + return 0; + case V4L2_CID_BLUE_BALANCE: + ret = pwc_get_blue_gain(pdev, &c->value); + if (ret<0) + return -EINVAL; + c->value >>= 8; + return 0; + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = pwc_get_awb(pdev); + if (ret<0) + return -EINVAL; + c->value = (ret == PWC_WB_MANUAL)?0:1; + return 0; + case V4L2_CID_GAIN: + ret = pwc_get_agc(pdev, &c->value); + if (ret<0) + return -EINVAL; + c->value >>= 8; + return 0; + case V4L2_CID_AUTOGAIN: + ret = pwc_get_agc(pdev, &c->value); + if (ret<0) + return -EINVAL; + c->value = (c->value < 0)?1:0; + return 0; + case V4L2_CID_EXPOSURE: + ret = pwc_get_shutter_speed(pdev, &c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_COLOUR_MODE: + ret = pwc_get_colour_mode(pdev, &c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_AUTOCONTOUR: + ret = pwc_get_contour(pdev, &c->value); + if (ret < 0) + return -EINVAL; + c->value=(c->value == -1?1:0); + return 0; + case V4L2_CID_PRIVATE_CONTOUR: + ret = pwc_get_contour(pdev, &c->value); + if (ret < 0) + return -EINVAL; + c->value >>= 10; + return 0; + case V4L2_CID_PRIVATE_BACKLIGHT: + ret = pwc_get_backlight(pdev, &c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_FLICKERLESS: + ret = pwc_get_flicker(pdev, &c->value); + if (ret < 0) + return -EINVAL; + c->value=(c->value?1:0); + return 0; + case V4L2_CID_PRIVATE_NOISE_REDUCTION: + ret = pwc_get_dynamic_noise(pdev, &c->value); + if (ret < 0) + return -EINVAL; + return 0; + + case V4L2_CID_PRIVATE_SAVE_USER: + case V4L2_CID_PRIVATE_RESTORE_USER: + case V4L2_CID_PRIVATE_RESTORE_FACTORY: + return -EINVAL; + } + return -EINVAL; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *c = arg; + int ret; + + switch (c->id) + { + case V4L2_CID_BRIGHTNESS: + c->value <<= 9; + ret = pwc_set_brightness(pdev, c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_CONTRAST: + c->value <<= 10; + ret = pwc_set_contrast(pdev, c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_SATURATION: + ret = pwc_set_saturation(pdev, c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_GAMMA: + c->value <<= 11; + ret = pwc_set_gamma(pdev, c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_RED_BALANCE: + c->value <<= 8; + ret = pwc_set_red_gain(pdev, c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_BLUE_BALANCE: + c->value <<= 8; + ret = pwc_set_blue_gain(pdev, c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_AUTO_WHITE_BALANCE: + c->value = (c->value == 0)?PWC_WB_MANUAL:PWC_WB_AUTO; + ret = pwc_set_awb(pdev, c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_EXPOSURE: + c->value <<= 8; + ret = pwc_set_shutter_speed(pdev, c->value?0:1, c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_AUTOGAIN: + /* autogain off means nothing without a gain */ + if (c->value == 0) + return 0; + ret = pwc_set_agc(pdev, c->value, 0); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_GAIN: + c->value <<= 8; + ret = pwc_set_agc(pdev, 0, c->value); + if (ret<0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_SAVE_USER: + if (pwc_save_user(pdev)) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_RESTORE_USER: + if (pwc_restore_user(pdev)) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_RESTORE_FACTORY: + if (pwc_restore_factory(pdev)) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_COLOUR_MODE: + ret = pwc_set_colour_mode(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_AUTOCONTOUR: + c->value=(c->value == 1)?-1:0; + ret = pwc_set_contour(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_CONTOUR: + c->value <<= 10; + ret = pwc_set_contour(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_BACKLIGHT: + ret = pwc_set_backlight(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + case V4L2_CID_PRIVATE_FLICKERLESS: + ret = pwc_set_flicker(pdev, c->value); + if (ret < 0) + return -EINVAL; + case V4L2_CID_PRIVATE_NOISE_REDUCTION: + ret = pwc_set_dynamic_noise(pdev, c->value); + if (ret < 0) + return -EINVAL; + return 0; + + } + return -EINVAL; + } + + case VIDIOC_ENUM_FMT: + { + struct v4l2_fmtdesc *f = arg; + int index; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* We only support two format: the raw format, and YUV */ + index = f->index; + memset(f,0,sizeof(struct v4l2_fmtdesc)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->index = index; + switch(index) + { + case 0: + /* RAW format */ + f->pixelformat = pdev->type<=646?V4L2_PIX_FMT_PWC1:V4L2_PIX_FMT_PWC2; + f->flags = V4L2_FMT_FLAG_COMPRESSED; + strlcpy(f->description,"Raw Philips Webcam",sizeof(f->description)); + break; + case 1: + f->pixelformat = V4L2_PIX_FMT_YUV420; + strlcpy(f->description,"4:2:0, planar, Y-Cb-Cr",sizeof(f->description)); + break; + default: + return -EINVAL; + } + return 0; + } + + case VIDIOC_G_FMT: + { + struct v4l2_format *f = arg; + + PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n",pdev->image.x,pdev->image.y); + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + pwc_vidioc_fill_fmt(pdev, f); + + return 0; + } + + case VIDIOC_TRY_FMT: + return pwc_vidioc_try_fmt(pdev, arg); + + case VIDIOC_S_FMT: + return pwc_vidioc_set_fmt(pdev, arg); + + case VIDIOC_G_STD: + { + v4l2_std_id *std = arg; + *std = V4L2_STD_UNKNOWN; + return 0; + } + + case VIDIOC_S_STD: + { + v4l2_std_id *std = arg; + if (*std != V4L2_STD_UNKNOWN) + return -EINVAL; + return 0; + } + + case VIDIOC_ENUMSTD: + { + struct v4l2_standard *std = arg; + if (std->index != 0) + return -EINVAL; + std->id = V4L2_STD_UNKNOWN; + strncpy(std->name, "webcam", sizeof(std->name)); + return 0; + } + + case VIDIOC_REQBUFS: + { + struct v4l2_requestbuffers *rb = arg; + int nbuffers; + + PWC_DEBUG_IOCTL("ioctl(VIDIOC_REQBUFS) count=%d\n",rb->count); + if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (rb->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + nbuffers = rb->count; + if (nbuffers < 2) + nbuffers = 2; + else if (nbuffers > pwc_mbufs) + nbuffers = pwc_mbufs; + /* Force to use our # of buffers */ + rb->count = pwc_mbufs; + return 0; + } + + case VIDIOC_QUERYBUF: + { + struct v4l2_buffer *buf = arg; + int index; + + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) index=%d\n",buf->index); + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad type\n"); + return -EINVAL; + } + if (buf->memory != V4L2_MEMORY_MMAP) { + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad memory type\n"); + return -EINVAL; + } + index = buf->index; + if (index < 0 || index >= pwc_mbufs) { + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad index %d\n", buf->index); + return -EINVAL; + } + + memset(buf, 0, sizeof(struct v4l2_buffer)); + buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->index = index; + buf->m.offset = index * pdev->len_per_image; + if (pdev->vpalette == VIDEO_PALETTE_RAW) + buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame); + else + buf->bytesused = pdev->view.size; + buf->field = V4L2_FIELD_NONE; + buf->memory = V4L2_MEMORY_MMAP; + //buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->length = pdev->len_per_image; + + PWC_DEBUG_READ("VIDIOC_QUERYBUF: index=%d\n",buf->index); + PWC_DEBUG_READ("VIDIOC_QUERYBUF: m.offset=%d\n",buf->m.offset); + PWC_DEBUG_READ("VIDIOC_QUERYBUF: bytesused=%d\n",buf->bytesused); + + return 0; + } + + case VIDIOC_QBUF: + { + struct v4l2_buffer *buf = arg; + + PWC_DEBUG_IOCTL("ioctl(VIDIOC_QBUF) index=%d\n",buf->index); + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + if (buf->index < 0 || buf->index >= pwc_mbufs) + return -EINVAL; + + buf->flags |= V4L2_BUF_FLAG_QUEUED; + buf->flags &= ~V4L2_BUF_FLAG_DONE; + + return 0; + } + + case VIDIOC_DQBUF: + { + struct v4l2_buffer *buf = arg; + int ret; + + PWC_DEBUG_IOCTL("ioctl(VIDIOC_DQBUF)\n"); + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* Add ourselves to the frame wait-queue. + + FIXME: needs auditing for safety. + QUESTION: In what respect? I think that using the + frameq is safe now. + */ + add_wait_queue(&pdev->frameq, &wait); + while (pdev->full_frames == NULL) { + if (pdev->error_status) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -pdev->error_status; + } + + if (signal_pending(current)) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -ERESTARTSYS; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: frame ready.\n"); + /* Decompress data in pdev->images[pdev->fill_image] */ + ret = pwc_handle_frame(pdev); + if (ret) + return -EFAULT; + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: after pwc_handle_frame\n"); + + buf->index = pdev->fill_image; + if (pdev->vpalette == VIDEO_PALETTE_RAW) + buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame); + else + buf->bytesused = pdev->view.size; + buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->field = V4L2_FIELD_NONE; + do_gettimeofday(&buf->timestamp); + buf->sequence = 0; + buf->memory = V4L2_MEMORY_MMAP; + buf->m.offset = pdev->fill_image * pdev->len_per_image; + buf->length = buf->bytesused; + pwc_next_image(pdev); + + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n",buf->index); + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->length=%d\n",buf->length); + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: m.offset=%d\n",buf->m.offset); + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: bytesused=%d\n",buf->bytesused); + PWC_DEBUG_IOCTL("VIDIOC_DQBUF: leaving\n"); + return 0; + + } + + case VIDIOC_STREAMON: + { + /* WARNING: pwc_try_video_mode() called pwc_isoc_init */ + pwc_isoc_init(pdev); + return 0; + } + + case VIDIOC_STREAMOFF: + { + pwc_isoc_cleanup(pdev); + return 0; + } + + default: + return pwc_ioctl(pdev, cmd, arg); + } /* ..switch */ + return 0; +} + +/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ diff --git a/linux/drivers/media/video/sn9c102/sn9c102.h b/linux/drivers/media/video/sn9c102/sn9c102.h index 1fcb7e73d..a6a793d35 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102.h +++ b/linux/drivers/media/video/sn9c102/sn9c102.h @@ -36,7 +36,7 @@ #include #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include -#endif +#endif #include #include @@ -152,7 +152,7 @@ struct sn9c102_device { struct mutex dev_mutex, fileop_mutex; #else struct semaphore dev_mutex, fileop_mutex; -#endif +#endif spinlock_t queue_lock; wait_queue_head_t open, wait_frame, wait_stream; }; diff --git a/linux/drivers/media/video/sn9c102/sn9c102_core.c b/linux/drivers/media/video/sn9c102/sn9c102_core.c index 3add1c79f..20999b5fe 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_core.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_core.c @@ -1773,7 +1773,7 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) unsigned long page = vmalloc_to_pfn(pos); if (remap_pfn_range(vma, start, page, PAGE_SIZE, vma->vm_page_prot)) { -#else +#else if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { #endif mutex_unlock(&cam->fileop_mutex); -- cgit v1.2.3 From dba498eafc72039f1c6f29001c830d23bd8fe193 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Apr 2006 11:28:23 -0300 Subject: ZC0301 driver updates From: Luca Risolia ZC0301 driver updates: - Add support for PB-0330 image sensor - Generic documentation cleanups and updates Signed-off-by: Luca Risolia Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/zc0301/Kconfig | 6 +- linux/drivers/media/video/zc0301/Makefile | 2 +- linux/drivers/media/video/zc0301/zc0301.h | 6 +- linux/drivers/media/video/zc0301/zc0301_core.c | 166 +++++++++--------- .../drivers/media/video/zc0301/zc0301_pas202bcb.c | 20 +-- linux/drivers/media/video/zc0301/zc0301_pb0330.c | 187 +++++++++++++++++++++ linux/drivers/media/video/zc0301/zc0301_sensor.h | 32 +++- 7 files changed, 311 insertions(+), 108 deletions(-) create mode 100644 linux/drivers/media/video/zc0301/zc0301_pb0330.c (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/zc0301/Kconfig b/linux/drivers/media/video/zc0301/Kconfig index 115833e4f..a859a6920 100644 --- a/linux/drivers/media/video/zc0301/Kconfig +++ b/linux/drivers/media/video/zc0301/Kconfig @@ -1,9 +1,9 @@ config USB_ZC0301 - tristate "USB ZC0301 Image Processor and Control Chip support" + tristate "USB ZC0301[P] Image Processor and Control Chip support" depends on USB && VIDEO_V4L1 ---help--- - Say Y here if you want support for cameras based on the ZC0301 - Image Processor and Control Chip. + Say Y here if you want support for cameras based on the ZC0301 or + ZC0301P Image Processors and Control Chips. See for more info. diff --git a/linux/drivers/media/video/zc0301/Makefile b/linux/drivers/media/video/zc0301/Makefile index d749199d8..d9e6d97fa 100644 --- a/linux/drivers/media/video/zc0301/Makefile +++ b/linux/drivers/media/video/zc0301/Makefile @@ -1,3 +1,3 @@ -zc0301-objs := zc0301_core.o zc0301_pas202bcb.o +zc0301-objs := zc0301_core.o zc0301_pb0330.o zc0301_pas202bcb.o obj-$(CONFIG_USB_ZC0301) += zc0301.o diff --git a/linux/drivers/media/video/zc0301/zc0301.h b/linux/drivers/media/video/zc0301/zc0301.h index 6959c980a..aca383d39 100644 --- a/linux/drivers/media/video/zc0301/zc0301.h +++ b/linux/drivers/media/video/zc0301/zc0301.h @@ -158,7 +158,7 @@ do { \ dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ else if ((level) >= 3) \ dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ - __FUNCTION__, __LINE__ , ## args); \ + __FUNCTION__, __LINE__ , ## args); \ } \ } while (0) # define KDBG(level, fmt, args...) \ @@ -168,7 +168,7 @@ do { \ pr_info("zc0301: " fmt "\n", ## args); \ else if ((level) == 3) \ pr_debug("zc0301: [%s:%d] " fmt "\n", __FUNCTION__, \ - __LINE__ , ## args); \ + __LINE__ , ## args); \ } \ } while (0) # define V4LDBG(level, name, cmd) \ @@ -185,7 +185,7 @@ do { \ #undef PDBG #define PDBG(fmt, args...) \ dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ - __FUNCTION__, __LINE__ , ## args) + __FUNCTION__, __LINE__ , ## args) #undef PDBGG #define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ diff --git a/linux/drivers/media/video/zc0301/zc0301_core.c b/linux/drivers/media/video/zc0301/zc0301_core.c index 0fad39754..6e34f3d2f 100644 --- a/linux/drivers/media/video/zc0301/zc0301_core.c +++ b/linux/drivers/media/video/zc0301/zc0301_core.c @@ -48,12 +48,12 @@ /*****************************************************************************/ #define ZC0301_MODULE_NAME "V4L2 driver for ZC0301 " \ - "Image Processor and Control Chip" + "Image Processor and Control Chip" #define ZC0301_MODULE_AUTHOR "(C) 2006 Luca Risolia" #define ZC0301_AUTHOR_EMAIL "" #define ZC0301_MODULE_LICENSE "GPL" -#define ZC0301_MODULE_VERSION "1:1.03" -#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 3) +#define ZC0301_MODULE_VERSION "1:1.04" +#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 4) /*****************************************************************************/ @@ -67,67 +67,67 @@ MODULE_LICENSE(ZC0301_MODULE_LICENSE); static short video_nr[] = {[0 ... ZC0301_MAX_DEVICES-1] = -1}; module_param_array(video_nr, short, NULL, 0444); MODULE_PARM_DESC(video_nr, - "\n<-1|n[,...]> Specify V4L2 minor mode number." - "\n -1 = use next available (default)" - "\n n = use minor number n (integer >= 0)" - "\nYou can specify up to " - __MODULE_STRING(ZC0301_MAX_DEVICES) " cameras this way." - "\nFor example:" - "\nvideo_nr=-1,2,-1 would assign minor number 2 to" - "\nthe second registered camera and use auto for the first" - "\none and for every other camera." - "\n"); - -static short force_munmap[] = {[0 ... ZC0301_MAX_DEVICES-1] = - ZC0301_FORCE_MUNMAP}; + "\n<-1|n[,...]> Specify V4L2 minor mode number." + "\n -1 = use next available (default)" + "\n n = use minor number n (integer >= 0)" + "\nYou can specify up to " + __MODULE_STRING(ZC0301_MAX_DEVICES) " cameras this way." + "\nFor example:" + "\nvideo_nr=-1,2,-1 would assign minor number 2 to" + "\nthe second registered camera and use auto for the first" + "\none and for every other camera." + "\n"); + +static short force_munmap[] = {[0 ... ZC0301_MAX_DEVICES-1] = + ZC0301_FORCE_MUNMAP}; module_param_array(force_munmap, bool, NULL, 0444); MODULE_PARM_DESC(force_munmap, - "\n<0|1[,...]> Force the application to unmap previously" - "\nmapped buffer memory before calling any VIDIOC_S_CROP or" - "\nVIDIOC_S_FMT ioctl's. Not all the applications support" - "\nthis feature. This parameter is specific for each" - "\ndetected camera." - "\n 0 = do not force memory unmapping" - "\n 1 = force memory unmapping (save memory)" - "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." - "\n"); - -static unsigned int frame_timeout[] = {[0 ... ZC0301_MAX_DEVICES-1] = - ZC0301_FRAME_TIMEOUT}; + "\n<0|1[,...]> Force the application to unmap previously" + "\nmapped buffer memory before calling any VIDIOC_S_CROP or" + "\nVIDIOC_S_FMT ioctl's. Not all the applications support" + "\nthis feature. This parameter is specific for each" + "\ndetected camera." + "\n 0 = do not force memory unmapping" + "\n 1 = force memory unmapping (save memory)" + "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." + "\n"); + +static unsigned int frame_timeout[] = {[0 ... ZC0301_MAX_DEVICES-1] = + ZC0301_FRAME_TIMEOUT}; module_param_array(frame_timeout, uint, NULL, 0644); MODULE_PARM_DESC(frame_timeout, - "\n Timeout for a video frame in seconds." - "\nThis parameter is specific for each detected camera." - "\nDefault value is "__MODULE_STRING(ZC0301_FRAME_TIMEOUT)"." - "\n"); + "\n Timeout for a video frame in seconds." + "\nThis parameter is specific for each detected camera." + "\nDefault value is "__MODULE_STRING(ZC0301_FRAME_TIMEOUT)"." + "\n"); #ifdef ZC0301_DEBUG static unsigned short debug = ZC0301_DEBUG_LEVEL; module_param(debug, ushort, 0644); MODULE_PARM_DESC(debug, - "\n Debugging information level, from 0 to 3:" - "\n0 = none (use carefully)" - "\n1 = critical errors" - "\n2 = significant informations" - "\n3 = more verbose messages" - "\nLevel 3 is useful for testing only, when only " - "one device is used." - "\nDefault value is "__MODULE_STRING(ZC0301_DEBUG_LEVEL)"." - "\n"); + "\n Debugging information level, from 0 to 3:" + "\n0 = none (use carefully)" + "\n1 = critical errors" + "\n2 = significant informations" + "\n3 = more verbose messages" + "\nLevel 3 is useful for testing only, when only " + "one device is used." + "\nDefault value is "__MODULE_STRING(ZC0301_DEBUG_LEVEL)"." + "\n"); #endif /*****************************************************************************/ static u32 zc0301_request_buffers(struct zc0301_device* cam, u32 count, - enum zc0301_io_method io) + enum zc0301_io_method io) { struct v4l2_pix_format* p = &(cam->sensor.pix_format); struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); const size_t imagesize = cam->module_param.force_munmap || - io == IO_READ ? - (p->width * p->height * p->priv) / 8 : - (r->width * r->height * p->priv) / 8; + io == IO_READ ? + (p->width * p->height * p->priv) / 8 : + (r->width * r->height * p->priv) / 8; void* buff = NULL; u32 i; @@ -216,7 +216,7 @@ int zc0301_write_reg(struct zc0301_device* cam, u16 index, u16 value) int res; res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0xa0, 0x40, - value, index, NULL, 0, ZC0301_CTRL_TIMEOUT); + value, index, NULL, 0, ZC0301_CTRL_TIMEOUT); if (res < 0) { DBG(3, "Failed to write a register (index 0x%04X, " "value 0x%02X, error %d)",index, value, res); @@ -234,7 +234,7 @@ int zc0301_read_reg(struct zc0301_device* cam, u16 index) int res; res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0xa1, 0xc0, - 0x0001, index, buff, 1, ZC0301_CTRL_TIMEOUT); + 0x0001, index, buff, 1, ZC0301_CTRL_TIMEOUT); if (res < 0) DBG(3, "Failed to read a register (index 0x%04X, error %d)", index, res); @@ -337,11 +337,11 @@ static void zc0301_urb_complete(struct urb *urb, struct pt_regs* regs) if (!(*f)) (*f) = list_entry(cam->inqueue.next, struct zc0301_frame_t, - frame); + frame); imagesize = (cam->sensor.pix_format.width * - cam->sensor.pix_format.height * - cam->sensor.pix_format.priv) / 8; + cam->sensor.pix_format.height * + cam->sensor.pix_format.priv) / 8; for (i = 0; i < urb->number_of_packets; i++) { unsigned int len, status; @@ -395,8 +395,8 @@ end_of_frame: list_move_tail(&(*f)->frame, &cam->outqueue); if (!list_empty(&cam->inqueue)) (*f) = list_entry(cam->inqueue.next, - struct zc0301_frame_t, - frame); + struct zc0301_frame_t, + frame); else (*f) = NULL; spin_unlock(&cam->queue_lock); @@ -429,14 +429,14 @@ static int zc0301_start_transfer(struct zc0301_device* cam) struct usb_device *udev = cam->usbdev; struct urb* urb; const unsigned int wMaxPacketSize[] = {0, 128, 192, 256, 384, - 512, 768, 1023}; + 512, 768, 1023}; const unsigned int psz = wMaxPacketSize[ZC0301_ALTERNATE_SETTING]; s8 i, j; int err = 0; for (i = 0; i < ZC0301_URBS; i++) { cam->transfer_buffer[i] = kzalloc(ZC0301_ISO_PACKETS * psz, - GFP_KERNEL); + GFP_KERNEL); if (!cam->transfer_buffer[i]) { err = -ENOMEM; DBG(1, "Not enough memory"); @@ -528,9 +528,9 @@ static int zc0301_stream_interrupt(struct zc0301_device* cam) cam->stream = STREAM_INTERRUPT; timeout = wait_event_timeout(cam->wait_stream, - (cam->stream == STREAM_OFF) || - (cam->state & DEV_DISCONNECTED), - ZC0301_URB_TIMEOUT); + (cam->stream == STREAM_OFF) || + (cam->state & DEV_DISCONNECTED), + ZC0301_URB_TIMEOUT); if (cam->state & DEV_DISCONNECTED) return -ENODEV; else if (cam->stream != STREAM_OFF) { @@ -548,7 +548,7 @@ static int zc0301_stream_interrupt(struct zc0301_device* cam) static int zc0301_set_compression(struct zc0301_device* cam, - struct v4l2_jpegcompression* compression) + struct v4l2_jpegcompression* compression) { int r, err = 0; @@ -600,7 +600,7 @@ static int zc0301_init(struct zc0301_device* cam) if (s->set_ctrl) { for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (s->qctrl[i].id != 0 && + if (s->qctrl[i].id != 0 && !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { ctrl.id = s->qctrl[i].id; ctrl.value = qctrl[i].default_value; @@ -622,7 +622,7 @@ static int zc0301_init(struct zc0301_device* cam) init_waitqueue_head(&cam->wait_stream); cam->nreadbuffers = 2; memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); - memcpy(&(s->_rect), &(s->cropcap.defrect), + memcpy(&(s->_rect), &(s->cropcap.defrect), sizeof(struct v4l2_rect)); cam->state |= DEV_INITIALIZED; } @@ -670,8 +670,8 @@ static int zc0301_open(struct inode* inode, struct file* filp) } mutex_unlock(&cam->dev_mutex); err = wait_event_interruptible_exclusive(cam->open, - cam->state & DEV_DISCONNECTED - || !cam->users); + cam->state & DEV_DISCONNECTED + || !cam->users); if (err) { up_read(&zc0301_disconnect); return err; @@ -802,12 +802,12 @@ zc0301_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) return -EAGAIN; } timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); if (timeout < 0) { mutex_unlock(&cam->fileop_mutex); return timeout; @@ -930,7 +930,7 @@ static int zc0301_mmap(struct file* filp, struct vm_area_struct *vma) { struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); unsigned long size = vma->vm_end - vma->vm_start, - start = vma->vm_start; + start = vma->vm_start; void *pos; u32 i; @@ -998,13 +998,13 @@ zc0301_vidioc_querycap(struct zc0301_device* cam, void __user * arg) .driver = "zc0301", .version = ZC0301_MODULE_VERSION_CODE, .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING, + V4L2_CAP_STREAMING, }; strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) strlcpy(cap.bus_info, cam->usbdev->dev.bus_id, - sizeof(cap.bus_info)); + sizeof(cap.bus_info)); if (copy_to_user(arg, &cap, sizeof(cap))) return -EFAULT; @@ -1337,7 +1337,7 @@ zc0301_vidioc_g_fmt(struct zc0301_device* cam, void __user * arg) static int zc0301_vidioc_try_s_fmt(struct zc0301_device* cam, unsigned int cmd, - void __user * arg) + void __user * arg) { struct zc0301_sensor* s = &cam->sensor; struct v4l2_format format; @@ -1600,7 +1600,7 @@ zc0301_vidioc_qbuf(struct zc0301_device* cam, void __user * arg) static int zc0301_vidioc_dqbuf(struct zc0301_device* cam, struct file* filp, - void __user * arg) + void __user * arg) { struct v4l2_buffer b; struct zc0301_frame_t *f; @@ -1619,12 +1619,12 @@ zc0301_vidioc_dqbuf(struct zc0301_device* cam, struct file* filp, if (filp->f_flags & O_NONBLOCK) return -EAGAIN; timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); if (timeout < 0) return timeout; if (cam->state & DEV_DISCONNECTED) @@ -1748,7 +1748,7 @@ zc0301_vidioc_s_parm(struct zc0301_device* cam, void __user * arg) static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp, - unsigned int cmd, void __user * arg) + unsigned int cmd, void __user * arg) { struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); @@ -1842,7 +1842,7 @@ static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp, static int zc0301_ioctl(struct inode* inode, struct file* filp, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); int err = 0; @@ -1948,7 +1948,7 @@ zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) mutex_lock(&cam->dev_mutex); err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, - video_nr[dev_nr]); + video_nr[dev_nr]); if (err) { DBG(1, "V4L2 device registration failed"); if (err == -ENFILE && video_nr[dev_nr] == -1) @@ -1992,7 +1992,7 @@ static void zc0301_usb_disconnect(struct usb_interface* intf) down_write(&zc0301_disconnect); - mutex_lock(&cam->dev_mutex); + mutex_lock(&cam->dev_mutex); DBG(2, "Disconnecting %s...", cam->v4ldev->name); diff --git a/linux/drivers/media/video/zc0301/zc0301_pas202bcb.c b/linux/drivers/media/video/zc0301/zc0301_pas202bcb.c index eaadf0252..005ef2469 100644 --- a/linux/drivers/media/video/zc0301/zc0301_pas202bcb.c +++ b/linux/drivers/media/video/zc0301/zc0301_pas202bcb.c @@ -1,10 +1,10 @@ /*************************************************************************** - * Plug-in for PAS202BCB image sensor connected to the ZC030! Image * + * Plug-in for PAS202BCB image sensor connected to the ZC0301[P] Image * * Processor and Control Chip * * * * Copyright (C) 2006 by Luca Risolia * * * - * Initialization values of the ZC0301 have been taken from the SPCA5XX * + * Initialization values of the ZC0301[P] have been taken from the SPCA5XX * * driver maintained by Michel Xhaard * * * * This program is free software; you can redistribute it and/or modify * @@ -24,10 +24,10 @@ /* NOTE: Sensor controls are disabled for now, becouse changing them while - streaming sometimes results in out-of-sync video frames. We'll use - the default initialization, until we know how to stop and start video - in the chip. However, the image quality still looks good under various - light conditions. + streaming sometimes results in out-of-sync video frames. We'll use + the default initialization, until we know how to stop and start video + in the chip. However, the image quality still looks good under various + light conditions. */ #include @@ -164,8 +164,8 @@ static int pas202bcb_init(struct zc0301_device* cam) } -static int pas202bcb_get_ctrl(struct zc0301_device* cam, - struct v4l2_control* ctrl) +static int pas202bcb_get_ctrl(struct zc0301_device* cam, + struct v4l2_control* ctrl) { switch (ctrl->id) { case V4L2_CID_EXPOSURE: @@ -207,8 +207,8 @@ static int pas202bcb_get_ctrl(struct zc0301_device* cam, } -static int pas202bcb_set_ctrl(struct zc0301_device* cam, - const struct v4l2_control* ctrl) +static int pas202bcb_set_ctrl(struct zc0301_device* cam, + const struct v4l2_control* ctrl) { int err = 0; diff --git a/linux/drivers/media/video/zc0301/zc0301_pb0330.c b/linux/drivers/media/video/zc0301/zc0301_pb0330.c new file mode 100644 index 000000000..ed8542e6c --- /dev/null +++ b/linux/drivers/media/video/zc0301/zc0301_pb0330.c @@ -0,0 +1,187 @@ +/*************************************************************************** + * Plug-in for PB-0330 image sensor connected to the ZC0301[P] Image * + * Processor and Control Chip * + * * + * Copyright (C) 2006 by Luca Risolia * + * * + * Initialization values of the ZC0301[P] have been taken from the SPCA5XX * + * driver maintained by Michel Xhaard * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include +#include "zc0301_sensor.h" + + +static struct zc0301_sensor pb0330; + + +static int pb0330_init(struct zc0301_device* cam) +{ + int err = 0; + + err += zc0301_write_reg(cam, 0x0000, 0x01); + err += zc0301_write_reg(cam, 0x0008, 0x03); + err += zc0301_write_reg(cam, 0x0010, 0x0A); + err += zc0301_write_reg(cam, 0x0002, 0x00); + err += zc0301_write_reg(cam, 0x0003, 0x02); + err += zc0301_write_reg(cam, 0x0004, 0x80); + err += zc0301_write_reg(cam, 0x0005, 0x01); + err += zc0301_write_reg(cam, 0x0006, 0xE0); + err += zc0301_write_reg(cam, 0x0001, 0x01); + err += zc0301_write_reg(cam, 0x0012, 0x05); + err += zc0301_write_reg(cam, 0x0012, 0x07); + err += zc0301_write_reg(cam, 0x0098, 0x00); + err += zc0301_write_reg(cam, 0x009A, 0x00); + err += zc0301_write_reg(cam, 0x011A, 0x00); + err += zc0301_write_reg(cam, 0x011C, 0x00); + err += zc0301_write_reg(cam, 0x0012, 0x05); + + err += zc0301_i2c_write(cam, 0x01, 0x0006); + err += zc0301_i2c_write(cam, 0x02, 0x0011); + err += zc0301_i2c_write(cam, 0x03, 0x01E7); + err += zc0301_i2c_write(cam, 0x04, 0x0287); + err += zc0301_i2c_write(cam, 0x06, 0x0003); + err += zc0301_i2c_write(cam, 0x07, 0x3002); + err += zc0301_i2c_write(cam, 0x20, 0x1100); + err += zc0301_i2c_write(cam, 0x2F, 0xF7B0); + err += zc0301_i2c_write(cam, 0x30, 0x0005); + err += zc0301_i2c_write(cam, 0x31, 0x0000); + err += zc0301_i2c_write(cam, 0x34, 0x0100); + err += zc0301_i2c_write(cam, 0x35, 0x0060); + err += zc0301_i2c_write(cam, 0x3D, 0x068F); + err += zc0301_i2c_write(cam, 0x40, 0x01E0); + err += zc0301_i2c_write(cam, 0x58, 0x0078); + err += zc0301_i2c_write(cam, 0x62, 0x0411); + + err += zc0301_write_reg(cam, 0x0087, 0x10); + err += zc0301_write_reg(cam, 0x0101, 0x37); + err += zc0301_write_reg(cam, 0x0012, 0x05); + err += zc0301_write_reg(cam, 0x0100, 0x0D); + err += zc0301_write_reg(cam, 0x0189, 0x06); + err += zc0301_write_reg(cam, 0x01AD, 0x00); + err += zc0301_write_reg(cam, 0x01C5, 0x03); + err += zc0301_write_reg(cam, 0x01CB, 0x13); + err += zc0301_write_reg(cam, 0x0250, 0x08); + err += zc0301_write_reg(cam, 0x0301, 0x08); + err += zc0301_write_reg(cam, 0x01A8, 0x60); + err += zc0301_write_reg(cam, 0x018D, 0x6C); + err += zc0301_write_reg(cam, 0x01AD, 0x09); + err += zc0301_write_reg(cam, 0x01AE, 0x15); + err += zc0301_write_reg(cam, 0x010A, 0x50); + err += zc0301_write_reg(cam, 0x010B, 0xF8); + err += zc0301_write_reg(cam, 0x010C, 0xF8); + err += zc0301_write_reg(cam, 0x010D, 0xF8); + err += zc0301_write_reg(cam, 0x010E, 0x50); + err += zc0301_write_reg(cam, 0x010F, 0xF8); + err += zc0301_write_reg(cam, 0x0110, 0xF8); + err += zc0301_write_reg(cam, 0x0111, 0xF8); + err += zc0301_write_reg(cam, 0x0112, 0x50); + err += zc0301_write_reg(cam, 0x0008, 0x03); + err += zc0301_write_reg(cam, 0x01C6, 0x08); + err += zc0301_write_reg(cam, 0x01CB, 0x0F); + err += zc0301_write_reg(cam, 0x010A, 0x50); + err += zc0301_write_reg(cam, 0x010B, 0xF8); + err += zc0301_write_reg(cam, 0x010C, 0xF8); + err += zc0301_write_reg(cam, 0x010D, 0xF8); + err += zc0301_write_reg(cam, 0x010E, 0x50); + err += zc0301_write_reg(cam, 0x010F, 0xF8); + err += zc0301_write_reg(cam, 0x0110, 0xF8); + err += zc0301_write_reg(cam, 0x0111, 0xF8); + err += zc0301_write_reg(cam, 0x0112, 0x50); + err += zc0301_write_reg(cam, 0x0180, 0x00); + err += zc0301_write_reg(cam, 0x0019, 0x00); + + err += zc0301_i2c_write(cam, 0x05, 0x0066); + err += zc0301_i2c_write(cam, 0x09, 0x02B2); + err += zc0301_i2c_write(cam, 0x10, 0x0002); + + err += zc0301_write_reg(cam, 0x011D, 0x60); + err += zc0301_write_reg(cam, 0x0190, 0x00); + err += zc0301_write_reg(cam, 0x0191, 0x07); + err += zc0301_write_reg(cam, 0x0192, 0x8C); + err += zc0301_write_reg(cam, 0x0195, 0x00); + err += zc0301_write_reg(cam, 0x0196, 0x00); + err += zc0301_write_reg(cam, 0x0197, 0x8A); + err += zc0301_write_reg(cam, 0x018C, 0x10); + err += zc0301_write_reg(cam, 0x018F, 0x20); + err += zc0301_write_reg(cam, 0x01A9, 0x14); + err += zc0301_write_reg(cam, 0x01AA, 0x24); + err += zc0301_write_reg(cam, 0x001D, 0xD7); + err += zc0301_write_reg(cam, 0x001E, 0xF0); + err += zc0301_write_reg(cam, 0x001F, 0xF8); + err += zc0301_write_reg(cam, 0x0020, 0xFF); + err += zc0301_write_reg(cam, 0x01AD, 0x09); + err += zc0301_write_reg(cam, 0x01AE, 0x15); + err += zc0301_write_reg(cam, 0x0180, 0x40); + err += zc0301_write_reg(cam, 0x0180, 0x42); + + msleep(100); + + return err; +} + + +static struct zc0301_sensor pb0330 = { + .name = "PB-0330", + .init = &pb0330_init, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_JPEG, + .priv = 8, + }, +}; + + +int zc0301_probe_pb0330(struct zc0301_device* cam) +{ + int r0, err = 0; + + err += zc0301_write_reg(cam, 0x0000, 0x01); + err += zc0301_write_reg(cam, 0x0010, 0x0a); + err += zc0301_write_reg(cam, 0x0001, 0x01); + err += zc0301_write_reg(cam, 0x0012, 0x03); + err += zc0301_write_reg(cam, 0x0012, 0x01); + + msleep(10); + + r0 = zc0301_i2c_read(cam, 0x00, 2); + + if (r0 < 0 || err) + return -EIO; + + if (r0 != 0x8243) + return -ENODEV; + + zc0301_attach_sensor(cam, &pb0330); + + return 0; +} diff --git a/linux/drivers/media/video/zc0301/zc0301_sensor.h b/linux/drivers/media/video/zc0301/zc0301_sensor.h index 7f11465ce..7f005a940 100644 --- a/linux/drivers/media/video/zc0301/zc0301_sensor.h +++ b/linux/drivers/media/video/zc0301/zc0301_sensor.h @@ -1,5 +1,5 @@ /*************************************************************************** - * API for image sensors connected to the ZC030! Image Processor and * + * API for image sensors connected to the ZC0301 Image Processor and * * Control Chip * * * * Copyright (C) 2006 by Luca Risolia * @@ -36,37 +36,53 @@ struct zc0301_sensor; /*****************************************************************************/ extern int zc0301_probe_pas202bcb(struct zc0301_device* cam); +extern int zc0301_probe_pb0330(struct zc0301_device* cam); #define ZC0301_SENSOR_TABLE \ /* Weak detections must go at the end of the list */ \ static int (*zc0301_sensor_table[])(struct zc0301_device*) = { \ &zc0301_probe_pas202bcb, \ + &zc0301_probe_pb0330, \ NULL, \ }; extern struct zc0301_device* zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id); -extern void +extern void zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor); #define ZC0301_USB_DEVICE(vend, prod, intclass) \ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ - USB_DEVICE_ID_MATCH_INT_CLASS, \ + USB_DEVICE_ID_MATCH_INT_CLASS, \ .idVendor = (vend), \ .idProduct = (prod), \ .bInterfaceClass = (intclass) #define ZC0301_ID_TABLE \ static const struct usb_device_id zc0301_id_table[] = { \ - { ZC0301_USB_DEVICE(0x041e, 0x4017, 0xff), }, \ + { ZC0301_USB_DEVICE(0x041e, 0x4017, 0xff), }, /* ICM105 */ \ { ZC0301_USB_DEVICE(0x041e, 0x401c, 0xff), }, /* PAS106 */ \ - { ZC0301_USB_DEVICE(0x041e, 0x401e, 0xff), }, /* HV7131B */ \ + { ZC0301_USB_DEVICE(0x041e, 0x401e, 0xff), }, /* HV7131 */ \ + { ZC0301_USB_DEVICE(0x041e, 0x401f, 0xff), }, /* TAS5130 */ \ + { ZC0301_USB_DEVICE(0x041e, 0x4022, 0xff), }, \ { ZC0301_USB_DEVICE(0x041e, 0x4034, 0xff), }, /* PAS106 */ \ { ZC0301_USB_DEVICE(0x041e, 0x4035, 0xff), }, /* PAS106 */ \ - { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202BCB */ \ + { ZC0301_USB_DEVICE(0x041e, 0x4036, 0xff), }, /* HV7131 */ \ + { ZC0301_USB_DEVICE(0x041e, 0x403a, 0xff), }, /* HV7131 */ \ + { ZC0301_USB_DEVICE(0x0458, 0x7007, 0xff), }, /* TAS5130 */ \ + { ZC0301_USB_DEVICE(0x0458, 0x700C, 0xff), }, /* TAS5130 */ \ + { ZC0301_USB_DEVICE(0x0458, 0x700f, 0xff), }, /* TAS5130 */ \ + { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \ + { ZC0301_USB_DEVICE(0x055f, 0xd003, 0xff), }, /* TAS5130 */ \ + { ZC0301_USB_DEVICE(0x055f, 0xd004, 0xff), }, /* TAS5130 */ \ + { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \ { ZC0301_USB_DEVICE(0x0ac8, 0x0301, 0xff), }, \ - { ZC0301_USB_DEVICE(0x10fd, 0x8050, 0xff), }, /* TAS5130D */ \ + { ZC0301_USB_DEVICE(0x0ac8, 0x301b, 0xff), }, /* PB-0330/HV7131 */ \ + { ZC0301_USB_DEVICE(0x0ac8, 0x303b, 0xff), }, /* PB-0330 */ \ + { ZC0301_USB_DEVICE(0x10fd, 0x0128, 0xff), }, /* TAS5130 */ \ + { ZC0301_USB_DEVICE(0x10fd, 0x8050, 0xff), }, /* TAS5130 */ \ + { ZC0301_USB_DEVICE(0x10fd, 0x804e, 0xff), }, /* TAS5130 */ \ { } \ }; @@ -93,7 +109,7 @@ struct zc0301_sensor { int (*init)(struct zc0301_device*); int (*get_ctrl)(struct zc0301_device*, struct v4l2_control* ctrl); int (*set_ctrl)(struct zc0301_device*, - const struct v4l2_control* ctrl); + const struct v4l2_control* ctrl); int (*set_crop)(struct zc0301_device*, const struct v4l2_rect* rect); /* Private */ -- cgit v1.2.3 From 9b46c2b9f7db1a87a4ad91531c3e390a8b1317fc Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Apr 2006 11:30:30 -0300 Subject: Removing bad whitespaces from pervious patch From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/zc0301/zc0301.h | 6 +- linux/drivers/media/video/zc0301/zc0301_core.c | 162 ++++++++++----------- .../drivers/media/video/zc0301/zc0301_pas202bcb.c | 16 +- linux/drivers/media/video/zc0301/zc0301_sensor.h | 6 +- 4 files changed, 95 insertions(+), 95 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/zc0301/zc0301.h b/linux/drivers/media/video/zc0301/zc0301.h index aca383d39..6959c980a 100644 --- a/linux/drivers/media/video/zc0301/zc0301.h +++ b/linux/drivers/media/video/zc0301/zc0301.h @@ -158,7 +158,7 @@ do { \ dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ else if ((level) >= 3) \ dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ - __FUNCTION__, __LINE__ , ## args); \ + __FUNCTION__, __LINE__ , ## args); \ } \ } while (0) # define KDBG(level, fmt, args...) \ @@ -168,7 +168,7 @@ do { \ pr_info("zc0301: " fmt "\n", ## args); \ else if ((level) == 3) \ pr_debug("zc0301: [%s:%d] " fmt "\n", __FUNCTION__, \ - __LINE__ , ## args); \ + __LINE__ , ## args); \ } \ } while (0) # define V4LDBG(level, name, cmd) \ @@ -185,7 +185,7 @@ do { \ #undef PDBG #define PDBG(fmt, args...) \ dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ - __FUNCTION__, __LINE__ , ## args) + __FUNCTION__, __LINE__ , ## args) #undef PDBGG #define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ diff --git a/linux/drivers/media/video/zc0301/zc0301_core.c b/linux/drivers/media/video/zc0301/zc0301_core.c index 6e34f3d2f..7b7a3629b 100644 --- a/linux/drivers/media/video/zc0301/zc0301_core.c +++ b/linux/drivers/media/video/zc0301/zc0301_core.c @@ -48,7 +48,7 @@ /*****************************************************************************/ #define ZC0301_MODULE_NAME "V4L2 driver for ZC0301 " \ - "Image Processor and Control Chip" + "Image Processor and Control Chip" #define ZC0301_MODULE_AUTHOR "(C) 2006 Luca Risolia" #define ZC0301_AUTHOR_EMAIL "" #define ZC0301_MODULE_LICENSE "GPL" @@ -67,67 +67,67 @@ MODULE_LICENSE(ZC0301_MODULE_LICENSE); static short video_nr[] = {[0 ... ZC0301_MAX_DEVICES-1] = -1}; module_param_array(video_nr, short, NULL, 0444); MODULE_PARM_DESC(video_nr, - "\n<-1|n[,...]> Specify V4L2 minor mode number." - "\n -1 = use next available (default)" - "\n n = use minor number n (integer >= 0)" - "\nYou can specify up to " - __MODULE_STRING(ZC0301_MAX_DEVICES) " cameras this way." - "\nFor example:" - "\nvideo_nr=-1,2,-1 would assign minor number 2 to" - "\nthe second registered camera and use auto for the first" - "\none and for every other camera." - "\n"); - -static short force_munmap[] = {[0 ... ZC0301_MAX_DEVICES-1] = - ZC0301_FORCE_MUNMAP}; + "\n<-1|n[,...]> Specify V4L2 minor mode number." + "\n -1 = use next available (default)" + "\n n = use minor number n (integer >= 0)" + "\nYou can specify up to " + __MODULE_STRING(ZC0301_MAX_DEVICES) " cameras this way." + "\nFor example:" + "\nvideo_nr=-1,2,-1 would assign minor number 2 to" + "\nthe second registered camera and use auto for the first" + "\none and for every other camera." + "\n"); + +static short force_munmap[] = {[0 ... ZC0301_MAX_DEVICES-1] = + ZC0301_FORCE_MUNMAP}; module_param_array(force_munmap, bool, NULL, 0444); MODULE_PARM_DESC(force_munmap, - "\n<0|1[,...]> Force the application to unmap previously" - "\nmapped buffer memory before calling any VIDIOC_S_CROP or" - "\nVIDIOC_S_FMT ioctl's. Not all the applications support" - "\nthis feature. This parameter is specific for each" - "\ndetected camera." - "\n 0 = do not force memory unmapping" - "\n 1 = force memory unmapping (save memory)" - "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." - "\n"); - -static unsigned int frame_timeout[] = {[0 ... ZC0301_MAX_DEVICES-1] = - ZC0301_FRAME_TIMEOUT}; + "\n<0|1[,...]> Force the application to unmap previously" + "\nmapped buffer memory before calling any VIDIOC_S_CROP or" + "\nVIDIOC_S_FMT ioctl's. Not all the applications support" + "\nthis feature. This parameter is specific for each" + "\ndetected camera." + "\n 0 = do not force memory unmapping" + "\n 1 = force memory unmapping (save memory)" + "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." + "\n"); + +static unsigned int frame_timeout[] = {[0 ... ZC0301_MAX_DEVICES-1] = + ZC0301_FRAME_TIMEOUT}; module_param_array(frame_timeout, uint, NULL, 0644); MODULE_PARM_DESC(frame_timeout, - "\n Timeout for a video frame in seconds." - "\nThis parameter is specific for each detected camera." - "\nDefault value is "__MODULE_STRING(ZC0301_FRAME_TIMEOUT)"." - "\n"); + "\n Timeout for a video frame in seconds." + "\nThis parameter is specific for each detected camera." + "\nDefault value is "__MODULE_STRING(ZC0301_FRAME_TIMEOUT)"." + "\n"); #ifdef ZC0301_DEBUG static unsigned short debug = ZC0301_DEBUG_LEVEL; module_param(debug, ushort, 0644); MODULE_PARM_DESC(debug, - "\n Debugging information level, from 0 to 3:" - "\n0 = none (use carefully)" - "\n1 = critical errors" - "\n2 = significant informations" - "\n3 = more verbose messages" - "\nLevel 3 is useful for testing only, when only " - "one device is used." - "\nDefault value is "__MODULE_STRING(ZC0301_DEBUG_LEVEL)"." - "\n"); + "\n Debugging information level, from 0 to 3:" + "\n0 = none (use carefully)" + "\n1 = critical errors" + "\n2 = significant informations" + "\n3 = more verbose messages" + "\nLevel 3 is useful for testing only, when only " + "one device is used." + "\nDefault value is "__MODULE_STRING(ZC0301_DEBUG_LEVEL)"." + "\n"); #endif /*****************************************************************************/ static u32 zc0301_request_buffers(struct zc0301_device* cam, u32 count, - enum zc0301_io_method io) + enum zc0301_io_method io) { struct v4l2_pix_format* p = &(cam->sensor.pix_format); struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); const size_t imagesize = cam->module_param.force_munmap || - io == IO_READ ? - (p->width * p->height * p->priv) / 8 : - (r->width * r->height * p->priv) / 8; + io == IO_READ ? + (p->width * p->height * p->priv) / 8 : + (r->width * r->height * p->priv) / 8; void* buff = NULL; u32 i; @@ -216,7 +216,7 @@ int zc0301_write_reg(struct zc0301_device* cam, u16 index, u16 value) int res; res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0xa0, 0x40, - value, index, NULL, 0, ZC0301_CTRL_TIMEOUT); + value, index, NULL, 0, ZC0301_CTRL_TIMEOUT); if (res < 0) { DBG(3, "Failed to write a register (index 0x%04X, " "value 0x%02X, error %d)",index, value, res); @@ -234,7 +234,7 @@ int zc0301_read_reg(struct zc0301_device* cam, u16 index) int res; res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0xa1, 0xc0, - 0x0001, index, buff, 1, ZC0301_CTRL_TIMEOUT); + 0x0001, index, buff, 1, ZC0301_CTRL_TIMEOUT); if (res < 0) DBG(3, "Failed to read a register (index 0x%04X, error %d)", index, res); @@ -337,11 +337,11 @@ static void zc0301_urb_complete(struct urb *urb, struct pt_regs* regs) if (!(*f)) (*f) = list_entry(cam->inqueue.next, struct zc0301_frame_t, - frame); + frame); imagesize = (cam->sensor.pix_format.width * - cam->sensor.pix_format.height * - cam->sensor.pix_format.priv) / 8; + cam->sensor.pix_format.height * + cam->sensor.pix_format.priv) / 8; for (i = 0; i < urb->number_of_packets; i++) { unsigned int len, status; @@ -395,8 +395,8 @@ end_of_frame: list_move_tail(&(*f)->frame, &cam->outqueue); if (!list_empty(&cam->inqueue)) (*f) = list_entry(cam->inqueue.next, - struct zc0301_frame_t, - frame); + struct zc0301_frame_t, + frame); else (*f) = NULL; spin_unlock(&cam->queue_lock); @@ -429,14 +429,14 @@ static int zc0301_start_transfer(struct zc0301_device* cam) struct usb_device *udev = cam->usbdev; struct urb* urb; const unsigned int wMaxPacketSize[] = {0, 128, 192, 256, 384, - 512, 768, 1023}; + 512, 768, 1023}; const unsigned int psz = wMaxPacketSize[ZC0301_ALTERNATE_SETTING]; s8 i, j; int err = 0; for (i = 0; i < ZC0301_URBS; i++) { cam->transfer_buffer[i] = kzalloc(ZC0301_ISO_PACKETS * psz, - GFP_KERNEL); + GFP_KERNEL); if (!cam->transfer_buffer[i]) { err = -ENOMEM; DBG(1, "Not enough memory"); @@ -528,9 +528,9 @@ static int zc0301_stream_interrupt(struct zc0301_device* cam) cam->stream = STREAM_INTERRUPT; timeout = wait_event_timeout(cam->wait_stream, - (cam->stream == STREAM_OFF) || - (cam->state & DEV_DISCONNECTED), - ZC0301_URB_TIMEOUT); + (cam->stream == STREAM_OFF) || + (cam->state & DEV_DISCONNECTED), + ZC0301_URB_TIMEOUT); if (cam->state & DEV_DISCONNECTED) return -ENODEV; else if (cam->stream != STREAM_OFF) { @@ -548,7 +548,7 @@ static int zc0301_stream_interrupt(struct zc0301_device* cam) static int zc0301_set_compression(struct zc0301_device* cam, - struct v4l2_jpegcompression* compression) + struct v4l2_jpegcompression* compression) { int r, err = 0; @@ -600,7 +600,7 @@ static int zc0301_init(struct zc0301_device* cam) if (s->set_ctrl) { for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (s->qctrl[i].id != 0 && + if (s->qctrl[i].id != 0 && !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { ctrl.id = s->qctrl[i].id; ctrl.value = qctrl[i].default_value; @@ -622,7 +622,7 @@ static int zc0301_init(struct zc0301_device* cam) init_waitqueue_head(&cam->wait_stream); cam->nreadbuffers = 2; memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); - memcpy(&(s->_rect), &(s->cropcap.defrect), + memcpy(&(s->_rect), &(s->cropcap.defrect), sizeof(struct v4l2_rect)); cam->state |= DEV_INITIALIZED; } @@ -670,8 +670,8 @@ static int zc0301_open(struct inode* inode, struct file* filp) } mutex_unlock(&cam->dev_mutex); err = wait_event_interruptible_exclusive(cam->open, - cam->state & DEV_DISCONNECTED - || !cam->users); + cam->state & DEV_DISCONNECTED + || !cam->users); if (err) { up_read(&zc0301_disconnect); return err; @@ -802,12 +802,12 @@ zc0301_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) return -EAGAIN; } timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); if (timeout < 0) { mutex_unlock(&cam->fileop_mutex); return timeout; @@ -930,7 +930,7 @@ static int zc0301_mmap(struct file* filp, struct vm_area_struct *vma) { struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); unsigned long size = vma->vm_end - vma->vm_start, - start = vma->vm_start; + start = vma->vm_start; void *pos; u32 i; @@ -998,13 +998,13 @@ zc0301_vidioc_querycap(struct zc0301_device* cam, void __user * arg) .driver = "zc0301", .version = ZC0301_MODULE_VERSION_CODE, .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING, + V4L2_CAP_STREAMING, }; strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) strlcpy(cap.bus_info, cam->usbdev->dev.bus_id, - sizeof(cap.bus_info)); + sizeof(cap.bus_info)); if (copy_to_user(arg, &cap, sizeof(cap))) return -EFAULT; @@ -1337,7 +1337,7 @@ zc0301_vidioc_g_fmt(struct zc0301_device* cam, void __user * arg) static int zc0301_vidioc_try_s_fmt(struct zc0301_device* cam, unsigned int cmd, - void __user * arg) + void __user * arg) { struct zc0301_sensor* s = &cam->sensor; struct v4l2_format format; @@ -1600,7 +1600,7 @@ zc0301_vidioc_qbuf(struct zc0301_device* cam, void __user * arg) static int zc0301_vidioc_dqbuf(struct zc0301_device* cam, struct file* filp, - void __user * arg) + void __user * arg) { struct v4l2_buffer b; struct zc0301_frame_t *f; @@ -1619,12 +1619,12 @@ zc0301_vidioc_dqbuf(struct zc0301_device* cam, struct file* filp, if (filp->f_flags & O_NONBLOCK) return -EAGAIN; timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); if (timeout < 0) return timeout; if (cam->state & DEV_DISCONNECTED) @@ -1748,7 +1748,7 @@ zc0301_vidioc_s_parm(struct zc0301_device* cam, void __user * arg) static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp, - unsigned int cmd, void __user * arg) + unsigned int cmd, void __user * arg) { struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); @@ -1842,7 +1842,7 @@ static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp, static int zc0301_ioctl(struct inode* inode, struct file* filp, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); int err = 0; @@ -1948,7 +1948,7 @@ zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) mutex_lock(&cam->dev_mutex); err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, - video_nr[dev_nr]); + video_nr[dev_nr]); if (err) { DBG(1, "V4L2 device registration failed"); if (err == -ENFILE && video_nr[dev_nr] == -1) @@ -1992,7 +1992,7 @@ static void zc0301_usb_disconnect(struct usb_interface* intf) down_write(&zc0301_disconnect); - mutex_lock(&cam->dev_mutex); + mutex_lock(&cam->dev_mutex); DBG(2, "Disconnecting %s...", cam->v4ldev->name); diff --git a/linux/drivers/media/video/zc0301/zc0301_pas202bcb.c b/linux/drivers/media/video/zc0301/zc0301_pas202bcb.c index 005ef2469..ecfd39a56 100644 --- a/linux/drivers/media/video/zc0301/zc0301_pas202bcb.c +++ b/linux/drivers/media/video/zc0301/zc0301_pas202bcb.c @@ -24,10 +24,10 @@ /* NOTE: Sensor controls are disabled for now, becouse changing them while - streaming sometimes results in out-of-sync video frames. We'll use - the default initialization, until we know how to stop and start video - in the chip. However, the image quality still looks good under various - light conditions. + streaming sometimes results in out-of-sync video frames. We'll use + the default initialization, until we know how to stop and start video + in the chip. However, the image quality still looks good under various + light conditions. */ #include @@ -164,8 +164,8 @@ static int pas202bcb_init(struct zc0301_device* cam) } -static int pas202bcb_get_ctrl(struct zc0301_device* cam, - struct v4l2_control* ctrl) +static int pas202bcb_get_ctrl(struct zc0301_device* cam, + struct v4l2_control* ctrl) { switch (ctrl->id) { case V4L2_CID_EXPOSURE: @@ -207,8 +207,8 @@ static int pas202bcb_get_ctrl(struct zc0301_device* cam, } -static int pas202bcb_set_ctrl(struct zc0301_device* cam, - const struct v4l2_control* ctrl) +static int pas202bcb_set_ctrl(struct zc0301_device* cam, + const struct v4l2_control* ctrl) { int err = 0; diff --git a/linux/drivers/media/video/zc0301/zc0301_sensor.h b/linux/drivers/media/video/zc0301/zc0301_sensor.h index 7f005a940..fd8abf969 100644 --- a/linux/drivers/media/video/zc0301/zc0301_sensor.h +++ b/linux/drivers/media/video/zc0301/zc0301_sensor.h @@ -49,12 +49,12 @@ static int (*zc0301_sensor_table[])(struct zc0301_device*) = { \ extern struct zc0301_device* zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id); -extern void +extern void zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor); #define ZC0301_USB_DEVICE(vend, prod, intclass) \ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ - USB_DEVICE_ID_MATCH_INT_CLASS, \ + USB_DEVICE_ID_MATCH_INT_CLASS, \ .idVendor = (vend), \ .idProduct = (prod), \ .bInterfaceClass = (intclass) @@ -109,7 +109,7 @@ struct zc0301_sensor { int (*init)(struct zc0301_device*); int (*get_ctrl)(struct zc0301_device*, struct v4l2_control* ctrl); int (*set_ctrl)(struct zc0301_device*, - const struct v4l2_control* ctrl); + const struct v4l2_control* ctrl); int (*set_crop)(struct zc0301_device*, const struct v4l2_rect* rect); /* Private */ -- cgit v1.2.3 From edb3b432e5bd483b361a5461c744c987a33b999d Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 24 Apr 2006 22:21:46 -0400 Subject: rename fe_lgh06xf.h to lg_h06xf.h From: Michael Krufky Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c | 2 +- linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h | 2 +- linux/drivers/media/dvb/dvb-usb/cxusb.c | 2 +- linux/drivers/media/dvb/frontends/fe_lgh06xf.h | 66 ------------------------- linux/drivers/media/dvb/frontends/lg_h06xf.h | 66 +++++++++++++++++++++++++ linux/drivers/media/video/cx88/cx88-dvb.c | 2 +- 6 files changed, 70 insertions(+), 70 deletions(-) delete mode 100644 linux/drivers/media/dvb/frontends/fe_lgh06xf.h create mode 100644 linux/drivers/media/dvb/frontends/lg_h06xf.h (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index 630782f88..353b9e987 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -14,7 +14,7 @@ #include "stv0297_cs2.h" #include "mt312.h" #include "lgdt330x.h" -#include "fe_lgh06xf.h" +#include "lg_h06xf.h" #include "dvb-pll.h" /* lnb control */ diff --git a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h index ad261b593..a7c1e7867 100644 --- a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h +++ b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h @@ -40,7 +40,7 @@ #include "cx24110.h" #include "or51211.h" #include "lgdt330x.h" -#include "fe_lgh06xf.h" +#include "lg_h06xf.h" #include "zl10353.h" struct dvb_bt8xx_card { diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index 11c8c9812..f0bdb7323 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -27,7 +27,7 @@ #include "cx22702.h" #include "lgdt330x.h" -#include "fe_lgh06xf.h" +#include "lg_h06xf.h" #include "mt352.h" #include "mt352_priv.h" diff --git a/linux/drivers/media/dvb/frontends/fe_lgh06xf.h b/linux/drivers/media/dvb/frontends/fe_lgh06xf.h deleted file mode 100644 index c5e8b5ff1..000000000 --- a/linux/drivers/media/dvb/frontends/fe_lgh06xf.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * fe_lgh06xf.h - ATSC Tuner support for LG TDVS H06xF - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef FE_LGH06XF_H -#define FE_LGH06XF_H -#include "dvb-pll.h" - -static int lg_h06xf_pll_set(struct dvb_frontend* fe, struct i2c_adapter* i2c_adap, - struct dvb_frontend_parameters* params) -{ - u8 buf[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, - .buf = buf, .len = sizeof(buf) }; - int err; - - dvb_pll_configure(&dvb_pll_tdvs_tua6034, buf, params->frequency, 0); - if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) { - printk(KERN_WARNING "lg_h06xf: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, buf[0], buf[1], err); - if (err < 0) - return err; - else - return -EREMOTEIO; - } - - /* Set the Auxiliary Byte. */ -#if 0 - buf[2] &= ~0x20; - buf[2] |= 0x18; - buf[3] = 0x50; -#else - buf[0] = buf[2]; - buf[0] &= ~0x20; - buf[0] |= 0x18; - buf[1] = 0x50; - msg.len = 2; -#endif - if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) { - printk(KERN_WARNING "lg_h06xf: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, buf[0], buf[1], err); - if (err < 0) - return err; - else - return -EREMOTEIO; - } - - return 0; -} -#endif diff --git a/linux/drivers/media/dvb/frontends/lg_h06xf.h b/linux/drivers/media/dvb/frontends/lg_h06xf.h new file mode 100644 index 000000000..355a4a498 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/lg_h06xf.h @@ -0,0 +1,66 @@ +/* + * lg_h06xf.h - ATSC Tuner support for LG TDVS-H06xF + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _LG_H06XF_H_ +#define _LG_H06XF_H_ +#include "dvb-pll.h" + +static int lg_h06xf_pll_set(struct dvb_frontend* fe, struct i2c_adapter* i2c_adap, + struct dvb_frontend_parameters* params) +{ + u8 buf[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + int err; + + dvb_pll_configure(&dvb_pll_tdvs_tua6034, buf, params->frequency, 0); + if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) { + printk(KERN_WARNING "lg_h06xf: %s error " + "(addr %02x <- %02x, err = %i)\n", + __FUNCTION__, buf[0], buf[1], err); + if (err < 0) + return err; + else + return -EREMOTEIO; + } + + /* Set the Auxiliary Byte. */ +#if 0 + buf[2] &= ~0x20; + buf[2] |= 0x18; + buf[3] = 0x50; +#else + buf[0] = buf[2]; + buf[0] &= ~0x20; + buf[0] |= 0x18; + buf[1] = 0x50; + msg.len = 2; +#endif + if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) { + printk(KERN_WARNING "lg_h06xf: %s error " + "(addr %02x <- %02x, err = %i)\n", + __FUNCTION__, buf[0], buf[1], err); + if (err < 0) + return err; + else + return -EREMOTEIO; + } + + return 0; +} +#endif diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index fbd8387de..a65306c0d 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -52,7 +52,7 @@ #endif #ifdef HAVE_LGDT330X # include "lgdt330x.h" -# include "fe_lgh06xf.h" +# include "lg_h06xf.h" #endif #ifdef HAVE_NXT200X # include "nxt200x.h" -- cgit v1.2.3 From 4f30f31f9948683dfd078344f9458f37dba6d75d Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Tue, 25 Apr 2006 09:36:19 +0100 Subject: Fix dvb-usb tuner code From: Andrew de Quincey I had forgotten to rename some of the tuner functions in the refactoring. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c b/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c index b067c72f8..6c868d667 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c @@ -77,7 +77,7 @@ int dvb_usb_tuner_init_i2c(struct dvb_frontend *fe) } EXPORT_SYMBOL(dvb_usb_tuner_init_i2c); -int dvb_usb_tuner_pllbuf(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep, u8 *b, int buf_len) +int dvb_usb_tuner_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep, u8 *b, int buf_len) { struct dvb_usb_device *d = fe->dvb->priv; @@ -95,16 +95,16 @@ int dvb_usb_tuner_pllbuf(struct dvb_frontend *fe, struct dvb_frontend_parameters return 5; } -EXPORT_SYMBOL(dvb_usb_tuner_pllbuf); +EXPORT_SYMBOL(dvb_usb_tuner_calc_regs); -int dvb_usb_tuner_set_frequency_i2c(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) +int dvb_usb_tuner_set_params_i2c(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) { struct dvb_usb_device *d = fe->dvb->priv; int ret = 0; u8 b[5]; struct i2c_msg msg = { .addr = d->pll_addr, .flags = 0, .buf = &b[1], .len = 4 }; - dvb_usb_tuner_pllbuf(fe,fep,b,5); + dvb_usb_tuner_calc_regs(fe,fep,b,5); if (d->tuner_pass_ctrl) d->tuner_pass_ctrl(fe,1,d->pll_addr); @@ -122,4 +122,4 @@ int dvb_usb_tuner_set_frequency_i2c(struct dvb_frontend *fe, struct dvb_frontend return ret; } -EXPORT_SYMBOL(dvb_usb_tuner_set_frequency_i2c); +EXPORT_SYMBOL(dvb_usb_tuner_set_params_i2c); -- cgit v1.2.3 From 6bfc9bfdb9ac2503b801f907c2c51af7275bb03c Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Tue, 25 Apr 2006 19:07:48 -0500 Subject: Remove dead code. From: Mike Isely Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 8a6e91e5a..f4284f927 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -33,14 +33,6 @@ #include #include - -#define PVR_WIDTH_DVD 720 -#define PVR_WIDTH_SVCD 480 -#define PVR_WIDTH_VCD 352 - -#define PVR_HEIGHT_PAL 480 -#define PVR_HEIGHT_NTSC 576 - struct pvr2_v4l2_dev; struct pvr2_v4l2_fh; struct pvr2_v4l2; -- cgit v1.2.3 From bec655f69df2e179760e48639daf3cc936d6c6e6 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Tue, 25 Apr 2006 19:09:51 -0500 Subject: Various pvrusb2 video standard cleanups From: Mike Isely Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-std.c | 81 +++++++++++++++++-------- 1 file changed, 55 insertions(+), 26 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-std.c b/linux/drivers/media/video/pvrusb2/pvrusb2-std.c index 857829120..64ba223c2 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-std.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-std.c @@ -27,6 +27,7 @@ struct std_name { v4l2_std_id id; }; + #define CSTD_PAL \ (V4L2_STD_PAL_B| \ V4L2_STD_PAL_B1| \ @@ -44,6 +45,7 @@ struct std_name { #define CSTD_NTSC \ (V4L2_STD_NTSC_M| \ V4L2_STD_NTSC_M_JP| \ + V4L2_STD_NTSC_M_KR| \ V4L2_STD_NTSC_443) #define CSTD_SECAM \ @@ -66,10 +68,7 @@ struct std_name { #define TSTD_K (V4L2_STD_PAL_K|V4L2_STD_SECAM_K) #define TSTD_K1 (V4L2_STD_SECAM_K1) #define TSTD_L (V4L2_STD_SECAM_L) -#define TSTD_LC (V4L2_STD_SECAM_LC) #define TSTD_M (V4L2_STD_PAL_M|V4L2_STD_NTSC_M) -#define TSTD_Mj (V4L2_STD_NTSC_M_JP) -#define TSTD_443 (V4L2_STD_NTSC_443) #define TSTD_N (V4L2_STD_PAL_N) #define TSTD_Nc (V4L2_STD_PAL_Nc) #define TSTD_60 (V4L2_STD_PAL_60) @@ -95,10 +94,11 @@ const static struct std_name std_items[] = { {"K",TSTD_K}, {"K1",TSTD_K1}, {"L",TSTD_L}, - {"LC",TSTD_LC}, + {"LC",V4L2_STD_SECAM_LC}, {"M",TSTD_M}, - {"Mj",TSTD_Mj}, - {"443",TSTD_443}, + {"Mj",V4L2_STD_NTSC_M_JP}, + {"443",V4L2_STD_NTSC_443}, + {"Mk",V4L2_STD_NTSC_M_KR}, {"N",TSTD_N}, {"Nc",TSTD_Nc}, {"60",TSTD_60}, @@ -236,7 +236,7 @@ static struct v4l2_standard generic_standards[] = { TSTD_I| TSTD_K|TSTD_K1| TSTD_L| - TSTD_LC| + V4L2_STD_SECAM_LC | TSTD_N|TSTD_Nc), .frameperiod = { @@ -246,7 +246,9 @@ static struct v4l2_standard generic_standards[] = { .framelines = 625, .reserved = {0,0,0,0} }, { - .id = (TSTD_M|TSTD_Mj), + .id = (TSTD_M| + V4L2_STD_NTSC_M_JP| + V4L2_STD_NTSC_M_KR), .frameperiod = { .numerator = 1001, @@ -264,7 +266,7 @@ static struct v4l2_standard generic_standards[] = { .framelines = 525, .reserved = {0,0,0,0} }, { // This is total wild guess - .id = (TSTD_443), + .id = V4L2_STD_NTSC_443, .frameperiod = { .numerator = 1001, @@ -288,15 +290,40 @@ static struct v4l2_standard *match_std(v4l2_std_id id) return 0; } +static int pvr2_std_fill(struct v4l2_standard *std,v4l2_std_id id) +{ + struct v4l2_standard *template; + int idx; + unsigned int bcnt; + template = match_std(id); + if (!template) return 0; + idx = std->index; + memcpy(std,template,sizeof(*template)); + std->index = idx; + std->id = id; + bcnt = pvr2_std_id_to_str(std->name,sizeof(std->name)-1,id); + std->name[bcnt] = 0; + pvr2_trace(PVR2_TRACE_INIT,"Set up standard idx=%u name=%s", + std->index,std->name); + return !0; +} + +/* These are special cases of combined standards that we should enumerate + separately if the component pieces are present. */ +static v4l2_std_id std_mixes[] = { + V4L2_STD_PAL_B | V4L2_STD_PAL_G, + V4L2_STD_PAL_D | V4L2_STD_PAL_K, + V4L2_STD_SECAM_B | V4L2_STD_SECAM_G, + V4L2_STD_SECAM_D | V4L2_STD_SECAM_K, +}; + struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, v4l2_std_id id) { unsigned int std_cnt = 0; - unsigned int idx,bcnt; + unsigned int idx,bcnt,idx2; v4l2_std_id idmsk,cmsk,fmsk; struct v4l2_standard *stddefs; - struct v4l2_standard *template; - struct v4l2_standard *std; if (pvrusb2_debug & PVR2_TRACE_INIT) { char buf[50]; @@ -319,6 +346,10 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, fmsk |= idmsk; } + for (idx2 = 0; idx2 < sizeof(std_mixes)/sizeof(std_mixes[0]); idx2++) { + if ((id & std_mixes[idx2]) == std_mixes[idx2]) std_cnt++; + } + if (fmsk) { char buf[50]; bcnt = pvr2_std_id_to_str(buf,sizeof(buf),fmsk); @@ -336,23 +367,21 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, stddefs = kmalloc(sizeof(struct v4l2_standard) * std_cnt, GFP_KERNEL); memset(stddefs,0,sizeof(struct v4l2_standard) * std_cnt); + for (idx = 0; idx < std_cnt; idx++) stddefs[idx].index = idx; + idx = 0; - for (idmsk = 1, cmsk = id; cmsk; idmsk <<= 1) { + + /* Enumerate potential special cases */ + for (idx2 = 0; ((idx2 < sizeof(std_mixes)/sizeof(std_mixes[0])) && + (idx < std_cnt)); idx2++) { + if (!(id & std_mixes[idx2])) continue; + if (pvr2_std_fill(stddefs+idx,std_mixes[idx2])) idx++; + } + /* Now enumerate individual pieces */ + for (idmsk = 1, cmsk = id; cmsk && (idx < std_cnt); idmsk <<= 1) { if (!(idmsk & cmsk)) continue; cmsk &= ~idmsk; - template = match_std(idmsk); - if (!template) continue; - if (idx >= std_cnt) break; - std = stddefs+idx; - memcpy(std,template,sizeof(*template)); - std->index = idx; - std->id = idmsk; - bcnt = pvr2_std_id_to_str(std->name, - sizeof(std->name)-1, - idmsk); - std->name[bcnt] = 0; - pvr2_trace(PVR2_TRACE_INIT,"Set up standard idx=%u name=%s", - idx,std->name); + if (!pvr2_std_fill(stddefs+idx,idmsk)) continue; idx++; } -- cgit v1.2.3 From 60f073ffa46dc14bcbc82b9c14ff5fb6cc2a5102 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Tue, 25 Apr 2006 19:22:38 -0500 Subject: Remove pvrusb2 video standard adjustment logic From: Mike Isely Remove logic which tries to infer additional video standards based on what was reported by tveeprom. Signed-off-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 41 ------------------------- 1 file changed, 41 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 07bfb7e3b..890aa4a06 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1399,60 +1399,19 @@ static int pvr2_hdw_check_firmware(struct pvr2_hdw *hdw) return result == 0; } -struct pvr2_std_hack { - v4l2_std_id pat; // Pattern to match - v4l2_std_id msk; // Which bits we care about - v4l2_std_id std; // What additional standards to set -}; - -// This is a mapping of additional standards to support if certain -// standards are already present. -const static struct pvr2_std_hack std_hacks[] = { - { - // Devices which are NTSC-M capable are also marked as - // NTSC-Mj capable on the device's sticker. - .pat = V4L2_STD_NTSC_M, - .std = V4L2_STD_NTSC_M_JP, - },{ - // Enable PAL-N for PAL-capable devices. One user in - // Argentina has encountered this. - .pat = V4L2_STD_PAL_BG|V4L2_STD_PAL_DK|V4L2_STD_PAL_I, - .std = V4L2_STD_PAL_N|V4L2_STD_PAL_Nc - } -}; - static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) { char buf[40]; unsigned int bcnt; v4l2_std_id std1,std2; - unsigned int idx; std1 = get_default_standard(hdw); - std2 = 0; - for (idx = 0; idx < sizeof(std_hacks)/sizeof(std_hacks[0]); idx++) { - if (std_hacks[idx].msk ? - ((std_hacks[idx].pat ^ hdw->std_mask_eeprom) & - std_hacks[idx].msk) : - (std_hacks[idx].pat & hdw->std_mask_eeprom)) { - std2 |= std_hacks[idx].std; - } - } - bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom); pvr2_trace(PVR2_TRACE_INIT, "Supported video standard(s) reported by eeprom: %.*s", bcnt,buf); - if (std2) { - bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std2); - pvr2_trace(PVR2_TRACE_INIT, - "Based on eeprom list, also supporting: %.*s", - bcnt,buf); - hdw->std_mask_eeprom |= std2; - } - hdw->std_mask_avail = hdw->std_mask_eeprom; std2 = std1 & ~hdw->std_mask_avail; -- cgit v1.2.3 From d81f0636d4fdf143055769c57ef3489142c50729 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 26 Apr 2006 10:19:49 -0300 Subject: ZC0301 driver updates From: Luca Risolia This is a patch fixing a small bug in zc0301_start_transfer() introduced in the ZC0301 driver v1.04. Signed-off-by: Luca Risolia Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/zc0301/zc0301_core.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/zc0301/zc0301_core.c b/linux/drivers/media/video/zc0301/zc0301_core.c index 7b7a3629b..f20eb61ee 100644 --- a/linux/drivers/media/video/zc0301/zc0301_core.c +++ b/linux/drivers/media/video/zc0301/zc0301_core.c @@ -1,5 +1,5 @@ /*************************************************************************** - * Video4Linux2 driver for ZC0301 Image Processor and Control Chip * + * Video4Linux2 driver for ZC0301[P] Image Processor and Control Chip * * * * Copyright (C) 2006 by Luca Risolia * * * @@ -47,13 +47,13 @@ /*****************************************************************************/ -#define ZC0301_MODULE_NAME "V4L2 driver for ZC0301 " \ +#define ZC0301_MODULE_NAME "V4L2 driver for ZC0301[P] " \ "Image Processor and Control Chip" #define ZC0301_MODULE_AUTHOR "(C) 2006 Luca Risolia" #define ZC0301_AUTHOR_EMAIL "" #define ZC0301_MODULE_LICENSE "GPL" -#define ZC0301_MODULE_VERSION "1:1.04" -#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 4) +#define ZC0301_MODULE_VERSION "1:1.05" +#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 5) /*****************************************************************************/ @@ -427,10 +427,11 @@ resubmit_urb: static int zc0301_start_transfer(struct zc0301_device* cam) { struct usb_device *udev = cam->usbdev; + struct usb_host_interface* altsetting = usb_altnum_to_altsetting( + usb_ifnum_to_if(udev, 0), + ZC0301_ALTERNATE_SETTING); + const unsigned int psz = altsetting->endpoint[0].desc.wMaxPacketSize; struct urb* urb; - const unsigned int wMaxPacketSize[] = {0, 128, 192, 256, 384, - 512, 768, 1023}; - const unsigned int psz = wMaxPacketSize[ZC0301_ALTERNATE_SETTING]; s8 i, j; int err = 0; @@ -1914,7 +1915,7 @@ zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) mutex_init(&cam->dev_mutex); - DBG(2, "ZC0301 Image Processor and Control Chip detected " + DBG(2, "ZC0301[P] Image Processor and Control Chip detected " "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct); for (i = 0; zc0301_sensor_table[i]; i++) { @@ -1936,7 +1937,7 @@ zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) cam->state |= DEV_MISCONFIGURED; } - strcpy(cam->v4ldev->name, "ZC0301 PC Camera"); + strcpy(cam->v4ldev->name, "ZC0301[P] PC Camera"); cam->v4ldev->owner = THIS_MODULE; cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; cam->v4ldev->hardware = 0; -- cgit v1.2.3 From e1526ffa31150849a329a3974cceb151193a1364 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 26 Apr 2006 15:35:10 -0300 Subject: Some improvements at Makefile From: Mauro Carvalho Chehab Included a INSTALL file make help shows the INSTALL file added make release to force compiling against a specified version Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/zc0301/zc0301_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/zc0301/zc0301_core.c b/linux/drivers/media/video/zc0301/zc0301_core.c index f20eb61ee..2b5ce2d79 100644 --- a/linux/drivers/media/video/zc0301/zc0301_core.c +++ b/linux/drivers/media/video/zc0301/zc0301_core.c @@ -428,8 +428,8 @@ static int zc0301_start_transfer(struct zc0301_device* cam) { struct usb_device *udev = cam->usbdev; struct usb_host_interface* altsetting = usb_altnum_to_altsetting( - usb_ifnum_to_if(udev, 0), - ZC0301_ALTERNATE_SETTING); + usb_ifnum_to_if(udev, 0), + ZC0301_ALTERNATE_SETTING); const unsigned int psz = altsetting->endpoint[0].desc.wMaxPacketSize; struct urb* urb; s8 i, j; -- cgit v1.2.3 From e87f301495ed4c2fc6b9d7efc7594f8e4abb5708 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Thu, 27 Apr 2006 00:29:17 -0400 Subject: v4l: rename TUNER_LG_TDVS_H062F to TUNER_LG_TDVS_H06XF From: Michael Krufky Signed-off-by: Michael Krufky --- linux/drivers/media/video/bt8xx/bttv-cards.c | 2 +- linux/drivers/media/video/cx88/cx88-cards.c | 4 ++-- linux/drivers/media/video/tuner-core.c | 2 +- linux/drivers/media/video/tuner-types.c | 10 +++++----- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/bt8xx/bttv-cards.c b/linux/drivers/media/video/bt8xx/bttv-cards.c index b11967fa5..cd45a2429 100644 --- a/linux/drivers/media/video/bt8xx/bttv-cards.c +++ b/linux/drivers/media/video/bt8xx/bttv-cards.c @@ -2768,7 +2768,7 @@ struct tvcard bttv_tvcards[] = { /* Michael Krufky */ .name = "DViCO FusionHDTV 5 Lite", .tuner = 0, - .tuner_type = TUNER_LG_TDVS_H062F, + .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H064F */ .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .video_inputs = 3, diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index 383af5609..05e2bc403 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -760,7 +760,7 @@ struct cx88_board cx88_boards[] = { }, [CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD] = { .name = "DViCO FusionHDTV 5 Gold", - .tuner_type = TUNER_LG_TDVS_H062F, + .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H062F */ .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, @@ -1107,7 +1107,7 @@ struct cx88_board cx88_boards[] = { }, [CX88_BOARD_PCHDTV_HD5500] = { .name = "pcHDTV HD5500 HDTV", - .tuner_type = TUNER_LG_TDVS_H062F, + .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H064F */ .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index 689f7b043..00e073944 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -227,7 +227,7 @@ static void set_type(struct i2c_client *c, unsigned int type, i2c_master_send(c, buffer, 4); default_tuner_init(c); break; - case TUNER_LG_TDVS_H062F: + case TUNER_LG_TDVS_H06XF: /* Set the Auxiliary Byte. */ buffer[2] &= ~0x20; buffer[2] |= 0x18; diff --git a/linux/drivers/media/video/tuner-types.c b/linux/drivers/media/video/tuner-types.c index f1d3a7556..4657df313 100644 --- a/linux/drivers/media/video/tuner-types.c +++ b/linux/drivers/media/video/tuner-types.c @@ -900,7 +900,7 @@ static struct tuner_params tuner_philips_fmd1216me_mk3_params[] = { }; -/* ------------ TUNER_LG_TDVS_H062F - INFINEON ATSC ------------ */ +/* ------ TUNER_LG_TDVS_H06XF - LG INNOTEK / INFINEON ATSC ----- */ static struct tuner_range tuner_tua6034_ntsc_ranges[] = { { 16 * 165.00 /*MHz*/, 0x8e, 0x01 }, @@ -913,7 +913,7 @@ static struct tuner_range tuner_tua6034_atsc_ranges[] = { }; #endif -static struct tuner_params tuner_tua6034_params[] = { +static struct tuner_params tuner_lg_tdvs_h06xf_params[] = { { .type = TUNER_PARAM_TYPE_NTSC, .ranges = tuner_tua6034_ntsc_ranges, @@ -1418,10 +1418,10 @@ struct tunertype tuners[] = { .params = tuner_philips_fmd1216me_mk3_params, .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_params), }, - [TUNER_LG_TDVS_H062F] = { /* LGINNOTEK ATSC */ + [TUNER_LG_TDVS_H06XF] = { /* LGINNOTEK ATSC */ .name = "LG TDVS-H06xF", /* H061F, H062F & H064F */ - .params = tuner_tua6034_params, - .count = ARRAY_SIZE(tuner_tua6034_params), + .params = tuner_lg_tdvs_h06xf_params, + .count = ARRAY_SIZE(tuner_lg_tdvs_h06xf_params), }, [TUNER_YMEC_TVF66T5_B_DFF] = { /* Philips PAL */ .name = "Ymec TVF66T5-B/DFF", -- cgit v1.2.3 From 248256f91d487672dd126161381f5364b0b29ce6 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Thu, 27 Apr 2006 00:31:26 -0400 Subject: dvb: rename dvb_pll_tdvs_tua6034 to dvb_pll_lg_tdvs_h06xf From: Michael Krufky Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/frontends/dvb-pll.c | 4 ++-- linux/drivers/media/dvb/frontends/dvb-pll.h | 2 +- linux/drivers/media/dvb/frontends/lg_h06xf.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.c b/linux/drivers/media/dvb/frontends/dvb-pll.c index 7e65841b1..a1a4be41e 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.c +++ b/linux/drivers/media/dvb/frontends/dvb-pll.c @@ -229,7 +229,7 @@ EXPORT_SYMBOL(dvb_pll_tua6034); /* Infineon TUA6034 * used in LG TDVS-H061F, LG TDVS-H062F and LG TDVS-H064F */ -struct dvb_pll_desc dvb_pll_tdvs_tua6034 = { +struct dvb_pll_desc dvb_pll_lg_tdvs_h06xf = { .name = "LG TDVS-H06xF", .min = 54000000, .max = 863000000, @@ -240,7 +240,7 @@ struct dvb_pll_desc dvb_pll_tdvs_tua6034 = { { 999999999, 44000000, 62500, 0xce, 0x04 }, }, }; -EXPORT_SYMBOL(dvb_pll_tdvs_tua6034); +EXPORT_SYMBOL(dvb_pll_lg_tdvs_h06xf); /* Philips FMD1216ME * used in Medion Hybrid PCMCIA card and USB Box diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.h b/linux/drivers/media/dvb/frontends/dvb-pll.h index 3908ae1d4..66361cd18 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.h +++ b/linux/drivers/media/dvb/frontends/dvb-pll.h @@ -34,7 +34,7 @@ extern struct dvb_pll_desc dvb_pll_unknown_1; extern struct dvb_pll_desc dvb_pll_tua6010xs; extern struct dvb_pll_desc dvb_pll_env57h1xd5; extern struct dvb_pll_desc dvb_pll_tua6034; -extern struct dvb_pll_desc dvb_pll_tdvs_tua6034; +extern struct dvb_pll_desc dvb_pll_lg_tdvs_h06xf; extern struct dvb_pll_desc dvb_pll_tda665x; extern struct dvb_pll_desc dvb_pll_fmd1216me; extern struct dvb_pll_desc dvb_pll_tded4; diff --git a/linux/drivers/media/dvb/frontends/lg_h06xf.h b/linux/drivers/media/dvb/frontends/lg_h06xf.h index 355a4a498..6869cbd03 100644 --- a/linux/drivers/media/dvb/frontends/lg_h06xf.h +++ b/linux/drivers/media/dvb/frontends/lg_h06xf.h @@ -28,7 +28,7 @@ static int lg_h06xf_pll_set(struct dvb_frontend* fe, struct i2c_adapter* i2c_ada .buf = buf, .len = sizeof(buf) }; int err; - dvb_pll_configure(&dvb_pll_tdvs_tua6034, buf, params->frequency, 0); + dvb_pll_configure(&dvb_pll_lg_tdvs_h06xf, buf, params->frequency, 0); if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) { printk(KERN_WARNING "lg_h06xf: %s error " "(addr %02x <- %02x, err = %i)\n", -- cgit v1.2.3 From a9cae36126fb519c71e57cd93fd8a035e8427478 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Apr 2006 10:09:27 -0300 Subject: Sparc32-vivi-fix From: Andrew Morton Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index e4dd4181d..da1817001 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -18,7 +18,7 @@ config VIDEO_ADV_DEBUG config VIDEO_VIVI tristate "Virtual Video Driver" - depends on VIDEO_V4L2 + depends on VIDEO_V4L2 && !SPARC32 && !SPARC64 default n ---help--- Enables a virtual video driver. This device shows a color bar -- cgit v1.2.3 From c9200428724317603727346a1245593378626343 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Apr 2006 10:10:35 -0300 Subject: Git-dvb-Kconfig-fix From: Andrew Morton x86 allmodconfig: Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/Kconfig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/Kconfig b/linux/drivers/media/Kconfig index 344d83aae..3ff3d6450 100644 --- a/linux/drivers/media/Kconfig +++ b/linux/drivers/media/Kconfig @@ -27,7 +27,6 @@ config VIDEO_DEV config VIDEO_V4L1 boolean "Enable Video For Linux API 1 (DEPRECATED)" depends on VIDEO_DEV - select VIDEO_V4L1_COMPAT default y ---help--- Enables a compatibility API used by most V4L2 devices to allow @@ -36,8 +35,8 @@ config VIDEO_V4L1 If you are unsure as to whether this is required, answer Y. config VIDEO_V4L1_COMPAT - boolean "Enable Video For Linux API 1 compatible Layer" - depends on VIDEO_DEV + tristate "Enable Video For Linux API 1 compatible Layer" + depends on VIDEO_DEV && COMPAT default y ---help--- This api were developed to be used at Kernel 2.2 and 2.4, but -- cgit v1.2.3 From 3a0da040ea124d6f58f05892da0f9a862b095f75 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Apr 2006 10:10:58 -0300 Subject: Vivi build fix From: Andrew Morton drivers/media/video/vivi.c: In function `vivi_map_sg': drivers/media/video/vivi.c:799: error: `DMA_NONE' undeclared (first use in this function) drivers/media/video/vivi.c:799: error: (Each undeclared identifier is reported only once drivers/media/video/vivi.c:799: error: for each function it appears in.) Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/vivi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/vivi.c b/linux/drivers/media/video/vivi.c index 843fdecbc..a60f14c14 100644 --- a/linux/drivers/media/video/vivi.c +++ b/linux/drivers/media/video/vivi.c @@ -26,6 +26,7 @@ #include #include #include +#include #ifdef CONFIG_VIDEO_V4L1_COMPAT /* Include V4L1 specific functions. Should be removed soon */ #include -- cgit v1.2.3 From 13a1f108bfa987434a7b60a33837b47dea5f50db Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Apr 2006 10:17:00 -0300 Subject: AverMedia 6 Eyes AVS6EYES support From: Martin Samuelsson Add support for the AverMedia 6 Eyes MJPEG card. - Updated drivers/media/video/Kconfig with AVS6EYES options. - Added CONFIG_VIDEO_ZORAN_AVS6EYES to drivers/media/video/Makefile. - Added I2C_DRIVERID_BT866 and I2C_DRIVERID_KS0127 to include/linux/i2c-id.h - Added drivers/media/video/ks0127.c, imported and modified from the Marvel project. - Added drivers/media/video/ks0127.h, imported and modified from the Marvel project. - Added drivers/media/video/bt866.c, ported from a 2.4 version by Christer Weinigel. - Added AVS6EYES to drivers/media/video/zoran_card.c - Added input_mux to all cards in drivers/media/video/zoran_card.c - Added input mux module parameter to drivers/media/video/zoran_card.c - Added AVS6EYES to card_type in drivers/media/video/zoran.h - Added input_mux to card_info in drivers/media/video/zoran.h - Upped BUZ_MAX_INPUT in drivers/media/video/zoran.h from 8 to 16, as the AVS6EYES has 10. - Updated Documentation/video4linux/Zoran with information about AVS6EYES. Signed-off-by: Martin Samuelsson Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/Kconfig | 6 + linux/drivers/media/video/Makefile | 1 + linux/drivers/media/video/bt866.c | 377 +++++++++++++++ linux/drivers/media/video/ks0127.c | 846 +++++++++++++++++++++++++++++++++ linux/drivers/media/video/ks0127.h | 53 +++ linux/drivers/media/video/zoran.h | 8 +- linux/drivers/media/video/zoran_card.c | 87 ++++ 7 files changed, 1377 insertions(+), 1 deletion(-) create mode 100644 linux/drivers/media/video/bt866.c create mode 100644 linux/drivers/media/video/ks0127.c create mode 100644 linux/drivers/media/video/ks0127.h (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index da1817001..4df2ede38 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -223,6 +223,12 @@ config VIDEO_ZORAN_LML33R10 support for the Linux Media Labs LML33R10 MJPEG capture/playback card. +config VIDEO_ZORAN_AVS6EYES + tristate "AverMedia 6 Eyes support (EXPERIMENTAL)" + depends on VIDEO_ZORAN && EXPERIMENTAL && VIDEO_V4L1 + help + Support for the AverMedia 6 Eyes video surveillance card. + config VIDEO_ZR36120 tristate "Zoran ZR36120/36125 Video For Linux" depends on PCI && I2C && VIDEO_V4L1 && BROKEN diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index bd3f1a006..a9046a603 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_VIDEO_ZORAN_DC30) += adv7175.o vpx3220.o zr36050.o \ zr36016.o obj-$(CONFIG_VIDEO_ZORAN_LML33) += bt819.o bt856.o zr36060.o obj-$(CONFIG_VIDEO_ZORAN_LML33R10) += saa7114.o adv7170.o zr36060.o +obj-$(CONFIG_VIDEO_ZORAN_AVS6EYES) += bt866.o ks0127.o zr36060.o obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o videocodec.o obj-$(CONFIG_VIDEO_PMS) += pms.o obj-$(CONFIG_VIDEO_PLANB) += planb.o diff --git a/linux/drivers/media/video/bt866.c b/linux/drivers/media/video/bt866.c new file mode 100644 index 000000000..05e42bbcf --- /dev/null +++ b/linux/drivers/media/video/bt866.c @@ -0,0 +1,377 @@ +/* + bt866 - BT866 Digital Video Encoder (Rockwell Part) + + Copyright (C) 1999 Mike Bernson + Copyright (C) 1998 Dave Perks + + Modifications for LML33/DC10plus unified driver + Copyright (C) 2000 Serguei Miridonov + + This code was modify/ported from the saa7111 driver written + by Dave Perks. + + This code was adapted for the bt866 by Christer Weinigel and ported + to 2.6 by Martin Samuelsson. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +MODULE_LICENSE("GPL"); + +#define BT866_DEVNAME "bt866" +#define I2C_BT866 0x88 + +MODULE_LICENSE("GPL"); + +#define DEBUG(x) /* Debug driver */ + +/* ----------------------------------------------------------------------- */ + +struct bt866 { + struct i2c_client *i2c; + int addr; + unsigned char reg[128]; + + int norm; + int enable; + int bright; + int contrast; + int hue; + int sat; +}; + +static int bt866_write(struct bt866 *dev, + unsigned char subaddr, unsigned char data); + +static int bt866_do_command(struct bt866 *encoder, + unsigned int cmd, void *arg) +{ + switch (cmd) { + case ENCODER_GET_CAPABILITIES: + { + struct video_encoder_capability *cap = arg; + + DEBUG(printk + (KERN_INFO "%s: get capabilities\n", + encoder->i2c->name)); + + cap->flags + = VIDEO_ENCODER_PAL + | VIDEO_ENCODER_NTSC + | VIDEO_ENCODER_CCIR; + cap->inputs = 2; + cap->outputs = 1; + } + break; + + case ENCODER_SET_NORM: + { + int *iarg = arg; + + DEBUG(printk(KERN_INFO "%s: set norm %d\n", + encoder->i2c->name, *iarg)); + + switch (*iarg) { + + case VIDEO_MODE_NTSC: + break; + + case VIDEO_MODE_PAL: + break; + + default: + return -EINVAL; + + } + encoder->norm = *iarg; + } + break; + + case ENCODER_SET_INPUT: + { + int *iarg = arg; + static const __u8 init[] = { + 0xc8, 0xcc, /* CRSCALE */ + 0xca, 0x91, /* CBSCALE */ + 0xcc, 0x24, /* YC16 | OSDNUM */ + 0xda, 0x00, /* */ + 0xdc, 0x24, /* SETMODE | PAL */ + 0xde, 0x02, /* EACTIVE */ + + /* overlay colors */ + 0x70, 0xEB, 0x90, 0x80, 0xB0, 0x80, /* white */ + 0x72, 0xA2, 0x92, 0x8E, 0xB2, 0x2C, /* yellow */ + 0x74, 0x83, 0x94, 0x2C, 0xB4, 0x9C, /* cyan */ + 0x76, 0x70, 0x96, 0x3A, 0xB6, 0x48, /* green */ + 0x78, 0x54, 0x98, 0xC6, 0xB8, 0xB8, /* magenta */ + 0x7A, 0x41, 0x9A, 0xD4, 0xBA, 0x64, /* red */ + 0x7C, 0x23, 0x9C, 0x72, 0xBC, 0xD4, /* blue */ + 0x7E, 0x10, 0x9E, 0x80, 0xBE, 0x80, /* black */ + + 0x60, 0xEB, 0x80, 0x80, 0xc0, 0x80, /* white */ + 0x62, 0xA2, 0x82, 0x8E, 0xc2, 0x2C, /* yellow */ + 0x64, 0x83, 0x84, 0x2C, 0xc4, 0x9C, /* cyan */ + 0x66, 0x70, 0x86, 0x3A, 0xc6, 0x48, /* green */ + 0x68, 0x54, 0x88, 0xC6, 0xc8, 0xB8, /* magenta */ + 0x6A, 0x41, 0x8A, 0xD4, 0xcA, 0x64, /* red */ + 0x6C, 0x23, 0x8C, 0x72, 0xcC, 0xD4, /* blue */ + 0x6E, 0x10, 0x8E, 0x80, 0xcE, 0x80, /* black */ + }; + int i; + u8 val; + + for (i = 0; i < ARRAY_SIZE(init) / 2; i += 2) + bt866_write(encoder, init[i], init[i+1]); + + val = encoder->reg[0xdc]; + + if (*iarg == 0) + val |= 0x40; /* CBSWAP */ + else + val &= ~0x40; /* !CBSWAP */ + + bt866_write(encoder, 0xdc, val); + + val = encoder->reg[0xcc]; + if (*iarg == 2) + val |= 0x01; /* OSDBAR */ + else + val &= ~0x01; /* !OSDBAR */ + bt866_write(encoder, 0xcc, val); + + DEBUG(printk(KERN_INFO "%s: set input %d\n", + encoder->i2c->name, *iarg)); + + switch (*iarg) { + case 0: + break; + case 1: + break; + default: + return -EINVAL; + + } + } + break; + + case ENCODER_SET_OUTPUT: + { + int *iarg = arg; + + DEBUG(printk(KERN_INFO "%s: set output %d\n", + encoder->i2c->name, *iarg)); + + /* not much choice of outputs */ + if (*iarg != 0) + return -EINVAL; + } + break; + + case ENCODER_ENABLE_OUTPUT: + { + int *iarg = arg; + encoder->enable = !!*iarg; + + DEBUG(printk + (KERN_INFO "%s: enable output %d\n", + encoder->i2c->name, encoder->enable)); + } + break; + + case 4711: + { + int *iarg = arg; + __u8 val; + + printk("bt866: square = %d\n", *iarg); + + val = encoder->reg[0xdc]; + if (*iarg) + val |= 1; /* SQUARE */ + else + val &= ~1; /* !SQUARE */ + bt866_write(encoder, 0xdc, val); + break; + } + + default: + return -EINVAL; + } + + return 0; +} + +static int bt866_write(struct bt866 *encoder, + unsigned char subaddr, unsigned char data) +{ + unsigned char buffer[2]; + int err; + + buffer[0] = subaddr; + buffer[1] = data; + + encoder->reg[subaddr] = data; + + DEBUG(printk + ("%s: write 0x%02X = 0x%02X\n", + encoder->i2c->name, subaddr, data)); + + for (err = 0; err < 3;) { + if (i2c_master_send(encoder->i2c, buffer, 2) == 2) + break; + err++; + printk(KERN_WARNING "%s: I/O error #%d " + "(write 0x%02x/0x%02x)\n", + encoder->i2c->name, err, encoder->addr, subaddr); + schedule_timeout_interruptible(HZ/10); + } + if (err == 3) { + printk(KERN_WARNING "%s: giving up\n", + encoder->i2c->name); + return -1; + } + + return 0; +} + +static int bt866_attach(struct i2c_adapter *adapter); +static int bt866_detach(struct i2c_client *client); +static int bt866_command(struct i2c_client *client, + unsigned int cmd, void *arg); + + +/* Addresses to scan */ +static unsigned short normal_i2c[] = {I2C_BT866>>1, I2C_CLIENT_END}; +static unsigned short probe[2] = {I2C_CLIENT_END, I2C_CLIENT_END}; +static unsigned short ignore[2] = {I2C_CLIENT_END, I2C_CLIENT_END}; + +static struct i2c_client_address_data addr_data = { + normal_i2c, + probe, + ignore, +}; + +static struct i2c_driver i2c_driver_bt866 = { + .driver.name = BT866_DEVNAME, + .id = I2C_DRIVERID_BT866, + .attach_adapter = bt866_attach, + .detach_client = bt866_detach, + .command = bt866_command +}; + + +static struct i2c_client bt866_client_tmpl = +{ + .name = "(nil)", + .addr = 0, + .adapter = NULL, + .driver = &i2c_driver_bt866, + .usage_count = 0 +}; + +static int bt866_found_proc(struct i2c_adapter *adapter, + int addr, int kind) +{ + struct bt866 *encoder; + struct i2c_client *client; + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (client == NULL) + return -ENOMEM; + memcpy(client, &bt866_client_tmpl, sizeof(*client)); + + encoder = kzalloc(sizeof(*encoder), GFP_KERNEL); + if (encoder == NULL) { + kfree(client); + return -ENOMEM; + } + + i2c_set_clientdata(client, encoder); + client->adapter = adapter; + client->addr = addr; + sprintf(client->name, "%s-%02x", BT866_DEVNAME, adapter->id); + + encoder->i2c = client; + encoder->addr = addr; + //encoder->encoder_type = ENCODER_TYPE_UNKNOWN; + + /* initialize */ + + i2c_attach_client(client); + + return 0; +} + +static int bt866_attach(struct i2c_adapter *adapter) +{ + if (adapter->id == I2C_HW_B_ZR36067) + return i2c_probe(adapter, &addr_data, bt866_found_proc); + return 0; +} + +static int bt866_detach(struct i2c_client *client) +{ + struct bt866 *encoder = i2c_get_clientdata(client); + + i2c_detach_client(client); + kfree(encoder); + kfree(client); + + return 0; +} + +static int bt866_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + struct bt866 *encoder = i2c_get_clientdata(client); + return bt866_do_command(encoder, cmd, arg); +} + +static int __devinit bt866_init(void) +{ + i2c_add_driver(&i2c_driver_bt866); + return 0; +} + +static void __devexit bt866_exit(void) +{ + i2c_del_driver(&i2c_driver_bt866); +} + +module_init(bt866_init); +module_exit(bt866_exit); diff --git a/linux/drivers/media/video/ks0127.c b/linux/drivers/media/video/ks0127.c new file mode 100644 index 000000000..3bf7ac4f5 --- /dev/null +++ b/linux/drivers/media/video/ks0127.c @@ -0,0 +1,846 @@ +/* + * Video Capture Driver (Video for Linux 1/2) + * for the Matrox Marvel G200,G400 and Rainbow Runner-G series + * + * This module is an interface to the KS0127 video decoder chip. + * + * Copyright (C) 1999 Ryan Drake + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + ***************************************************************************** + * + * Modified and extended by + * Mike Bernson + * Gerard v.d. Horst + * Leon van Stuivenberg + * Gernot Ziegler + * + * Version History: + * V1.0 Ryan Drake Initial version by Ryan Drake + * V1.1 Gerard v.d. Horst Added some debugoutput, reset the video-standard + */ + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "ks0127.h" + +#include +#include + +#define dprintk if (debug) printk + +/* i2c identification */ +#define I2C_KS0127_ADDON 0xD8 +#define I2C_KS0127_ONBOARD 0xDA + +#define KS_TYPE_UNKNOWN 0 +#define KS_TYPE_0122S 1 +#define KS_TYPE_0127 2 +#define KS_TYPE_0127B 3 + +/* ks0127 control registers */ +#define KS_STAT 0x00 +#define KS_CMDA 0x01 +#define KS_CMDB 0x02 +#define KS_CMDC 0x03 +#define KS_CMDD 0x04 +#define KS_HAVB 0x05 +#define KS_HAVE 0x06 +#define KS_HS1B 0x07 +#define KS_HS1E 0x08 +#define KS_HS2B 0x09 +#define KS_HS2E 0x0a +#define KS_AGC 0x0b +#define KS_HXTRA 0x0c +#define KS_CDEM 0x0d +#define KS_PORTAB 0x0e +#define KS_LUMA 0x0f +#define KS_CON 0x10 +#define KS_BRT 0x11 +#define KS_CHROMA 0x12 +#define KS_CHROMB 0x13 +#define KS_DEMOD 0x14 +#define KS_SAT 0x15 +#define KS_HUE 0x16 +#define KS_VERTIA 0x17 +#define KS_VERTIB 0x18 +#define KS_VERTIC 0x19 +#define KS_HSCLL 0x1a +#define KS_HSCLH 0x1b +#define KS_VSCLL 0x1c +#define KS_VSCLH 0x1d +#define KS_OFMTA 0x1e +#define KS_OFMTB 0x1f +#define KS_VBICTL 0x20 +#define KS_CCDAT2 0x21 +#define KS_CCDAT1 0x22 +#define KS_VBIL30 0x23 +#define KS_VBIL74 0x24 +#define KS_VBIL118 0x25 +#define KS_VBIL1512 0x26 +#define KS_TTFRAM 0x27 +#define KS_TESTA 0x28 +#define KS_UVOFFH 0x29 +#define KS_UVOFFL 0x2a +#define KS_UGAIN 0x2b +#define KS_VGAIN 0x2c +#define KS_VAVB 0x2d +#define KS_VAVE 0x2e +#define KS_CTRACK 0x2f +#define KS_POLCTL 0x30 +#define KS_REFCOD 0x31 +#define KS_INVALY 0x32 +#define KS_INVALU 0x33 +#define KS_INVALV 0x34 +#define KS_UNUSEY 0x35 +#define KS_UNUSEU 0x36 +#define KS_UNUSEV 0x37 +#define KS_USRSAV 0x38 +#define KS_USREAV 0x39 +#define KS_SHS1A 0x3a +#define KS_SHS1B 0x3b +#define KS_SHS1C 0x3c +#define KS_CMDE 0x3d +#define KS_VSDEL 0x3e +#define KS_CMDF 0x3f +#define KS_GAMMA0 0x40 +#define KS_GAMMA1 0x41 +#define KS_GAMMA2 0x42 +#define KS_GAMMA3 0x43 +#define KS_GAMMA4 0x44 +#define KS_GAMMA5 0x45 +#define KS_GAMMA6 0x46 +#define KS_GAMMA7 0x47 +#define KS_GAMMA8 0x48 +#define KS_GAMMA9 0x49 +#define KS_GAMMA10 0x4a +#define KS_GAMMA11 0x4b +#define KS_GAMMA12 0x4c +#define KS_GAMMA13 0x4d +#define KS_GAMMA14 0x4e +#define KS_GAMMA15 0x4f +#define KS_GAMMA16 0x50 +#define KS_GAMMA17 0x51 +#define KS_GAMMA18 0x52 +#define KS_GAMMA19 0x53 +#define KS_GAMMA20 0x54 +#define KS_GAMMA21 0x55 +#define KS_GAMMA22 0x56 +#define KS_GAMMA23 0x57 +#define KS_GAMMA24 0x58 +#define KS_GAMMA25 0x59 +#define KS_GAMMA26 0x5a +#define KS_GAMMA27 0x5b +#define KS_GAMMA28 0x5c +#define KS_GAMMA29 0x5d +#define KS_GAMMA30 0x5e +#define KS_GAMMA31 0x5f +#define KS_GAMMAD0 0x60 +#define KS_GAMMAD1 0x61 +#define KS_GAMMAD2 0x62 +#define KS_GAMMAD3 0x63 +#define KS_GAMMAD4 0x64 +#define KS_GAMMAD5 0x65 +#define KS_GAMMAD6 0x66 +#define KS_GAMMAD7 0x67 +#define KS_GAMMAD8 0x68 +#define KS_GAMMAD9 0x69 +#define KS_GAMMAD10 0x6a +#define KS_GAMMAD11 0x6b +#define KS_GAMMAD12 0x6c +#define KS_GAMMAD13 0x6d +#define KS_GAMMAD14 0x6e +#define KS_GAMMAD15 0x6f +#define KS_GAMMAD16 0x70 +#define KS_GAMMAD17 0x71 +#define KS_GAMMAD18 0x72 +#define KS_GAMMAD19 0x73 +#define KS_GAMMAD20 0x74 +#define KS_GAMMAD21 0x75 +#define KS_GAMMAD22 0x76 +#define KS_GAMMAD23 0x77 +#define KS_GAMMAD24 0x78 +#define KS_GAMMAD25 0x79 +#define KS_GAMMAD26 0x7a +#define KS_GAMMAD27 0x7b +#define KS_GAMMAD28 0x7c +#define KS_GAMMAD29 0x7d +#define KS_GAMMAD30 0x7e +#define KS_GAMMAD31 0x7f + + +/**************************************************************************** +* mga_dev : represents one ks0127 chip. +****************************************************************************/ + +struct adjust { + int contrast; + int bright; + int hue; + int ugain; + int vgain; +}; + +struct ks0127 { + struct i2c_client *client; + unsigned char addr; + int format_width; + int format_height; + int cap_width; + int cap_height; + int norm; + int ks_type; + u8 regs[256]; +}; + + +static int debug; /* insmod parameter */ + +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug output"); +MODULE_LICENSE("GPL"); + +static u8 reg_defaults[64]; + + + +static void init_reg_defaults(void) +{ + u8 *table = reg_defaults; + + table[KS_CMDA] = 0x2c; /* VSE=0, CCIR 601, autodetect standard */ + table[KS_CMDB] = 0x12; /* VALIGN=0, AGC control and input */ + table[KS_CMDC] = 0x00; /* Test options */ + /* clock & input select, write 1 to PORTA */ + table[KS_CMDD] = 0x01; + table[KS_HAVB] = 0x00; /* HAV Start Control */ + table[KS_HAVE] = 0x00; /* HAV End Control */ + table[KS_HS1B] = 0x10; /* HS1 Start Control */ + table[KS_HS1E] = 0x00; /* HS1 End Control */ + table[KS_HS2B] = 0x00; /* HS2 Start Control */ + table[KS_HS2E] = 0x00; /* HS2 End Control */ + table[KS_AGC] = 0x53; /* Manual setting for AGC */ + table[KS_HXTRA] = 0x00; /* Extra Bits for HAV and HS1/2 */ + table[KS_CDEM] = 0x00; /* Chroma Demodulation Control */ + table[KS_PORTAB] = 0x0f; /* port B is input, port A output GPPORT */ + table[KS_LUMA] = 0x01; /* Luma control */ + table[KS_CON] = 0x00; /* Contrast Control */ + table[KS_BRT] = 0x00; /* Brightness Control */ + table[KS_CHROMA] = 0x2a; /* Chroma control A */ + table[KS_CHROMB] = 0x90; /* Chroma control B */ + table[KS_DEMOD] = 0x00; /* Chroma Demodulation Control & Status */ + table[KS_SAT] = 0x00; /* Color Saturation Control*/ + table[KS_HUE] = 0x00; /* Hue Control */ + table[KS_VERTIA] = 0x00; /* Vertical Processing Control A */ + /* Vertical Processing Control B, luma 1 line delayed */ + table[KS_VERTIB] = 0x12; + table[KS_VERTIC] = 0x0b; /* Vertical Processing Control C */ + table[KS_HSCLL] = 0x00; /* Horizontal Scaling Ratio Low */ + table[KS_HSCLH] = 0x00; /* Horizontal Scaling Ratio High */ + table[KS_VSCLL] = 0x00; /* Vertical Scaling Ratio Low */ + table[KS_VSCLH] = 0x00; /* Vertical Scaling Ratio High */ + /* 16 bit YCbCr 4:2:2 output; I can't make the bt866 like 8 bit /Sam */ + table[KS_OFMTA] = 0x30; + table[KS_OFMTB] = 0x00; /* Output Control B */ + /* VBI Decoder Control; 4bit fmt: avoid Y overflow */ + table[KS_VBICTL] = 0x5d; + table[KS_CCDAT2] = 0x00; /* Read Only register */ + table[KS_CCDAT1] = 0x00; /* Read Only register */ + table[KS_VBIL30] = 0xa8; /* VBI data decoding options */ + table[KS_VBIL74] = 0xaa; /* VBI data decoding options */ + table[KS_VBIL118] = 0x2a; /* VBI data decoding options */ + table[KS_VBIL1512] = 0x00; /* VBI data decoding options */ + table[KS_TTFRAM] = 0x00; /* Teletext frame alignment pattern */ + table[KS_TESTA] = 0x00; /* test register, shouldn't be written */ + table[KS_UVOFFH] = 0x00; /* UV Offset Adjustment High */ + table[KS_UVOFFL] = 0x00; /* UV Offset Adjustment Low */ + table[KS_UGAIN] = 0x00; /* U Component Gain Adjustment */ + table[KS_VGAIN] = 0x00; /* V Component Gain Adjustment */ + table[KS_VAVB] = 0x07; /* VAV Begin */ + table[KS_VAVE] = 0x00; /* VAV End */ + table[KS_CTRACK] = 0x00; /* Chroma Tracking Control */ + table[KS_POLCTL] = 0x41; /* Timing Signal Polarity Control */ + table[KS_REFCOD] = 0x80; /* Reference Code Insertion Control */ + table[KS_INVALY] = 0x10; /* Invalid Y Code */ + table[KS_INVALU] = 0x80; /* Invalid U Code */ + table[KS_INVALV] = 0x80; /* Invalid V Code */ + table[KS_UNUSEY] = 0x10; /* Unused Y Code */ + table[KS_UNUSEU] = 0x80; /* Unused U Code */ + table[KS_UNUSEV] = 0x80; /* Unused V Code */ + table[KS_USRSAV] = 0x00; /* reserved */ + table[KS_USREAV] = 0x00; /* reserved */ + table[KS_SHS1A] = 0x00; /* User Defined SHS1 A */ + /* User Defined SHS1 B, ALT656=1 on 0127B */ + table[KS_SHS1B] = 0x80; + table[KS_SHS1C] = 0x00; /* User Defined SHS1 C */ + table[KS_CMDE] = 0x00; /* Command Register E */ + table[KS_VSDEL] = 0x00; /* VS Delay Control */ + /* Command Register F, update -immediately- */ + /* (there might come no vsync)*/ + table[KS_CMDF] = 0x02; +} + + +/* We need to manually read because of a bug in the KS0127 chip. + * + * An explanation from kayork@mail.utexas.edu: + * + * During I2C reads, the KS0127 only samples for a stop condition + * during the place where the acknoledge bit should be. Any standard + * I2C implementation (correctly) throws in another clock transition + * at the 9th bit, and the KS0127 will not recognize the stop condition + * and will continue to clock out data. + * + * So we have to do the read ourself. Big deal. + workaround in i2c-algo-bit + */ + + +static u8 ks0127_read(struct ks0127 *ks, u8 reg) +{ + struct i2c_client *c = ks->client; + char val = 0; + struct i2c_msg msgs[] = { + {c->addr, 0, sizeof(reg), ®}, + {c->addr, I2C_M_RD | I2C_M_NO_RD_ACK, sizeof(val), &val}}; + int ret; + + ret = i2c_transfer(c->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + dprintk("ks0127_write error\n"); + + return val; +} + + +static void ks0127_write(struct ks0127 *ks, u8 reg, u8 val) +{ + char msg[] = {reg, val}; + + if (i2c_master_send(ks->client, msg, sizeof(msg)) != sizeof(msg)) + dprintk("ks0127_write error\n"); + + ks->regs[reg] = val; +} + + +/* generic bit-twiddling */ +static void ks0127_and_or(struct ks0127 *ks, u8 reg, u8 and_v, u8 or_v) +{ + u8 val = ks->regs[reg]; + val = (val & and_v) | or_v; + ks0127_write(ks, reg, val); +} + + + +/**************************************************************************** +* ks0127 private api +****************************************************************************/ +static void ks0127_reset(struct ks0127* ks) +{ + int i; + u8 *table = reg_defaults; + + ks->ks_type = KS_TYPE_UNKNOWN; + + dprintk("ks0127: reset\n"); + msleep(1); + + /* initialize all registers to known values */ + /* (except STAT, 0x21, 0x22, TEST and 0x38,0x39) */ + + for(i = 1; i < 33; i++) + ks0127_write(ks, i, table[i]); + + for(i = 35; i < 40; i++) + ks0127_write(ks, i, table[i]); + + for(i = 41; i < 56; i++) + ks0127_write(ks, i, table[i]); + + for(i = 58; i < 64; i++) + ks0127_write(ks, i, table[i]); + + + if ((ks0127_read(ks, KS_STAT) & 0x80) == 0) { + ks->ks_type = KS_TYPE_0122S; + dprintk("ks0127: ks0122s Found\n"); + return; + } + + switch(ks0127_read(ks, KS_CMDE) & 0x0f) { + + case 0: + ks->ks_type = KS_TYPE_0127; + dprintk("ks0127: ks0127 found\n"); + break; + + case 9: + ks->ks_type = KS_TYPE_0127B; + dprintk("ks0127: ks0127B Revision A found\n"); + break; + + default: + dprintk("ks0127: unknown revision\n"); + break; + } +} + +static int ks0127_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + struct ks0127 *ks = i2c_get_clientdata(client); + + int *iarg = (int*)arg; + + int status; + + if (!ks) + return -ENODEV; + + switch (cmd) { + + case DECODER_INIT: + dprintk("ks0127: command DECODER_INIT\n"); + ks0127_reset(ks); + break; + + case DECODER_SET_INPUT: + switch(*iarg) { + case KS_INPUT_COMPOSITE_1: + case KS_INPUT_COMPOSITE_2: + case KS_INPUT_COMPOSITE_3: + case KS_INPUT_COMPOSITE_4: + case KS_INPUT_COMPOSITE_5: + case KS_INPUT_COMPOSITE_6: + dprintk("ks0127: command DECODER_SET_INPUT %d: " + "Composite\n", *iarg); + /* autodetect 50/60 Hz */ + ks0127_and_or(ks, KS_CMDA, 0xfc, 0x00); + /* VSE=0 */ + ks0127_and_or(ks, KS_CMDA, ~0x40, 0x00); + /* set input line */ + ks0127_and_or(ks, KS_CMDB, 0xb0, *iarg); + /* non-freerunning mode */ + ks0127_and_or(ks, KS_CMDC, 0x70, 0x0a); + /* analog input */ + ks0127_and_or(ks, KS_CMDD, 0x03, 0x00); + /* enable chroma demodulation */ + ks0127_and_or(ks, KS_CTRACK, 0xcf, 0x00); + /* chroma trap, HYBWR=1 */ + ks0127_and_or(ks, KS_LUMA, 0x00, + (reg_defaults[KS_LUMA])|0x0c); + /* scaler fullbw, luma comb off */ + ks0127_and_or(ks, KS_VERTIA, 0x08, 0x81); + /* manual chroma comb .25 .5 .25 */ + ks0127_and_or(ks, KS_VERTIC, 0x0f, 0x90); + + /* chroma path delay */ + ks0127_and_or(ks, KS_CHROMB, 0x0f, 0x90); + + ks0127_write(ks, KS_UGAIN, reg_defaults[KS_UGAIN]); + ks0127_write(ks, KS_VGAIN, reg_defaults[KS_VGAIN]); + ks0127_write(ks, KS_UVOFFH, reg_defaults[KS_UVOFFH]); + ks0127_write(ks, KS_UVOFFL, reg_defaults[KS_UVOFFL]); + break; + + case KS_INPUT_SVIDEO_1: + case KS_INPUT_SVIDEO_2: + case KS_INPUT_SVIDEO_3: + dprintk("ks0127: command DECODER_SET_INPUT %d: " + "S-Video\n", *iarg); + /* autodetect 50/60 Hz */ + ks0127_and_or(ks, KS_CMDA, 0xfc, 0x00); + /* VSE=0 */ + ks0127_and_or(ks, KS_CMDA, ~0x40, 0x00); + /* set input line */ + ks0127_and_or(ks, KS_CMDB, 0xb0, *iarg); + /* non-freerunning mode */ + ks0127_and_or(ks, KS_CMDC, 0x70, 0x0a); + /* analog input */ + ks0127_and_or(ks, KS_CMDD, 0x03, 0x00); + /* enable chroma demodulation */ + ks0127_and_or(ks, KS_CTRACK, 0xcf, 0x00); + ks0127_and_or(ks, KS_LUMA, 0x00, + reg_defaults[KS_LUMA]); + /* disable luma comb */ + ks0127_and_or(ks, KS_VERTIA, 0x08, + (reg_defaults[KS_VERTIA]&0xf0)|0x01); + ks0127_and_or(ks, KS_VERTIC, 0x0f, + reg_defaults[KS_VERTIC]&0xf0); + + ks0127_and_or(ks, KS_CHROMB, 0x0f, + reg_defaults[KS_CHROMB]&0xf0); + + ks0127_write(ks, KS_UGAIN, reg_defaults[KS_UGAIN]); + ks0127_write(ks, KS_VGAIN, reg_defaults[KS_VGAIN]); + ks0127_write(ks, KS_UVOFFH, reg_defaults[KS_UVOFFH]); + ks0127_write(ks, KS_UVOFFL, reg_defaults[KS_UVOFFL]); + break; + + case KS_INPUT_YUV656: + dprintk("ks0127: command DECODER_SET_INPUT 15: " + "YUV656\n"); + if (ks->norm == VIDEO_MODE_NTSC || + ks->norm == KS_STD_PAL_M) + /* force 60 Hz */ + ks0127_and_or(ks, KS_CMDA, 0xfc, 0x03); + else + /* force 50 Hz */ + ks0127_and_or(ks, KS_CMDA, 0xfc, 0x02); + + ks0127_and_or(ks, KS_CMDA, 0xff, 0x40); /* VSE=1 */ + /* set input line and VALIGN */ + ks0127_and_or(ks, KS_CMDB, 0xb0, (*iarg | 0x40)); + /* freerunning mode, */ + /* TSTGEN = 1 TSTGFR=11 TSTGPH=0 TSTGPK=0 VMEM=1*/ + ks0127_and_or(ks, KS_CMDC, 0x70, 0x87); + /* digital input, SYNDIR = 0 INPSL=01 CLKDIR=0 EAV=0 */ + ks0127_and_or(ks, KS_CMDD, 0x03, 0x08); + /* disable chroma demodulation */ + ks0127_and_or(ks, KS_CTRACK, 0xcf, 0x30); + /* HYPK =01 CTRAP = 0 HYBWR=0 PED=1 RGBH=1 UNIT=1 */ + ks0127_and_or(ks, KS_LUMA, 0x00, 0x71); + ks0127_and_or(ks, KS_VERTIC, 0x0f, + reg_defaults[KS_VERTIC]&0xf0); + + /* scaler fullbw, luma comb off */ + ks0127_and_or(ks, KS_VERTIA, 0x08, 0x81); + + ks0127_and_or(ks, KS_CHROMB, 0x0f, + reg_defaults[KS_CHROMB]&0xf0); + + ks0127_and_or(ks, KS_CON, 0x00, 0x00); + ks0127_and_or(ks, KS_BRT, 0x00, 32); /* spec: 34 */ + /* spec: 229 (e5) */ + ks0127_and_or(ks, KS_SAT, 0x00, 0xe8); + ks0127_and_or(ks, KS_HUE, 0x00, 0); + + ks0127_and_or(ks, KS_UGAIN, 0x00, 238); + ks0127_and_or(ks, KS_VGAIN, 0x00, 0x00); + + /*UOFF:0x30, VOFF:0x30, TSTCGN=1 */ + ks0127_and_or(ks, KS_UVOFFH, 0x00, 0x4f); + ks0127_and_or(ks, KS_UVOFFL, 0x00, 0x00); + break; + + default: + dprintk("ks0127: command DECODER_SET_INPUT: " + "Unknown input %d\n", *iarg); + break; + } + + /* hack: CDMLPF sometimes spontaneously switches on; */ + /* force back off */ + ks0127_write(ks, KS_DEMOD, reg_defaults[KS_DEMOD]); + break; + + case DECODER_SET_OUTPUT: + switch(*iarg) { + case KS_OUTPUT_YUV656E: + dprintk("ks0127: command DECODER_SET_OUTPUT: " + "OUTPUT_YUV656E (Missing)\n"); + return -EINVAL; + break; + + case KS_OUTPUT_EXV: + dprintk("ks0127: command DECODER_SET_OUTPUT: " + "OUTPUT_EXV\n"); + ks0127_and_or(ks, KS_OFMTA, 0xf0, 0x09); + break; + } + break; + + case DECODER_SET_NORM: //sam This block mixes old and new norm names... + /* Set to automatic SECAM/Fsc mode */ + ks0127_and_or(ks, KS_DEMOD, 0xf0, 0x00); + + ks->norm = *iarg; + switch(*iarg) + { + /* this is untested !! */ + /* It just detects PAL_N/NTSC_M (no special frequencies) */ + /* And you have to set the standard a second time afterwards */ + case VIDEO_MODE_AUTO: + dprintk("ks0127: command DECODER_SET_NORM: AUTO\n"); + + /* The chip determines the format */ + /* based on the current field rate */ + ks0127_and_or(ks, KS_CMDA, 0xfc, 0x00); + ks0127_and_or(ks, KS_CHROMA, 0x9f, 0x20); + /* This is wrong for PAL ! As I said, */ + /* you need to set the standard once again !! */ + ks->format_height = 240; + ks->format_width = 704; + break; + + case VIDEO_MODE_NTSC: + dprintk("ks0127: command DECODER_SET_NORM: NTSC_M\n"); + ks0127_and_or(ks, KS_CHROMA, 0x9f, 0x20); + ks->format_height = 240; + ks->format_width = 704; + break; + + case KS_STD_NTSC_N: + dprintk("ks0127: command KS0127_SET_STANDARD: " + "NTSC_N (fixme)\n"); + ks0127_and_or(ks, KS_CHROMA, 0x9f, 0x40); + ks->format_height = 240; + ks->format_width = 704; + break; + + case VIDEO_MODE_PAL: + dprintk("ks0127: command DECODER_SET_NORM: PAL_N\n"); + ks0127_and_or(ks, KS_CHROMA, 0x9f, 0x20); + ks->format_height = 290; + ks->format_width = 704; + break; + + case KS_STD_PAL_M: + dprintk("ks0127: command KS0127_SET_STANDARD: " + "PAL_M (fixme)\n"); + ks0127_and_or(ks, KS_CHROMA, 0x9f, 0x40); + ks->format_height = 290; + ks->format_width = 704; + break; + + case VIDEO_MODE_SECAM: + dprintk("ks0127: command KS0127_SET_STANDARD: " + "SECAM\n"); + ks->format_height = 290; + ks->format_width = 704; + + /* set to secam autodetection */ + ks0127_and_or(ks, KS_CHROMA, 0xdf, 0x20); + ks0127_and_or(ks, KS_DEMOD, 0xf0, 0x00); + schedule_timeout_interruptible(HZ/10+1); + + /* did it autodetect? */ + if (ks0127_read(ks, KS_DEMOD) & 0x40) + break; + + /* force to secam mode */ + ks0127_and_or(ks, KS_DEMOD, 0xf0, 0x0f); + break; + + default: + dprintk("ks0127: command DECODER_SET_NORM: " + "Unknown norm %d\n", *iarg); + break; + } + break; + + case DECODER_SET_PICTURE: + dprintk("ks0127: command DECODER_SET_PICTURE " + "not yet supported (fixme)\n"); + return -EINVAL; + + //sam todo: KS0127_SET_BRIGHTNESS: Merge into DECODER_SET_PICTURE + //sam todo: KS0127_SET_CONTRAST: Merge into DECODER_SET_PICTURE + //sam todo: KS0127_SET_HUE: Merge into DECODER_SET_PICTURE? + //sam todo: KS0127_SET_SATURATION: Merge into DECODER_SET_PICTURE + //sam todo: KS0127_SET_AGC_MODE: + //sam todo: KS0127_SET_AGC: + //sam todo: KS0127_SET_CHROMA_MODE: + //sam todo: KS0127_SET_PIXCLK_MODE: + //sam todo: KS0127_SET_GAMMA_MODE: + //sam todo: KS0127_SET_UGAIN: + //sam todo: KS0127_SET_VGAIN: + //sam todo: KS0127_SET_INVALY: + //sam todo: KS0127_SET_INVALU: + //sam todo: KS0127_SET_INVALV: + //sam todo: KS0127_SET_UNUSEY: + //sam todo: KS0127_SET_UNUSEU: + //sam todo: KS0127_SET_UNUSEV: + //sam todo: KS0127_SET_VSALIGN_MODE: + + case DECODER_ENABLE_OUTPUT: + { + + int *iarg = arg; + int enable = (*iarg != 0); + if (enable) { + dprintk("ks0127: command " + "DECODER_ENABLE_OUTPUT on " + "(%d)\n", enable); + /* All output pins on */ + ks0127_and_or(ks, KS_OFMTA, 0xcf, 0x30); + /* Obey the OEN pin */ + ks0127_and_or(ks, KS_CDEM, 0x7f, 0x00); + } else { + dprintk("ks0127: command " + "DECODER_ENABLE_OUTPUT off " + "(%d)\n", enable); + /* Video output pins off */ + ks0127_and_or(ks, KS_OFMTA, 0xcf, 0x00); + /* Ignore the OEN pin */ + ks0127_and_or(ks, KS_CDEM, 0x7f, 0x80); + } + } + break; + + //sam todo: KS0127_SET_OUTPUT_MODE: + //sam todo: KS0127_SET_WIDTH: + //sam todo: KS0127_SET_HEIGHT: + //sam todo: KS0127_SET_HSCALE: + + case DECODER_GET_STATUS: + dprintk("ks0127: command DECODER_GET_STATUS\n"); + *iarg = 0; + status = ks0127_read(ks, KS_STAT); + if (!(status & 0x20)) /* NOVID not set */ + *iarg = (*iarg & DECODER_STATUS_GOOD); + if ((status & 0x01)) /* CLOCK set */ + *iarg = (*iarg & DECODER_STATUS_COLOR); + if ((status & 0x08)) /* PALDET set */ + *iarg = (*iarg & DECODER_STATUS_PAL); + else + *iarg = (*iarg & DECODER_STATUS_NTSC); + break; + + //Catch any unknown command + default: + dprintk("ks0127: command unknown: %04X\n", cmd); + return -EINVAL; + } + return 0; +} + + + + +static int ks0127_probe(struct i2c_adapter *adapter); +static int ks0127_detach(struct i2c_client *client); +static int ks0127_command(struct i2c_client *client, + unsigned int cmd, void *arg); + + + +/* Addresses to scan */ +static unsigned short normal_i2c[] = {I2C_KS0127_ADDON>>1, + I2C_KS0127_ONBOARD>>1, I2C_CLIENT_END}; +static unsigned short probe[2] = {I2C_CLIENT_END, I2C_CLIENT_END}; +static unsigned short ignore[2] = {I2C_CLIENT_END, I2C_CLIENT_END}; +static struct i2c_client_address_data addr_data = { + normal_i2c, + probe, + ignore, +}; + +static struct i2c_driver i2c_driver_ks0127 = { + .driver.name = "ks0127", + .id = I2C_DRIVERID_KS0127, + .attach_adapter = ks0127_probe, + .detach_client = ks0127_detach, + .command = ks0127_command +}; + +static struct i2c_client ks0127_client_tmpl = +{ + .name = "(ks0127 unset)", + .addr = 0, + .adapter = NULL, + .driver = &i2c_driver_ks0127, + .usage_count = 0 +}; + +static int ks0127_found_proc(struct i2c_adapter *adapter, int addr, int kind) +{ + struct ks0127 *ks; + struct i2c_client *client; + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (client == NULL) + return -ENOMEM; + memcpy(client, &ks0127_client_tmpl, sizeof(*client)); + + ks = kzalloc(sizeof(*ks), GFP_KERNEL); + if (ks == NULL) { + kfree(client); + return -ENOMEM; + } + + i2c_set_clientdata(client, ks); + client->adapter = adapter; + client->addr = addr; + sprintf(client->name, "ks0127-%02x", adapter->id); + + ks->client = client; + ks->addr = addr; + ks->ks_type = KS_TYPE_UNKNOWN; + + /* power up */ + ks0127_write(ks, KS_CMDA, 0x2c); + mdelay(10); + + /* reset the device */ + ks0127_reset(ks); + printk(KERN_INFO "ks0127: attach: %s video decoder\n", + ks->addr==(I2C_KS0127_ADDON>>1) ? "addon" : "on-board"); + + i2c_attach_client(client); + return 0; +} + + +static int ks0127_probe(struct i2c_adapter *adapter) +{ + if (adapter->id == I2C_HW_B_ZR36067) + return i2c_probe(adapter, &addr_data, ks0127_found_proc); + return 0; +} + +static int ks0127_detach(struct i2c_client *client) +{ + struct ks0127 *ks = i2c_get_clientdata(client); + + ks0127_write(ks, KS_OFMTA, 0x20); /*tristate*/ + ks0127_write(ks, KS_CMDA, 0x2c | 0x80); /* power down */ + + i2c_detach_client(client); + kfree(ks); + kfree(client); + + dprintk("ks0127: detach\n"); + return 0; +} + + +static int __devinit ks0127_init_module(void) +{ + init_reg_defaults(); + i2c_add_driver(&i2c_driver_ks0127); + return 0; +} + +static void __devexit ks0127_cleanup_module(void) +{ + i2c_del_driver(&i2c_driver_ks0127); +} + + +module_init(ks0127_init_module); +module_exit(ks0127_cleanup_module); diff --git a/linux/drivers/media/video/ks0127.h b/linux/drivers/media/video/ks0127.h new file mode 100644 index 000000000..1ec578833 --- /dev/null +++ b/linux/drivers/media/video/ks0127.h @@ -0,0 +1,53 @@ +/* + * Video Capture Driver ( Video for Linux 1/2 ) + * for the Matrox Marvel G200,G400 and Rainbow Runner-G series + * + * This module is an interface to the KS0127 video decoder chip. + * + * Copyright (C) 1999 Ryan Drake + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef KS0127_H +#define KS0127_H + +#include + +/* input channels */ +#define KS_INPUT_COMPOSITE_1 0 +#define KS_INPUT_COMPOSITE_2 1 +#define KS_INPUT_COMPOSITE_3 2 +#define KS_INPUT_COMPOSITE_4 4 +#define KS_INPUT_COMPOSITE_5 5 +#define KS_INPUT_COMPOSITE_6 6 + +#define KS_INPUT_SVIDEO_1 8 +#define KS_INPUT_SVIDEO_2 9 +#define KS_INPUT_SVIDEO_3 10 + +#define KS_INPUT_YUV656 15 +#define KS_INPUT_COUNT 10 + +/* output channels */ +#define KS_OUTPUT_YUV656E 0 +#define KS_OUTPUT_EXV 1 + +/* video standards */ +#define KS_STD_NTSC_N 112 /* 50 Hz NTSC */ +#define KS_STD_PAL_M 113 /* 60 Hz PAL */ + +#endif /* KS0127_H */ + diff --git a/linux/drivers/media/video/zoran.h b/linux/drivers/media/video/zoran.h index 0dc51a46c..19c6e3aa0 100644 --- a/linux/drivers/media/video/zoran.h +++ b/linux/drivers/media/video/zoran.h @@ -159,7 +159,7 @@ Private IOCTL to set up for displaying MJPEG #define BUZ_MAX_FRAME 256 /* Must be a power of 2 */ #define BUZ_MASK_FRAME 255 /* Must be BUZ_MAX_FRAME-1 */ -#define BUZ_MAX_INPUT 8 +#define BUZ_MAX_INPUT 16 #if VIDEO_MAX_FRAME <= 32 # define V4L_MAX_FRAME 32 @@ -191,6 +191,9 @@ enum card_type { /* Iomega */ BUZ, + /* AverMedia */ + AVS6EYES, + /* total number of cards */ NUM_CARDS }; @@ -379,6 +382,9 @@ struct card_info { /* is the /GWS line conected? */ u8 gws_not_connected; + /* avs6eyes mux setting */ + u8 input_mux; + void (*init) (struct zoran * zr); }; diff --git a/linux/drivers/media/video/zoran_card.c b/linux/drivers/media/video/zoran_card.c index a55bd31ea..cc52eb745 100644 --- a/linux/drivers/media/video/zoran_card.c +++ b/linux/drivers/media/video/zoran_card.c @@ -27,6 +27,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include + #include #include #include @@ -96,6 +98,11 @@ module_param(default_input, int, 0); MODULE_PARM_DESC(default_input, "Default input (0=Composite, 1=S-Video, 2=Internal)"); +static int default_mux = 1; /* 6 Eyes input selection */ +module_param(default_mux, int, 0); +MODULE_PARM_DESC(default_mux, + "Default 6 Eyes mux setting (Input selection)"); + static int default_norm = 0; /* 0=PAL, 1=NTSC 2=SECAM */ module_param(default_norm, int, 0); MODULE_PARM_DESC(default_norm, "Default norm (0=PAL, 1=NTSC, 2=SECAM)"); @@ -304,6 +311,30 @@ lml33_init (struct zoran *zr) GPIO(zr, 2, 1); // Set Composite input/output } +static void +avs6eyes_init (struct zoran *zr) +{ + // AverMedia 6-Eyes original driver by Christer Weinigel + + // Lifted straight from Christer's old driver and + // modified slightly by Martin Samuelsson. + + int mux = default_mux; /* 1 = BT866, 7 = VID1 */ + + GPIO(zr, 4, 1); /* Bt866 SLEEP on */ + udelay(2); + + GPIO(zr, 0, 1); /* ZR36060 /RESET on */ + GPIO(zr, 1, 0); /* ZR36060 /SLEEP on */ + GPIO(zr, 2, mux & 1); /* MUX S0 */ + GPIO(zr, 3, 0); /* /FRAME on */ + GPIO(zr, 4, 0); /* Bt866 SLEEP off */ + GPIO(zr, 5, mux & 2); /* MUX S1 */ + GPIO(zr, 6, 0); /* ? */ + GPIO(zr, 7, mux & 4); /* MUX S2 */ + +} + static char * i2cid_to_modulename (u16 i2c_id) { @@ -394,6 +425,14 @@ static struct tvnorm f60sqpixel_dc10 = { 780, 640, 0, 716, 525, 480, 12 }; static struct tvnorm f50ccir601_lm33r10 = { 864, 720, 74+54, 804, 625, 576, 18 }; static struct tvnorm f60ccir601_lm33r10 = { 858, 720, 56+54, 788, 525, 480, 16 }; +/* FIXME: The ks0127 seem incapable of swapping U and V, too, which is why I + * copy Maxim's left shift hack for the 6 Eyes. + * + * Christer's driver used the unshifted norms, though... + * /Sam */ +static struct tvnorm f50ccir601_avs6eyes = { 864, 720, 74, 804, 625, 576, 18 }; +static struct tvnorm f60ccir601_avs6eyes = { 858, 720, 56, 788, 525, 480, 16 }; + static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { { .type = DC10_old, @@ -422,6 +461,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { .gpcs = { -1, 0 }, .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, .gws_not_connected = 0, + .input_mux = 0, .init = &dc10_init, }, { .type = DC10_new, @@ -448,6 +488,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { .gpcs = { -1, 1}, .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 }, .gws_not_connected = 0, + .input_mux = 0, .init = &dc10plus_init, }, { .type = DC10plus, @@ -477,6 +518,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { .gpcs = { -1, 1 }, .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 }, .gws_not_connected = 0, + .input_mux = 0, .init = &dc10plus_init, }, { .type = DC30, @@ -505,6 +547,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { .gpcs = { -1, 0 }, .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, .gws_not_connected = 0, + .input_mux = 0, .init = &dc10_init, }, { .type = DC30plus, @@ -535,6 +578,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { .gpcs = { -1, 0 }, .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, .gws_not_connected = 0, + .input_mux = 0, .init = &dc10_init, }, { .type = LML33, @@ -561,6 +605,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { .gpcs = { 3, 1 }, .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, .gws_not_connected = 1, + .input_mux = 0, .init = &lml33_init, }, { .type = LML33R10, @@ -589,6 +634,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { .gpcs = { 3, 1 }, .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, .gws_not_connected = 1, + .input_mux = 0, .init = &lml33_init, }, { .type = BUZ, @@ -617,8 +663,49 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { .gpcs = { 3, 1 }, .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, .gws_not_connected = 1, + .input_mux = 0, .init = &buz_init, + }, { + .type = AVS6EYES, + .name = "6-Eyes", + /* AverMedia chose not to brand the 6-Eyes. Thus it + can't be autodetected, and requires card=x. */ + .vendor_id = -1, + .device_id = -1, + .i2c_decoder = I2C_DRIVERID_KS0127, + .i2c_encoder = I2C_DRIVERID_BT866, + .video_codec = CODEC_TYPE_ZR36060, + + .inputs = 10, + .input = { + { 0, "Composite 1" }, + { 1, "Composite 2" }, + { 2, "Composite 3" }, + { 4, "Composite 4" }, + { 5, "Composite 5" }, + { 6, "Composite 6" }, + { 8, "S-Video 1" }, + { 9, "S-Video 2" }, + {10, "S-Video 3" }, + {15, "YCbCr" } + }, + .norms = 2, + .tvn = { + &f50ccir601_avs6eyes, + &f60ccir601_avs6eyes, + NULL + }, + .jpeg_int = ZR36057_ISR_GIRQ1, + .vsync_int = ZR36057_ISR_GIRQ0, + .gpio = { 1, 0, 3, -1, -1, -1, -1, -1 },// Validity unknown /Sam + .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, // Validity unknown /Sam + .gpcs = { 3, 1 }, // Validity unknown /Sam + .vfe_pol = { 1, 0, 0, 0, 0, 1, 0, 0 }, // Validity unknown /Sam + .gws_not_connected = 1, + .input_mux = 1, + .init = &avs6eyes_init, } + }; /* -- cgit v1.2.3 From 9a2bbcda776b51c070c6859a114fb00b06f1e392 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Apr 2006 10:29:01 -0300 Subject: Make bt866 and ks0127 work at V4L/DVB tree From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/bt866.c | 1 + linux/drivers/media/video/ks0127.h | 1 + 2 files changed, 2 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/bt866.c b/linux/drivers/media/video/bt866.c index 05e42bbcf..64f21c0a2 100644 --- a/linux/drivers/media/video/bt866.c +++ b/linux/drivers/media/video/bt866.c @@ -46,6 +46,7 @@ #include #include +#include "compat.h" #include #include diff --git a/linux/drivers/media/video/ks0127.h b/linux/drivers/media/video/ks0127.h index 1ec578833..841717a21 100644 --- a/linux/drivers/media/video/ks0127.h +++ b/linux/drivers/media/video/ks0127.h @@ -24,6 +24,7 @@ #ifndef KS0127_H #define KS0127_H +#include "compat.h" #include /* input channels */ -- cgit v1.2.3 From edde0f5e0f83c7ce443153eb57353529b042383e Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Thu, 27 Apr 2006 19:59:54 +0100 Subject: Disable stv0297_cs2 code from being used normally. From: Mauro Carvalho Chehab stv0297_cs2 should be merged into the main stv0297. In order not to lose information we have come up with this temporary compromise. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/b2c2/Makefile | 2 + linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c | 108 ++++++++++++++++++++++++ 2 files changed, 110 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/b2c2/Makefile b/linux/drivers/media/dvb/b2c2/Makefile index 7f16c445b..d288a67fb 100644 --- a/linux/drivers/media/dvb/b2c2/Makefile +++ b/linux/drivers/media/dvb/b2c2/Makefile @@ -3,8 +3,10 @@ b2c2-flexcop-objs = flexcop.o flexcop-fe-tuner.o flexcop-i2c.o \ flexcop-dma.o obj-$(CONFIG_DVB_B2C2_FLEXCOP) += b2c2-flexcop.o +ifdef STV0297_CS2 obj-$(CONFIG_DVB_B2C2_FLEXCOP) += stv0297_cs2.o +endif b2c2-flexcop-pci-objs = flexcop-pci.o obj-$(CONFIG_DVB_B2C2_FLEXCOP_PCI) += b2c2-flexcop-pci.o diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index 353b9e987..21af462c0 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -11,7 +11,11 @@ #include "mt352.h" #include "nxt200x.h" #include "bcm3510.h" +#ifdef STV0297_CS2 #include "stv0297_cs2.h" +#else +#include "stv0297.h" +#endif #include "mt312.h" #include "lgdt330x.h" #include "lg_h06xf.h" @@ -398,11 +402,111 @@ int alps_tdee4_stv0297_tuner_set_params (struct dvb_frontend* fe, struct dvb_fro return 0; } +#ifndef STV0297_CS2 +static u8 alps_tdee4_stv0297_inittab[] = { + 0x80, 0x01, + 0x80, 0x00, + 0x81, 0x01, + 0x81, 0x00, + 0x00, 0x09, + 0x01, 0x69, + 0x03, 0x00, + 0x04, 0x00, + 0x07, 0x00, + 0x08, 0x00, + 0x20, 0x00, + 0x21, 0x40, + 0x22, 0x00, + 0x23, 0x00, + 0x24, 0x40, + 0x25, 0x88, + 0x30, 0xff, + 0x31, 0x00, + 0x32, 0xff, + 0x33, 0x00, + 0x34, 0x50, + 0x35, 0x7f, + 0x36, 0x00, + 0x37, 0x20, + 0x38, 0x00, + 0x40, 0x1c, + 0x41, 0xff, + 0x42, 0x29, + 0x43, 0x00, + 0x44, 0xff, + 0x45, 0x00, + 0x46, 0x00, + 0x49, 0x04, + 0x4a, 0x00, + 0x4b, 0xf8, + 0x52, 0x30, + 0x55, 0xae, + 0x56, 0x47, + 0x57, 0xe1, + 0x58, 0x3a, + 0x5a, 0x1e, + 0x5b, 0x34, + 0x60, 0x00, + 0x63, 0x00, + 0x64, 0x00, + 0x65, 0x00, + 0x66, 0x00, + 0x67, 0x00, + 0x68, 0x00, + 0x69, 0x00, + 0x6a, 0x02, + 0x6b, 0x00, + 0x70, 0xff, + 0x71, 0x00, + 0x72, 0x00, + 0x73, 0x00, + 0x74, 0x0c, + 0x80, 0x00, + 0x81, 0x00, + 0x82, 0x00, + 0x83, 0x00, + 0x84, 0x04, + 0x85, 0x80, + 0x86, 0x24, + 0x87, 0x78, + 0x88, 0x10, + 0x89, 0x00, + 0x90, 0x01, + 0x91, 0x01, + 0xa0, 0x04, + 0xa1, 0x00, + 0xa2, 0x00, + 0xb0, 0x91, + 0xb1, 0x0b, + 0xc0, 0x53, + 0xc1, 0x70, + 0xc2, 0x12, + 0xd0, 0x00, + 0xd1, 0x00, + 0xd2, 0x00, + 0xd3, 0x00, + 0xd4, 0x00, + 0xd5, 0x00, + 0xde, 0x00, + 0xdf, 0x00, + 0x61, 0x49, + 0x62, 0x0b, + 0x53, 0x08, + 0x59, 0x08, + 0xff, 0xff, +}; +#endif static struct stv0297_config alps_tdee4_stv0297_config = { .demod_address = 0x1c, +#ifdef STV0297_CS2 .fclk = STV0297_PAL_FCLK, .demodfreq = STV0297_PAL_IF - STV0297_PAL_FCLK, +#else + .inittab = alps_tdee4_stv0297_inittab, +// .invert = 1, +// .pll_set = alps_tdee4_stv0297_pll_set, +#endif }; /* try to figure out the frontend, each card/box can have on of the following list */ @@ -448,7 +552,11 @@ int flexcop_frontend_init(struct flexcop_device *fc) info("found the bcm3510 at i2c address: 0x%02x",air2pc_atsc_first_gen_config.demod_address); } else /* try the cable dvb (stv0297) */ +#ifdef STV0297_CS2 if ((fc->fe = stv0297_cs2_attach(&alps_tdee4_stv0297_config, &fc->i2c_adap)) != NULL) { +#else + if ((fc->fe = stv0297_attach(&alps_tdee4_stv0297_config, &fc->i2c_adap)) != NULL) { +#endif fc->dev_type = FC_CABLE; fc->fe->ops->tuner_ops.set_params = alps_tdee4_stv0297_tuner_set_params; info("found the stv0297 at i2c address: 0x%02x",alps_tdee4_stv0297_config.demod_address); -- cgit v1.2.3 From b752601577f8dcc225f1e4d3a18d51851baef713 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Apr 2006 17:10:50 -0300 Subject: Patch reverted since it is broken. From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/Kconfig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/Kconfig b/linux/drivers/media/Kconfig index 3ff3d6450..344d83aae 100644 --- a/linux/drivers/media/Kconfig +++ b/linux/drivers/media/Kconfig @@ -27,6 +27,7 @@ config VIDEO_DEV config VIDEO_V4L1 boolean "Enable Video For Linux API 1 (DEPRECATED)" depends on VIDEO_DEV + select VIDEO_V4L1_COMPAT default y ---help--- Enables a compatibility API used by most V4L2 devices to allow @@ -35,8 +36,8 @@ config VIDEO_V4L1 If you are unsure as to whether this is required, answer Y. config VIDEO_V4L1_COMPAT - tristate "Enable Video For Linux API 1 compatible Layer" - depends on VIDEO_DEV && COMPAT + boolean "Enable Video For Linux API 1 compatible Layer" + depends on VIDEO_DEV default y ---help--- This api were developed to be used at Kernel 2.2 and 2.4, but -- cgit v1.2.3 From 31b5fbd7ae069b8084ac54ac19e7f5151b1616f9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Apr 2006 17:11:31 -0300 Subject: Fix VIDEO_DEV=m, VIDEO_V4L1_COMPAT=y From: Adrian Bunk If CONFIG_VIDEO_DEV=m and CONFIG_VIDEO_V4L1_COMPAT=y, v4l1-compat should be built as a module (currently, it isn't built at all leading to problems with modules using it). Signed-off-by: Adrian Bunk Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index a9046a603..ad54ecdd7 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -11,7 +11,10 @@ tuner-objs := tuner-core.o tuner-types.o tuner-simple.o \ msp3400-objs := msp3400-driver.o msp3400-kthreads.o obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o compat_ioctl32.o -obj-$(CONFIG_VIDEO_V4L1_COMPAT) += v4l1-compat.o + +ifeq ($(CONFIG_VIDEO_V4L1_COMPAT),y) + obj-$(CONFIG_VIDEO_DEV) += v4l1-compat.o +endif obj-$(CONFIG_VIDEO_BT848) += bt8xx/ obj-$(CONFIG_VIDEO_BT848) += tvaudio.o tda7432.o tda9875.o ir-kbd-i2c.o -- cgit v1.2.3 From dc9f8439dc12eaa1f581a7908ea154dffb7c1e29 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Apr 2006 18:34:03 -0300 Subject: Compilation for DVB_AV7110_FIRMWARE seems to be weird From: Mauro Carvalho Chehab Some systems use a previous config information to generate a default config. This seems to cause troubles on some systems. This patch fixes compilation for AV7110 firmware inside V4L/DVB tree. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/ttpci/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/ttpci/Makefile b/linux/drivers/media/dvb/ttpci/Makefile index a690730ac..c98c8e013 100644 --- a/linux/drivers/media/dvb/ttpci/Makefile +++ b/linux/drivers/media/dvb/ttpci/Makefile @@ -15,7 +15,7 @@ EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ hostprogs-y := fdump -ifdef CONFIG_DVB_AV7110_FIRMWARE +ifeq (CONFIG_DVB_AV7110_FIRMWARE,y) $(obj)/av7110.o: $(obj)/fdump $(obj)/av7110_firm.h $(obj)/av7110_firm.h: -- cgit v1.2.3 From 283e80b3d7bf8f4eaf0b741c2b40979b2d3e5ab3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Apr 2006 21:06:50 -0300 Subject: Vivi.c: possible cleanups From: Adrian Bunk This patch contains the following possible cleanup: - make needlessly global functions static - remove unused #ifndef kzalloc kzalloc() #define - remove inline's from functions Signed-off-by: Adrian Bunk Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/vivi.c | 44 ++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 27 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/vivi.c b/linux/drivers/media/video/vivi.c index a60f14c14..170712515 100644 --- a/linux/drivers/media/video/vivi.c +++ b/linux/drivers/media/video/vivi.c @@ -50,16 +50,6 @@ #include "font.h" -#ifndef kzalloc -#define kzalloc(size, flags) \ -({ \ - void *__ret = kmalloc(size, flags); \ - if (__ret) \ - memset(__ret, 0, size); \ - __ret; \ -}) -#endif - MODULE_DESCRIPTION("Video Technology Magazine Virtual Video Capture Board"); MODULE_AUTHOR("Mauro Carvalho Chehab, Ted Walther and John Sokol"); MODULE_LICENSE("Dual BSD/GPL"); @@ -254,7 +244,8 @@ static u8 bars[8][3] = { #define TSTAMP_MAX_Y TSTAMP_MIN_Y+15 #define TSTAMP_MIN_X 64 -void prep_to_addr(struct sg_to_addr to_addr[],struct videobuf_buffer *vb) +static void prep_to_addr(struct sg_to_addr to_addr[], + struct videobuf_buffer *vb) { int i, pos=0; @@ -265,7 +256,7 @@ void prep_to_addr(struct sg_to_addr to_addr[],struct videobuf_buffer *vb) } } -inline int get_addr_pos(int pos, int pages, struct sg_to_addr to_addr[]) +static int get_addr_pos(int pos, int pages, struct sg_to_addr to_addr[]) { int p1=0,p2=pages-1,p3=pages/2; @@ -286,8 +277,8 @@ inline int get_addr_pos(int pos, int pages, struct sg_to_addr to_addr[]) return (p1); } -void gen_line(struct sg_to_addr to_addr[],int inipos,int pages,int wmax, - int hmax, int line, char *timestr) +static void gen_line(struct sg_to_addr to_addr[],int inipos,int pages,int wmax, + int hmax, int line, char *timestr) { int w,i,j,pos=inipos,pgpos,oldpg,y; char *p,*s,*basep; @@ -509,7 +500,7 @@ static void vivi_thread_tick(struct vivi_dmaqueue *dma_q) dprintk(1,"%s: %d buffers handled (should be 1)\n",__FUNCTION__,bc); } -void vivi_sleep(struct vivi_dmaqueue *dma_q) +static void vivi_sleep(struct vivi_dmaqueue *dma_q) { int timeout; DECLARE_WAITQUEUE(wait, current); @@ -560,7 +551,7 @@ void vivi_sleep(struct vivi_dmaqueue *dma_q) #endif } -int vivi_thread(void *data) +static int vivi_thread(void *data) { struct vivi_dmaqueue *dma_q=data; @@ -600,7 +591,7 @@ int vivi_thread(void *data) return 0; } -int vivi_start_thread(struct vivi_dmaqueue *dma_q) +static int vivi_start_thread(struct vivi_dmaqueue *dma_q) { dma_q->frame=0; dma_q->ini_jiffies=jiffies; @@ -628,7 +619,7 @@ int vivi_start_thread(struct vivi_dmaqueue *dma_q) return 0; } -void vivi_stop_thread(struct vivi_dmaqueue *dma_q) +static void vivi_stop_thread(struct vivi_dmaqueue *dma_q) { dprintk(1,"%s\n",__FUNCTION__); /* shutdown control thread */ @@ -746,8 +737,7 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) return 0; } -void -free_buffer(struct videobuf_queue *vq, struct vivi_buffer *buf) +static void free_buffer(struct videobuf_queue *vq, struct vivi_buffer *buf) { dprintk(1,"%s\n",__FUNCTION__); @@ -871,8 +861,8 @@ static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb free_buffer(vq,buf); } -int vivi_map_sg (void *dev, struct scatterlist *sg, int nents, - int direction) +static int vivi_map_sg(void *dev, struct scatterlist *sg, int nents, + int direction) { int i; @@ -888,15 +878,15 @@ int vivi_map_sg (void *dev, struct scatterlist *sg, int nents, return nents; } -int vivi_unmap_sg(void *dev,struct scatterlist *sglist,int nr_pages, - int direction) +static int vivi_unmap_sg(void *dev,struct scatterlist *sglist,int nr_pages, + int direction) { dprintk(1,"%s\n",__FUNCTION__); return 0; } -int vivi_dma_sync_sg(void *dev,struct scatterlist *sglist,int nr_pages, - int direction) +static int vivi_dma_sync_sg(void *dev,struct scatterlist *sglist, int nr_pages, + int direction) { // dprintk(1,"%s\n",__FUNCTION__); @@ -980,7 +970,7 @@ static int res_get(struct vivi_dev *dev, struct vivi_fh *fh) return 1; } -static inline int res_locked(struct vivi_dev *dev) +static int res_locked(struct vivi_dev *dev) { return (dev->resources); } -- cgit v1.2.3 From 761ff2c05b56e086da5a180666c4d9160155c4ec Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Fri, 28 Apr 2006 01:45:01 +0100 Subject: Convert lnbp21 to a module From: Andrew de Quincey Convert lnbp21.h into a linux kernel module. Fix up previous users to use it. Convert dvb-ttusb-budget to use it. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/Kconfig | 10 ++ linux/drivers/media/dvb/frontends/Makefile | 1 + linux/drivers/media/dvb/frontends/lnbp21.c | 145 +++++++++++++++++++++ linux/drivers/media/dvb/frontends/lnbp21.h | 102 +-------------- linux/drivers/media/dvb/ttpci/Kconfig | 3 + linux/drivers/media/dvb/ttpci/av7110.c | 2 +- linux/drivers/media/dvb/ttpci/budget-ci.c | 2 +- linux/drivers/media/dvb/ttpci/budget.c | 2 +- linux/drivers/media/dvb/ttusb-budget/Kconfig | 1 + .../media/dvb/ttusb-budget/dvb-ttusb-budget.c | 28 +--- 10 files changed, 169 insertions(+), 127 deletions(-) create mode 100644 linux/drivers/media/dvb/frontends/lnbp21.c (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/Kconfig b/linux/drivers/media/dvb/frontends/Kconfig index 6d90ff3f7..64cd63623 100644 --- a/linux/drivers/media/dvb/frontends/Kconfig +++ b/linux/drivers/media/dvb/frontends/Kconfig @@ -216,4 +216,14 @@ config DVB_LGDT330X An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. + +comment "Miscellaneous devices" + depends on DVB_CORE + +config DVB_LNBP21 + tristate "LNBP21 SEC controller" + depends on DVB_CORE + help + An SEC control chip. + endmenu diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile index d09b6071f..7238e6ef2 100644 --- a/linux/drivers/media/dvb/frontends/Makefile +++ b/linux/drivers/media/dvb/frontends/Makefile @@ -31,3 +31,4 @@ obj-$(CONFIG_DVB_BCM3510) += bcm3510.o obj-$(CONFIG_DVB_S5H1420) += s5h1420.o obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o obj-$(CONFIG_DVB_CX24123) += cx24123.o +obj-$(CONFIG_DVB_LNBP21) += lnbp21.o diff --git a/linux/drivers/media/dvb/frontends/lnbp21.c b/linux/drivers/media/dvb/frontends/lnbp21.c new file mode 100644 index 000000000..c9152c1fb --- /dev/null +++ b/linux/drivers/media/dvb/frontends/lnbp21.c @@ -0,0 +1,145 @@ +/* + * lnbp21.h - driver for lnb supply and control ic lnbp21 + * + * Copyright (C) 2006 Oliver Endriss + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + * + * the project's page is at http://www.linuxtv.org + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "lnbp21.h" + +struct lnbp21 { + u8 config; + u8 override_or; + u8 override_and; + struct i2c_adapter *i2c; + void (*release_chain)(struct dvb_frontend* fe); +}; + +static int lnbp21_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->misc_priv; + struct i2c_msg msg = { .addr = 0x08, .flags = 0, + .buf = &lnbp21->config, + .len = sizeof(lnbp21->config) }; + + lnbp21->config &= ~(LNBP21_VSEL | LNBP21_EN); + + switch(voltage) { + case SEC_VOLTAGE_OFF: + break; + case SEC_VOLTAGE_13: + lnbp21->config |= LNBP21_EN; + break; + case SEC_VOLTAGE_18: + lnbp21->config |= (LNBP21_EN | LNBP21_VSEL); + break; + default: + return -EINVAL; + }; + + lnbp21->config |= lnbp21->override_or; + lnbp21->config &= lnbp21->override_and; + + return (i2c_transfer(lnbp21->i2c, &msg, 1) == 1) ? 0 : -EIO; +} + +static int lnbp21_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) +{ + struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->misc_priv; + struct i2c_msg msg = { .addr = 0x08, .flags = 0, + .buf = &lnbp21->config, + .len = sizeof(lnbp21->config) }; + + if (arg) + lnbp21->config |= LNBP21_LLC; + else + lnbp21->config &= ~LNBP21_LLC; + + lnbp21->config |= lnbp21->override_or; + lnbp21->config &= lnbp21->override_and; + + return (i2c_transfer(lnbp21->i2c, &msg, 1) == 1) ? 0 : -EIO; +} + +static void lnbp21_release(struct dvb_frontend *fe) +{ + struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->misc_priv; + + /* LNBP power off */ + lnbp21_set_voltage(fe, SEC_VOLTAGE_OFF); + + /* free data & call next release routine */ + fe->ops->release = lnbp21->release_chain; + kfree(fe->misc_priv); + fe->misc_priv = NULL; + if (fe->ops->release) + fe->ops->release(fe); +} + +int lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear) +{ + struct lnbp21 *lnbp21 = kmalloc(sizeof(struct lnbp21), GFP_KERNEL); + if (!lnbp21) + return -ENOMEM; + + /* default configuration */ + lnbp21->config = LNBP21_ISEL; + lnbp21->i2c = i2c; + fe->misc_priv = lnbp21; + + /* bits which should be forced to '1' */ + lnbp21->override_or = override_set; + + /* bits which should be forced to '0' */ + lnbp21->override_and = ~override_clear; + + /* detect if it is present or not */ + if (lnbp21_set_voltage(fe, SEC_VOLTAGE_OFF)) { + kfree(lnbp21); + fe->misc_priv = NULL; + return -EIO; + } + + /* install release callback */ + lnbp21->release_chain = fe->ops->release; + fe->ops->release = lnbp21_release; + + /* override frontend ops */ + fe->ops->set_voltage = lnbp21_set_voltage; + fe->ops->enable_high_lnb_voltage = lnbp21_enable_high_lnb_voltage; + + return 0; +} +EXPORT_SYMBOL(lnbp21_attach); + +MODULE_DESCRIPTION("Driver for lnb supply and control ic lnbp21"); +MODULE_AUTHOR("Oliver Endriss"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/frontends/lnbp21.h b/linux/drivers/media/dvb/frontends/lnbp21.h index 0dcbe61b6..047a4ab68 100644 --- a/linux/drivers/media/dvb/frontends/lnbp21.h +++ b/linux/drivers/media/dvb/frontends/lnbp21.h @@ -27,7 +27,7 @@ #ifndef _LNBP21_H #define _LNBP21_H -/* system register */ +/* system register bits */ #define LNBP21_OLF 0x01 #define LNBP21_OTF 0x02 #define LNBP21_EN 0x04 @@ -37,103 +37,9 @@ #define LNBP21_ISEL 0x40 #define LNBP21_PCL 0x80 -struct lnbp21 { - u8 config; - u8 override_or; - u8 override_and; - struct i2c_adapter *i2c; - void (*release_chain)(struct dvb_frontend* fe); -}; +#include -static int lnbp21_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) -{ - struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->misc_priv; - struct i2c_msg msg = { .addr = 0x08, .flags = 0, - .buf = &lnbp21->config, - .len = sizeof(lnbp21->config) }; - - lnbp21->config &= ~(LNBP21_VSEL | LNBP21_EN); - - switch(voltage) { - case SEC_VOLTAGE_OFF: - break; - case SEC_VOLTAGE_13: - lnbp21->config |= LNBP21_EN; - break; - case SEC_VOLTAGE_18: - lnbp21->config |= (LNBP21_EN | LNBP21_VSEL); - break; - default: - return -EINVAL; - }; - - lnbp21->config |= lnbp21->override_or; - lnbp21->config &= lnbp21->override_and; - - return (i2c_transfer(lnbp21->i2c, &msg, 1) == 1) ? 0 : -EIO; -} - -static int lnbp21_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) -{ - struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->misc_priv; - struct i2c_msg msg = { .addr = 0x08, .flags = 0, - .buf = &lnbp21->config, - .len = sizeof(lnbp21->config) }; - - if (arg) - lnbp21->config |= LNBP21_LLC; - else - lnbp21->config &= ~LNBP21_LLC; - - lnbp21->config |= lnbp21->override_or; - lnbp21->config &= lnbp21->override_and; - - return (i2c_transfer(lnbp21->i2c, &msg, 1) == 1) ? 0 : -EIO; -} - -static void lnbp21_exit(struct dvb_frontend *fe) -{ - struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->misc_priv; - - /* LNBP power off */ - lnbp21_set_voltage(fe, SEC_VOLTAGE_OFF); - - /* free data & call next release routine */ - fe->ops->release = lnbp21->release_chain; - kfree(fe->misc_priv); - fe->misc_priv = NULL; - if (fe->ops->release) - fe->ops->release(fe); -} - -static int lnbp21_init(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear) -{ - struct lnbp21 *lnbp21 = kmalloc(sizeof(struct lnbp21), GFP_KERNEL); - - if (!lnbp21) - return -ENOMEM; - - /* default configuration */ - lnbp21->config = LNBP21_ISEL; - - /* bits which should be forced to '1' */ - lnbp21->override_or = override_set; - - /* bits which should be forced to '0' */ - lnbp21->override_and = ~override_clear; - - /* install release callback */ - lnbp21->release_chain = fe->ops->release; - fe->ops->release = lnbp21_exit; - - /* override frontend ops */ - fe->ops->set_voltage = lnbp21_set_voltage; - fe->ops->enable_high_lnb_voltage = lnbp21_enable_high_lnb_voltage; - - lnbp21->i2c = i2c; - fe->misc_priv = lnbp21; - - return lnbp21_set_voltage(fe, SEC_VOLTAGE_OFF); -} +/* override_set and override_clear control which system register bits (above) to always set & clear */ +extern int lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear); #endif diff --git a/linux/drivers/media/dvb/ttpci/Kconfig b/linux/drivers/media/dvb/ttpci/Kconfig index c26e23291..8ffaac634 100644 --- a/linux/drivers/media/dvb/ttpci/Kconfig +++ b/linux/drivers/media/dvb/ttpci/Kconfig @@ -10,6 +10,7 @@ config DVB_AV7110 select DVB_SP8870 select DVB_STV0297 select DVB_L64781 + select DVB_LNBP21 help Support for SAA7146 and AV7110 based DVB cards as produced by Fujitsu-Siemens, Technotrend, Hauppauge and others. @@ -67,6 +68,7 @@ config DVB_BUDGET select DVB_TDA8083 select DVB_TDA10021 select DVB_S5H1420 + select DVB_LNBP21 help Support for simple SAA7146 based DVB cards (so called Budget- or Nova-PCI cards) without onboard @@ -84,6 +86,7 @@ config DVB_BUDGET_CI select DVB_STV0297 select DVB_STV0299 select DVB_TDA1004X + select DVB_LNBP21 help Support for simple SAA7146 based DVB cards (so called Budget- or Nova-PCI cards) without onboard diff --git a/linux/drivers/media/dvb/ttpci/av7110.c b/linux/drivers/media/dvb/ttpci/av7110.c index 9567e03a1..77cdeb7c9 100644 --- a/linux/drivers/media/dvb/ttpci/av7110.c +++ b/linux/drivers/media/dvb/ttpci/av7110.c @@ -2223,7 +2223,7 @@ static int frontend_init(struct av7110 *av7110) av7110->fe->ops->tuner_ops.set_params = alps_bsbe1_tuner_set_params; av7110->fe->tuner_priv = &av7110->i2c_adap; - if (lnbp21_init(av7110->fe, &av7110->i2c_adap, 0, 0)) { + if (lnbp21_attach(av7110->fe, &av7110->i2c_adap, 0, 0)) { printk("dvb-ttpci: LNBP21 not found!\n"); if (av7110->fe->ops->release) av7110->fe->ops->release(av7110->fe); diff --git a/linux/drivers/media/dvb/ttpci/budget-ci.c b/linux/drivers/media/dvb/ttpci/budget-ci.c index ed610e56b..145e12f9a 100644 --- a/linux/drivers/media/dvb/ttpci/budget-ci.c +++ b/linux/drivers/media/dvb/ttpci/budget-ci.c @@ -1043,7 +1043,7 @@ static void frontend_init(struct budget_ci *budget_ci) budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; budget_ci->budget.dvb_frontend->ops->dishnetwork_send_legacy_command = NULL; - if (lnbp21_init(budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0)) { + if (lnbp21_attach(budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0)) { printk("%s: No LNBP21 found!\n", __FUNCTION__); if (budget_ci->budget.dvb_frontend->ops->release) budget_ci->budget.dvb_frontend->ops->release(budget_ci->budget.dvb_frontend); diff --git a/linux/drivers/media/dvb/ttpci/budget.c b/linux/drivers/media/dvb/ttpci/budget.c index a231975cb..d98395fef 100644 --- a/linux/drivers/media/dvb/ttpci/budget.c +++ b/linux/drivers/media/dvb/ttpci/budget.c @@ -423,7 +423,7 @@ static void frontend_init(struct budget *budget) budget->dvb_frontend = s5h1420_attach(&s5h1420_config, &budget->i2c_adap); if (budget->dvb_frontend) { budget->dvb_frontend->ops->tuner_ops.set_params = s5h1420_tuner_set_params; - if (lnbp21_init(budget->dvb_frontend, &budget->i2c_adap, 0, 0)) { + if (lnbp21_attach(budget->dvb_frontend, &budget->i2c_adap, 0, 0)) { printk("%s: No LNBP21 found!\n", __FUNCTION__); goto error_out; } diff --git a/linux/drivers/media/dvb/ttusb-budget/Kconfig b/linux/drivers/media/dvb/ttusb-budget/Kconfig index 914587d52..d1004e863 100644 --- a/linux/drivers/media/dvb/ttusb-budget/Kconfig +++ b/linux/drivers/media/dvb/ttusb-budget/Kconfig @@ -6,6 +6,7 @@ config DVB_TTUSB_BUDGET select DVB_VES1820 select DVB_TDA8083 select DVB_STV0299 + select DVB_LNBP21 help Support for external USB adapters designed by Technotrend and produced by Hauppauge, shipped under the brand name 'Nova-USB'. diff --git a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index f2aa23d76..66bc296be 100644 --- a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -33,6 +33,7 @@ #include "tda1004x.h" #include "stv0299.h" #include "tda8083.h" +#include "lnbp21.h" #include #include @@ -494,31 +495,6 @@ static int ttusb_send_diseqc(struct dvb_frontend* fe, } #endif -static int lnbp21_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) -{ - struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; - int ret; - u8 data[1]; - struct i2c_msg msg = { .addr = 0x08, .flags = 0, .buf = data, .len = sizeof(data) }; - - switch(voltage) { - case SEC_VOLTAGE_OFF: - data[0] = 0x00; - break; - case SEC_VOLTAGE_13: - data[0] = 0x44; - break; - case SEC_VOLTAGE_18: - data[0] = 0x4c; - break; - default: - return -EINVAL; - }; - - ret = i2c_transfer(&ttusb->i2c_adap, &msg, 1); - return (ret != 1) ? -EIO : 0; -} - static int ttusb_update_lnb(struct ttusb *ttusb) { u8 b[] = { 0xaa, ++ttusb->c, 0x16, 5, /*power: */ 1, @@ -1423,7 +1399,7 @@ static void frontend_init(struct ttusb* ttusb) if(ttusb->revision == TTUSB_REV_2_2) { // ALPS BSBE1 alps_stv0299_config.inittab = alps_bsbe1_inittab; - ttusb->fe->ops->set_voltage = lnbp21_set_voltage; + lnbp21_attach(ttusb->fe, &ttusb->i2c_adap, 0, 0); } else { // ALPS BSRU6 ttusb->fe->ops->set_voltage = ttusb_set_voltage; } -- cgit v1.2.3 From b5fe0362594b93998d9ab6c01554cc2b9dd95951 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Fri, 28 Apr 2006 01:45:09 +0100 Subject: Add isl6421 module From: Andrew de Quincey Driver for the ISL6421 LNB chip Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/Kconfig | 6 ++ linux/drivers/media/dvb/frontends/Makefile | 1 + linux/drivers/media/dvb/frontends/isl6421.c | 149 ++++++++++++++++++++++++++++ linux/drivers/media/dvb/frontends/isl6421.h | 46 +++++++++ 4 files changed, 202 insertions(+) create mode 100644 linux/drivers/media/dvb/frontends/isl6421.c create mode 100644 linux/drivers/media/dvb/frontends/isl6421.h (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/Kconfig b/linux/drivers/media/dvb/frontends/Kconfig index 64cd63623..0ef361f03 100644 --- a/linux/drivers/media/dvb/frontends/Kconfig +++ b/linux/drivers/media/dvb/frontends/Kconfig @@ -226,4 +226,10 @@ config DVB_LNBP21 help An SEC control chip. +config DVB_ISL6421 + tristate "ISL6421 SEC controller" + depends on DVB_CORE + help + An SEC control chip. + endmenu diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile index 7238e6ef2..5222245c7 100644 --- a/linux/drivers/media/dvb/frontends/Makefile +++ b/linux/drivers/media/dvb/frontends/Makefile @@ -32,3 +32,4 @@ obj-$(CONFIG_DVB_S5H1420) += s5h1420.o obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o obj-$(CONFIG_DVB_CX24123) += cx24123.o obj-$(CONFIG_DVB_LNBP21) += lnbp21.o +obj-$(CONFIG_DVB_ISL6421) += isl6421.o diff --git a/linux/drivers/media/dvb/frontends/isl6421.c b/linux/drivers/media/dvb/frontends/isl6421.c new file mode 100644 index 000000000..36992077a --- /dev/null +++ b/linux/drivers/media/dvb/frontends/isl6421.c @@ -0,0 +1,149 @@ +/* + * isl6421.h - driver for lnb supply and control ic ISL6421 + * + * Copyright (C) 2006 Andrew de Quincey + * Copyright (C) 2006 Oliver Endriss + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + * + * the project's page is at http://www.linuxtv.org + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "isl6421.h" + +struct isl6421 { + u8 config; + u8 override_or; + u8 override_and; + struct i2c_adapter *i2c; + u8 i2c_addr; + void (*release_chain)(struct dvb_frontend* fe); +}; + +static int isl6421_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct isl6421 *isl6421 = (struct isl6421 *) fe->misc_priv; + struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0, + .buf = &isl6421->config, + .len = sizeof(isl6421->config) }; + + isl6421->config &= ~(ISL6421_VSEL1 | ISL6421_EN1); + + switch(voltage) { + case SEC_VOLTAGE_OFF: + break; + case SEC_VOLTAGE_13: + isl6421->config |= ISL6421_EN1; + break; + case SEC_VOLTAGE_18: + isl6421->config |= (ISL6421_EN1 | ISL6421_VSEL1); + break; + default: + return -EINVAL; + }; + + isl6421->config |= isl6421->override_or; + isl6421->config &= isl6421->override_and; + + return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO; +} + +static int isl6421_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) +{ + struct isl6421 *isl6421 = (struct isl6421 *) fe->misc_priv; + struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0, + .buf = &isl6421->config, + .len = sizeof(isl6421->config) }; + + if (arg) + isl6421->config |= ISL6421_LLC1; + else + isl6421->config &= ~ISL6421_LLC1; + + isl6421->config |= isl6421->override_or; + isl6421->config &= isl6421->override_and; + + return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO; +} + +static void isl6421_release(struct dvb_frontend *fe) +{ + struct isl6421 *isl6421 = (struct isl6421 *) fe->misc_priv; + + /* power off */ + isl6421_set_voltage(fe, SEC_VOLTAGE_OFF); + + /* free data & call next release routine */ + fe->ops->release = isl6421->release_chain; + kfree(fe->misc_priv); + fe->misc_priv = NULL; + if (fe->ops->release) + fe->ops->release(fe); +} + +int isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, + u8 override_set, u8 override_clear) +{ + struct isl6421 *isl6421 = kmalloc(sizeof(struct isl6421), GFP_KERNEL); + if (!isl6421) + return -ENOMEM; + + /* default configuration */ + isl6421->config = ISL6421_ISEL1; + isl6421->i2c = i2c; + isl6421->i2c_addr = i2c_addr; + fe->misc_priv = isl6421; + + /* bits which should be forced to '1' */ + isl6421->override_or = override_set; + + /* bits which should be forced to '0' */ + isl6421->override_and = ~override_clear; + + /* detect if it is present or not */ + if (isl6421_set_voltage(fe, SEC_VOLTAGE_OFF)) { + kfree(isl6421); + fe->misc_priv = NULL; + return -EIO; + } + + /* install release callback */ + isl6421->release_chain = fe->ops->release; + fe->ops->release = isl6421_release; + + /* override frontend ops */ + fe->ops->set_voltage = isl6421_set_voltage; + fe->ops->enable_high_lnb_voltage = isl6421_enable_high_lnb_voltage; + + return 0; +} +EXPORT_SYMBOL(isl6421_attach); + +MODULE_DESCRIPTION("Driver for lnb supply and control ic isl6421"); +MODULE_AUTHOR("Andrew de Quincey & Oliver Endriss"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/frontends/isl6421.h b/linux/drivers/media/dvb/frontends/isl6421.h new file mode 100644 index 000000000..675f80a19 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/isl6421.h @@ -0,0 +1,46 @@ +/* + * isl6421.h - driver for lnb supply and control ic ISL6421 + * + * Copyright (C) 2006 Andrew de Quincey + * Copyright (C) 2006 Oliver Endriss + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + * + * the project's page is at http://www.linuxtv.org + */ + +#ifndef _ISL6421_H +#define _ISL6421_H + +#include + +/* system register bits */ +#define ISL6421_OLF1 0x01 +#define ISL6421_EN1 0x02 +#define ISL6421_VSEL1 0x04 +#define ISL6421_LLC1 0x08 +#define ISL6421_ENT1 0x10 +#define ISL6421_ISEL1 0x20 +#define ISL6421_DCL 0x40 + +/* override_set and override_clear control which system register bits (above) to always set & clear */ +extern int isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, + u8 override_set, u8 override_clear); + +#endif -- cgit v1.2.3 From a8030bb8f293d1eba3527eba4f91543aa9a48a0d Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Fri, 28 Apr 2006 01:45:20 +0100 Subject: Support new dvb-ttusb-budget boards with stv0297 From: Thomas Kaiser Added config switch to stv0297 to control i2c STOP during write behaviour. Update frontend init in dvb-ttusb-budget. Enable i2c STOP on other users of stv0297. Signed-off-by: Thomas Kaiser Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/stv0297.c | 49 +++--- linux/drivers/media/dvb/frontends/stv0297.h | 3 + linux/drivers/media/dvb/ttpci/av7110.c | 1 + linux/drivers/media/dvb/ttpci/budget-ci.c | 1 + linux/drivers/media/dvb/ttusb-budget/Kconfig | 1 + .../media/dvb/ttusb-budget/dvb-ttusb-budget.c | 175 +++++++++++++++++++++ 6 files changed, 212 insertions(+), 18 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/stv0297.c b/linux/drivers/media/dvb/frontends/stv0297.c index 7e8855723..9c5788c2e 100644 --- a/linux/drivers/media/dvb/frontends/stv0297.c +++ b/linux/drivers/media/dvb/frontends/stv0297.c @@ -68,19 +68,25 @@ static int stv0297_readreg(struct stv0297_state *state, u8 reg) int ret; u8 b0[] = { reg }; u8 b1[] = { 0 }; - struct i2c_msg msg[] = { {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = - 1}, - {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1} - }; + struct i2c_msg msg[] = { {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 1}, + {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1} + }; // this device needs a STOP between the register and data - if ((ret = i2c_transfer(state->i2c, &msg[0], 1)) != 1) { - dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg, ret); - return -1; - } - if ((ret = i2c_transfer(state->i2c, &msg[1], 1)) != 1) { - dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg, ret); - return -1; + if (state->config->stop_during_read) { + if ((ret = i2c_transfer(state->i2c, &msg[0], 1)) != 1) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg, ret); + return -1; + } + if ((ret = i2c_transfer(state->i2c, &msg[1], 1)) != 1) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg, ret); + return -1; + } + } else { + if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg, ret); + return -1; + } } return b1[0]; @@ -107,13 +113,20 @@ static int stv0297_readregs(struct stv0297_state *state, u8 reg1, u8 * b, u8 len }; // this device needs a STOP between the register and data - if ((ret = i2c_transfer(state->i2c, &msg[0], 1)) != 1) { - dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg1, ret); - return -1; - } - if ((ret = i2c_transfer(state->i2c, &msg[1], 1)) != 1) { - dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg1, ret); - return -1; + if (state->config->stop_during_read) { + if ((ret = i2c_transfer(state->i2c, &msg[0], 1)) != 1) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg1, ret); + return -1; + } + if ((ret = i2c_transfer(state->i2c, &msg[1], 1)) != 1) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg1, ret); + return -1; + } + } else { + if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg1, ret); + return -1; + } } return 0; diff --git a/linux/drivers/media/dvb/frontends/stv0297.h b/linux/drivers/media/dvb/frontends/stv0297.h index 8ff793c90..1da5384fb 100644 --- a/linux/drivers/media/dvb/frontends/stv0297.h +++ b/linux/drivers/media/dvb/frontends/stv0297.h @@ -37,6 +37,9 @@ struct stv0297_config /* does the "inversion" need inverted? */ u8 invert:1; + + /* set to 1 if the device requires an i2c STOP during reading */ + u8 stop_during_read:1; }; extern struct dvb_frontend* stv0297_attach(const struct stv0297_config* config, diff --git a/linux/drivers/media/dvb/ttpci/av7110.c b/linux/drivers/media/dvb/ttpci/av7110.c index 77cdeb7c9..dbb332304 100644 --- a/linux/drivers/media/dvb/ttpci/av7110.c +++ b/linux/drivers/media/dvb/ttpci/av7110.c @@ -1859,6 +1859,7 @@ static struct stv0297_config nexusca_stv0297_config = { .demod_address = 0x1C, .inittab = nexusca_stv0297_inittab, .invert = 1, + .stop_during_read = 1, }; diff --git a/linux/drivers/media/dvb/ttpci/budget-ci.c b/linux/drivers/media/dvb/ttpci/budget-ci.c index 145e12f9a..eb03b140b 100644 --- a/linux/drivers/media/dvb/ttpci/budget-ci.c +++ b/linux/drivers/media/dvb/ttpci/budget-ci.c @@ -977,6 +977,7 @@ static struct stv0297_config dvbc_philips_tdm1316l_config = { .demod_address = 0x1c, .inittab = dvbc_philips_tdm1316l_inittab, .invert = 0, + .stop_during_read = 1, }; diff --git a/linux/drivers/media/dvb/ttusb-budget/Kconfig b/linux/drivers/media/dvb/ttusb-budget/Kconfig index d1004e863..92c7cdcf8 100644 --- a/linux/drivers/media/dvb/ttusb-budget/Kconfig +++ b/linux/drivers/media/dvb/ttusb-budget/Kconfig @@ -6,6 +6,7 @@ config DVB_TTUSB_BUDGET select DVB_VES1820 select DVB_TDA8083 select DVB_STV0299 + select DVB_STV0297 select DVB_LNBP21 help Support for external USB adapters designed by Technotrend and diff --git a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index 66bc296be..30c26a867 100644 --- a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -33,6 +33,7 @@ #include "tda1004x.h" #include "stv0299.h" #include "tda8083.h" +#include "stv0297.h" #include "lnbp21.h" #include @@ -1388,6 +1389,174 @@ static u8 read_pwm(struct ttusb* ttusb) } +static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + struct ttusb *ttusb = (struct ttusb *) fe->dvb->priv; + u8 tuner_buf[5]; + struct i2c_msg tuner_msg = {.addr = 0x60, + .flags = 0, + .buf = tuner_buf, + .len = sizeof(tuner_buf) }; + int tuner_frequency = 0; + u8 band, cp, filter; + + // determine charge pump + tuner_frequency = params->frequency; + if (tuner_frequency < 87000000) {return -EINVAL;} + else if (tuner_frequency < 130000000) {cp = 3; band = 1;} + else if (tuner_frequency < 160000000) {cp = 5; band = 1;} + else if (tuner_frequency < 200000000) {cp = 6; band = 1;} + else if (tuner_frequency < 290000000) {cp = 3; band = 2;} + else if (tuner_frequency < 420000000) {cp = 5; band = 2;} + else if (tuner_frequency < 480000000) {cp = 6; band = 2;} + else if (tuner_frequency < 620000000) {cp = 3; band = 4;} + else if (tuner_frequency < 830000000) {cp = 5; band = 4;} + else if (tuner_frequency < 895000000) {cp = 7; band = 4;} + else {return -EINVAL;} + + // assume PLL filter should always be 8MHz for the moment. + filter = 1; + + // calculate divisor + // (Finput + Fif)/Fref; Fif = 36125000 Hz, Fref = 62500 Hz + tuner_frequency = ((params->frequency + 36125000) / 62500); + + // setup tuner buffer + tuner_buf[0] = tuner_frequency >> 8; + tuner_buf[1] = tuner_frequency & 0xff; + tuner_buf[2] = 0xc8; + tuner_buf[3] = (cp << 5) | (filter << 3) | band; + tuner_buf[4] = 0x80; + + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) { + printk("dvb-ttusb-budget: dvbc_philips_tdm1316l_pll_set Error 1\n"); + return -EIO; + } + + msleep(50); + + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) { + printk("dvb-ttusb-budget: dvbc_philips_tdm1316l_pll_set Error 2\n"); + return -EIO; + } + + msleep(1); + + return 0; +} + +static u8 dvbc_philips_tdm1316l_inittab[] = { + 0x80, 0x21, + 0x80, 0x20, + 0x81, 0x01, + 0x81, 0x00, + 0x00, 0x09, + 0x01, 0x69, + 0x03, 0x00, + 0x04, 0x00, + 0x07, 0x00, + 0x08, 0x00, + 0x20, 0x00, + 0x21, 0x40, + 0x22, 0x00, + 0x23, 0x00, + 0x24, 0x40, + 0x25, 0x88, + 0x30, 0xff, + 0x31, 0x00, + 0x32, 0xff, + 0x33, 0x00, + 0x34, 0x50, + 0x35, 0x7f, + 0x36, 0x00, + 0x37, 0x20, + 0x38, 0x00, + 0x40, 0x1c, + 0x41, 0xff, + 0x42, 0x29, + 0x43, 0x20, + 0x44, 0xff, + 0x45, 0x00, + 0x46, 0x00, + 0x49, 0x04, + 0x4a, 0xff, + 0x4b, 0x7f, + 0x52, 0x30, + 0x55, 0xae, + 0x56, 0x47, + 0x57, 0xe1, + 0x58, 0x3a, + 0x5a, 0x1e, + 0x5b, 0x34, + 0x60, 0x00, + 0x63, 0x00, + 0x64, 0x00, + 0x65, 0x00, + 0x66, 0x00, + 0x67, 0x00, + 0x68, 0x00, + 0x69, 0x00, + 0x6a, 0x02, + 0x6b, 0x00, + 0x70, 0xff, + 0x71, 0x00, + 0x72, 0x00, + 0x73, 0x00, + 0x74, 0x0c, + 0x80, 0x00, + 0x81, 0x00, + 0x82, 0x00, + 0x83, 0x00, + 0x84, 0x04, + 0x85, 0x80, + 0x86, 0x24, + 0x87, 0x78, + 0x88, 0x00, + 0x89, 0x00, + 0x90, 0x01, + 0x91, 0x01, + 0xa0, 0x00, + 0xa1, 0x00, + 0xa2, 0x00, + 0xb0, 0x91, + 0xb1, 0x0b, + 0xc0, 0x4b, + 0xc1, 0x00, + 0xc2, 0x00, + 0xd0, 0x00, + 0xd1, 0x00, + 0xd2, 0x00, + 0xd3, 0x00, + 0xd4, 0x00, + 0xd5, 0x00, + 0xde, 0x00, + 0xdf, 0x00, + 0x61, 0x38, + 0x62, 0x0a, + 0x53, 0x13, + 0x59, 0x08, + 0x55, 0x00, + 0x56, 0x40, + 0x57, 0x08, + 0x58, 0x3d, + 0x88, 0x10, + 0xa0, 0x00, + 0xa0, 0x00, + 0xa0, 0x00, + 0xa0, 0x04, + 0xff, 0xff, +}; + +static struct stv0297_config dvbc_philips_tdm1316l_config = { + .demod_address = 0x1c, + .inittab = dvbc_philips_tdm1316l_inittab, + .invert = 0, +}; + static void frontend_init(struct ttusb* ttusb) { switch(le16_to_cpu(ttusb->dev->descriptor.idProduct)) { @@ -1421,6 +1590,12 @@ static void frontend_init(struct ttusb* ttusb) ttusb->fe->ops->tuner_ops.set_params = alps_tdbe2_tuner_set_params; break; } + + ttusb->fe = stv0297_attach(&dvbc_philips_tdm1316l_config, &ttusb->i2c_adap); + if (ttusb->fe != NULL) { + ttusb->fe->ops->tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params; + break; + } break; case 0x1005: // Hauppauge/TT Nova-USB-t budget (tda10046/Philips td1316(tda6651tt) OR cx22700/ALPS TDMB7(??)) -- cgit v1.2.3 From ab12813a48c3dbc4f0d9f2b3e93e7a5465744f9c Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Fri, 28 Apr 2006 01:47:05 +0100 Subject: Add missing pll gate control calls From: Andrew de Quincey I'd missed some of these out, breaking the av7110 tuning. I then checked for more and added them in where necessary. They may not actually be necessary in all these locations, but if not, they'll simply have no effect. Add small delay to stv0299 pll gate control to fix tuning problems. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/bsbe1.h | 2 ++ linux/drivers/media/dvb/frontends/bsru6.h | 2 ++ linux/drivers/media/dvb/frontends/lg_h06xf.h | 4 ++++ linux/drivers/media/dvb/frontends/stv0299.c | 6 ++++-- linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c | 14 ++++++++++++++ linux/drivers/media/video/saa7134/saa7134-dvb.c | 4 ++++ 6 files changed, 30 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/bsbe1.h b/linux/drivers/media/dvb/frontends/bsbe1.h index 6b52d5abf..b2aeddb14 100644 --- a/linux/drivers/media/dvb/frontends/bsbe1.h +++ b/linux/drivers/media/dvb/frontends/bsbe1.h @@ -106,6 +106,8 @@ static int alps_bsbe1_tuner_set_params(struct dvb_frontend* fe, struct dvb_front data[2] = 0x80 | ((div & 0x18000) >> 10) | 4; data[3] = (params->frequency > 1530000) ? 0xE0 : 0xE4; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); ret = i2c_transfer(i2c, &msg, 1); return (ret != 1) ? -EIO : 0; } diff --git a/linux/drivers/media/dvb/frontends/bsru6.h b/linux/drivers/media/dvb/frontends/bsru6.h index 5d13c5e4d..5533512b0 100644 --- a/linux/drivers/media/dvb/frontends/bsru6.h +++ b/linux/drivers/media/dvb/frontends/bsru6.h @@ -120,6 +120,8 @@ static int alps_bsru6_tuner_set_params(struct dvb_frontend *fe, struct dvb_front if (params->frequency > 1530000) buf[3] = 0xc0; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(i2c, &msg, 1) != 1) return -EIO; return 0; diff --git a/linux/drivers/media/dvb/frontends/lg_h06xf.h b/linux/drivers/media/dvb/frontends/lg_h06xf.h index 6869cbd03..21acb5bad 100644 --- a/linux/drivers/media/dvb/frontends/lg_h06xf.h +++ b/linux/drivers/media/dvb/frontends/lg_h06xf.h @@ -29,6 +29,8 @@ static int lg_h06xf_pll_set(struct dvb_frontend* fe, struct i2c_adapter* i2c_ada int err; dvb_pll_configure(&dvb_pll_lg_tdvs_h06xf, buf, params->frequency, 0); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) { printk(KERN_WARNING "lg_h06xf: %s error " "(addr %02x <- %02x, err = %i)\n", @@ -51,6 +53,8 @@ static int lg_h06xf_pll_set(struct dvb_frontend* fe, struct i2c_adapter* i2c_ada buf[1] = 0x50; msg.len = 2; #endif + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) { printk(KERN_WARNING "lg_h06xf: %s error " "(addr %02x <- %02x, err = %i)\n", diff --git a/linux/drivers/media/dvb/frontends/stv0299.c b/linux/drivers/media/dvb/frontends/stv0299.c index 5a8131023..e91bb5842 100644 --- a/linux/drivers/media/dvb/frontends/stv0299.c +++ b/linux/drivers/media/dvb/frontends/stv0299.c @@ -604,10 +604,12 @@ static int stv0299_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) struct stv0299_state* state = fe->demodulator_priv; if (enable) { - return stv0299_writeregI(state, 0x05, 0xb5); + stv0299_writeregI(state, 0x05, 0xb5); } else { - return stv0299_writeregI(state, 0x05, 0x35); + stv0299_writeregI(state, 0x05, 0x35); } + udelay(1); + return 0; } static int stv0299_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) diff --git a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index 30c26a867..af1631a25 100644 --- a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -1047,6 +1047,8 @@ static int alps_tdmb7_tuner_set_params(struct dvb_frontend* fe, struct dvb_front data[2] = ((div >> 10) & 0x60) | 0x85; data[3] = params->frequency < 592000000 ? 0x40 : 0x80; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1) return -EIO; return 0; } @@ -1067,6 +1069,8 @@ static int philips_tdm1316l_tuner_init(struct dvb_frontend* fe) struct i2c_msg tuner_msg = { .addr=0x60, .flags=0, .buf=td1316_init, .len=sizeof(td1316_init) }; // setup PLL configuration + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) return -EIO; msleep(1); @@ -1074,6 +1078,8 @@ static int philips_tdm1316l_tuner_init(struct dvb_frontend* fe) tuner_msg.addr = 0x65; tuner_msg.buf = disable_mc44BC374c; tuner_msg.len = sizeof(disable_mc44BC374c); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) { i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1); } @@ -1141,6 +1147,8 @@ static int philips_tdm1316l_tuner_set_params(struct dvb_frontend* fe, struct dvb tuner_buf[2] = 0xca; tuner_buf[3] = (cp << 5) | (filter << 3) | band; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) return -EIO; @@ -1304,6 +1312,8 @@ static int philips_tsa5059_tuner_set_params(struct dvb_frontend *fe, struct dvb_ if (ttusb->revision == TTUSB_REV_2_2) buf[3] |= 0x20; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1) return -EIO; @@ -1336,6 +1346,8 @@ static int ttusb_novas_grundig_29504_491_tuner_set_params(struct dvb_frontend *f buf[2] = 0x8e; buf[3] = 0x00; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1) return -EIO; @@ -1361,6 +1373,8 @@ static int alps_tdbe2_tuner_set_params(struct dvb_frontend* fe, struct dvb_front data[2] = 0x85 | ((div >> 10) & 0x60); data[3] = (params->frequency < 174000000 ? 0x88 : params->frequency < 470000000 ? 0x84 : 0x81); + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); if (i2c_transfer (&ttusb->i2c_adap, &msg, 1) != 1) return -EIO; diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index b659b96e5..ea0a1290d 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -151,9 +151,13 @@ static int mt352_pinnacle_tuner_calc_regs(struct dvb_frontend* fe, f.tuner = 0; f.type = V4L2_TUNER_DIGITAL_TV; f.frequency = params->frequency / 1000 * 16 / 1000; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &msg, 1); saa7134_i2c_call_clients(dev,VIDIOC_S_FREQUENCY,&f); msg.buf = on; + if (fe->ops->i2c_gate_ctrl) + fe->ops->i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &msg, 1); pinnacle_antenna_pwr(dev, antenna_pwr); -- cgit v1.2.3 From 9f5fd95e9b91a458a32bd8687d3924d6d6e1ac0a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 28 Apr 2006 10:48:01 -0300 Subject: Em28xx/: possible cleanups From: Adrian Bunk This patch contains the following possible cleanups: - make the following needlessly global functions static: - em28xx-core.c: em28xx_accumulator_set() - em28xx-core.c: em28xx_capture_area_set() - em28xx-core.c: em28xx_scaler_set() - em28xx-core.c: em28xx_isocIrq() - remove the following unused EXPORT_SYMBOL's: - em28xx-cards.c: em28xx_boards - em28xx-cards.c: em28xx_bcount - em28xx-cards.c: em28xx_id_table Signed-off-by: Adrian Bunk Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 4 ---- linux/drivers/media/video/em28xx/em28xx-core.c | 10 +++++----- linux/drivers/media/video/em28xx/em28xx.h | 6 ------ 3 files changed, 5 insertions(+), 15 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 84b764b7d..48240b3ec 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -402,8 +402,4 @@ void em28xx_card_setup(struct em28xx *dev) } } -EXPORT_SYMBOL(em28xx_boards); -EXPORT_SYMBOL(em28xx_bcount); -EXPORT_SYMBOL(em28xx_id_table); - MODULE_DEVICE_TABLE (usb, em28xx_id_table); diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 91422b642..ff0cd9563 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -375,8 +375,8 @@ int em28xx_outfmt_set_yuv422(struct em28xx *dev) return em28xx_write_regs(dev, VINCTRL_REG, "\x11", 1); } -int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, u8 ymin, - u8 ymax) +static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, + u8 ymin, u8 ymax) { em28xx_coredbg("em28xx Scale: (%d,%d)-(%d,%d)\n", xmin, ymin, xmax, ymax); @@ -386,7 +386,7 @@ int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, u8 ymin, return em28xx_write_regs(dev, YMAX_REG, &ymax, 1); } -int em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, +static int em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, u16 width, u16 height) { u8 cwidth = width; @@ -403,7 +403,7 @@ int em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, return em28xx_write_regs(dev, OFLOW_REG, &overflow, 1); } -int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) +static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) { u8 mode; /* the em2800 scaler only supports scaling down to 50% */ @@ -592,7 +592,7 @@ static inline void em28xx_isoc_video_copy(struct em28xx *dev, * em28xx_isoIrq() * handles the incoming isoc urbs and fills the frames from our inqueue */ -void em28xx_isocIrq(struct urb *urb, struct pt_regs *regs) +static void em28xx_isocIrq(struct urb *urb, struct pt_regs *regs) { struct em28xx *dev = urb->context; int i, status; diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 97744214b..8b4c54f9f 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -326,13 +326,7 @@ int em28xx_audio_analog_set(struct em28xx *dev); int em28xx_colorlevels_set_default(struct em28xx *dev); int em28xx_capture_start(struct em28xx *dev, int start); int em28xx_outfmt_set_yuv422(struct em28xx *dev); -int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, u8 ymin, - u8 ymax); -int em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, - u16 width, u16 height); -int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v); int em28xx_resolution_set(struct em28xx *dev); -void em28xx_isocIrq(struct urb *urb, struct pt_regs *regs); int em28xx_init_isoc(struct em28xx *dev); void em28xx_uninit_isoc(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); -- cgit v1.2.3 From 7445f5a6addde5bb3f20242677399e4790e5a4d9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 28 Apr 2006 10:48:41 -0300 Subject: Bt8xx/bttv-cards.c: fix off-by-one errors From: Adrian Bunk This patch fixes two off-by-one errors spotted by the Coverity checker. Signed-off-by: Adrian Bunk Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/bt8xx/bttv-cards.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/bt8xx/bttv-cards.c b/linux/drivers/media/video/bt8xx/bttv-cards.c index cd45a2429..909084515 100644 --- a/linux/drivers/media/video/bt8xx/bttv-cards.c +++ b/linux/drivers/media/video/bt8xx/bttv-cards.c @@ -3014,13 +3014,13 @@ void __devinit bttv_idcard(struct bttv *btv) if (UNSET != audiomux[0]) { gpiobits = 0; - for (i = 0; i < 5; i++) { + for (i = 0; i < 4; i++) { bttv_tvcards[btv->c.type].gpiomux[i] = audiomux[i]; gpiobits |= audiomux[i]; } } else { gpiobits = audioall; - for (i = 0; i < 5; i++) { + for (i = 0; i < 4; i++) { bttv_tvcards[btv->c.type].gpiomux[i] = audioall; } } -- cgit v1.2.3 From c26243a19635a77fda4261cec814349a61e74780 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 29 Apr 2006 17:11:18 +0200 Subject: From: Hans Verkuil Stop/start microcontroller when changing sampling frequency Stop/start microcontroller when changing sampling frequency to prevent tinny audio. Signed-off-by: Hans Verkuil --- linux/drivers/media/video/cx25840/cx25840-audio.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx25840/cx25840-audio.c b/linux/drivers/media/video/cx25840/cx25840-audio.c index c836feeb5..f732880d4 100644 --- a/linux/drivers/media/video/cx25840/cx25840-audio.c +++ b/linux/drivers/media/video/cx25840/cx25840-audio.c @@ -185,13 +185,15 @@ void cx25840_audio_set_path(struct i2c_client *client) } else { /* Set Path1 to Analog Demod Main Channel */ cx25840_write4(client, 0x8d0, 0x7038061f); + } + + set_audclk_freq(client, state->audclk_freq); + if (state->aud_input != CX25840_AUDIO_SERIAL) { /* When the microcontroller detects the * audio format, it will unmute the lines */ cx25840_and_or(client, 0x803, ~0x10, 0x10); } - - set_audclk_freq(client, state->audclk_freq); } static int get_volume(struct i2c_client *client) @@ -315,11 +317,21 @@ static void set_mute(struct i2c_client *client, int mute) int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg) { + struct cx25840_state *state = i2c_get_clientdata(client); struct v4l2_control *ctrl = arg; + int retval; switch (cmd) { case VIDIOC_INT_AUDIO_CLOCK_FREQ: - return set_audclk_freq(client, *(u32 *)arg); + if (state->aud_input != CX25840_AUDIO_SERIAL) { + cx25840_and_or(client, 0x803, ~0x10, 0); + cx25840_write(client, 0x8d3, 0x1f); + } + retval = set_audclk_freq(client, *(u32 *)arg); + if (state->aud_input != CX25840_AUDIO_SERIAL) { + cx25840_and_or(client, 0x803, ~0x10, 0x10); + } + return retval; case VIDIOC_G_CTRL: switch (ctrl->id) { -- cgit v1.2.3 From 1de6f4cd655f4ebf23c5df5734b40dab8e6c905a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 29 Apr 2006 17:26:46 +0200 Subject: Audio soft reset improvements From: Hans Verkuil The soft reset (de)assert commands where not at the right place. Signed-off-by: Hans Verkuil --- linux/drivers/media/video/cx25840/cx25840-audio.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx25840/cx25840-audio.c b/linux/drivers/media/video/cx25840/cx25840-audio.c index f732880d4..4cdf4c60e 100644 --- a/linux/drivers/media/video/cx25840/cx25840-audio.c +++ b/linux/drivers/media/video/cx25840/cx25840-audio.c @@ -34,10 +34,6 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) if (freq != 32000 && freq != 44100 && freq != 48000) return -EINVAL; - /* assert soft reset */ - if (!state->is_cx25836) - cx25840_and_or(client, 0x810, ~0x1, 0x01); - /* common for all inputs and rates */ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */ cx25840_write(client, 0x127, 0x50); @@ -156,10 +152,6 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) } } - /* deassert soft reset */ - if (!state->is_cx25836) - cx25840_and_or(client, 0x810, ~0x1, 0x00); - state->audclk_freq = freq; return 0; @@ -172,6 +164,10 @@ void cx25840_audio_set_path(struct i2c_client *client) /* stop microcontroller */ cx25840_and_or(client, 0x803, ~0x10, 0); + /* assert soft reset */ + if (!state->is_cx25836) + cx25840_and_or(client, 0x810, ~0x1, 0x01); + /* Mute everything to prevent the PFFT! */ cx25840_write(client, 0x8d3, 0x1f); @@ -189,6 +185,10 @@ void cx25840_audio_set_path(struct i2c_client *client) set_audclk_freq(client, state->audclk_freq); + /* deassert soft reset */ + if (!state->is_cx25836) + cx25840_and_or(client, 0x810, ~0x1, 0x00); + if (state->aud_input != CX25840_AUDIO_SERIAL) { /* When the microcontroller detects the * audio format, it will unmute the lines */ @@ -327,7 +327,11 @@ int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg) cx25840_and_or(client, 0x803, ~0x10, 0); cx25840_write(client, 0x8d3, 0x1f); } + if (!state->is_cx25836) + cx25840_and_or(client, 0x810, ~0x1, 1); retval = set_audclk_freq(client, *(u32 *)arg); + if (!state->is_cx25836) + cx25840_and_or(client, 0x810, ~0x1, 0); if (state->aud_input != CX25840_AUDIO_SERIAL) { cx25840_and_or(client, 0x803, ~0x10, 0x10); } -- cgit v1.2.3 From 02fcdd405f0481e03cf7f88ceaf7c6fd8b981b49 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 30 Apr 2006 23:08:00 -0400 Subject: cx88-blackbird: pause the encoder during frequency change From: Valentin Zagura Added code to cx88-blackbird.c to treat the VIDIOC_S_FREQUENCY ioctl to stop mpeg stream before changing the frequency and restart it after. It seems that the mpeg stream needs to be paused while changing the channel frequency, otherwise the mpeg encoder enters into a bad state, producing artifacts. Signed-off-by: Valentin Zagura Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/cx88-blackbird.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index 353dff4b4..844115b35 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -1489,6 +1489,20 @@ static int mpeg_do_ioctl(struct inode *inode, struct file *file, blackbird_set_params(dev, f); return 0; } + case VIDIOC_S_FREQUENCY: + { + blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, + BLACKBIRD_END_NOW, + BLACKBIRD_MPEG_CAPTURE, + BLACKBIRD_RAW_BITS_NONE); + + cx88_do_ioctl( inode, file, 0, dev->core, cmd, arg, cx88_ioctl_hook ); + + blackbird_initialize_codec(dev); + cx88_set_scale(dev->core, dev->width, dev->height, + fh->mpegq.field); + return 0; + } default: return cx88_do_ioctl( inode, file, 0, dev->core, cmd, arg, cx88_ioctl_hook ); -- cgit v1.2.3 From c9d707b6322f604c9a3a40ddc0a2b778a080806f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 2 May 2006 09:00:31 -0300 Subject: Fix CONFIG_VIDEO_VIVI=y build bug From: Ingo Molnar CONFIG_VIDEO_VIVI depends on CONFIG_VIDEO_BUF. Signed-off-by: Ingo Molnar Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index 4df2ede38..cf36fd123 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -19,6 +19,7 @@ config VIDEO_ADV_DEBUG config VIDEO_VIVI tristate "Virtual Video Driver" depends on VIDEO_V4L2 && !SPARC32 && !SPARC64 + select VIDEO_BUF default n ---help--- Enables a virtual video driver. This device shows a color bar -- cgit v1.2.3 From 9a31a0d73f490c724252a18e59a5313561380c8c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 2 May 2006 09:01:13 -0300 Subject: Core: ULE fixes and RFC4326 additions From: Christian Praehauser Fix some problems regarding support for Unidirectional Lightweight Encapsulation (ULE) in dvbnet.c. The original ULE code was based on a draft. In the meantime, ULE has been published in RFC 4326 (ftp://ftp.rfc-editor.org/in-notes/rfc4326.txt). With these fixes, and some additions (which are included in the patch), the decaps code should now be complient to RFC4326. Signed-off-by: Christian Praehauser Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-core/dvb_net.c | 230 +++++++++++++++++++---------- 1 file changed, 154 insertions(+), 76 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_net.c b/linux/drivers/media/dvb/dvb-core/dvb_net.c index 5f3c9b71f..a1989e336 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_net.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_net.c @@ -12,7 +12,7 @@ * Hilmar Linder * and Wolfram Stering * - * ULE Decaps according to draft-ietf-ipdvb-ule-03.txt. + * ULE Decaps according to RFC 4326. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -42,6 +42,9 @@ * Bugfixes and robustness improvements. * Filtering on dest MAC addresses, if present (D-Bit = 0) * ULE_DEBUG compile-time option. + * Apr 2006: cp v3: Bugfixes and compliency with RFC 4326 (ULE) by + * Christian Praehauser , + * Paris Lodron University of Salzburg. */ /* @@ -49,9 +52,6 @@ * * Unloading does not work for 2.6.9 kernels: a refcount doesn't go to zero. * - * TS_FEED callback is called once for every single TS cell although it is - * registered (in dvb_net_feed_start()) for 100 TS cells (used for dvb_net_ule()). - * */ #include @@ -92,6 +92,9 @@ static inline __u32 iov_crc32( __u32 c, struct kvec *iov, unsigned int cnt ) #ifdef ULE_DEBUG +#define MAC_ADDR_PRINTFMT "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x" +#define MAX_ADDR_PRINTFMT_ARGS(macap) (macap)[0],(macap)[1],(macap)[2],(macap)[3],(macap)[4],(macap)[5] + #define isprint(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) static void hexdump( const unsigned char *buf, unsigned short len ) @@ -221,6 +224,8 @@ static unsigned short dvb_net_eth_type_trans(struct sk_buff *skb, #define ULE_TEST 0 #define ULE_BRIDGED 1 +#define ULE_OPTEXTHDR_PADDING 0 + static int ule_test_sndu( struct dvb_net_priv *p ) { return -1; @@ -228,14 +233,28 @@ static int ule_test_sndu( struct dvb_net_priv *p ) static int ule_bridged_sndu( struct dvb_net_priv *p ) { - /* BRIDGE SNDU handling sucks in draft-ietf-ipdvb-ule-03.txt. - * This has to be the last extension header, otherwise it won't work. - * Blame the authors! + struct ethhdr *hdr = (struct ethhdr*) p->ule_next_hdr; + if(ntohs(hdr->h_proto) < 1536) { + int framelen = p->ule_sndu_len - ((p->ule_next_hdr+sizeof(struct ethhdr)) - p->ule_skb->data); + /* A frame Type < 1536 for a bridged frame, introduces a LLC Length field. */ + if(framelen != ntohs(hdr->h_proto)) { + return -1; + } + } + /* Note: + * From RFC4326: + * "A bridged SNDU is a Mandatory Extension Header of Type 1. + * It must be the final (or only) extension header specified in the header chain of a SNDU." + * The 'ule_bridged' flag will cause the extension header processing loop to terminate. */ p->ule_bridged = 1; return 0; } +static int ule_exthdr_padding(struct dvb_net_priv *p) +{ + return 0; +} /** Handle ULE extension headers. * Function is called after a successful CRC32 verification of an ULE SNDU to complete its decoding. @@ -249,7 +268,8 @@ static int handle_one_ule_extension( struct dvb_net_priv *p ) { [0] = ule_test_sndu, [1] = ule_bridged_sndu, [2] = NULL, }; /* Table of optional extension header handlers. The header type is the index. */ - static int (*ule_optional_ext_handlers[255])( struct dvb_net_priv *p ) = { NULL, }; + static int (*ule_optional_ext_handlers[255])( struct dvb_net_priv *p ) = + { [0] = ule_exthdr_padding, [1] = NULL, }; int ext_len = 0; unsigned char hlen = (p->ule_sndu_type & 0x0700) >> 8; @@ -260,25 +280,31 @@ static int handle_one_ule_extension( struct dvb_net_priv *p ) /* Mandatory extension header */ if (ule_mandatory_ext_handlers[htype]) { ext_len = ule_mandatory_ext_handlers[htype]( p ); - p->ule_next_hdr += ext_len; - if (! p->ule_bridged) { - p->ule_sndu_type = ntohs( *(unsigned short *)p->ule_next_hdr ); - p->ule_next_hdr += 2; - } else { - p->ule_sndu_type = ntohs( *(unsigned short *)(p->ule_next_hdr + ((p->ule_dbit ? 2 : 3) * ETH_ALEN)) ); - /* This assures the extension handling loop will terminate. */ + if(ext_len >= 0) { + p->ule_next_hdr += ext_len; + if (!p->ule_bridged) { + p->ule_sndu_type = ntohs(*(unsigned short *)p->ule_next_hdr); + p->ule_next_hdr += 2; + } else { + p->ule_sndu_type = ntohs(*(unsigned short *)(p->ule_next_hdr + ((p->ule_dbit ? 2 : 3) * ETH_ALEN))); + /* This assures the extension handling loop will terminate. */ + } } + // else: extension handler failed or SNDU should be discarded } else ext_len = -1; /* SNDU has to be discarded. */ } else { /* Optional extension header. Calculate the length. */ - ext_len = hlen << 2; + ext_len = hlen << 1; /* Process the optional extension header according to its type. */ if (ule_optional_ext_handlers[htype]) (void)ule_optional_ext_handlers[htype]( p ); p->ule_next_hdr += ext_len; - p->ule_sndu_type = ntohs( *(unsigned short *)p->ule_next_hdr ); - p->ule_next_hdr += 2; + p->ule_sndu_type = ntohs( *(unsigned short *)(p->ule_next_hdr-2) ); + /* + * note: the length of the next header type is included in the + * length of THIS optional extension header + */ } return ext_len; @@ -291,8 +317,14 @@ static int handle_ule_extensions( struct dvb_net_priv *p ) p->ule_next_hdr = p->ule_skb->data; do { l = handle_one_ule_extension( p ); - if (l == -1) return -1; /* Stop extension header processing and discard SNDU. */ + if (l < 0) + return l; /* Stop extension header processing and discard SNDU. */ total_ext_len += l; +#ifdef ULE_DEBUG + dprintk("handle_ule_extensions: ule_next_hdr=%p, ule_sndu_type=%i, " + "l=%i, total_ext_len=%i\n", p->ule_next_hdr, + (int) p->ule_sndu_type, l, total_ext_len); +#endif } while (p->ule_sndu_type < 1536); @@ -362,8 +394,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) if (priv->ule_skb) { dev_kfree_skb( priv->ule_skb ); /* Prepare for next SNDU. */ - ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++; - ((struct dvb_net_priv *) dev->priv)->stats.rx_frame_errors++; + priv->stats.rx_errors++; + priv->stats.rx_frame_errors++; } reset_ule(priv); priv->need_pusi = 1; @@ -403,27 +435,25 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) } } - /* Check continuity counter. */ if (new_ts) { + /* Check continuity counter. */ if ((ts[3] & 0x0F) == priv->tscc) priv->tscc = (priv->tscc + 1) & 0x0F; else { /* TS discontinuity handling: */ printk(KERN_WARNING "%lu: TS discontinuity: got %#x, " - "exptected %#x.\n", priv->ts_count, ts[3] & 0x0F, priv->tscc); + "expected %#x.\n", priv->ts_count, ts[3] & 0x0F, priv->tscc); /* Drop partly decoded SNDU, reset state, resync on PUSI. */ if (priv->ule_skb) { dev_kfree_skb( priv->ule_skb ); /* Prepare for next SNDU. */ // reset_ule(priv); moved to below. - ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++; - ((struct dvb_net_priv *) dev->priv)->stats.rx_frame_errors++; + priv->stats.rx_errors++; + priv->stats.rx_frame_errors++; } reset_ule(priv); /* skip to next PUSI. */ priv->need_pusi = 1; - ts += TS_SZ; - priv->ts_count++; continue; } /* If we still have an incomplete payload, but PUSI is @@ -432,7 +462,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) * cells (continuity counter wrap). */ if (ts[1] & TS_PUSI) { if (! priv->need_pusi) { - if (*from_where > 181) { + if (!(*from_where < (ts_remain-1)) || *from_where != priv->ule_sndu_remain) { /* Pointer field is invalid. Drop this TS cell and any started ULE SNDU. */ printk(KERN_WARNING "%lu: Invalid pointer " "field: %u.\n", priv->ts_count, *from_where); @@ -445,8 +475,6 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) } reset_ule(priv); priv->need_pusi = 1; - ts += TS_SZ; - priv->ts_count++; continue; } /* Skip pointer field (we're processing a @@ -459,8 +487,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) if (priv->ule_sndu_remain > 183) { /* Current SNDU lacks more data than there could be available in the * current TS cell. */ - ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++; - ((struct dvb_net_priv *) dev->priv)->stats.rx_length_errors++; + priv->stats.rx_errors++; + priv->stats.rx_length_errors++; printk(KERN_WARNING "%lu: Expected %d more SNDU bytes, but " "got PUSI (pf %d, ts_remain %d). Flushing incomplete payload.\n", priv->ts_count, priv->ule_sndu_remain, ts[4], ts_remain); @@ -499,9 +527,11 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) } else priv->ule_dbit = 0; - if (priv->ule_sndu_len > 32763) { + if (priv->ule_sndu_len < 5) { printk(KERN_WARNING "%lu: Invalid ULE SNDU length %u. " "Resyncing.\n", priv->ts_count, priv->ule_sndu_len); + priv->stats.rx_errors++; + priv->stats.rx_length_errors++; priv->ule_sndu_len = 0; priv->need_pusi = 1; new_ts = 1; @@ -615,58 +645,103 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) ule_dump = 1; #endif - ((struct dvb_net_priv *) dev->priv)->stats.rx_errors++; - ((struct dvb_net_priv *) dev->priv)->stats.rx_crc_errors++; + priv->stats.rx_errors++; + priv->stats.rx_crc_errors++; dev_kfree_skb(priv->ule_skb); } else { /* CRC32 verified OK. */ + u8 dest_addr[ETH_ALEN]; + static const u8 bc_addr[ETH_ALEN] = + { [ 0 ... ETH_ALEN-1] = 0xff }; + + /* CRC32 was OK. Remove it from skb. */ + priv->ule_skb->tail -= 4; + priv->ule_skb->len -= 4; + + if (!priv->ule_dbit) { + /* + * The destination MAC address is the + * next data in the skb. It comes + * before any extension headers. + * + * Check if the payload of this SNDU + * should be passed up the stack. + */ + register int drop = 0; + if (priv->rx_mode != RX_MODE_PROMISC) { + if (priv->ule_skb->data[0] & 0x01) { + /* multicast or broadcast */ + if (memcmp(priv->ule_skb->data, bc_addr, ETH_ALEN)) { + /* multicast */ + if (priv->rx_mode == RX_MODE_MULTI) { + int i; + for(i = 0; i < priv->multi_num && memcmp(priv->ule_skb->data, priv->multi_macs[i], ETH_ALEN); i++) + ; + if (i == priv->multi_num) + drop = 1; + } else if (priv->rx_mode != RX_MODE_ALL_MULTI) + drop = 1; /* no broadcast; */ + /* else: all multicast mode: accept all multicast packets */ + } + /* else: broadcast */ + } + else if (memcmp(priv->ule_skb->data, dev->dev_addr, ETH_ALEN)) + drop = 1; + /* else: destination address matches the MAC address of our receiver device */ + } + /* else: promiscious mode; pass everything up the stack */ + + if (drop) { +#ifdef ULE_DEBUG + dprintk("Dropping SNDU: MAC destination address does not match: dest addr: "MAC_ADDR_PRINTFMT", dev addr: "MAC_ADDR_PRINTFMT"\n", + MAX_ADDR_PRINTFMT_ARGS(priv->ule_skb->data), MAX_ADDR_PRINTFMT_ARGS(dev->dev_addr)); +#endif + dev_kfree_skb(priv->ule_skb); + goto sndu_done; + } + else + { + memcpy(dest_addr, priv->ule_skb->data, ETH_ALEN); + skb_pull(priv->ule_skb, ETH_ALEN); + } + } + /* Handle ULE Extension Headers. */ if (priv->ule_sndu_type < 1536) { /* There is an extension header. Handle it accordingly. */ - int l = handle_ule_extensions( priv ); + int l = handle_ule_extensions(priv); if (l < 0) { /* Mandatory extension header unknown or TEST SNDU. Drop it. */ // printk( KERN_WARNING "Dropping SNDU, extension headers.\n" ); - dev_kfree_skb( priv->ule_skb ); + dev_kfree_skb(priv->ule_skb); goto sndu_done; } - skb_pull( priv->ule_skb, l ); + skb_pull(priv->ule_skb, l); } - /* CRC32 was OK. Remove it from skb. */ - priv->ule_skb->tail -= 4; - priv->ule_skb->len -= 4; - - /* Filter on receiver's destination MAC address, if present. */ - if (!priv->ule_dbit) { - /* The destination MAC address is the next data in the skb. */ - if (memcmp( priv->ule_skb->data, dev->dev_addr, ETH_ALEN )) { - /* MAC addresses don't match. Drop SNDU. */ - // printk( KERN_WARNING "Dropping SNDU, MAC address.\n" ); - dev_kfree_skb( priv->ule_skb ); - goto sndu_done; - } - if (! priv->ule_bridged) { - skb_push( priv->ule_skb, ETH_ALEN + 2 ); - ethh = (struct ethhdr *)priv->ule_skb->data; - memcpy( ethh->h_dest, ethh->h_source, ETH_ALEN ); - memset( ethh->h_source, 0, ETH_ALEN ); - ethh->h_proto = htons( priv->ule_sndu_type ); - } else { - /* Skip the Receiver destination MAC address. */ - skb_pull( priv->ule_skb, ETH_ALEN ); - } - } else { - if (! priv->ule_bridged) { - skb_push( priv->ule_skb, ETH_HLEN ); - ethh = (struct ethhdr *)priv->ule_skb->data; - memcpy( ethh->h_dest, dev->dev_addr, ETH_ALEN ); - memset( ethh->h_source, 0, ETH_ALEN ); - ethh->h_proto = htons( priv->ule_sndu_type ); - } else { - /* skb is in correct state; nothing to do. */ + /* + * Construct/assure correct ethernet header. + * Note: in bridged mode (priv->ule_bridged != + * 0) we already have the (original) ethernet + * header at the start of the payload (after + * optional dest. address and any extension + * headers). + */ + + if (!priv->ule_bridged) { + skb_push(priv->ule_skb, ETH_HLEN); + ethh = (struct ethhdr *)priv->ule_skb->data; + if (!priv->ule_dbit) { + /* dest_addr buffer is only valid if priv->ule_dbit == 0 */ + memcpy(ethh->h_dest, dest_addr, ETH_ALEN); + memset(ethh->h_source, 0, ETH_ALEN); } + else /* zeroize source and dest */ + memset( ethh, 0, ETH_ALEN*2 ); + + ethh->h_proto = htons(priv->ule_sndu_type); } + /* else: skb is in correct state; nothing to do. */ priv->ule_bridged = 0; /* Stuff into kernel's protocol stack. */ @@ -675,8 +750,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) * receive the packet anyhow. */ /* if (priv->ule_dbit && skb->pkt_type == PACKET_OTHERHOST) priv->ule_skb->pkt_type = PACKET_HOST; */ - ((struct dvb_net_priv *) dev->priv)->stats.rx_packets++; - ((struct dvb_net_priv *) dev->priv)->stats.rx_bytes += priv->ule_skb->len; + priv->stats.rx_packets++; + priv->stats.rx_bytes += priv->ule_skb->len; netif_rx(priv->ule_skb); } sndu_done: @@ -951,7 +1026,7 @@ static int dvb_net_feed_start(struct net_device *dev) dprintk("%s: start filtering\n", __FUNCTION__); priv->secfeed->start_filtering(priv->secfeed); } else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) { - struct timespec timeout = { 0, 30000000 }; // 30 msec + struct timespec timeout = { 0, 10000000 }; // 10 msec /* we have payloads encapsulated in TS */ dprintk("%s: alloc tsfeed\n", __FUNCTION__); @@ -963,10 +1038,13 @@ static int dvb_net_feed_start(struct net_device *dev) /* Set netdevice pointer for ts decaps callback. */ priv->tsfeed->priv = (void *)dev; - ret = priv->tsfeed->set(priv->tsfeed, priv->pid, - TS_PACKET, DMX_TS_PES_OTHER, + ret = priv->tsfeed->set(priv->tsfeed, + priv->pid, /* pid */ + TS_PACKET, /* type */ + DMX_TS_PES_OTHER, /* pes type */ 32768, /* circular buffer size */ - timeout); + timeout /* timeout */ + ); if (ret < 0) { printk("%s: could not set ts feed\n", dev->name); -- cgit v1.2.3 From 750b7fc24921cc90350f9fcb3eab4edbaeeb5965 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 2 May 2006 09:02:15 -0300 Subject: Pwc-dec23 oops fix From: akpm@osdl.org drivers/media/video/pwc/pwc-dec23.c: In function `pwc_dec23_decompress': drivers/media/video/pwc/pwc-dec23.c:885: warning: null argument where non-null required (arg 2) Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/pwc/pwc-dec23.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pwc/pwc-dec23.c b/linux/drivers/media/video/pwc/pwc-dec23.c index f9096c72c..39eaf5839 100644 --- a/linux/drivers/media/video/pwc/pwc-dec23.c +++ b/linux/drivers/media/video/pwc/pwc-dec23.c @@ -882,7 +882,7 @@ void pwc_dec23_decompress(const struct pwc_device *pwc, } #else - memcpy(dst, 0, pwc->view.x * pwc->view.y); + memset(dst, 0, pwc->view.x * pwc->view.y); #endif } else { -- cgit v1.2.3 From 91a3da5165321c89bb4a35937d47f19ac53f8899 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 2 May 2006 09:29:34 -0300 Subject: Fix checking logic for a broken xawtv version From: Mauro Carvalho Chehab moved from if to ifndef. Also added such tests at gentree script. Gentree updated to its latest version. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/pwc/pwc-v4l.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pwc/pwc-v4l.c b/linux/drivers/media/video/pwc/pwc-v4l.c index 961ce2ddf..df4bbdb1e 100644 --- a/linux/drivers/media/video/pwc/pwc-v4l.c +++ b/linux/drivers/media/video/pwc/pwc-v4l.c @@ -127,7 +127,7 @@ static struct v4l2_queryctrl pwc_controls[] = { .step = 1, .default_value = 0, }, -#if XAWTV_HAS_BEEN_FIXED +#ifndef BROKEN_XAWTV { .id = V4L2_CID_PRIVATE_SAVE_USER, .type = V4L2_CTRL_TYPE_BUTTON, -- cgit v1.2.3 From b67050b2cbc3429dcde05d2e90ef266743f4540e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 2 May 2006 11:43:05 -0300 Subject: [PATCH] Last DMA_xBIT_MASK cleanups These are the last conversions of pci_set_dma_mask(), pci_set_consistent_dma_mask() and pci_dma_supported() to use DMA_xBIT_MASK constants from linux/dma-mapping.h kernel-sync: Signed-off-by: Tobias Klauser Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- linux/drivers/media/video/saa7134/saa7134-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/saa7134/saa7134-core.c b/linux/drivers/media/video/saa7134/saa7134-core.c index e3433d894..bdaf6c10b 100644 --- a/linux/drivers/media/video/saa7134/saa7134-core.c +++ b/linux/drivers/media/video/saa7134/saa7134-core.c @@ -35,6 +35,7 @@ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include #endif +#include #include "saa7134-reg.h" #include "saa7134.h" @@ -944,7 +945,7 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, pci_name(pci_dev), dev->pci_rev, pci_dev->irq, dev->pci_lat,pci_resource_start(pci_dev,0)); pci_set_master(pci_dev); - if (!pci_dma_supported(pci_dev,0xffffffff)) { + if (!pci_dma_supported(pci_dev, DMA_32BIT_MASK)) { printk("%s: Oops: no 32bit PCI DMA ???\n",dev->name); err = -EIO; goto fail1; -- cgit v1.2.3 From e7db9b9952eddc8ac52a8a902ad1af7b048c8e17 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 2 May 2006 15:44:02 -0300 Subject: Removed obsoleted CVS files From: Mauro Carvalho Chehab Those files were used on cvs tree. they make no sense for hg. kenel-sync: Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/.cvsignore | 1 - linux/drivers/media/dvb/.cvsignore | 1 - 2 files changed, 2 deletions(-) delete mode 100644 linux/drivers/media/.cvsignore delete mode 100644 linux/drivers/media/dvb/.cvsignore (limited to 'linux/drivers') diff --git a/linux/drivers/media/.cvsignore b/linux/drivers/media/.cvsignore deleted file mode 100644 index c095fc3ef..000000000 --- a/linux/drivers/media/.cvsignore +++ /dev/null @@ -1 +0,0 @@ -dvb \ No newline at end of file diff --git a/linux/drivers/media/dvb/.cvsignore b/linux/drivers/media/dvb/.cvsignore deleted file mode 100644 index 72e8ffc0d..000000000 --- a/linux/drivers/media/dvb/.cvsignore +++ /dev/null @@ -1 +0,0 @@ -* -- cgit v1.2.3 From 113182f6138901821b513eaf424f403ae0d7aa23 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 3 May 2006 12:39:08 -0300 Subject: [ALSA] add __devinitdata to all pci_device_id From: Henrik Kretzschmar kernel-sync: Signed-off-by: Henrik Kretzschmar Signed-off-by: Takashi Iwai Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/cx88/cx88-alsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-alsa.c b/linux/drivers/media/video/cx88/cx88-alsa.c index 69d3c3c8f..aff6f4dd3 100644 --- a/linux/drivers/media/video/cx88/cx88-alsa.c +++ b/linux/drivers/media/video/cx88/cx88-alsa.c @@ -710,7 +710,7 @@ static struct snd_kcontrol_new snd_cx88_capture_volume = { * Only boards with eeprom and byte 1 at eeprom=1 have it */ -static struct pci_device_id cx88_audio_pci_tbl[] = { +static struct pci_device_id cx88_audio_pci_tbl[] __devinitdata = { {0x14f1,0x8801,PCI_ANY_ID,PCI_ANY_ID,0,0,0}, {0x14f1,0x8811,PCI_ANY_ID,PCI_ANY_ID,0,0,0}, {0, } -- cgit v1.2.3 From dee79512a9d26c008623ef7b193eb7440567e186 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Wed, 3 May 2006 14:17:31 -0400 Subject: fix build for older gcc's that dont support compiler macros in a function call From: Julian Scheel This patch fixes the build when using older gcc-versions which do not support compiler-macros in a function-call. Signed-off-by: Julian Scheel Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/cx88-i2c.c | 3 ++- linux/drivers/media/video/tuner-core.c | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-i2c.c b/linux/drivers/media/video/cx88/cx88-i2c.c index 1e9bd01de..69a39ad68 100644 --- a/linux/drivers/media/video/cx88/cx88-i2c.c +++ b/linux/drivers/media/video/cx88/cx88-i2c.c @@ -98,10 +98,11 @@ static int attach_inform(struct i2c_client *client) struct tuner_setup tun_setup; struct cx88_core *core = i2c_get_adapdata(client->adapter); - dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n", #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) + dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n", client->driver->name, client->addr, client->name); #else + dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n", client->driver->driver.name, client->addr, client->name); #endif if (!client->driver->command) diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index 00e073944..21bc09dda 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -257,13 +257,15 @@ static void set_type(struct i2c_client *c, unsigned int type, t->mode_mask = new_mode_mask; set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq); - tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n", #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) + tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n", c->adapter->name, c->driver->name, c->addr << 1, type, + t->mode_mask); #else + tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n", c->adapter->name, c->driver->driver.name, c->addr << 1, type, -#endif t->mode_mask); +#endif } /* -- cgit v1.2.3 From 41c8016db3ad2a330bd28bc5304f6ae4408d5cb8 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Thu, 4 May 2006 11:16:41 -0400 Subject: cx88-blackbird: use encoder firmware filename defined in cx2341x.h From: Michael Krufky Since the filename of the cx23416 encoder firmware image is defined in cx2341x.h, we don't need to explicitly define it in cx88-blackbird.c anymore. This changeset removes BLACKBIRD_FIRM_ENC_FILENAME, replacing all occurrances with CX2341X_FIRM_ENC_FILENAME. Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/cx88-blackbird.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index 844115b35..05904b655 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -57,7 +57,6 @@ static LIST_HEAD(cx8802_devlist); /* ------------------------------------------------------------------ */ -#define BLACKBIRD_FIRM_ENC_FILENAME "v4l-cx2341x-enc.fw" #define BLACKBIRD_FIRM_IMAGE_SIZE 256*1024 /* defines below are from ivtv-driver.h */ @@ -582,17 +581,17 @@ static int blackbird_load_firmware(struct cx8802_dev *dev) dprintk(0, "Error with register_write\n"); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) - retval = request_firmware(&firmware, BLACKBIRD_FIRM_ENC_FILENAME, + retval = request_firmware(&firmware, CX2341X_FIRM_ENC_FILENAME, &dev->pci->dev); #else - retval = request_firmware(&firmware, BLACKBIRD_FIRM_ENC_FILENAME, + retval = request_firmware(&firmware, CX2341X_FIRM_ENC_FILENAME, pci_name(dev->pci)); #endif if (retval != 0) { dprintk(0, "ERROR: Hotplug firmware request failed (%s).\n", - BLACKBIRD_FIRM_ENC_FILENAME); + CX2341X_FIRM_ENC_FILENAME); dprintk(0, "Please fix your hotplug setup, the board will " "not work without firmware loaded!\n"); return -1; -- cgit v1.2.3 From 304dfa39df9db3695e21bd87ac2c84819c889359 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Thu, 4 May 2006 16:39:44 +0100 Subject: Fix CI interface on KNC1 DVBT and DVBC cards From: Andrew de Quincey These cards need special handling for CI - reinitialising the frontend device when the CI module is reset. Additionally the tda10021 needs to be set into a different transport stream mode when a CI module is present. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c | 25 ++- linux/drivers/media/dvb/frontends/tda10021.c | 8 + linux/drivers/media/dvb/frontends/tda10021.h | 2 + linux/drivers/media/dvb/ttpci/budget-av.c | 194 +++++++++++++--------- 4 files changed, 153 insertions(+), 76 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c b/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c index 00347a750..2a03bf53c 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c @@ -1060,8 +1060,18 @@ static int dvb_ca_en50221_thread(void *data) break; case DVB_CA_SLOTSTATE_VALIDATE: - if (dvb_ca_en50221_parse_attributes(ca, slot) - != 0) { + if (dvb_ca_en50221_parse_attributes(ca, slot) != 0) { + /* we need this extra check for annoying interfaces like the budget-av */ + if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) && + (ca->pub->poll_slot_status)) { + int status = ca->pub->poll_slot_status(ca->pub, slot, 0); + if (!(status & DVB_CA_EN50221_POLL_CAM_PRESENT)) { + ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE; + dvb_ca_en50221_thread_update_delay(ca); + break; + } + } + printk("dvb_ca adapter %d: Invalid PC card inserted :(\n", ca->dvbdev->adapter->num); ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; @@ -1108,6 +1118,17 @@ static int dvb_ca_en50221_thread(void *data) case DVB_CA_SLOTSTATE_LINKINIT: if (dvb_ca_en50221_link_init(ca, slot) != 0) { + /* we need this extra check for annoying interfaces like the budget-av */ + if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) && + (ca->pub->poll_slot_status)) { + int status = ca->pub->poll_slot_status(ca->pub, slot, 0); + if (!(status & DVB_CA_EN50221_POLL_CAM_PRESENT)) { + ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE; + dvb_ca_en50221_thread_update_delay(ca); + break; + } + } + printk("dvb_ca adapter %d: DVB CAM link initialisation failed :(\n", ca->dvbdev->adapter->num); ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; dvb_ca_en50221_thread_update_delay(ca); diff --git a/linux/drivers/media/dvb/frontends/tda10021.c b/linux/drivers/media/dvb/frontends/tda10021.c index e49cfa027..1950c72fd 100644 --- a/linux/drivers/media/dvb/frontends/tda10021.c +++ b/linux/drivers/media/dvb/frontends/tda10021.c @@ -90,6 +90,14 @@ static int tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data) return (ret != 1) ? -EREMOTEIO : 0; } +int tda10021_write_byte(struct dvb_frontend* fe, int reg, int data) +{ + struct tda10021_state* state = fe->demodulator_priv; + + return tda10021_writereg(state, reg, data); +} +EXPORT_SYMBOL(tda10021_write_byte); + static u8 tda10021_readreg (struct tda10021_state* state, u8 reg) { u8 b0 [] = { reg }; diff --git a/linux/drivers/media/dvb/frontends/tda10021.h b/linux/drivers/media/dvb/frontends/tda10021.h index 3fc338e0e..b1df4259b 100644 --- a/linux/drivers/media/dvb/frontends/tda10021.h +++ b/linux/drivers/media/dvb/frontends/tda10021.h @@ -35,4 +35,6 @@ struct tda10021_config extern struct dvb_frontend* tda10021_attach(const struct tda10021_config* config, struct i2c_adapter* i2c, u8 pwm); +extern int tda10021_write_byte(struct dvb_frontend* fe, int reg, int data); + #endif // TDA10021_H diff --git a/linux/drivers/media/dvb/ttpci/budget-av.c b/linux/drivers/media/dvb/ttpci/budget-av.c index 680041878..3ff67523c 100644 --- a/linux/drivers/media/dvb/ttpci/budget-av.c +++ b/linux/drivers/media/dvb/ttpci/budget-av.c @@ -50,6 +50,12 @@ #define DEBICICAM 0x02420000 +#define SLOTSTATUS_NONE 1 +#define SLOTSTATUS_PRESENT 2 +#define SLOTSTATUS_RESET 4 +#define SLOTSTATUS_READY 8 +#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) + struct budget_av { struct budget budget; struct video_device *vd; @@ -58,8 +64,15 @@ struct budget_av { struct tasklet_struct ciintf_irq_tasklet; int slot_status; struct dvb_ca_en50221 ca; + u8 reinitialise_demod:1; + u8 tda10021_poclkp:1; + u8 tda10021_ts_enabled; + int (*tda10021_set_frontend)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p); }; +static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot); + + /* GPIO Connections: * 0 - Vcc/Reset (Reset is controlled by capacitor). Resets the frontend *AS WELL*! * 1 - CI memory select 0=>IO memory, 1=>Attribute Memory @@ -129,9 +142,10 @@ static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int ad udelay(1); result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 0xfff, 1, 0, 1); - - if (result == -ETIMEDOUT) - budget_av->slot_status = 0; + if (result == -ETIMEDOUT) { + ciintf_slot_shutdown(ca, slot); + printk(KERN_INFO "budget-av: cam ejected 1\n"); + } return result; } @@ -147,9 +161,10 @@ static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int a udelay(1); result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 0xfff, 1, value, 0, 1); - - if (result == -ETIMEDOUT) - budget_av->slot_status = 0; + if (result == -ETIMEDOUT) { + ciintf_slot_shutdown(ca, slot); + printk(KERN_INFO "budget-av: cam ejected 2\n"); + } return result; } @@ -165,9 +180,11 @@ static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 addre udelay(1); result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 3, 1, 0, 0); - - if (result == -ETIMEDOUT) - budget_av->slot_status = 0; + if ((result == -ETIMEDOUT) || ((result == 0xff) && ((address & 3) < 2))) { + ciintf_slot_shutdown(ca, slot); + printk(KERN_INFO "budget-av: cam ejected 3\n"); + return -ETIMEDOUT; + } return result; } @@ -183,9 +200,10 @@ static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 addr udelay(1); result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 3, 1, value, 0, 0); - - if (result == -ETIMEDOUT) - budget_av->slot_status = 0; + if (result == -ETIMEDOUT) { + ciintf_slot_shutdown(ca, slot); + printk(KERN_INFO "budget-av: cam ejected 5\n"); + } return result; } @@ -193,12 +211,12 @@ static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) { struct budget_av *budget_av = (struct budget_av *) ca->data; struct saa7146_dev *saa = budget_av->budget.dev; - int timeout = 50; // 5 seconds (4.4.6 Ready) if (slot != 0) return -EINVAL; dprintk(1, "ciintf_slot_reset\n"); + budget_av->slot_status = SLOTSTATUS_RESET; saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTHI); /* disable card */ @@ -208,20 +226,17 @@ static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) msleep(20); /* 20 ms Vcc settling time */ saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); /* enable card */ + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); + msleep(20); - /* This should have been based on pin 16 READY of the pcmcia port, - * but AFAICS it is not routed to the saa7146 */ - while (--timeout > 0 && ciintf_read_attribute_mem(ca, slot, 0) != 0x1d) - msleep(100); - - /* reinitialise the frontend */ - dvb_frontend_reinitialise(budget_av->budget.dvb_frontend); + /* reinitialise the frontend if necessary */ + if (budget_av->reinitialise_demod) + dvb_frontend_reinitialise(budget_av->budget.dvb_frontend); - if (timeout <= 0) - { - printk(KERN_ERR "budget-av: cam reset failed (timeout).\n"); - saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTHI); /* disable card */ - return -ETIMEDOUT; + /* set tda10021 back to original clock configuration on reset */ + if (budget_av->tda10021_poclkp) { + tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa0); + budget_av->tda10021_ts_enabled = 0; } return 0; @@ -238,7 +253,13 @@ static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) dprintk(1, "ciintf_slot_shutdown\n"); ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); - budget_av->slot_status = 0; + budget_av->slot_status = SLOTSTATUS_NONE; + + /* set tda10021 back to original clock configuration when cam removed */ + if (budget_av->tda10021_poclkp) { + tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa0); + budget_av->tda10021_ts_enabled = 0; + } return 0; } @@ -253,6 +274,13 @@ static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) dprintk(1, "ciintf_slot_ts_enable: %d\n", budget_av->slot_status); ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); + + /* tda10021 seems to need a different TS clock config when data is routed to the CAM */ + if (budget_av->tda10021_poclkp) { + tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa1); + budget_av->tda10021_ts_enabled = 1; + } + return 0; } @@ -260,50 +288,61 @@ static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open { struct budget_av *budget_av = (struct budget_av *) ca->data; struct saa7146_dev *saa = budget_av->budget.dev; - int cam_present = 0; + int result; if (slot != 0) return -EINVAL; - if (!budget_av->slot_status) - { - // first of all test the card detect line + /* test the card detect line - needs to be done carefully + * since it never goes high for some CAMs on this interface (e.g. topuptv) */ + if (budget_av->slot_status == SLOTSTATUS_NONE) { saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); udelay(1); - if (saa7146_read(saa, PSR) & MASK_06) - { - cam_present = 1; + if (saa7146_read(saa, PSR) & MASK_06) { + if (budget_av->slot_status == SLOTSTATUS_NONE) { + budget_av->slot_status = SLOTSTATUS_PRESENT; + printk(KERN_INFO "budget-av: cam inserted A\n"); + } } saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); + } - // that is unreliable however, so try and read from IO memory - if (!cam_present) - { - saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); - if (ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1) != -ETIMEDOUT) - { - cam_present = 1; + /* We also try and read from IO memory to work round the above detection bug. If + * there is no CAM, we will get a timeout. Only done if there is no cam + * present, since this test actually breaks some cams :( + * + * if the CI interface is not open, we also do the above test since we + * don't care if the cam has problems - we'll be resetting it on open() anyway */ + if ((budget_av->slot_status == SLOTSTATUS_NONE) || (!open)) { + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); + result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1); + if ((result >= 0) && (budget_av->slot_status == SLOTSTATUS_NONE)) { + budget_av->slot_status = SLOTSTATUS_PRESENT; + printk(KERN_INFO "budget-av: cam inserted B\n"); + } else if (result < 0) { + if (budget_av->slot_status != SLOTSTATUS_NONE) { + ciintf_slot_shutdown(ca, slot); + printk(KERN_INFO "budget-av: cam ejected 5\n"); + return 0; } } + } - // did we find something? - if (cam_present) { - printk(KERN_INFO "budget-av: cam inserted\n"); - budget_av->slot_status = 1; - } - } else if (!open) { - saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); - if (ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1) == -ETIMEDOUT) - { - printk(KERN_INFO "budget-av: cam ejected\n"); - saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTHI); /* disable card */ - budget_av->slot_status = 0; + /* read from attribute memory in reset/ready state to know when the CAM is ready */ + if (budget_av->slot_status == SLOTSTATUS_RESET) { + result = ciintf_read_attribute_mem(ca, slot, 0); + if (result == 0x1d) { + budget_av->slot_status = SLOTSTATUS_READY; } } - if (budget_av->slot_status == 1) - return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; - + /* work out correct return code */ + if (budget_av->slot_status != SLOTSTATUS_NONE) { + if (budget_av->slot_status & SLOTSTATUS_READY) { + return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; + } + return DVB_CA_EN50221_POLL_CAM_PRESENT; + } return 0; } @@ -333,6 +372,8 @@ static int ciintf_init(struct budget_av *budget_av) budget_av->ca.slot_ts_enable = ciintf_slot_ts_enable; budget_av->ca.poll_slot_status = ciintf_poll_slot_status; budget_av->ca.data = budget_av; + budget_av->budget.ci_present = 1; + budget_av->slot_status = SLOTSTATUS_NONE; if ((result = dvb_ca_en50221_init(&budget_av->budget.dvb_adapter, &budget_av->ca, 0, 1)) != 0) { @@ -341,7 +382,6 @@ static int ciintf_init(struct budget_av *budget_av) } printk(KERN_INFO "budget-av: ci interface initialised.\n"); - budget_av->budget.ci_present = 1; return 0; error: @@ -976,7 +1016,7 @@ static int philips_sd1878_ci_set_symbol_rate(struct dvb_frontend *fe, static struct stv0299_config philips_sd1878_config = { .demod_address = 0x68, - .inittab = philips_sd1878_inittab, + .inittab = philips_sd1878_inittab, .mclk = 88000000UL, .invert = 0, .skip_reinit = 0, @@ -1018,6 +1058,23 @@ static u8 read_pwm(struct budget_av *budget_av) #define SUBID_DVBT_KNC1 0x0030 #define SUBID_DVBT_CINERGY1200 0x1157 + +static int tda10021_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct budget_av* budget_av = fe->dvb->priv; + int result; + + result = budget_av->tda10021_set_frontend(fe, p); + if (budget_av->tda10021_ts_enabled) { + tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa1); + } else { + tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa0); + } + + return result; +} + static void frontend_init(struct budget_av *budget_av) { struct saa7146_dev * saa = budget_av->budget.dev; @@ -1083,34 +1140,23 @@ static void frontend_init(struct budget_av *budget_av) case SUBID_DVBC_KNC1: case SUBID_DVBC_KNC1_PLUS: + case SUBID_DVBC_CINERGY1200: + budget_av->reinitialise_demod = 1; fe = tda10021_attach(&philips_cu1216_config, &budget_av->budget.i2c_adap, read_pwm(budget_av)); if (fe) { + budget_av->tda10021_poclkp = 1; + budget_av->tda10021_set_frontend = fe->ops->set_frontend; + fe->ops->set_frontend = tda10021_set_frontend; fe->ops->tuner_ops.set_params = philips_cu1216_tuner_set_params; } break; case SUBID_DVBT_KNC1: case SUBID_DVBT_KNC1_PLUS: - fe = tda10046_attach(&philips_tu1216_config, - &budget_av->budget.i2c_adap); - if (fe) { - fe->ops->tuner_ops.init = philips_tu1216_tuner_init; - fe->ops->tuner_ops.set_params = philips_tu1216_tuner_set_params; - } - break; - - case SUBID_DVBC_CINERGY1200: - fe = tda10021_attach(&philips_cu1216_config, - &budget_av->budget.i2c_adap, - read_pwm(budget_av)); - if (fe) { - fe->ops->tuner_ops.set_params = philips_cu1216_tuner_set_params; - } - break; - case SUBID_DVBT_CINERGY1200: + budget_av->reinitialise_demod = 1; fe = tda10046_attach(&philips_tu1216_config, &budget_av->budget.i2c_adap); if (fe) { -- cgit v1.2.3 From 2c385b02114906a6056069225cc771449eaa7e5e Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Thu, 4 May 2006 12:49:41 -0400 Subject: pvrusb2: use encoder firmware filename defined in cx2341x.h From: Michael Krufky This patch removes the explicit definition of the cx23416 encoder firmware image filename, in favor of CX2341X_FIRM_ENC_FILENAME as defined in cx2341x.h Signed-off-by: Michael Krufky Acked-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 890aa4a06..333419d6f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "pvrusb2.h" #include "pvrusb2-std.h" #include "pvrusb2-util.h" @@ -943,7 +944,7 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) int ret = 0; int fwidx; static const char *fw_files[] = { - "v4l-cx2341x-enc.fw", + CX2341X_FIRM_ENC_FILENAME, }; trace_firmware("pvr2_upload_firmware2"); -- cgit v1.2.3 From b4d9770fbc462b4f10ebbf8950e94654309352c2 Mon Sep 17 00:00:00 2001 From: Manu Abraham Date: Fri, 5 May 2006 22:24:46 +0400 Subject: Add frontend support for new Delivery systems and modulations -DVB-S2, 8PSK and higher order modulations, some advanced DVB-S2 features require additional support at the demux level -DVB-T, Add support for HP/LP stream selection -DVB-H, Add basic support to the frontend API -DSS, basic frontend API support only, will require additional support at the demux level Ack'd by: Marcel Siegert Acked-by: Andrew de Quincey Ack'd by: Patrick Boettcher Signed-off-by: Manu Abraham --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 80 ++++++++++++++++++++++++- linux/drivers/media/dvb/dvb-core/dvb_frontend.h | 9 ++- 2 files changed, 86 insertions(+), 3 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index bc752929f..5d980ee9d 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -94,6 +94,8 @@ struct dvb_frontend_private { /* thread/frontend values */ struct dvb_device *dvbdev; struct dvb_frontend_parameters parameters; + struct dvb_frontend_params params; + struct dvb_frontend_cap caps; struct dvb_fe_events events; struct semaphore sem; struct list_head list_head; @@ -124,6 +126,62 @@ struct dvb_frontend_private { unsigned int check_wrapped; }; +struct modcod_table { + __u32 modcod; + __u32 modulation; + __u32 fecrate; +}; + +struct modcod_table dvbs2_modcod_lookup[] = { + { FE_MODCOD_DUMMY_PLFRAME, FE_MOD_NONE, FE_FECRATE_NONE }, + { FE_MODCOD_QPSK_1_4, FE_MOD_QPSK, FE_FECRATE_1_4 }, + { FE_MODCOD_QPSK_1_3, FE_MOD_QPSK, FE_FECRATE_1_3 }, + { FE_MODCOD_QPSK_2_5, FE_MOD_QPSK, FE_FECRATE_2_5 }, + { FE_MODCOD_QPSK_1_2, FE_MOD_QPSK, FE_FECRATE_1_2 }, + { FE_MODCOD_QPSK_3_5, FE_MOD_QPSK, FE_FECRATE_3_5 }, + { FE_MODCOD_QPSK_2_3, FE_MOD_QPSK, FE_FECRATE_2_3 }, + { FE_MODCOD_QPSK_3_4, FE_MOD_QPSK, FE_FECRATE_3_4 }, + { FE_MODCOD_QPSK_4_5, FE_MOD_QPSK, FE_FECRATE_4_5 }, + { FE_MODCOD_QPSK_5_6, FE_MOD_QPSK, FE_FECRATE_5_6 }, + { FE_MODCOD_QPSK_8_9, FE_MOD_QPSK, FE_FECRATE_8_9 }, + { FE_MODCOD_QPSK_9_10, FE_MOD_QPSK, FE_FECRATE_9_10 }, + { FE_MODCOD_8PSK_3_5, FE_MOD_8PSK, FE_FECRATE_3_5 }, + { FE_MODCOD_8PSK_2_3, FE_MOD_8PSK, FE_FECRATE_2_3 }, + { FE_MODCOD_8PSK_3_4, FE_MOD_8PSK, FE_FECRATE_3_4 }, + { FE_MODCOD_8PSK_5_6, FE_MOD_8PSK, FE_FECRATE_5_6 }, + { FE_MODCOD_8PSK_8_9, FE_MOD_8PSK, FE_FECRATE_8_9 }, + { FE_MODCOD_8PSK_9_10, FE_MOD_8PSK, FE_FECRATE_9_10 }, + { FE_MODCOD_16APSK_2_3, FE_MOD_16APSK, FE_FECRATE_2_3 }, + { FE_MODCOD_16APSK_3_4, FE_MOD_16APSK, FE_FECRATE_3_4 }, + { FE_MODCOD_16APSK_4_5, FE_MOD_16APSK, FE_FECRATE_4_5 }, + { FE_MODCOD_16APSK_5_6, FE_MOD_16APSK, FE_FECRATE_5_6 }, + { FE_MODCOD_16APSK_8_9, FE_MOD_16APSK, FE_FECRATE_8_9 }, + { FE_MODCOD_16APSK_9_10, FE_MOD_16APSK, FE_FECRATE_9_10 }, + { FE_MODCOD_32APSK_3_4, FE_MOD_32APSK, FE_FECRATE_3_4 }, + { FE_MODCOD_32APSK_4_5, FE_MOD_32APSK, FE_FECRATE_4_5 }, + { FE_MODCOD_32APSK_5_6, FE_MOD_32APSK, FE_FECRATE_5_6 }, + { FE_MODCOD_32APSK_8_9, FE_MOD_32APSK, FE_FECRATE_8_9 }, + { FE_MODCOD_32APSK_9_10, FE_MOD_32APSK, FE_FECRATE_9_10 }, + { FE_MODCOD_RESERVED_1, FE_MOD_RSVD, FE_FECRATE_RSVD }, + { FE_MODCOD_BPSK_1_3, FE_MOD_BPSK, FE_FECRATE_1_3 }, + { FE_MODCOD_BPSK_1_4, FE_MOD_BPSK, FE_FECRATE_1_4 }, + { FE_MODCOD_RESERVED_2, FE_MOD_RSVD, FE_FECRATE_RSVD } +}; + +int decode_dvbs2_modcod(struct dvb_frontend *fe, enum fe_modcod modcod) +{ + struct modcod_table *table = dvbs2_modcod_lookup; + struct dvb_frontend_private *fepriv = fe->frontend_priv; + struct dvb_frontend_params *params = &fepriv->params; + + table += modcod; + params->delsys.dvbs2.modulation = table->modulation; + params->delsys.dvbs2.fecrate = table->fecrate; + + return 0; +} +EXPORT_SYMBOL(decode_dvbs2_modcod); + static void dvb_frontend_wakeup(struct dvb_frontend *fe); static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status) @@ -508,7 +566,7 @@ static int dvb_frontend_thread(void *data) unsigned long timeout; char name [15]; fe_status_t s; - struct dvb_frontend_parameters *params; + struct dvb_frontend_params *params; dprintk("%s\n", __FUNCTION__); @@ -564,7 +622,7 @@ static int dvb_frontend_thread(void *data) /* have we been asked to retune? */ params = NULL; if (fepriv->state & FESTATE_RETUNE) { - params = &fepriv->parameters; + params = &fepriv->params; fepriv->state = FESTATE_TUNED; } @@ -977,6 +1035,24 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, case FE_SET_FRONTEND_TUNE_MODE: fepriv->tune_mode_flags = (unsigned long) parg; break; + + case FE_GET_CAPS: + if (fe->ops->get_caps) { + memcpy(parg, &fepriv->caps, sizeof (struct dvb_frontend_cap)); + err = fe->ops->get_caps(fe, (struct dvb_frontend_cap*) parg); + } + break; + + case FE_SET_PARAMS: + if (fe->ops->set_params) + err = fe->ops->set_params(fe, (struct dvb_frontend_params*) parg); + break; + + case FE_GET_PARAMS: + if (fe->ops->get_params){ + memcpy(parg, &fepriv->params, sizeof (struct dvb_frontend_params)); + err = fe->ops->get_params(fe, (struct dvb_frontend_params*) parg); + } }; up (&fepriv->sem); diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h index 05ec9954b..4d1b45be0 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -98,7 +98,7 @@ struct dvb_frontend_ops { /* if this is set, it overrides the default swzigzag */ int (*tune)(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params, + struct dvb_frontend_params* params, unsigned int mode_flags, int *delay, fe_status_t *status); @@ -125,6 +125,11 @@ struct dvb_frontend_ops { int (*dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd); int (*i2c_gate_ctrl)(struct dvb_frontend* fe, int enable); + /* New callbacks based on the new IOCTL's */ + int (*get_caps)(struct dvb_frontend* fe, struct dvb_frontend_cap* caps); + int (*set_params)(struct dvb_frontend* fe, struct dvb_frontend_params* params); + int (*get_params)(struct dvb_frontend* fe, struct dvb_frontend_params* params); + struct dvb_tuner_ops tuner_ops; }; @@ -158,4 +163,6 @@ extern void dvb_frontend_reinitialise(struct dvb_frontend *fe); extern void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec); extern s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime); +extern int decode_dvbs2_modcod(struct dvb_frontend *fe, enum fe_modcod modcod); + #endif -- cgit v1.2.3 From 9af6ebbba1b633bd0a5636df1c4645b72feb7ad2 Mon Sep 17 00:00:00 2001 From: Manu Abraham Date: Sat, 6 May 2006 02:12:19 +0400 Subject: Add math routines required by DVB demods From: Christoph Pfister Signed-off-by: Christoph Pfister Signed-off-by: Manu Abraham --- linux/drivers/media/dvb/dvb-core/Makefile | 6 +- linux/drivers/media/dvb/dvb-core/dvb_math.c | 144 ++++++++++++++++++++++++++++ linux/drivers/media/dvb/dvb-core/dvb_math.h | 58 +++++++++++ 3 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 linux/drivers/media/dvb/dvb-core/dvb_math.c create mode 100644 linux/drivers/media/dvb/dvb-core/dvb_math.h (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-core/Makefile b/linux/drivers/media/dvb/dvb-core/Makefile index 7adb50c1e..11054657f 100644 --- a/linux/drivers/media/dvb/dvb-core/Makefile +++ b/linux/drivers/media/dvb/dvb-core/Makefile @@ -2,8 +2,8 @@ # Makefile for the kernel DVB device drivers. # -dvb-core-objs = dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o \ - dvb_ca_en50221.o dvb_frontend.o \ - dvb_net.o dvb_ringbuffer.o +dvb-core-objs = dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o \ + dvb_ca_en50221.o dvb_frontend.o \ + dvb_net.o dvb_ringbuffer.o dvb_math.o obj-$(CONFIG_DVB_CORE) += dvb-core.o diff --git a/linux/drivers/media/dvb/dvb-core/dvb_math.c b/linux/drivers/media/dvb/dvb-core/dvb_math.c new file mode 100644 index 000000000..db6d6b7f7 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/dvb_math.c @@ -0,0 +1,144 @@ +/* + * dvb-math provides some complex fixed-point math + * operations shared between the dvb related stuff + * + * Copyright (C) 2006 Christoph Pfister (christophpfister@gmail.com) + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include "dvb_math.h" + +const unsigned short logtable[256] = { + 0x0000, 0x0171, 0x02e0, 0x044e, 0x05ba, 0x0725, 0x088e, 0x09f7, + 0x0b5d, 0x0cc3, 0x0e27, 0x0f8a, 0x10eb, 0x124b, 0x13aa, 0x1508, + 0x1664, 0x17bf, 0x1919, 0x1a71, 0x1bc8, 0x1d1e, 0x1e73, 0x1fc6, + 0x2119, 0x226a, 0x23ba, 0x2508, 0x2656, 0x27a2, 0x28ed, 0x2a37, + 0x2b80, 0x2cc8, 0x2e0f, 0x2f54, 0x3098, 0x31dc, 0x331e, 0x345f, + 0x359f, 0x36de, 0x381b, 0x3958, 0x3a94, 0x3bce, 0x3d08, 0x3e41, + 0x3f78, 0x40af, 0x41e4, 0x4319, 0x444c, 0x457f, 0x46b0, 0x47e1, + 0x4910, 0x4a3f, 0x4b6c, 0x4c99, 0x4dc5, 0x4eef, 0x5019, 0x5142, + 0x526a, 0x5391, 0x54b7, 0x55dc, 0x5700, 0x5824, 0x5946, 0x5a68, + 0x5b89, 0x5ca8, 0x5dc7, 0x5ee5, 0x6003, 0x611f, 0x623a, 0x6355, + 0x646f, 0x6588, 0x66a0, 0x67b7, 0x68ce, 0x69e4, 0x6af8, 0x6c0c, + 0x6d20, 0x6e32, 0x6f44, 0x7055, 0x7165, 0x7274, 0x7383, 0x7490, + 0x759d, 0x76aa, 0x77b5, 0x78c0, 0x79ca, 0x7ad3, 0x7bdb, 0x7ce3, + 0x7dea, 0x7ef0, 0x7ff6, 0x80fb, 0x81ff, 0x8302, 0x8405, 0x8507, + 0x8608, 0x8709, 0x8809, 0x8908, 0x8a06, 0x8b04, 0x8c01, 0x8cfe, + 0x8dfa, 0x8ef5, 0x8fef, 0x90e9, 0x91e2, 0x92db, 0x93d2, 0x94ca, + 0x95c0, 0x96b6, 0x97ab, 0x98a0, 0x9994, 0x9a87, 0x9b7a, 0x9c6c, + 0x9d5e, 0x9e4f, 0x9f3f, 0xa02e, 0xa11e, 0xa20c, 0xa2fa, 0xa3e7, + 0xa4d4, 0xa5c0, 0xa6ab, 0xa796, 0xa881, 0xa96a, 0xaa53, 0xab3c, + 0xac24, 0xad0c, 0xadf2, 0xaed9, 0xafbe, 0xb0a4, 0xb188, 0xb26c, + 0xb350, 0xb433, 0xb515, 0xb5f7, 0xb6d9, 0xb7ba, 0xb89a, 0xb97a, + 0xba59, 0xbb38, 0xbc16, 0xbcf4, 0xbdd1, 0xbead, 0xbf8a, 0xc065, + 0xc140, 0xc21b, 0xc2f5, 0xc3cf, 0xc4a8, 0xc580, 0xc658, 0xc730, + 0xc807, 0xc8de, 0xc9b4, 0xca8a, 0xcb5f, 0xcc34, 0xcd08, 0xcddc, + 0xceaf, 0xcf82, 0xd054, 0xd126, 0xd1f7, 0xd2c8, 0xd399, 0xd469, + 0xd538, 0xd607, 0xd6d6, 0xd7a4, 0xd872, 0xd93f, 0xda0c, 0xdad9, + 0xdba5, 0xdc70, 0xdd3b, 0xde06, 0xded0, 0xdf9a, 0xe063, 0xe12c, + 0xe1f5, 0xe2bd, 0xe385, 0xe44c, 0xe513, 0xe5d9, 0xe69f, 0xe765, + 0xe82a, 0xe8ef, 0xe9b3, 0xea77, 0xeb3b, 0xebfe, 0xecc1, 0xed83, + 0xee45, 0xef06, 0xefc8, 0xf088, 0xf149, 0xf209, 0xf2c8, 0xf387, + 0xf446, 0xf505, 0xf5c3, 0xf680, 0xf73e, 0xf7fb, 0xf8b7, 0xf973, + 0xfa2f, 0xfaea, 0xfba5, 0xfc60, 0xfd1a, 0xfdd4, 0xfe8e, 0xff47 +}; + +unsigned int intlog2(u32 value) +{ + /** + * returns: log2(value) * 2^24 + * wrong result if value = 0 (log2(0) is undefined) + */ + unsigned int msb; + unsigned int logentry; + unsigned int significand; + unsigned int interpolation; + + if (unlikely(value == 0)) { + WARN_ON(1); + return 0; + } + + /* first detect the msb (count begins at 0) */ + msb = fls(value) - 1; + + /** + * now we use a logtable after the following method: + * + * log2(2^x * y) * 2^24 = x * 2^24 + log2(y) * 2^24 + * where x = msb and therefore 1 <= y < 2 + * first y is determined by shifting the value left + * so that msb is bit 31 + * 0x00231f56 -> 0x8C7D5800 + * the result is y * 2^31 -> "significand" + * then the highest 9 bits are used for a table lookup + * the highest bit is discarded because it's always set + * the highest nine bits in our example are 100011000 + * so we would use the entry 0x18 + */ + significand = value << (31 - msb); + logentry = (significand >> 23) & 0xff; + + /** + * last step we do is interpolation because of the + * limitations of the log table the error is that part of + * the significand which isn't used for lookup then we + * compute the ratio between the error and the next table entry + * and interpolate it between the log table entry used and the + * next one the biggest error possible is 0x7fffff + * (in our example it's 0x7D5800) + * needed value for next table entry is 0x800000 + * so the interpolation is + * (error / 0x800000) * (logtable_next - logtable_current) + * in the implementation the division is moved to the end for + * better accuracy there is also an overflow correction if + * logtable_next is 256 + */ + interpolation = ((significand & 0x7fffff) * + ((logtable[(logentry + 1) & 0xff] - + logtable[logentry]) & 0xffff)) >> 15; + + /* now we return the result */ + return ((msb << 24) + (logtable[logentry] << 8) + interpolation); +} +EXPORT_SYMBOL(intlog2); + +unsigned int intlog10(u32 value) +{ + /** + * returns: log10(value) * 2^24 + * wrong result if value = 0 (log10(0) is undefined) + */ + u64 log; + + if (unlikely(value == 0)) { + WARN_ON(1); + return 0; + } + + log = intlog2(value); + + /** + * we use the following method: + * log10(x) = log2(x) * log10(2) + */ + + return (log * 646456993) >> 31; +} +EXPORT_SYMBOL(intlog10); diff --git a/linux/drivers/media/dvb/dvb-core/dvb_math.h b/linux/drivers/media/dvb/dvb-core/dvb_math.h new file mode 100644 index 000000000..aecc867e9 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/dvb_math.h @@ -0,0 +1,58 @@ +/* + * dvb-math provides some complex fixed-point math + * operations shared between the dvb related stuff + * + * Copyright (C) 2006 Christoph Pfister (christophpfister@gmail.com) + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __DVB_MATH_H +#define __DVB_MATH_H + +#include + +/** + * computes log2 of a value; the result is shifted left by 24 bits + * + * to use rational values you can use the following method: + * intlog2(value) = intlog2(value * 2^x) - x * 2^24 + * + * example: intlog2(8) will give 3 << 24 = 3 * 2^24 + * example: intlog2(9) will give 3 << 24 + ... = 3.16... * 2^24 + * example: intlog2(1.5) = intlog2(3) - 2^24 = 0.584... * 2^24 + * + * @param value The value (must be != 0) + * @return log2(value) * 2^24 + */ +extern unsigned int intlog2(u32 value); + +/** + * computes log10 of a value; the result is shifted left by 24 bits + * + * to use rational values you can use the following method: + * intlog10(value) = intlog10(value * 10^x) - x * 2^24 + * + * example: intlog10(1000) will give 3 << 24 = 3 * 2^24 + * due to the implementation intlog10(1000) might be not exactly 3 * 2^24 + * + * look at intlog2 for similar examples + * + * @param value The value (must be != 0) + * @return log10(value) * 2^24 + */ +extern unsigned int intlog10(u32 value); + +#endif -- cgit v1.2.3 From 5fa5a296f99947e67379995c284facd6c4e78bcc Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 6 May 2006 11:22:23 -0400 Subject: cx88: add autodetection for another Twinhan VP-3054 board. From: Michael Krufky Added autodetection for PCI subsystem id 1822:0019 to use the card definition for CX88_BOARD_DNTV_LIVE_DVB_T_PRO Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/cx88-cards.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index 05e2bc403..89347103a 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -1384,6 +1384,10 @@ struct cx88_subid cx88_subids[] = { .subvendor = 0x17de, .subdevice = 0x0841, .card = CX88_BOARD_KWORLD_MCE200_DELUXE, + },{ + .subvendor = 0x1822, + .subdevice = 0x0019, + .card = CX88_BOARD_DNTV_LIVE_DVB_T_PRO, }, }; const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids); -- cgit v1.2.3 From b6e187a6550c6685b2e52b2d37053ab3dfe7c2cd Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 7 May 2006 14:29:46 -0400 Subject: bug-fix: pvrusb2 must #include for compile-time options From: Michael Krufky Added necessary header file in order for kconfig build options to be considered at compile time. Without this patch all options will always be disabled, regardless of whether they are selected or not. Signed-off-by: Michael Krufky Acked-by: Mike Isely --- linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h | 1 + linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c | 1 + 2 files changed, 2 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index ff18fc548..0d527abb5 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -34,6 +34,7 @@ */ #include "compat.h" +#include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 3bf8f166c..075882877 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -19,6 +19,7 @@ * */ +#include #include #include #include -- cgit v1.2.3 From 9a5a7b6c35191d8c0681eba05fa6b2d0e63c4583 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 7 May 2006 19:10:00 -0300 Subject: revert manu's multiproto patch by his ask. From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 80 +------------------------ linux/drivers/media/dvb/dvb-core/dvb_frontend.h | 9 +-- 2 files changed, 3 insertions(+), 86 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 5d980ee9d..bc752929f 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -94,8 +94,6 @@ struct dvb_frontend_private { /* thread/frontend values */ struct dvb_device *dvbdev; struct dvb_frontend_parameters parameters; - struct dvb_frontend_params params; - struct dvb_frontend_cap caps; struct dvb_fe_events events; struct semaphore sem; struct list_head list_head; @@ -126,62 +124,6 @@ struct dvb_frontend_private { unsigned int check_wrapped; }; -struct modcod_table { - __u32 modcod; - __u32 modulation; - __u32 fecrate; -}; - -struct modcod_table dvbs2_modcod_lookup[] = { - { FE_MODCOD_DUMMY_PLFRAME, FE_MOD_NONE, FE_FECRATE_NONE }, - { FE_MODCOD_QPSK_1_4, FE_MOD_QPSK, FE_FECRATE_1_4 }, - { FE_MODCOD_QPSK_1_3, FE_MOD_QPSK, FE_FECRATE_1_3 }, - { FE_MODCOD_QPSK_2_5, FE_MOD_QPSK, FE_FECRATE_2_5 }, - { FE_MODCOD_QPSK_1_2, FE_MOD_QPSK, FE_FECRATE_1_2 }, - { FE_MODCOD_QPSK_3_5, FE_MOD_QPSK, FE_FECRATE_3_5 }, - { FE_MODCOD_QPSK_2_3, FE_MOD_QPSK, FE_FECRATE_2_3 }, - { FE_MODCOD_QPSK_3_4, FE_MOD_QPSK, FE_FECRATE_3_4 }, - { FE_MODCOD_QPSK_4_5, FE_MOD_QPSK, FE_FECRATE_4_5 }, - { FE_MODCOD_QPSK_5_6, FE_MOD_QPSK, FE_FECRATE_5_6 }, - { FE_MODCOD_QPSK_8_9, FE_MOD_QPSK, FE_FECRATE_8_9 }, - { FE_MODCOD_QPSK_9_10, FE_MOD_QPSK, FE_FECRATE_9_10 }, - { FE_MODCOD_8PSK_3_5, FE_MOD_8PSK, FE_FECRATE_3_5 }, - { FE_MODCOD_8PSK_2_3, FE_MOD_8PSK, FE_FECRATE_2_3 }, - { FE_MODCOD_8PSK_3_4, FE_MOD_8PSK, FE_FECRATE_3_4 }, - { FE_MODCOD_8PSK_5_6, FE_MOD_8PSK, FE_FECRATE_5_6 }, - { FE_MODCOD_8PSK_8_9, FE_MOD_8PSK, FE_FECRATE_8_9 }, - { FE_MODCOD_8PSK_9_10, FE_MOD_8PSK, FE_FECRATE_9_10 }, - { FE_MODCOD_16APSK_2_3, FE_MOD_16APSK, FE_FECRATE_2_3 }, - { FE_MODCOD_16APSK_3_4, FE_MOD_16APSK, FE_FECRATE_3_4 }, - { FE_MODCOD_16APSK_4_5, FE_MOD_16APSK, FE_FECRATE_4_5 }, - { FE_MODCOD_16APSK_5_6, FE_MOD_16APSK, FE_FECRATE_5_6 }, - { FE_MODCOD_16APSK_8_9, FE_MOD_16APSK, FE_FECRATE_8_9 }, - { FE_MODCOD_16APSK_9_10, FE_MOD_16APSK, FE_FECRATE_9_10 }, - { FE_MODCOD_32APSK_3_4, FE_MOD_32APSK, FE_FECRATE_3_4 }, - { FE_MODCOD_32APSK_4_5, FE_MOD_32APSK, FE_FECRATE_4_5 }, - { FE_MODCOD_32APSK_5_6, FE_MOD_32APSK, FE_FECRATE_5_6 }, - { FE_MODCOD_32APSK_8_9, FE_MOD_32APSK, FE_FECRATE_8_9 }, - { FE_MODCOD_32APSK_9_10, FE_MOD_32APSK, FE_FECRATE_9_10 }, - { FE_MODCOD_RESERVED_1, FE_MOD_RSVD, FE_FECRATE_RSVD }, - { FE_MODCOD_BPSK_1_3, FE_MOD_BPSK, FE_FECRATE_1_3 }, - { FE_MODCOD_BPSK_1_4, FE_MOD_BPSK, FE_FECRATE_1_4 }, - { FE_MODCOD_RESERVED_2, FE_MOD_RSVD, FE_FECRATE_RSVD } -}; - -int decode_dvbs2_modcod(struct dvb_frontend *fe, enum fe_modcod modcod) -{ - struct modcod_table *table = dvbs2_modcod_lookup; - struct dvb_frontend_private *fepriv = fe->frontend_priv; - struct dvb_frontend_params *params = &fepriv->params; - - table += modcod; - params->delsys.dvbs2.modulation = table->modulation; - params->delsys.dvbs2.fecrate = table->fecrate; - - return 0; -} -EXPORT_SYMBOL(decode_dvbs2_modcod); - static void dvb_frontend_wakeup(struct dvb_frontend *fe); static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status) @@ -566,7 +508,7 @@ static int dvb_frontend_thread(void *data) unsigned long timeout; char name [15]; fe_status_t s; - struct dvb_frontend_params *params; + struct dvb_frontend_parameters *params; dprintk("%s\n", __FUNCTION__); @@ -622,7 +564,7 @@ static int dvb_frontend_thread(void *data) /* have we been asked to retune? */ params = NULL; if (fepriv->state & FESTATE_RETUNE) { - params = &fepriv->params; + params = &fepriv->parameters; fepriv->state = FESTATE_TUNED; } @@ -1035,24 +977,6 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, case FE_SET_FRONTEND_TUNE_MODE: fepriv->tune_mode_flags = (unsigned long) parg; break; - - case FE_GET_CAPS: - if (fe->ops->get_caps) { - memcpy(parg, &fepriv->caps, sizeof (struct dvb_frontend_cap)); - err = fe->ops->get_caps(fe, (struct dvb_frontend_cap*) parg); - } - break; - - case FE_SET_PARAMS: - if (fe->ops->set_params) - err = fe->ops->set_params(fe, (struct dvb_frontend_params*) parg); - break; - - case FE_GET_PARAMS: - if (fe->ops->get_params){ - memcpy(parg, &fepriv->params, sizeof (struct dvb_frontend_params)); - err = fe->ops->get_params(fe, (struct dvb_frontend_params*) parg); - } }; up (&fepriv->sem); diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h index 4d1b45be0..05ec9954b 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -98,7 +98,7 @@ struct dvb_frontend_ops { /* if this is set, it overrides the default swzigzag */ int (*tune)(struct dvb_frontend* fe, - struct dvb_frontend_params* params, + struct dvb_frontend_parameters* params, unsigned int mode_flags, int *delay, fe_status_t *status); @@ -125,11 +125,6 @@ struct dvb_frontend_ops { int (*dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd); int (*i2c_gate_ctrl)(struct dvb_frontend* fe, int enable); - /* New callbacks based on the new IOCTL's */ - int (*get_caps)(struct dvb_frontend* fe, struct dvb_frontend_cap* caps); - int (*set_params)(struct dvb_frontend* fe, struct dvb_frontend_params* params); - int (*get_params)(struct dvb_frontend* fe, struct dvb_frontend_params* params); - struct dvb_tuner_ops tuner_ops; }; @@ -163,6 +158,4 @@ extern void dvb_frontend_reinitialise(struct dvb_frontend *fe); extern void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec); extern s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime); -extern int decode_dvbs2_modcod(struct dvb_frontend *fe, enum fe_modcod modcod); - #endif -- cgit v1.2.3 From f778635ea05c9a95f8f1c3df6e6348a2980ffe97 Mon Sep 17 00:00:00 2001 From: Manu Abraham Date: Mon, 8 May 2006 23:06:32 +0400 Subject: Add missing include From: Manu Abraham Signed-off-by: Manu Abraham --- linux/drivers/media/dvb/dvb-core/dvb_math.c | 1 + 1 file changed, 1 insertion(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_math.c b/linux/drivers/media/dvb/dvb-core/dvb_math.c index db6d6b7f7..b201dbfc1 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_math.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_math.c @@ -21,6 +21,7 @@ #include #include +#include #include #include "dvb_math.h" -- cgit v1.2.3 From a55bfded81a0bffe5d07618b1983ad4abc6c2fbb Mon Sep 17 00:00:00 2001 From: Manu Abraham Date: Mon, 8 May 2006 23:14:56 +0400 Subject: make the table static From: Christoph Pfister Signed-off-by: Christoph Pfister Signed-off-by: Manu Abraham --- linux/drivers/media/dvb/dvb-core/dvb_math.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_math.c b/linux/drivers/media/dvb/dvb-core/dvb_math.c index b201dbfc1..beb7c93aa 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_math.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_math.c @@ -25,7 +25,7 @@ #include #include "dvb_math.h" -const unsigned short logtable[256] = { +static const unsigned short logtable[256] = { 0x0000, 0x0171, 0x02e0, 0x044e, 0x05ba, 0x0725, 0x088e, 0x09f7, 0x0b5d, 0x0cc3, 0x0e27, 0x0f8a, 0x10eb, 0x124b, 0x13aa, 0x1508, 0x1664, 0x17bf, 0x1919, 0x1a71, 0x1bc8, 0x1d1e, 0x1e73, 0x1fc6, -- cgit v1.2.3 From 977f86a2e6d0ba8ccc854f6b5eb2a2b88f9a0e22 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 8 May 2006 21:05:18 -0300 Subject: Compat.h were missing in vivi.c. Fixed. From: Mauro Carvalho Chehab Without compat.h, it were not compiling for kernel <2.6.15 kernel-sync Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/vivi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/vivi.c b/linux/drivers/media/video/vivi.c index 170712515..7da5487f2 100644 --- a/linux/drivers/media/video/vivi.c +++ b/linux/drivers/media/video/vivi.c @@ -25,6 +25,8 @@ #include #include #include +#include "compat.h" + #include #include #ifdef CONFIG_VIDEO_V4L1_COMPAT -- cgit v1.2.3 From a3a9bd1f37aac1eac329c68dfe544ad560e53eb8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 9 May 2006 10:15:42 -0300 Subject: Cx88 NTSC VBI fixes From: Michael Schimek Hi, VBI (Closed Caption, CC) capturing never worked in NTSC mode with the cx88 driver. They were tested with libzvbi and a patched version of tvtime. Signed-off-by: Michael H. Schimek Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/cx88/cx88-core.c | 2 +- linux/drivers/media/video/cx88/cx88-vbi.c | 4 ++-- linux/drivers/media/video/cx88/cx88.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-core.c b/linux/drivers/media/video/cx88/cx88-core.c index b657894e8..9d7574ac2 100644 --- a/linux/drivers/media/video/cx88/cx88-core.c +++ b/linux/drivers/media/video/cx88/cx88-core.c @@ -717,7 +717,7 @@ static unsigned int inline norm_htotal(struct cx88_tvnorm *norm) static unsigned int inline norm_vbipack(struct cx88_tvnorm *norm) { - return (norm->id & V4L2_STD_625_50) ? 511 : 288; + return (norm->id & V4L2_STD_625_50) ? 511 : 400; } int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height, diff --git a/linux/drivers/media/video/cx88/cx88-vbi.c b/linux/drivers/media/video/cx88/cx88-vbi.c index 749187d69..5cd0ee9ce 100644 --- a/linux/drivers/media/video/cx88/cx88-vbi.c +++ b/linux/drivers/media/video/cx88/cx88-vbi.c @@ -35,8 +35,8 @@ void cx8800_vbi_fmt(struct cx8800_dev *dev, struct v4l2_format *f) if (dev->core->tvnorm->id & V4L2_STD_525_60) { /* ntsc */ f->fmt.vbi.sampling_rate = 28636363; - f->fmt.vbi.start[0] = 10 -1; - f->fmt.vbi.start[1] = 273 -1; + f->fmt.vbi.start[0] = 10; + f->fmt.vbi.start[1] = 273; } else if (dev->core->tvnorm->id & V4L2_STD_625_50) { /* pal */ diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h index 88fff918d..0c7986572 100644 --- a/linux/drivers/media/video/cx88/cx88.h +++ b/linux/drivers/media/video/cx88/cx88.h @@ -40,7 +40,7 @@ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) #include #endif -#define CX88_VERSION_CODE KERNEL_VERSION(0,0,5) +#define CX88_VERSION_CODE KERNEL_VERSION(0,0,6) #ifndef TRUE # define TRUE (1==1) -- cgit v1.2.3 From ad8c1665001c90893f44e6cb1d22a8309d16e16b Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 9 May 2006 17:27:48 -0400 Subject: cx88-blackbird: add support for ProLink Pixelview Playtv@P7000 From: Angelo Marconi - Add support for ProLink Pixelview Playtv@P7000 Raw video and MPEG encoded video confirmed to work properly. SVideo, Composite and FM inputs are untested - disabled for now. Signed-off-by: Angelo Marconi Signed-off-by: Michael Krufky --- linux/drivers/media/video/cx88/cx88-cards.c | 36 +++++++++++++++++++++++++++ linux/drivers/media/video/cx88/cx88-tvaudio.c | 1 + linux/drivers/media/video/cx88/cx88.h | 1 + 3 files changed, 38 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index 89347103a..2bc60d04f 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -1156,6 +1156,38 @@ struct cx88_board cx88_boards[] = { .gpio0 = 0x0000BDE6, .type = CX88_RADIO, }, +#endif + .blackbird = 1, + }, + [CX88_BOARD_PIXELVIEW_PLAYTV_P7000] = { + /* FIXME: SVideo, Composite and FM inputs are untested */ + .name = "PixelView PlayTV P7000", + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE | + TDA9887_PORT2_ACTIVE, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x5da6, +#if 0 + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x5da4, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x5da5, +#endif + }}, +#if 0 + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x5da3, + }, #endif .blackbird = 1, }, @@ -1388,6 +1420,10 @@ struct cx88_subid cx88_subids[] = { .subvendor = 0x1822, .subdevice = 0x0019, .card = CX88_BOARD_DNTV_LIVE_DVB_T_PRO, + },{ + .subvendor = 0x1554, + .subdevice = 0x4813, + .card = CX88_BOARD_PIXELVIEW_PLAYTV_P7000, }, }; const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids); diff --git a/linux/drivers/media/video/cx88/cx88-tvaudio.c b/linux/drivers/media/video/cx88/cx88-tvaudio.c index 78f6d2c76..efd8d231c 100644 --- a/linux/drivers/media/video/cx88/cx88-tvaudio.c +++ b/linux/drivers/media/video/cx88/cx88-tvaudio.c @@ -155,6 +155,7 @@ static void set_audio_finish(struct cx88_core *core, u32 ctl) case CX88_BOARD_HAUPPAUGE_ROSLYN: case CX88_BOARD_KWORLD_MCE200_DELUXE: case CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT: + case CX88_BOARD_PIXELVIEW_PLAYTV_P7000: cx_clear(AUD_CTL, EN_I2SIN_ENABLE); break; default: diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h index 0c7986572..7ab469f1e 100644 --- a/linux/drivers/media/video/cx88/cx88.h +++ b/linux/drivers/media/video/cx88/cx88.h @@ -199,6 +199,7 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID 46 #define CX88_BOARD_PCHDTV_HD5500 47 #define CX88_BOARD_KWORLD_MCE200_DELUXE 48 +#define CX88_BOARD_PIXELVIEW_PLAYTV_P7000 49 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, -- cgit v1.2.3 From 2c3c793a5e742a92dff1a2ebaf25b349ec5dee40 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 11 May 2006 09:21:14 -0300 Subject: Adjust VBI offset to match value reported in VBI format From: Trent Piepho The actual offset of the start of VBI data is incorrectly documented for both the cx2388x and bt8x8. For the cx2388x, it appears to be about 220 to 224 + VBI_V_DEL*2. The driver has been incorrectly reporting an offset of 244. This patch adjusts VBI_V_DEL to 10, so that the offset of 244 is correct. This is the same offset as the bttv driver, and expected by some software. Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/cx88/cx88-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/cx88/cx88-core.c b/linux/drivers/media/video/cx88/cx88-core.c index 9d7574ac2..49dfc04d9 100644 --- a/linux/drivers/media/video/cx88/cx88-core.c +++ b/linux/drivers/media/video/cx88/cx88-core.c @@ -976,9 +976,9 @@ int cx88_set_tvnorm(struct cx88_core *core, struct cx88_tvnorm *norm) htotal, cx_read(MO_HTOTAL), (u32)tmp64); cx_write(MO_HTOTAL, htotal); - // vbi stuff - cx_write(MO_VBI_PACKET, ((1 << 11) | /* (norm_vdelay(norm) << 11) | */ - norm_vbipack(norm))); + // vbi stuff, set vbi offset to 10 (for 20 Clk*2 pixels), this makes + // the effective vbi offset ~244 samples, the same as the Bt8x8 + cx_write(MO_VBI_PACKET, (10<<11) | norm_vbipack(norm)); // this is needed as well to set all tvnorm parameter cx88_set_scale(core, 320, 240, V4L2_FIELD_INTERLACED); -- cgit v1.2.3 From 348451c7a70fd16d28358881e282612275e19dd2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 12 May 2006 17:30:37 -0300 Subject: Removed blank line to be in sync with kernel From: Mauro Carvalho Chehab kernel-sync: Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/vivi.c | 1 - 1 file changed, 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/vivi.c b/linux/drivers/media/video/vivi.c index 7da5487f2..21fbaa009 100644 --- a/linux/drivers/media/video/vivi.c +++ b/linux/drivers/media/video/vivi.c @@ -26,7 +26,6 @@ #include #include #include "compat.h" - #include #include #ifdef CONFIG_VIDEO_V4L1_COMPAT -- cgit v1.2.3 From 4ded687d1a3b606f30b62e7acef61b75519e5402 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Sat, 13 May 2006 00:31:51 +0100 Subject: Fix PLL refactoring breakage to WinTV NOVA T USB 2 driver From: Andrew de Quincey The calls to set the pll ops were in the wrong place - moved them. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/dvb-usb/dibusb-common.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/dvb-usb/dibusb-common.c b/linux/drivers/media/dvb/dvb-usb/dibusb-common.c index b0435c8f8..99279f963 100644 --- a/linux/drivers/media/dvb/dvb-usb/dibusb-common.c +++ b/linux/drivers/media/dvb/dvb-usb/dibusb-common.c @@ -173,11 +173,10 @@ int dibusb_dib3000mc_frontend_attach(struct dvb_usb_device *d) struct dib3000_config demod_cfg; struct dibusb_state *st = d->priv; - d->fe->ops->tuner_ops.init = dvb_usb_tuner_init_i2c; - d->fe->ops->tuner_ops.set_params = dvb_usb_tuner_set_params_i2c; - for (demod_cfg.demod_address = 0x8; demod_cfg.demod_address < 0xd; demod_cfg.demod_address++) if ((d->fe = dib3000mc_attach(&demod_cfg,&d->i2c_adap,&st->ops)) != NULL) { + d->fe->ops->tuner_ops.init = dvb_usb_tuner_init_i2c; + d->fe->ops->tuner_ops.set_params = dvb_usb_tuner_set_params_i2c; d->tuner_pass_ctrl = st->ops.tuner_pass_ctrl; return 0; } -- cgit v1.2.3 From 201eab753e49c4d8d6fe340e174b3907ac22325a Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Sat, 13 May 2006 00:31:51 +0100 Subject: Fix cx24123 diseqc From: Andrew de Quincey Rework diseqc support to be more in line with the other demod drivers. Fix Nova-S-Plus/Nova-SE2 diseqc. Cleanup API. Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/cx24123.c | 184 ++++++++-------------------- linux/drivers/media/dvb/frontends/cx24123.h | 9 -- linux/drivers/media/video/cx88/Kconfig | 2 + linux/drivers/media/video/cx88/cx88-dvb.c | 24 ++-- linux/drivers/media/video/cx88/cx88.h | 1 + 5 files changed, 68 insertions(+), 152 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/cx24123.c b/linux/drivers/media/dvb/frontends/cx24123.c index 89b3a6c63..a2647472c 100644 --- a/linux/drivers/media/dvb/frontends/cx24123.c +++ b/linux/drivers/media/dvb/frontends/cx24123.c @@ -48,7 +48,6 @@ struct cx24123_state u32 lastber; u16 snr; - u8 lnbreg; /* Some PLL specifics for tuning */ u32 VCAarg; @@ -260,29 +259,6 @@ static int cx24123_writereg(struct cx24123_state* state, int reg, int data) return 0; } -static int cx24123_writelnbreg(struct cx24123_state* state, int reg, int data) -{ - u8 buf[] = { reg, data }; - /* fixme: put the intersil addr int the config */ - struct i2c_msg msg = { .addr = 0x08, .flags = 0, .buf = buf, .len = 2 }; - int err; - - if (debug>1) - printk("cx24123: %s: writeln addr=0x08, reg 0x%02x, value 0x%02x\n", - __FUNCTION__,reg, data); - - if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { - printk("%s: writelnbreg error (err == %i, reg == 0x%02x," - " data == 0x%02x)\n", __FUNCTION__, err, reg, data); - return -EREMOTEIO; - } - - /* cache the write, no way to read back */ - state->lnbreg = data; - - return 0; -} - static int cx24123_readreg(struct cx24123_state* state, u8 reg) { int ret; @@ -306,11 +282,6 @@ static int cx24123_readreg(struct cx24123_state* state, u8 reg) return b1[0]; } -static int cx24123_readlnbreg(struct cx24123_state* state, u8 reg) -{ - return state->lnbreg; -} - static int cx24123_set_inversion(struct cx24123_state* state, fe_spectral_inversion_t inversion) { u8 nom_reg = cx24123_readreg(state, 0x0e); @@ -698,10 +669,6 @@ static int cx24123_initfe(struct dvb_frontend* fe) for (i = 0; i < sizeof(cx24123_regdata) / sizeof(cx24123_regdata[0]); i++) cx24123_writereg(state, cx24123_regdata[i].reg, cx24123_regdata[i].data); - /* Configure the LNB for 14V */ - if (state->config->use_isl6421) - cx24123_writelnbreg(state, 0x0, 0x2a); - return 0; } @@ -710,50 +677,18 @@ static int cx24123_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage struct cx24123_state *state = fe->demodulator_priv; u8 val; - switch (state->config->use_isl6421) { - - case 1: + val = cx24123_readreg(state, 0x29) & ~0x40; - val = cx24123_readlnbreg(state, 0x0); - - switch (voltage) { - case SEC_VOLTAGE_13: - dprintk("%s: isl6421 voltage = 13V\n",__FUNCTION__); - return cx24123_writelnbreg(state, 0x0, val & 0x32); /* V 13v */ - case SEC_VOLTAGE_18: - dprintk("%s: isl6421 voltage = 18V\n",__FUNCTION__); - return cx24123_writelnbreg(state, 0x0, val | 0x04); /* H 18v */ - case SEC_VOLTAGE_OFF: - dprintk("%s: isl5421 voltage off\n",__FUNCTION__); - return cx24123_writelnbreg(state, 0x0, val & 0x30); - default: - return -EINVAL; - }; - - case 0: - - val = cx24123_readreg(state, 0x29); - - switch (voltage) { - case SEC_VOLTAGE_13: - dprintk("%s: setting voltage 13V\n", __FUNCTION__); - if (state->config->enable_lnb_voltage) - state->config->enable_lnb_voltage(fe, 1); - return cx24123_writereg(state, 0x29, val | 0x80); - case SEC_VOLTAGE_18: - dprintk("%s: setting voltage 18V\n", __FUNCTION__); - if (state->config->enable_lnb_voltage) - state->config->enable_lnb_voltage(fe, 1); - return cx24123_writereg(state, 0x29, val & 0x7f); - case SEC_VOLTAGE_OFF: - dprintk("%s: setting voltage off\n", __FUNCTION__); - if (state->config->enable_lnb_voltage) - state->config->enable_lnb_voltage(fe, 0); - return 0; - default: - return -EINVAL; - }; - } + switch (voltage) { + case SEC_VOLTAGE_13: + dprintk("%s: setting voltage 13V\n", __FUNCTION__); + return cx24123_writereg(state, 0x29, val | 0x80); + case SEC_VOLTAGE_18: + dprintk("%s: setting voltage 18V\n", __FUNCTION__); + return cx24123_writereg(state, 0x29, val & 0x7f); + default: + return -EINVAL; + }; return 0; } @@ -774,27 +709,20 @@ static void cx24123_wait_for_diseqc(struct cx24123_state *state) static int cx24123_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd) { struct cx24123_state *state = fe->demodulator_priv; - int i, val; + int i, val, tone; dprintk("%s:\n",__FUNCTION__); - /* check if continuous tone has been stopped */ - if (state->config->use_isl6421) - val = cx24123_readlnbreg(state, 0x00) & 0x10; - else - val = cx24123_readreg(state, 0x29) & 0x10; - - - if (val) { - printk("%s: ERROR: attempt to send diseqc command before tone is off\n", __FUNCTION__); - return -ENOTSUPP; - } + /* stop continuous tone if enabled */ + tone = cx24123_readreg(state, 0x29); + if (tone & 0x10) + cx24123_writereg(state, 0x29, tone & ~0x50); /* wait for diseqc queue ready */ cx24123_wait_for_diseqc(state); /* select tone mode */ - cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) & 0xf8); + cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) & 0xfb); for (i = 0; i < cmd->msg_len; i++) cx24123_writereg(state, 0x2C + i, cmd->msg[i]); @@ -805,36 +733,33 @@ static int cx24123_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_ma /* wait for diseqc message to finish sending */ cx24123_wait_for_diseqc(state); + /* restart continuous tone if enabled */ + if (tone & 0x10) { + cx24123_writereg(state, 0x29, tone & ~0x40); + } + return 0; } static int cx24123_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) { struct cx24123_state *state = fe->demodulator_priv; - int val; + int val, tone; dprintk("%s:\n", __FUNCTION__); - /* check if continuous tone has been stoped */ - if (state->config->use_isl6421) - val = cx24123_readlnbreg(state, 0x00) & 0x10; - else - val = cx24123_readreg(state, 0x29) & 0x10; - - - if (val) { - printk("%s: ERROR: attempt to send diseqc command before tone is off\n", __FUNCTION__); - return -ENOTSUPP; - } + /* stop continuous tone if enabled */ + tone = cx24123_readreg(state, 0x29); + if (tone & 0x10) + cx24123_writereg(state, 0x29, tone & ~0x50); + /* wait for diseqc queue ready */ cx24123_wait_for_diseqc(state); /* select tone mode */ - val = cx24123_readreg(state, 0x2a) & 0xf8; - cx24123_writereg(state, 0x2a, val | 0x04); - + cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) | 0x4); + msleep(30); val = cx24123_readreg(state, 0x29); - if (burst == SEC_MINI_A) cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40 | 0x00)); else if (burst == SEC_MINI_B) @@ -843,7 +768,12 @@ static int cx24123_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t return -EINVAL; cx24123_wait_for_diseqc(state); + cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) & 0xfb); + /* restart continuous tone if enabled */ + if (tone & 0x10) { + cx24123_writereg(state, 0x29, tone & ~0x40); + } return 0; } @@ -984,38 +914,21 @@ static int cx24123_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) struct cx24123_state *state = fe->demodulator_priv; u8 val; - switch (state->config->use_isl6421) { - case 1: - - val = cx24123_readlnbreg(state, 0x0); - - switch (tone) { - case SEC_TONE_ON: - dprintk("%s: isl6421 sec tone on\n",__FUNCTION__); - return cx24123_writelnbreg(state, 0x0, val | 0x10); - case SEC_TONE_OFF: - dprintk("%s: isl6421 sec tone off\n",__FUNCTION__); - return cx24123_writelnbreg(state, 0x0, val & 0x2f); - default: - printk("%s: CASE reached default with tone=%d\n", __FUNCTION__, tone); - return -EINVAL; - } - - case 0: + /* wait for diseqc queue ready */ + cx24123_wait_for_diseqc(state); - val = cx24123_readreg(state, 0x29); + val = cx24123_readreg(state, 0x29) & ~0x40; - switch (tone) { - case SEC_TONE_ON: - dprintk("%s: setting tone on\n", __FUNCTION__); - return cx24123_writereg(state, 0x29, val | 0x10); - case SEC_TONE_OFF: - dprintk("%s: setting tone off\n",__FUNCTION__); - return cx24123_writereg(state, 0x29, val & 0xef); - default: - printk("%s: CASE reached default with tone=%d\n", __FUNCTION__, tone); - return -EINVAL; - } + switch (tone) { + case SEC_TONE_ON: + dprintk("%s: setting tone on\n", __FUNCTION__); + return cx24123_writereg(state, 0x29, val | 0x10); + case SEC_TONE_OFF: + dprintk("%s: setting tone off\n",__FUNCTION__); + return cx24123_writereg(state, 0x29, val & 0xef); + default: + printk("%s: CASE reached default with tone=%d\n", __FUNCTION__, tone); + return -EINVAL; } return 0; @@ -1051,7 +964,6 @@ struct dvb_frontend* cx24123_attach(const struct cx24123_config* config, memcpy(&state->ops, &cx24123_ops, sizeof(struct dvb_frontend_ops)); state->lastber = 0; state->snr = 0; - state->lnbreg = 0; state->VCAarg = 0; state->VGAarg = 0; state->bandselectarg = 0; diff --git a/linux/drivers/media/dvb/frontends/cx24123.h b/linux/drivers/media/dvb/frontends/cx24123.h index 7ec4ee856..9606f8259 100644 --- a/linux/drivers/media/dvb/frontends/cx24123.h +++ b/linux/drivers/media/dvb/frontends/cx24123.h @@ -28,17 +28,8 @@ struct cx24123_config /* the demodulator's i2c address */ u8 demod_address; - /* - cards like Hauppauge Nova-S Plus/Nova-SE2 use an Intersil ISL6421 chip - for LNB control, while KWorld DVB-S 100 use the LNBDC and LNBTone bits - from register 0x29 of the CX24123 demodulator - */ - int use_isl6421; - /* Need to set device param for start_dma */ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); - - void (*enable_lnb_voltage)(struct dvb_frontend* fe, int on); }; extern struct dvb_frontend* cx24123_attach(const struct cx24123_config* config, diff --git a/linux/drivers/media/video/cx88/Kconfig b/linux/drivers/media/video/cx88/Kconfig index 630273992..c092d2219 100644 --- a/linux/drivers/media/video/cx88/Kconfig +++ b/linux/drivers/media/video/cx88/Kconfig @@ -61,6 +61,7 @@ config VIDEO_CX88_DVB_ALL_FRONTENDS select DVB_LGDT330X select DVB_NXT200X select DVB_CX24123 + select DVB_ISL6421 ---help--- This builds cx88-dvb with all currently supported frontend demodulators. If you wish to tweak your configuration, and @@ -139,6 +140,7 @@ config VIDEO_CX88_DVB_CX24123 default y depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS select DVB_CX24123 + select DVB_ISL6421 ---help--- This adds DVB-S support for cards based on the Connexant 2388x chip and the CX24123 demodulator. diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index a65306c0d..e088884d4 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -60,6 +60,7 @@ #ifdef HAVE_CX24123 # include "cx24123.h" #endif +#include "isl6421.h" MODULE_DESCRIPTION("driver for cx2388x based DVB cards"); MODULE_AUTHOR("Chris Pascoe "); @@ -480,28 +481,30 @@ static int cx24123_set_ts_param(struct dvb_frontend* fe, return 0; } -static void cx24123_enable_lnb_voltage(struct dvb_frontend* fe, int on) +static int kworld_dvbs_100_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) { struct cx8802_dev *dev= fe->dvb->priv; struct cx88_core *core = dev->core; - if (on) - cx_write(MO_GP0_IO, 0x000006f9); - else + if (voltage == SEC_VOLTAGE_OFF) { cx_write(MO_GP0_IO, 0x000006fB); + } else { + cx_write(MO_GP0_IO, 0x000006f9); + } + + if (core->prev_set_voltage) + return core->prev_set_voltage(fe, voltage); + return 0; } static struct cx24123_config hauppauge_novas_config = { .demod_address = 0x55, - .use_isl6421 = 1, .set_ts_params = cx24123_set_ts_param, }; static struct cx24123_config kworld_dvbs_100_config = { .demod_address = 0x15, - .use_isl6421 = 0, .set_ts_params = cx24123_set_ts_param, - .enable_lnb_voltage = cx24123_enable_lnb_voltage, }; #endif @@ -711,10 +714,17 @@ static int dvb_register(struct cx8802_dev *dev) case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: dev->dvb.frontend = cx24123_attach(&hauppauge_novas_config, &dev->core->i2c_adap); + if (dev->dvb.frontend) { + isl6421_attach(dev->dvb.frontend, &dev->core->i2c_adap, 0x08, 0x00, 0x00); + } break; case CX88_BOARD_KWORLD_DVBS_100: dev->dvb.frontend = cx24123_attach(&kworld_dvbs_100_config, &dev->core->i2c_adap); + if (dev->dvb.frontend) { + dev->core->prev_set_voltage = dev->dvb.frontend->ops->set_voltage; + dev->dvb.frontend->ops->set_voltage = kworld_dvbs_100_set_voltage; + } break; #endif default: diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h index 0c7986572..08beef8a6 100644 --- a/linux/drivers/media/video/cx88/cx88.h +++ b/linux/drivers/media/video/cx88/cx88.h @@ -309,6 +309,7 @@ struct cx88_core { /* config info -- dvb */ struct dvb_pll_desc *pll_desc; unsigned int pll_addr; + int (*prev_set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage); /* state info */ struct task_struct *kthread; -- cgit v1.2.3 From 7a655551e942a6eae6fee9e25411a41f841046c9 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Sat, 13 May 2006 00:31:51 +0100 Subject: Change the sweeprate for TT C1500 using QAM64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: Per Dalén This patch changes the sweeprate for TT C1500 using QAM64. It has been proven to work using QAM64 at a SRate of 6875 for the two different cable networks. Signed-off-by: Per Dalén Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/frontends/stv0297.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/frontends/stv0297.c b/linux/drivers/media/dvb/frontends/stv0297.c index 9c5788c2e..9dd0766d1 100644 --- a/linux/drivers/media/dvb/frontends/stv0297.c +++ b/linux/drivers/media/dvb/frontends/stv0297.c @@ -401,7 +401,7 @@ static int stv0297_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par case QAM_32: case QAM_64: delay = 100; - sweeprate = 1500; + sweeprate = 1000; break; case QAM_128: -- cgit v1.2.3 From da14f3340bd6879661ed106f47b8ae0101594e22 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Sat, 13 May 2006 00:36:24 +0100 Subject: Remove the spagetti code gotos that aren't useful From: Trent Piepho Some code had pointless gotos that just didn't make any sense. They didn't make the code smaller, or faster, or easier to understand. Signed-off-by: Trent Piepho Signed-off-by: Andrew de Quincey --- linux/drivers/media/dvb/b2c2/flexcop-pci.c | 19 +++++++------------ linux/drivers/media/dvb/b2c2/flexcop-usb.c | 10 ++++------ linux/drivers/media/dvb/b2c2/flexcop.c | 10 +++------- linux/drivers/media/dvb/dvb-usb/dtt200u-fe.c | 4 +--- linux/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c | 7 +++---- linux/drivers/media/dvb/dvb-usb/vp702x-fe.c | 4 +--- linux/drivers/media/dvb/dvb-usb/vp7045-fe.c | 4 +--- 7 files changed, 20 insertions(+), 38 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/dvb/b2c2/flexcop-pci.c b/linux/drivers/media/dvb/b2c2/flexcop-pci.c index 9bc40bdcc..f04041702 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-pci.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-pci.c @@ -242,19 +242,16 @@ static int flexcop_pci_dma_init(struct flexcop_pci *fc_pci) if ((ret = flexcop_dma_allocate(fc_pci->pdev,&fc_pci->dma[0],FC_DEFAULT_DMA1_BUFSIZE)) != 0) return ret; - if ((ret = flexcop_dma_allocate(fc_pci->pdev,&fc_pci->dma[1],FC_DEFAULT_DMA2_BUFSIZE)) != 0) - goto dma1_free; + if ((ret = flexcop_dma_allocate(fc_pci->pdev,&fc_pci->dma[1],FC_DEFAULT_DMA2_BUFSIZE)) != 0) { + flexcop_dma_free(&fc_pci->dma[0]); + return ret; + } flexcop_sram_set_dest(fc_pci->fc_dev,FC_SRAM_DEST_MEDIA | FC_SRAM_DEST_NET, FC_SRAM_DEST_TARGET_DMA1); flexcop_sram_set_dest(fc_pci->fc_dev,FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI, FC_SRAM_DEST_TARGET_DMA2); fc_pci->init_state |= FC_PCI_DMA_INIT; - goto success; -dma1_free: - flexcop_dma_free(&fc_pci->dma[0]); - -success: return ret; } @@ -303,7 +300,7 @@ static int flexcop_pci_init(struct flexcop_pci *fc_pci) spin_lock_init(&fc_pci->irq_lock); fc_pci->init_state |= FC_PCI_INIT; - goto success; + return ret; err_pci_iounmap: pci_iounmap(fc_pci->pdev, fc_pci->io_mem); @@ -312,8 +309,6 @@ err_pci_release_regions: pci_release_regions(fc_pci->pdev); err_pci_disable_device: pci_disable_device(fc_pci->pdev); - -success: return ret; } @@ -378,14 +373,14 @@ static int flexcop_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e INIT_WORK(&fc_pci->irq_check_work, flexcop_pci_irq_check_work, fc_pci); - goto success; + return ret; + err_fc_exit: flexcop_device_exit(fc); err_pci_exit: flexcop_pci_exit(fc_pci); err_kfree: flexcop_device_kfree(fc); -success: return ret; } diff --git a/linux/drivers/media/dvb/b2c2/flexcop-usb.c b/linux/drivers/media/dvb/b2c2/flexcop-usb.c index c29abd803..8a3ae27f8 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-usb.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-usb.c @@ -433,11 +433,10 @@ static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb) flexcop_wan_set_speed(fc_usb->fc_dev,FC_WAN_SPEED_8MBITS); flexcop_sram_ctrl(fc_usb->fc_dev,1,1,1); - ret = 0; - goto success; + return 0; + urb_error: flexcop_usb_transfer_exit(fc_usb); -success: return ret; } @@ -515,15 +514,14 @@ static int flexcop_usb_probe(struct usb_interface *intf, goto err_fc_exit; info("%s successfully initialized and connected.",DRIVER_NAME); - ret = 0; - goto success; + return 0; + err_fc_exit: flexcop_device_exit(fc); err_usb_exit: flexcop_usb_exit(fc_usb); err_kfree: flexcop_device_kfree(fc); -success: return ret; } diff --git a/linux/drivers/media/dvb/b2c2/flexcop.c b/linux/drivers/media/dvb/b2c2/flexcop.c index 5c276b379..29ec41831 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop.c +++ b/linux/drivers/media/dvb/b2c2/flexcop.c @@ -116,7 +116,7 @@ static int flexcop_dvb_init(struct flexcop_device *fc) dvb_net_init(&fc->dvb_adapter, &fc->dvbnet, &fc->demux.dmx); fc->init_state |= FC_STATE_DVB_INIT; - goto success; + return 0; err_connect_frontend: fc->demux.dmx.remove_frontend(&fc->demux.dmx,&fc->mem_frontend); @@ -129,9 +129,6 @@ err_dmx_dev: err_dmx: dvb_unregister_adapter(&fc->dvb_adapter); return ret; - -success: - return 0; } static void flexcop_dvb_exit(struct flexcop_device *fc) @@ -279,11 +276,10 @@ int flexcop_device_initialize(struct flexcop_device *fc) flexcop_device_name(fc,"initialization of","complete"); - ret = 0; - goto success; + return 0; + error: flexcop_device_exit(fc); -success: return ret; } EXPORT_SYMBOL(flexcop_device_initialize); diff --git a/linux/drivers/media/dvb/dvb-usb/dtt200u-fe.c b/linux/drivers/media/dvb/dvb-usb/dtt200u-fe.c index cd21ddbfd..2df6da2b5 100644 --- a/linux/drivers/media/dvb/dvb-usb/dtt200u-fe.c +++ b/linux/drivers/media/dvb/dvb-usb/dtt200u-fe.c @@ -168,11 +168,9 @@ struct dvb_frontend* dtt200u_fe_attach(struct dvb_usb_device *d) state->frontend.ops = &state->ops; state->frontend.demodulator_priv = state; - goto success; + return &state->frontend; error: return NULL; -success: - return &state->frontend; } static struct dvb_frontend_ops dtt200u_fe_ops = { diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c b/linux/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c index 500896d02..2517b2283 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c @@ -121,16 +121,15 @@ int dvb_usb_dvb_init(struct dvb_usb_device *d) dvb_net_init(&d->dvb_adap, &d->dvb_net, &d->demux.dmx); - goto success; + d->state |= DVB_USB_STATE_DVB; + return 0; + err_dmx_dev: dvb_dmx_release(&d->demux); err_dmx: dvb_unregister_adapter(&d->dvb_adap); err: return ret; -success: - d->state |= DVB_USB_STATE_DVB; - return 0; } int dvb_usb_dvb_exit(struct dvb_usb_device *d) diff --git a/linux/drivers/media/dvb/dvb-usb/vp702x-fe.c b/linux/drivers/media/dvb/dvb-usb/vp702x-fe.c index 2a89f8c5d..9d26f46de 100644 --- a/linux/drivers/media/dvb/dvb-usb/vp702x-fe.c +++ b/linux/drivers/media/dvb/dvb-usb/vp702x-fe.c @@ -293,11 +293,9 @@ struct dvb_frontend * vp702x_fe_attach(struct dvb_usb_device *d) s->lnb_buf[1] = SET_LNB_POWER; s->lnb_buf[3] = 0xff; /* 0=tone burst, 2=data burst, ff=off */ - goto success; + return &s->fe; error: return NULL; -success: - return &s->fe; } diff --git a/linux/drivers/media/dvb/dvb-usb/vp7045-fe.c b/linux/drivers/media/dvb/dvb-usb/vp7045-fe.c index 9999336ae..e98e5a517 100644 --- a/linux/drivers/media/dvb/dvb-usb/vp7045-fe.c +++ b/linux/drivers/media/dvb/dvb-usb/vp7045-fe.c @@ -155,11 +155,9 @@ struct dvb_frontend * vp7045_fe_attach(struct dvb_usb_device *d) s->fe.ops = &s->ops; s->fe.demodulator_priv = s; - goto success; + return &s->fe; error: return NULL; -success: - return &s->fe; } -- cgit v1.2.3 From 86cbc2b425cbc461fa8e02e45f0e55ee57f4bc6a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 17 May 2006 15:01:07 -0300 Subject: Usbvideo/quickcam_messenger driver v4l From: jayakumar.video@gmail.com Adds a usbvideo driver for the Logitech Quickcam Messenger USB webcam. Signed-off-by: Jaya Kumar Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/usbvideo/Kconfig | 12 + linux/drivers/media/video/usbvideo/Makefile | 1 + .../media/video/usbvideo/quickcam_messenger.c | 1120 ++++++++++++++++++++ .../media/video/usbvideo/quickcam_messenger.h | 126 +++ 4 files changed, 1259 insertions(+) create mode 100644 linux/drivers/media/video/usbvideo/quickcam_messenger.c create mode 100644 linux/drivers/media/video/usbvideo/quickcam_messenger.h (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/usbvideo/Kconfig b/linux/drivers/media/video/usbvideo/Kconfig index 39269a2c5..bb6c84d18 100644 --- a/linux/drivers/media/video/usbvideo/Kconfig +++ b/linux/drivers/media/video/usbvideo/Kconfig @@ -36,3 +36,15 @@ config USB_KONICAWC To compile this driver as a module, choose M here: the module will be called konicawc. + +config USB_QUICKCAM_MESSENGER + tristate "USB Logitech Quickcam Messenger" + depends on USB && VIDEO_DEV + select VIDEO_USBVIDEO + ---help--- + Say Y or M here to enable support for the USB Logitech Quickcam + Messenger webcam. + + To compile this driver as a module, choose M here: the + module will be called quickcam_messenger. + diff --git a/linux/drivers/media/video/usbvideo/Makefile b/linux/drivers/media/video/usbvideo/Makefile index bb52eb8dc..b20844787 100644 --- a/linux/drivers/media/video/usbvideo/Makefile +++ b/linux/drivers/media/video/usbvideo/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_VIDEO_USBVIDEO) += usbvideo.o obj-$(CONFIG_USB_IBMCAM) += ibmcam.o ultracam.o obj-$(CONFIG_USB_KONICAWC) += konicawc.o obj-$(CONFIG_USB_VICAM) += vicam.o +obj-$(CONFIG_USB_QUICKCAM_MESSENGER) += quickcam_messenger.o diff --git a/linux/drivers/media/video/usbvideo/quickcam_messenger.c b/linux/drivers/media/video/usbvideo/quickcam_messenger.c new file mode 100644 index 000000000..cedda9dad --- /dev/null +++ b/linux/drivers/media/video/usbvideo/quickcam_messenger.c @@ -0,0 +1,1120 @@ +/* + * Driver for Logitech Quickcam Messenger usb video camera + * Copyright (C) Jaya Kumar + * + * This work was sponsored by CIS(M) Sdn Bhd. + * History: + * 05/08/2006 - Jaya Kumar + * I wrote this based on the konicawc by Simon Evans. + * - + * Full credit for reverse engineering and creating an initial + * working linux driver for the VV6422 goes to the qce-ga project by + * Tuukka Toivonen, Jochen Hoenicke, Peter McConnell, + * Cristiano De Michele, Georg Acher, Jean-Frederic Clere as well as + * others. + * --- + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +#include "usbvideo.h" +#include "quickcam_messenger.h" + +/* + * Version Information + */ + +#ifdef CONFIG_USB_DEBUG +static int debug; +#define DEBUG(n, format, arg...) \ + if (n <= debug) { \ + printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __FUNCTION__ , ## arg); \ + } +#else +#define DEBUG(n, arg...) +static const int debug = 0; +#endif + +#define DRIVER_VERSION "v0.01" +#define DRIVER_DESC "Logitech Quickcam Messenger USB" + +#define USB_LOGITECH_VENDOR_ID 0x046D +#define USB_QCM_PRODUCT_ID 0x08F0 + +#define MAX_CAMERAS 1 + +#define MAX_COLOUR 32768 +#define MAX_HUE 32768 +#define MAX_BRIGHTNESS 32768 +#define MAX_CONTRAST 32768 +#define MAX_WHITENESS 32768 + +static int size = SIZE_320X240; +static int colour = MAX_COLOUR; +static int hue = MAX_HUE; +static int brightness = MAX_BRIGHTNESS; +static int contrast = MAX_CONTRAST; +static int whiteness = MAX_WHITENESS; + +static struct usbvideo *cams; + +static struct usb_device_id qcm_table [] = { + { USB_DEVICE(USB_LOGITECH_VENDOR_ID, USB_QCM_PRODUCT_ID) }, + { } +}; +MODULE_DEVICE_TABLE(usb, qcm_table); + +#ifdef CONFIG_INPUT +static void qcm_register_input(struct qcm *cam, struct usb_device *dev) +{ + struct input_dev *input_dev; + + usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname)); + strncat(cam->input_physname, "/input0", sizeof(cam->input_physname)); + + cam->input = input_dev = input_allocate_device(); + if (!input_dev) { + warn("insufficient mem for cam input device"); + return; + } + + input_dev->name = "QCM button"; + input_dev->phys = cam->input_physname; + usb_to_input_id(dev, &input_dev->id); + input_dev->cdev.dev = &dev->dev; + + input_dev->evbit[0] = BIT(EV_KEY); + input_dev->keybit[LONG(BTN_0)] = BIT(BTN_0); + + input_dev->private = cam; + + input_register_device(cam->input); +} + +static void qcm_unregister_input(struct qcm *cam) +{ + if (cam->input) { + input_unregister_device(cam->input); + cam->input = NULL; + } +} + +static void qcm_report_buttonstat(struct qcm *cam) +{ + if (cam->input) { + input_report_key(cam->input, BTN_0, cam->button_sts); + input_sync(cam->input); + } +} + +static void qcm_int_irq(struct urb *urb, struct pt_regs *regs) +{ + int ret; + struct uvd *uvd = urb->context; + struct qcm *cam; + + if (!CAMERA_IS_OPERATIONAL(uvd)) + return; + + if (!uvd->streaming) + return; + + uvd->stats.urb_count++; + + if (urb->status < 0) + uvd->stats.iso_err_count++; + else { + if (urb->actual_length > 0 ) { + cam = (struct qcm *) uvd->user_data; + if (cam->button_sts_buf == 0x88) + cam->button_sts = 0x0; + else if (cam->button_sts_buf == 0x80) + cam->button_sts = 0x1; + qcm_report_buttonstat(cam); + } + } + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret < 0) + err("usb_submit_urb error (%d)", ret); +} + +static int qcm_setup_input_int(struct qcm *cam, struct uvd *uvd) +{ + int errflag; + usb_fill_int_urb(cam->button_urb, uvd->dev, + usb_rcvintpipe(uvd->dev, uvd->video_endp + 1), + &cam->button_sts_buf, + 1, + qcm_int_irq, + uvd, 16); + + errflag = usb_submit_urb(cam->button_urb, GFP_KERNEL); + if (errflag) + err ("usb_submit_int ret %d", errflag); + return errflag; +} + +static void qcm_stop_int_data(struct qcm *cam) +{ + usb_kill_urb(cam->button_urb); +} + +static int qcm_alloc_int_urb(struct qcm *cam) +{ + cam->button_urb = usb_alloc_urb(0, GFP_KERNEL); + + if (!cam->button_urb) + return -ENOMEM; + + return 0; +} + +static void qcm_free_int(struct qcm *cam) +{ + if (cam->button_urb) + usb_free_urb(cam->button_urb); +} +#endif /* CONFIG_INPUT */ + +static int qcm_stv_setb(struct usb_device *dev, u16 reg, u8 val) +{ + int ret; + + /* we'll wait up to 3 slices but no more */ + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x04, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + reg, 0, &val, 1, 3*HZ); + return ret; +} + +static int qcm_stv_setw(struct usb_device *dev, u16 reg, u16 val) +{ + int ret; + + /* we'll wait up to 3 slices but no more */ + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x04, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + reg, 0, &val, 2, 3*HZ); + return ret; +} + +static int qcm_stv_getw(struct usb_device *dev, unsigned short reg, + __le16 *val) +{ + int ret; + + /* we'll wait up to 3 slices but no more */ + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x04, USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, + reg, 0, val, 2, 3*HZ); + return ret; +} + +static int qcm_camera_on(struct uvd *uvd) +{ + int ret; + CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x01)); + return 0; +} + +static int qcm_camera_off(struct uvd *uvd) +{ + int ret; + CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x00)); + return 0; +} + +static void qcm_hsv2rgb(u16 hue, u16 sat, u16 val, u16 *r, u16 *g, u16 *b) +{ + unsigned int segment, valsat; + signed int h = (signed int) hue; + unsigned int s = (sat - 32768) * 2; /* rescale */ + unsigned int v = val; + unsigned int p; + + /* + the registers controling gain are 8 bit of which + we affect only the last 4 bits with our gain. + we know that if saturation is 0, (unsaturated) then + we're grayscale (center axis of the colour cone) so + we set rgb=value. we use a formula obtained from + wikipedia to map the cone to the RGB plane. it's + as follows for the human value case of h=0..360, + s=0..1, v=0..1 + h_i = h/60 % 6 , f = h/60 - h_i , p = v(1-s) + q = v(1 - f*s) , t = v(1 - (1-f)s) + h_i==0 => r=v , g=t, b=p + h_i==1 => r=q , g=v, b=p + h_i==2 => r=p , g=v, b=t + h_i==3 => r=p , g=q, b=v + h_i==4 => r=t , g=p, b=v + h_i==5 => r=v , g=p, b=q + the bottom side (the point) and the stuff just up + of that is black so we simplify those two cases. + */ + if (sat < 32768) { + /* anything less than this is unsaturated */ + *r = val; + *g = val; + *b = val; + return; + } + if (val <= (0xFFFF/8)) { + /* anything less than this is black */ + *r = 0; + *g = 0; + *b = 0; + return; + } + + /* the rest of this code is copying tukkat's + implementation of the hsv2rgb conversion as taken + from qc-usb-messenger code. the 10923 is 0xFFFF/6 + to divide the cone into 6 sectors. */ + + segment = (h + 10923) & 0xFFFF; + segment = segment*3 >> 16; /* 0..2: 0=R, 1=G, 2=B */ + hue -= segment * 21845; /* -10923..10923 */ + h = hue; + h *= 3; + valsat = v*s >> 16; /* 0..65534 */ + p = v - valsat; + if (h >= 0) { + unsigned int t = v - (valsat * (32769 - h) >> 15); + switch (segment) { + case 0: /* R-> */ + *r = v; + *g = t; + *b = p; + break; + case 1: /* G-> */ + *r = p; + *g = v; + *b = t; + break; + case 2: /* B-> */ + *r = t; + *g = p; + *b = v; + break; + } + } else { + unsigned int q = v - (valsat * (32769 + h) >> 15); + switch (segment) { + case 0: /* ->R */ + *r = v; + *g = p; + *b = q; + break; + case 1: /* ->G */ + *r = q; + *g = v; + *b = p; + break; + case 2: /* ->B */ + *r = p; + *g = q; + *b = v; + break; + } + } +} + +static int qcm_sensor_set_gains(struct uvd *uvd, u16 hue, + u16 saturation, u16 value) +{ + int ret; + u16 r,g,b; + + /* this code is based on qc-usb-messenger */ + qcm_hsv2rgb(hue, saturation, value, &r, &g, &b); + + r >>= 12; + g >>= 12; + b >>= 12; + + /* min val is 8 */ + r = max((u16) 8, r); + g = max((u16) 8, g); + b = max((u16) 8, b); + + r |= 0x30; + g |= 0x30; + b |= 0x30; + + /* set the r,g,b gain registers */ + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x0509, r)); + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050A, g)); + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050B, b)); + + /* doing as qc-usb did */ + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050C, 0x2A)); + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050D, 0x01)); + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01)); + + return 0; +} + +static int qcm_sensor_set_exposure(struct uvd *uvd, int exposure) +{ + int ret; + int formedval; + + /* calculation was from qc-usb-messenger driver */ + formedval = ( exposure >> 12 ); + + /* max value for formedval is 14 */ + formedval = min(formedval, 14); + + CHECK_RET(ret, qcm_stv_setb(uvd->dev, + 0x143A, 0xF0 | formedval)); + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01)); + return 0; +} + +static int qcm_sensor_setlevels(struct uvd *uvd, int brightness, int contrast, + int hue, int colour) +{ + int ret; + /* brightness is exposure, contrast is gain, colour is saturation */ + CHECK_RET(ret, + qcm_sensor_set_exposure(uvd, brightness)); + CHECK_RET(ret, qcm_sensor_set_gains(uvd, hue, colour, contrast)); + + return 0; +} + +static int qcm_sensor_setsize(struct uvd *uvd, u8 size) +{ + int ret; + + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x1505, size)); + return 0; +} + +static int qcm_sensor_set_shutter(struct uvd *uvd, int whiteness) +{ + int ret; + /* some rescaling as done by the qc-usb-messenger code */ + if (whiteness > 0xC000) + whiteness = 0xC000 + (whiteness & 0x3FFF)*8; + + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143D, + (whiteness >> 8) & 0xFF)); + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143E, + (whiteness >> 16) & 0x03)); + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01)); + + return 0; +} + +static int qcm_sensor_init(struct uvd *uvd) +{ + struct qcm *cam = (struct qcm *) uvd->user_data; + int ret; + int i; + + for (i=0; i < sizeof(regval_table)/sizeof(regval_table[0]) ; i++) { + CHECK_RET(ret, qcm_stv_setb(uvd->dev, + regval_table[i].reg, + regval_table[i].val)); + } + + CHECK_RET(ret, qcm_stv_setw(uvd->dev, 0x15c1, + cpu_to_le16(ISOC_PACKET_SIZE))); + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x15c3, 0x08)); + CHECK_RET(ret, ret = qcm_stv_setb(uvd->dev, 0x143f, 0x01)); + + CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x00)); + + CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd)); + + CHECK_RET(ret, qcm_sensor_setlevels(uvd, uvd->vpic.brightness, + uvd->vpic.contrast, uvd->vpic.hue, uvd->vpic.colour)); + + CHECK_RET(ret, qcm_sensor_set_shutter(uvd, uvd->vpic.whiteness)); + CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd)); + + return 0; +} + +static int qcm_set_camera_size(struct uvd *uvd) +{ + int ret; + struct qcm *cam = (struct qcm *) uvd->user_data; + + CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd)); + cam->width = camera_sizes[cam->size].width; + cam->height = camera_sizes[cam->size].height; + uvd->videosize = VIDEOSIZE(cam->width, cam->height); + + return 0; +} + +static int qcm_setup_on_open(struct uvd *uvd) +{ + int ret; + + CHECK_RET(ret, qcm_sensor_set_gains(uvd, uvd->vpic.hue, + uvd->vpic.colour, uvd->vpic.contrast)); + CHECK_RET(ret, qcm_sensor_set_exposure(uvd, uvd->vpic.brightness)); + CHECK_RET(ret, qcm_sensor_set_shutter(uvd, uvd->vpic.whiteness)); + CHECK_RET(ret, qcm_set_camera_size(uvd)); + CHECK_RET(ret, qcm_camera_on(uvd)); + return 0; +} + +static void qcm_adjust_picture(struct uvd *uvd) +{ + int ret; + struct qcm *cam = (struct qcm *) uvd->user_data; + + ret = qcm_camera_off(uvd); + if (ret) { + err("can't turn camera off. abandoning pic adjustment"); + return; + } + + /* if there's been a change in contrast, hue, or + colour then we need to recalculate hsv in order + to update gains */ + if ((cam->contrast != uvd->vpic.contrast) || + (cam->hue != uvd->vpic.hue) || + (cam->colour != uvd->vpic.colour)) { + cam->contrast = uvd->vpic.contrast; + cam->hue = uvd->vpic.hue; + cam->colour = uvd->vpic.colour; + ret = qcm_sensor_set_gains(uvd, cam->hue, cam->colour, + cam->contrast); + if (ret) { + err("can't set gains. abandoning pic adjustment"); + return; + } + } + + if (cam->brightness != uvd->vpic.brightness) { + cam->brightness = uvd->vpic.brightness; + ret = qcm_sensor_set_exposure(uvd, cam->brightness); + if (ret) { + err("can't set exposure. abandoning pic adjustment"); + return; + } + } + + if (cam->whiteness != uvd->vpic.whiteness) { + cam->whiteness = uvd->vpic.whiteness; + qcm_sensor_set_shutter(uvd, cam->whiteness); + if (ret) { + err("can't set shutter. abandoning pic adjustment"); + return; + } + } + + ret = qcm_camera_on(uvd); + if (ret) { + err("can't reenable camera. pic adjustment failed"); + return; + } +} + +static int qcm_process_frame(struct uvd *uvd, u8 *cdata, int framelen) +{ + int datalen; + int totaldata; + struct framehdr { + __be16 id; + __be16 len; + }; + struct framehdr *fhdr; + + totaldata = 0; + while (framelen) { + fhdr = (struct framehdr *) cdata; + datalen = be16_to_cpu(fhdr->len); + framelen -= 4; + cdata += 4; + + if ((fhdr->id) == cpu_to_be16(0x8001)) { + RingQueue_Enqueue(&uvd->dp, marker, 4); + totaldata += 4; + continue; + } + if ((fhdr->id & cpu_to_be16(0xFF00)) == cpu_to_be16(0x0200)) { + RingQueue_Enqueue(&uvd->dp, cdata, datalen); + totaldata += datalen; + } + framelen -= datalen; + cdata += datalen; + } + return totaldata; +} + +static int qcm_compress_iso(struct uvd *uvd, struct urb *dataurb) +{ + int totlen; + int i; + unsigned char *cdata; + + totlen=0; + for (i = 0; i < dataurb->number_of_packets; i++) { + int n = dataurb->iso_frame_desc[i].actual_length; + int st = dataurb->iso_frame_desc[i].status; + + cdata = dataurb->transfer_buffer + + dataurb->iso_frame_desc[i].offset; + + if (st < 0) { + warn("Data error: packet=%d. len=%d. status=%d.", + i, n, st); + uvd->stats.iso_err_count++; + continue; + } + if (!n) + continue; + + totlen += qcm_process_frame(uvd, cdata, n); + } + return totlen; +} + +static void resubmit_urb(struct uvd *uvd, struct urb *urb) +{ + int ret; + + urb->dev = uvd->dev; + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) + err("usb_submit_urb error (%d)", ret); +} + +static void qcm_isoc_irq(struct urb *urb, struct pt_regs *regs) +{ + int len; + struct uvd *uvd = urb->context; + + if (!CAMERA_IS_OPERATIONAL(uvd)) + return; + + if (!uvd->streaming) + return; + + uvd->stats.urb_count++; + + if (!urb->actual_length) { + resubmit_urb(uvd, urb); + return; + } + + len = qcm_compress_iso(uvd, urb); + resubmit_urb(uvd, urb); + uvd->stats.urb_length = len; + uvd->stats.data_count += len; + if (len) + RingQueue_WakeUpInterruptible(&uvd->dp); +} + +static int qcm_start_data(struct uvd *uvd) +{ + struct qcm *cam = (struct qcm *) uvd->user_data; + int i; + int errflag; + int pktsz; + int err; + + pktsz = uvd->iso_packet_len; + if (!CAMERA_IS_OPERATIONAL(uvd)) { + err("Camera is not operational"); + return -EFAULT; + } + + err = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltActive); + if (err < 0) { + err("usb_set_interface error"); + uvd->last_error = err; + return -EBUSY; + } + + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + int j, k; + struct urb *urb = uvd->sbuf[i].urb; + urb->dev = uvd->dev; + urb->context = uvd; + urb->pipe = usb_rcvisocpipe(uvd->dev, uvd->video_endp); + urb->interval = 1; + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = uvd->sbuf[i].data; + urb->complete = qcm_isoc_irq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = pktsz * FRAMES_PER_DESC; + for (j=k=0; j < FRAMES_PER_DESC; j++, k += pktsz) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = pktsz; + } + } + + uvd->streaming = 1; + uvd->curframe = -1; + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + errflag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL); + if (errflag) + err ("usb_submit_isoc(%d) ret %d", i, errflag); + } + + CHECK_RET(err, qcm_setup_input_int(cam, uvd)); + CHECK_RET(err, qcm_camera_on(uvd)); + return 0; +} + +static void qcm_stop_data(struct uvd *uvd) +{ + struct qcm *cam = (struct qcm *) uvd->user_data; + int i, j; + int ret; + + if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL)) + return; + + ret = qcm_camera_off(uvd); + if (ret) + warn("couldn't turn the cam off."); + + uvd->streaming = 0; + + /* Unschedule all of the iso td's */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) + usb_kill_urb(uvd->sbuf[i].urb); + + qcm_stop_int_data(cam); + + if (!uvd->remove_pending) { + /* Set packet size to 0 */ + j = usb_set_interface(uvd->dev, uvd->iface, + uvd->ifaceAltInactive); + if (j < 0) { + err("usb_set_interface() error %d.", j); + uvd->last_error = j; + } + } +} + +static void qcm_process_isoc(struct uvd *uvd, struct usbvideo_frame *frame) +{ + struct qcm *cam = (struct qcm *) uvd->user_data; + int x; + struct rgb *rgbL0; + struct rgb *rgbL1; + struct bayL0 *bayL0; + struct bayL1 *bayL1; + int hor,ver,hordel,verdel; + assert(frame != NULL); + + switch (cam->size) { + case SIZE_160X120: + hor = 162; ver = 124; hordel = 1; verdel = 2; + break; + case SIZE_320X240: + default: + hor = 324; ver = 248; hordel = 2; verdel = 4; + break; + } + + if (frame->scanstate == ScanState_Scanning) { + while (RingQueue_GetLength(&uvd->dp) >= + 4 + (hor*verdel + hordel)) { + if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) && + (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xff) && + (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00) && + (RING_QUEUE_PEEK(&uvd->dp, 3) == 0xff)) { + frame->curline = 0; + frame->scanstate = ScanState_Lines; + frame->frameState = FrameState_Grabbing; + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 4); + /* + * if we're starting, we need to discard the first + * 4 lines of y bayer data + * and the first 2 gr elements of x bayer data + */ + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, + (hor*verdel + hordel)); + break; + } + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1); + } + } + + if (frame->scanstate == ScanState_Scanning) + return; + + /* now we can start processing bayer data so long as we have at least + * 2 lines worth of data. this is the simplest demosaicing method that + * I could think of. I use each 2x2 bayer element without interpolation + * to generate 4 rgb pixels. + */ + while ( frame->curline < cam->height && + (RingQueue_GetLength(&uvd->dp) >= hor*2)) { + /* get 2 lines of bayer for demosaicing + * into 2 lines of RGB */ + RingQueue_Dequeue(&uvd->dp, cam->scratch, hor*2); + bayL0 = (struct bayL0 *) cam->scratch; + bayL1 = (struct bayL1 *) (cam->scratch + hor); + /* frame->curline is the rgb y line */ + rgbL0 = (struct rgb *) + ( frame->data + (cam->width*3*frame->curline)); + /* w/2 because we're already doing 2 pixels */ + rgbL1 = rgbL0 + (cam->width/2); + + for (x=0; x < cam->width; x+=2) { + rgbL0->r = bayL0->r; + rgbL0->g = bayL0->g; + rgbL0->b = bayL1->b; + + rgbL0->r2 = bayL0->r; + rgbL0->g2 = bayL1->g; + rgbL0->b2 = bayL1->b; + + rgbL1->r = bayL0->r; + rgbL1->g = bayL1->g; + rgbL1->b = bayL1->b; + + rgbL1->r2 = bayL0->r; + rgbL1->g2 = bayL1->g; + rgbL1->b2 = bayL1->b; + + rgbL0++; + rgbL1++; + + bayL0++; + bayL1++; + } + + frame->seqRead_Length += cam->width*3*2; + frame->curline += 2; + } + /* See if we filled the frame */ + if (frame->curline == cam->height) { + frame->frameState = FrameState_Done_Hold; + frame->curline = 0; + uvd->curframe = -1; + uvd->stats.frame_num++; + } +} + +/* taken from konicawc */ +static int qcm_set_video_mode(struct uvd *uvd, struct video_window *vw) +{ + int ret; + int newsize; + int oldsize; + int x = vw->width; + int y = vw->height; + struct qcm *cam = (struct qcm *) uvd->user_data; + + if (x > 0 && y > 0) { + DEBUG(2, "trying to find size %d,%d", x, y); + for (newsize = 0; newsize <= MAX_FRAME_SIZE; newsize++) { + if ((camera_sizes[newsize].width == x) && + (camera_sizes[newsize].height == y)) + break; + } + } else + newsize = cam->size; + + if (newsize > MAX_FRAME_SIZE) { + DEBUG(1, "couldn't find size %d,%d", x, y); + return -EINVAL; + } + + if (newsize == cam->size) { + DEBUG(1, "Nothing to do"); + return 0; + } + + qcm_stop_data(uvd); + + if (cam->size != newsize) { + oldsize = cam->size; + cam->size = newsize; + ret = qcm_set_camera_size(uvd); + if (ret) { + err("Couldn't set camera size, err=%d",ret); + /* restore the original size */ + cam->size = oldsize; + return ret; + } + } + + /* Flush the input queue and clear any current frame in progress */ + + RingQueue_Flush(&uvd->dp); + if (uvd->curframe != -1) { + uvd->frame[uvd->curframe].curline = 0; + uvd->frame[uvd->curframe].seqRead_Length = 0; + uvd->frame[uvd->curframe].seqRead_Index = 0; + } + + CHECK_RET(ret, qcm_start_data(uvd)); + return 0; +} + +static int qcm_configure_video(struct uvd *uvd) +{ + int ret; + memset(&uvd->vpic, 0, sizeof(uvd->vpic)); + memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old)); + + uvd->vpic.colour = colour; + uvd->vpic.hue = hue; + uvd->vpic.brightness = brightness; + uvd->vpic.contrast = contrast; + uvd->vpic.whiteness = whiteness; + uvd->vpic.depth = 24; + uvd->vpic.palette = VIDEO_PALETTE_RGB24; + + memset(&uvd->vcap, 0, sizeof(uvd->vcap)); + strcpy(uvd->vcap.name, "QCM USB Camera"); + uvd->vcap.type = VID_TYPE_CAPTURE; + uvd->vcap.channels = 1; + uvd->vcap.audios = 0; + + uvd->vcap.minwidth = camera_sizes[SIZE_160X120].width; + uvd->vcap.minheight = camera_sizes[SIZE_160X120].height; + uvd->vcap.maxwidth = camera_sizes[SIZE_320X240].width; + uvd->vcap.maxheight = camera_sizes[SIZE_320X240].height; + + memset(&uvd->vchan, 0, sizeof(uvd->vchan)); + uvd->vchan.flags = 0 ; + uvd->vchan.tuners = 0; + uvd->vchan.channel = 0; + uvd->vchan.type = VIDEO_TYPE_CAMERA; + strcpy(uvd->vchan.name, "Camera"); + + CHECK_RET(ret, qcm_sensor_init(uvd)); + return 0; +} + +static int qcm_probe(struct usb_interface *intf, + const struct usb_device_id *devid) +{ + int err; + struct uvd *uvd; + struct usb_device *dev = interface_to_usbdev(intf); + struct qcm *cam; + size_t buffer_size; + unsigned char video_ep; + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + int i,j; + unsigned int ifacenum, ifacenum_inact=0; + __le16 sensor_id; + + /* we don't support multiconfig cams */ + if (dev->descriptor.bNumConfigurations != 1) + return -ENODEV; + + /* first check for the video interface and not + * the audio interface */ + interface = &intf->cur_altsetting[0]; + if ((interface->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC) + || (interface->desc.bInterfaceSubClass != + USB_CLASS_VENDOR_SPEC)) + return -ENODEV; + + /* + walk through each endpoint in each setting in the interface + stop when we find the one that's an isochronous IN endpoint. + */ + for (i=0; i < intf->num_altsetting; i++) { + interface = &intf->cur_altsetting[i]; + ifacenum = interface->desc.bAlternateSetting; + /* walk the end points */ + for (j=0; j < interface->desc.bNumEndpoints; j++) { + endpoint = &interface->endpoint[j].desc; + + if ((endpoint->bEndpointAddress & + USB_ENDPOINT_DIR_MASK) != USB_DIR_IN) + continue; /* not input then not good */ + + buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + if (!buffer_size) { + ifacenum_inact = ifacenum; + continue; /* 0 pkt size is not what we want */ + } + + if ((endpoint->bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_ISOC) { + video_ep = endpoint->bEndpointAddress; + /* break out of the search */ + goto good_videoep; + } + } + } + /* failed out since nothing useful was found */ + err("No suitable endpoint was found\n"); + return -ENODEV; + +good_videoep: + /* disable isochronous stream before doing anything else */ + err = qcm_stv_setb(dev, STV_ISO_ENABLE, 0); + if (err < 0) { + err("Failed to disable sensor stream"); + return -EIO; + } + + /* + Check that this is the same unknown sensor that is known to work. This + sensor is suspected to be the ST VV6422C001. I'll check the same value + that the qc-usb driver checks. This value is probably not even the + sensor ID since it matches the USB dev ID. Oh well. If it doesn't + match, it's probably a diff sensor so exit and apologize. + */ + err = qcm_stv_getw(dev, CMOS_SENSOR_IDREV, &sensor_id); + if (err < 0) { + err("Couldn't read sensor values. Err %d\n",err); + return err; + } + if (sensor_id != cpu_to_le16(0x08F0)) { + err("Sensor ID %x != %x. Unsupported. Sorry\n", + le16_to_cpu(sensor_id), (0x08F0)); + return -ENODEV; + } + + uvd = usbvideo_AllocateDevice(cams); + if (!uvd) + return -ENOMEM; + + cam = (struct qcm *) uvd->user_data; + + /* buf for doing demosaicing */ + cam->scratch = kmalloc(324*2, GFP_KERNEL); + if (!cam->scratch) /* uvd freed in dereg */ + return -ENOMEM; + + /* yes, if we fail after here, cam->scratch gets freed + by qcm_free_uvd */ + + err = qcm_alloc_int_urb(cam); + if (err < 0) + return err; + + /* yes, if we fail after here, int urb gets freed + by qcm_free_uvd */ + + RESTRICT_TO_RANGE(size, SIZE_160X120, SIZE_320X240); + cam->width = camera_sizes[size].width; + cam->height = camera_sizes[size].height; + cam->size = size; + + uvd->debug = debug; + uvd->flags = 0; + uvd->dev = dev; + uvd->iface = intf->altsetting->desc.bInterfaceNumber; + uvd->ifaceAltActive = ifacenum; + uvd->ifaceAltInactive = ifacenum_inact; + uvd->video_endp = video_ep; + uvd->iso_packet_len = buffer_size; + uvd->paletteBits = 1L << VIDEO_PALETTE_RGB24; + uvd->defaultPalette = VIDEO_PALETTE_RGB24; + uvd->canvas = VIDEOSIZE(320, 240); + uvd->videosize = VIDEOSIZE(cam->width, cam->height); + err = qcm_configure_video(uvd); + if (err) { + err("failed to configure video settings"); + return err; + } + + err = usbvideo_RegisterVideoDevice(uvd); + if (err) { /* the uvd gets freed in Deregister */ + err("usbvideo_RegisterVideoDevice() failed."); + return err; + } + + uvd->max_frame_size = (320 * 240 * 3); + qcm_register_input(cam, dev); + usb_set_intfdata(intf, uvd); + return 0; +} + +static void qcm_free_uvd(struct uvd *uvd) +{ + struct qcm *cam = (struct qcm *) uvd->user_data; + + kfree(cam->scratch); + qcm_unregister_input(cam); + qcm_free_int(cam); +} + +static struct usbvideo_cb qcm_driver = { + .probe = qcm_probe, + .setupOnOpen = qcm_setup_on_open, + .processData = qcm_process_isoc, + .setVideoMode = qcm_set_video_mode, + .startDataPump = qcm_start_data, + .stopDataPump = qcm_stop_data, + .adjustPicture = qcm_adjust_picture, + .userFree = qcm_free_uvd +}; + +static int __init qcm_init(void) +{ + info(DRIVER_DESC " " DRIVER_VERSION); + + return usbvideo_register( + &cams, + MAX_CAMERAS, + sizeof(struct qcm), + "QCM", + &qcm_driver, + THIS_MODULE, + qcm_table); +} + +static void __exit qcm_exit(void) +{ + usbvideo_Deregister(&cams); +} + +module_param(size, int, 0); +MODULE_PARM_DESC(size, "Initial Size 0: 160x120 1: 320x240"); +module_param(colour, int, 0); +MODULE_PARM_DESC(colour, "Initial colour"); +module_param(hue, int, 0); +MODULE_PARM_DESC(hue, "Initial hue"); +module_param(brightness, int, 0); +MODULE_PARM_DESC(brightness, "Initial brightness"); +module_param(contrast, int, 0); +MODULE_PARM_DESC(contrast, "Initial contrast"); +module_param(whiteness, int, 0); +MODULE_PARM_DESC(whiteness, "Initial whiteness"); + +#ifdef CONFIG_USB_DEBUG +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)"); +#endif + +module_init(qcm_init); +module_exit(qcm_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jaya Kumar"); +MODULE_DESCRIPTION("QCM USB Camera"); +MODULE_SUPPORTED_DEVICE("QCM USB Camera"); diff --git a/linux/drivers/media/video/usbvideo/quickcam_messenger.h b/linux/drivers/media/video/usbvideo/quickcam_messenger.h new file mode 100644 index 000000000..a4bb397ac --- /dev/null +++ b/linux/drivers/media/video/usbvideo/quickcam_messenger.h @@ -0,0 +1,126 @@ +#ifndef quickcam_messenger_h +#define quickcam_messenger_h + +#ifndef CONFIG_INPUT +/* if we're not using input we dummy out these functions */ +#define qcm_register_input(...) +#define qcm_unregister_input(...) +#define qcm_report_buttonstat(...) +#define qcm_setup_input_int(...) 0 +#define qcm_stop_int_data(...) +#define qcm_alloc_int_urb(...) 0 +#define qcm_free_int(...) +#endif + + +#define CHECK_RET(ret, expr) \ + if ((ret = expr) < 0) return ret + +/* Control Registers for the STVV6422 ASIC + * - this define is taken from the qc-usb-messenger code + */ +#define STV_ISO_ENABLE 0x1440 +#define ISOC_PACKET_SIZE 1023 + +/* Chip identification number including revision indicator */ +#define CMOS_SENSOR_IDREV 0xE00A + +struct rgb { + u8 b; + u8 g; + u8 r; + u8 b2; + u8 g2; + u8 r2; +}; + +struct bayL0 { +#ifdef __BIG_ENDIAN + u8 r; + u8 g; +#elif __LITTLE_ENDIAN + u8 g; + u8 r; +#else +#error not byte order defined +#endif +}; + +struct bayL1 { +#ifdef __BIG_ENDIAN + u8 g; + u8 b; +#elif __LITTLE_ENDIAN + u8 b; + u8 g; +#else +#error not byte order defined +#endif +}; + +struct cam_size { + u16 width; + u16 height; + u8 cmd; +}; + +static const struct cam_size camera_sizes[] = { + { 160, 120, 0xf }, + { 320, 240, 0x2 }, +}; + +enum frame_sizes { + SIZE_160X120 = 0, + SIZE_320X240 = 1, +}; + +#define MAX_FRAME_SIZE SIZE_320X240 + +struct qcm { + u16 colour; + u16 hue; + u16 brightness; + u16 contrast; + u16 whiteness; + + u8 size; + int height; + int width; + u8 *scratch; + struct urb *button_urb; + u8 button_sts; + u8 button_sts_buf; + +#ifdef CONFIG_INPUT + struct input_dev *input; + char input_physname[64]; +#endif +}; + +struct regval { + u16 reg; + u8 val; +}; +/* this table is derived from the +qc-usb-messenger code */ +static const struct regval regval_table[] = { + { STV_ISO_ENABLE, 0x00 }, + { 0x1436, 0x00 }, { 0x1432, 0x03 }, + { 0x143a, 0xF9 }, { 0x0509, 0x38 }, + { 0x050a, 0x38 }, { 0x050b, 0x38 }, + { 0x050c, 0x2A }, { 0x050d, 0x01 }, + { 0x1431, 0x00 }, { 0x1433, 0x34 }, + { 0x1438, 0x18 }, { 0x1439, 0x00 }, + { 0x143b, 0x05 }, { 0x143c, 0x00 }, + { 0x143e, 0x01 }, { 0x143d, 0x00 }, + { 0x1442, 0xe2 }, { 0x1500, 0xd0 }, + { 0x1500, 0xd0 }, { 0x1500, 0x50 }, + { 0x1501, 0xaf }, { 0x1502, 0xc2 }, + { 0x1503, 0x45 }, { 0x1505, 0x02 }, + { 0x150e, 0x8e }, { 0x150f, 0x37 }, + { 0x15c0, 0x00 }, +}; + +static const unsigned char marker[] = { 0x00, 0xff, 0x00, 0xFF }; + +#endif /* quickcam_messenger_h */ -- cgit v1.2.3 From acbc613601ba93f26adb3ba1d91aabeff718298b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 17 May 2006 15:43:51 -0300 Subject: Fix compilation for Miropcm20 and Cinergy From: Mauro Carvalho Chehab Miropcm compilation were assuming to compile under linux/drivers/media/video, but this is not true when compiling in-tree. Also, make allyesconfig were making "y" to an advanced configuration for Cinergy. gentree.pl also updated. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/radio/miropcm20-radio.c | 4 ++++ linux/drivers/media/radio/miropcm20-rds-core.c | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'linux/drivers') diff --git a/linux/drivers/media/radio/miropcm20-radio.c b/linux/drivers/media/radio/miropcm20-radio.c index ec8b16e8f..1a625e24c 100644 --- a/linux/drivers/media/radio/miropcm20-radio.c +++ b/linux/drivers/media/radio/miropcm20-radio.c @@ -23,7 +23,11 @@ #include #include "compat.h" #include +#ifdef _COMPAT_H +#include "../sound/oss/aci.h" +#else #include "../../../sound/oss/aci.h" +#endif #include "miropcm20-rds-core.h" static int radio_nr = -1; diff --git a/linux/drivers/media/radio/miropcm20-rds-core.c b/linux/drivers/media/radio/miropcm20-rds-core.c index 3f5aaf5fa..a9972dfc1 100644 --- a/linux/drivers/media/radio/miropcm20-rds-core.c +++ b/linux/drivers/media/radio/miropcm20-rds-core.c @@ -24,7 +24,11 @@ #endif #include +#ifdef _COMPAT_H +#include "../sound/oss/aci.h" +#else #include "../../../sound/oss/aci.h" +#endif #include "miropcm20-rds-core.h" #define DEBUG 0 -- cgit v1.2.3 From 9f2b9729fdb51b3b58468825946a5ab4f271efc2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 17 May 2006 15:54:15 -0300 Subject: Whitespace removal from previous patch From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/usbvideo/Kconfig | 2 +- linux/drivers/media/video/usbvideo/Makefile | 2 +- .../media/video/usbvideo/quickcam_messenger.c | 180 ++++++++++----------- .../media/video/usbvideo/quickcam_messenger.h | 14 +- 4 files changed, 99 insertions(+), 99 deletions(-) (limited to 'linux/drivers') diff --git a/linux/drivers/media/video/usbvideo/Kconfig b/linux/drivers/media/video/usbvideo/Kconfig index bb6c84d18..59fb899f3 100644 --- a/linux/drivers/media/video/usbvideo/Kconfig +++ b/linux/drivers/media/video/usbvideo/Kconfig @@ -43,7 +43,7 @@ config USB_QUICKCAM_MESSENGER select VIDEO_USBVIDEO ---help--- Say Y or M here to enable support for the USB Logitech Quickcam - Messenger webcam. + Messenger webcam. To compile this driver as a module, choose M here: the module will be called quickcam_messenger. diff --git a/linux/drivers/media/video/usbvideo/Makefile b/linux/drivers/media/video/usbvideo/Makefile index b20844787..4a1b144be 100644 --- a/linux/drivers/media/video/usbvideo/Makefile +++ b/linux/drivers/media/video/usbvideo/Makefile @@ -2,4 +2,4 @@ obj-$(CONFIG_VIDEO_USBVIDEO) += usbvideo.o obj-$(CONFIG_USB_IBMCAM) += ibmcam.o ultracam.o obj-$(CONFIG_USB_KONICAWC) += konicawc.o obj-$(CONFIG_USB_VICAM) += vicam.o -obj-$(CONFIG_USB_QUICKCAM_MESSENGER) += quickcam_messenger.o +obj-$(CONFIG_USB_QUICKCAM_MESSENGER) += quickcam_messenger.o diff --git a/linux/drivers/media/video/usbvideo/quickcam_messenger.c b/linux/drivers/media/video/usbvideo/quickcam_messenger.c index cedda9dad..8ad9f6af8 100644 --- a/linux/drivers/media/video/usbvideo/quickcam_messenger.c +++ b/linux/drivers/media/video/usbvideo/quickcam_messenger.c @@ -1,17 +1,17 @@ /* - * Driver for Logitech Quickcam Messenger usb video camera + * Driver for Logitech Quickcam Messenger usb video camera * Copyright (C) Jaya Kumar - * + * * This work was sponsored by CIS(M) Sdn Bhd. * History: * 05/08/2006 - Jaya Kumar - * I wrote this based on the konicawc by Simon Evans. + * I wrote this based on the konicawc by Simon Evans. * - * Full credit for reverse engineering and creating an initial * working linux driver for the VV6422 goes to the qce-ga project by - * Tuukka Toivonen, Jochen Hoenicke, Peter McConnell, + * Tuukka Toivonen, Jochen Hoenicke, Peter McConnell, * Cristiano De Michele, Georg Acher, Jean-Frederic Clere as well as - * others. + * others. * --- * 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 @@ -96,29 +96,29 @@ static void qcm_register_input(struct qcm *cam, struct usb_device *dev) return; } - input_dev->name = "QCM button"; + input_dev->name = "QCM button"; input_dev->phys = cam->input_physname; usb_to_input_id(dev, &input_dev->id); input_dev->cdev.dev = &dev->dev; input_dev->evbit[0] = BIT(EV_KEY); input_dev->keybit[LONG(BTN_0)] = BIT(BTN_0); - + input_dev->private = cam; - + input_register_device(cam->input); } -static void qcm_unregister_input(struct qcm *cam) +static void qcm_unregister_input(struct qcm *cam) { if (cam->input) { input_unregister_device(cam->input); cam->input = NULL; - } + } } -static void qcm_report_buttonstat(struct qcm *cam) -{ +static void qcm_report_buttonstat(struct qcm *cam) +{ if (cam->input) { input_report_key(cam->input, BTN_0, cam->button_sts); input_sync(cam->input); @@ -129,17 +129,17 @@ static void qcm_int_irq(struct urb *urb, struct pt_regs *regs) { int ret; struct uvd *uvd = urb->context; - struct qcm *cam; + struct qcm *cam; if (!CAMERA_IS_OPERATIONAL(uvd)) return; - if (!uvd->streaming) + if (!uvd->streaming) return; uvd->stats.urb_count++; - if (urb->status < 0) + if (urb->status < 0) uvd->stats.iso_err_count++; else { if (urb->actual_length > 0 ) { @@ -153,14 +153,14 @@ static void qcm_int_irq(struct urb *urb, struct pt_regs *regs) } ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret < 0) + if (ret < 0) err("usb_submit_urb error (%d)", ret); } static int qcm_setup_input_int(struct qcm *cam, struct uvd *uvd) { - int errflag; - usb_fill_int_urb(cam->button_urb, uvd->dev, + int errflag; + usb_fill_int_urb(cam->button_urb, uvd->dev, usb_rcvintpipe(uvd->dev, uvd->video_endp + 1), &cam->button_sts_buf, 1, @@ -179,7 +179,7 @@ static void qcm_stop_int_data(struct qcm *cam) } static int qcm_alloc_int_urb(struct qcm *cam) -{ +{ cam->button_urb = usb_alloc_urb(0, GFP_KERNEL); if (!cam->button_urb) @@ -217,7 +217,7 @@ static int qcm_stv_setw(struct usb_device *dev, u16 reg, u16 val) return ret; } -static int qcm_stv_getw(struct usb_device *dev, unsigned short reg, +static int qcm_stv_getw(struct usb_device *dev, unsigned short reg, __le16 *val) { int ret; @@ -248,13 +248,13 @@ static void qcm_hsv2rgb(u16 hue, u16 sat, u16 val, u16 *r, u16 *g, u16 *b) unsigned int segment, valsat; signed int h = (signed int) hue; unsigned int s = (sat - 32768) * 2; /* rescale */ - unsigned int v = val; + unsigned int v = val; unsigned int p; /* the registers controling gain are 8 bit of which we affect only the last 4 bits with our gain. - we know that if saturation is 0, (unsaturated) then + we know that if saturation is 0, (unsaturated) then we're grayscale (center axis of the colour cone) so we set rgb=value. we use a formula obtained from wikipedia to map the cone to the RGB plane. it's @@ -271,10 +271,10 @@ static void qcm_hsv2rgb(u16 hue, u16 sat, u16 val, u16 *r, u16 *g, u16 *b) the bottom side (the point) and the stuff just up of that is black so we simplify those two cases. */ - if (sat < 32768) { + if (sat < 32768) { /* anything less than this is unsaturated */ - *r = val; - *g = val; + *r = val; + *g = val; *b = val; return; } @@ -291,7 +291,7 @@ static void qcm_hsv2rgb(u16 hue, u16 sat, u16 val, u16 *r, u16 *g, u16 *b) from qc-usb-messenger code. the 10923 is 0xFFFF/6 to divide the cone into 6 sectors. */ - segment = (h + 10923) & 0xFFFF; + segment = (h + 10923) & 0xFFFF; segment = segment*3 >> 16; /* 0..2: 0=R, 1=G, 2=B */ hue -= segment * 21845; /* -10923..10923 */ h = hue; @@ -339,7 +339,7 @@ static void qcm_hsv2rgb(u16 hue, u16 sat, u16 val, u16 *r, u16 *g, u16 *b) } } -static int qcm_sensor_set_gains(struct uvd *uvd, u16 hue, +static int qcm_sensor_set_gains(struct uvd *uvd, u16 hue, u16 saturation, u16 value) { int ret; @@ -362,7 +362,7 @@ static int qcm_sensor_set_gains(struct uvd *uvd, u16 hue, b |= 0x30; /* set the r,g,b gain registers */ - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x0509, r)); + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x0509, r)); CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050A, g)); CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050B, b)); @@ -385,7 +385,7 @@ static int qcm_sensor_set_exposure(struct uvd *uvd, int exposure) /* max value for formedval is 14 */ formedval = min(formedval, 14); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143A, 0xF0 | formedval)); CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01)); return 0; @@ -396,7 +396,7 @@ static int qcm_sensor_setlevels(struct uvd *uvd, int brightness, int contrast, { int ret; /* brightness is exposure, contrast is gain, colour is saturation */ - CHECK_RET(ret, + CHECK_RET(ret, qcm_sensor_set_exposure(uvd, brightness)); CHECK_RET(ret, qcm_sensor_set_gains(uvd, hue, colour, contrast)); @@ -415,12 +415,12 @@ static int qcm_sensor_set_shutter(struct uvd *uvd, int whiteness) { int ret; /* some rescaling as done by the qc-usb-messenger code */ - if (whiteness > 0xC000) + if (whiteness > 0xC000) whiteness = 0xC000 + (whiteness & 0x3FFF)*8; - - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143D, + + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143D, (whiteness >> 8) & 0xFF)); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143E, + CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143E, (whiteness >> 16) & 0x03)); CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01)); @@ -434,12 +434,12 @@ static int qcm_sensor_init(struct uvd *uvd) int i; for (i=0; i < sizeof(regval_table)/sizeof(regval_table[0]) ; i++) { - CHECK_RET(ret, qcm_stv_setb(uvd->dev, - regval_table[i].reg, + CHECK_RET(ret, qcm_stv_setb(uvd->dev, + regval_table[i].reg, regval_table[i].val)); } - CHECK_RET(ret, qcm_stv_setw(uvd->dev, 0x15c1, + CHECK_RET(ret, qcm_stv_setw(uvd->dev, 0x15c1, cpu_to_le16(ISOC_PACKET_SIZE))); CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x15c3, 0x08)); CHECK_RET(ret, ret = qcm_stv_setb(uvd->dev, 0x143f, 0x01)); @@ -448,9 +448,9 @@ static int qcm_sensor_init(struct uvd *uvd) CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd)); - CHECK_RET(ret, qcm_sensor_setlevels(uvd, uvd->vpic.brightness, + CHECK_RET(ret, qcm_sensor_setlevels(uvd, uvd->vpic.brightness, uvd->vpic.contrast, uvd->vpic.hue, uvd->vpic.colour)); - + CHECK_RET(ret, qcm_sensor_set_shutter(uvd, uvd->vpic.whiteness)); CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd)); @@ -474,7 +474,7 @@ static int qcm_setup_on_open(struct uvd *uvd) { int ret; - CHECK_RET(ret, qcm_sensor_set_gains(uvd, uvd->vpic.hue, + CHECK_RET(ret, qcm_sensor_set_gains(uvd, uvd->vpic.hue, uvd->vpic.colour, uvd->vpic.contrast)); CHECK_RET(ret, qcm_sensor_set_exposure(uvd, uvd->vpic.brightness)); CHECK_RET(ret, qcm_sensor_set_shutter(uvd, uvd->vpic.whiteness)); @@ -494,22 +494,22 @@ static void qcm_adjust_picture(struct uvd *uvd) return; } - /* if there's been a change in contrast, hue, or - colour then we need to recalculate hsv in order + /* if there's been a change in contrast, hue, or + colour then we need to recalculate hsv in order to update gains */ - if ((cam->contrast != uvd->vpic.contrast) || + if ((cam->contrast != uvd->vpic.contrast) || (cam->hue != uvd->vpic.hue) || - (cam->colour != uvd->vpic.colour)) { + (cam->colour != uvd->vpic.colour)) { cam->contrast = uvd->vpic.contrast; cam->hue = uvd->vpic.hue; cam->colour = uvd->vpic.colour; - ret = qcm_sensor_set_gains(uvd, cam->hue, cam->colour, + ret = qcm_sensor_set_gains(uvd, cam->hue, cam->colour, cam->contrast); if (ret) { err("can't set gains. abandoning pic adjustment"); return; } - } + } if (cam->brightness != uvd->vpic.brightness) { cam->brightness = uvd->vpic.brightness; @@ -588,7 +588,7 @@ static int qcm_compress_iso(struct uvd *uvd, struct urb *dataurb) uvd->stats.iso_err_count++; continue; } - if (!n) + if (!n) continue; totlen += qcm_process_frame(uvd, cdata, n); @@ -599,7 +599,7 @@ static int qcm_compress_iso(struct uvd *uvd, struct urb *dataurb) static void resubmit_urb(struct uvd *uvd, struct urb *urb) { int ret; - + urb->dev = uvd->dev; ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret) @@ -614,7 +614,7 @@ static void qcm_isoc_irq(struct urb *urb, struct pt_regs *regs) if (!CAMERA_IS_OPERATIONAL(uvd)) return; - if (!uvd->streaming) + if (!uvd->streaming) return; uvd->stats.urb_count++; @@ -689,25 +689,25 @@ static void qcm_stop_data(struct uvd *uvd) struct qcm *cam = (struct qcm *) uvd->user_data; int i, j; int ret; - + if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL)) return; ret = qcm_camera_off(uvd); - if (ret) + if (ret) warn("couldn't turn the cam off."); uvd->streaming = 0; /* Unschedule all of the iso td's */ - for (i=0; i < USBVIDEO_NUMSBUF; i++) + for (i=0; i < USBVIDEO_NUMSBUF; i++) usb_kill_urb(uvd->sbuf[i].urb); qcm_stop_int_data(cam); if (!uvd->remove_pending) { /* Set packet size to 0 */ - j = usb_set_interface(uvd->dev, uvd->iface, + j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive); if (j < 0) { err("usb_set_interface() error %d.", j); @@ -717,7 +717,7 @@ static void qcm_stop_data(struct uvd *uvd) } static void qcm_process_isoc(struct uvd *uvd, struct usbvideo_frame *frame) -{ +{ struct qcm *cam = (struct qcm *) uvd->user_data; int x; struct rgb *rgbL0; @@ -736,7 +736,7 @@ static void qcm_process_isoc(struct uvd *uvd, struct usbvideo_frame *frame) hor = 324; ver = 248; hordel = 2; verdel = 4; break; } - + if (frame->scanstate == ScanState_Scanning) { while (RingQueue_GetLength(&uvd->dp) >= 4 + (hor*verdel + hordel)) { @@ -748,12 +748,12 @@ static void qcm_process_isoc(struct uvd *uvd, struct usbvideo_frame *frame) frame->scanstate = ScanState_Lines; frame->frameState = FrameState_Grabbing; RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 4); - /* - * if we're starting, we need to discard the first + /* + * if we're starting, we need to discard the first * 4 lines of y bayer data * and the first 2 gr elements of x bayer data - */ - RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, + */ + RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, (hor*verdel + hordel)); break; } @@ -763,13 +763,13 @@ static void qcm_process_isoc(struct uvd *uvd, struct usbvideo_frame *frame) if (frame->scanstate == ScanState_Scanning) return; - + /* now we can start processing bayer data so long as we have at least * 2 lines worth of data. this is the simplest demosaicing method that - * I could think of. I use each 2x2 bayer element without interpolation - * to generate 4 rgb pixels. + * I could think of. I use each 2x2 bayer element without interpolation + * to generate 4 rgb pixels. */ - while ( frame->curline < cam->height && + while ( frame->curline < cam->height && (RingQueue_GetLength(&uvd->dp) >= hor*2)) { /* get 2 lines of bayer for demosaicing * into 2 lines of RGB */ @@ -777,7 +777,7 @@ static void qcm_process_isoc(struct uvd *uvd, struct usbvideo_frame *frame) bayL0 = (struct bayL0 *) cam->scratch; bayL1 = (struct bayL1 *) (cam->scratch + hor); /* frame->curline is the rgb y line */ - rgbL0 = (struct rgb *) + rgbL0 = (struct rgb *) ( frame->data + (cam->width*3*frame->curline)); /* w/2 because we're already doing 2 pixels */ rgbL1 = rgbL0 + (cam->width/2); @@ -801,7 +801,7 @@ static void qcm_process_isoc(struct uvd *uvd, struct usbvideo_frame *frame) rgbL0++; rgbL1++; - + bayL0++; bayL1++; } @@ -831,11 +831,11 @@ static int qcm_set_video_mode(struct uvd *uvd, struct video_window *vw) if (x > 0 && y > 0) { DEBUG(2, "trying to find size %d,%d", x, y); for (newsize = 0; newsize <= MAX_FRAME_SIZE; newsize++) { - if ((camera_sizes[newsize].width == x) && + if ((camera_sizes[newsize].width == x) && (camera_sizes[newsize].height == y)) break; } - } else + } else newsize = cam->size; if (newsize > MAX_FRAME_SIZE) { @@ -851,7 +851,7 @@ static int qcm_set_video_mode(struct uvd *uvd, struct video_window *vw) qcm_stop_data(uvd); if (cam->size != newsize) { - oldsize = cam->size; + oldsize = cam->size; cam->size = newsize; ret = qcm_set_camera_size(uvd); if (ret) { @@ -911,7 +911,7 @@ static int qcm_configure_video(struct uvd *uvd) return 0; } -static int qcm_probe(struct usb_interface *intf, +static int qcm_probe(struct usb_interface *intf, const struct usb_device_id *devid) { int err; @@ -933,12 +933,12 @@ static int qcm_probe(struct usb_interface *intf, /* first check for the video interface and not * the audio interface */ interface = &intf->cur_altsetting[0]; - if ((interface->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC) - || (interface->desc.bInterfaceSubClass != + if ((interface->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC) + || (interface->desc.bInterfaceSubClass != USB_CLASS_VENDOR_SPEC)) return -ENODEV; - /* + /* walk through each endpoint in each setting in the interface stop when we find the one that's an isochronous IN endpoint. */ @@ -949,7 +949,7 @@ static int qcm_probe(struct usb_interface *intf, for (j=0; j < interface->desc.bNumEndpoints; j++) { endpoint = &interface->endpoint[j].desc; - if ((endpoint->bEndpointAddress & + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_IN) continue; /* not input then not good */ @@ -959,8 +959,8 @@ static int qcm_probe(struct usb_interface *intf, continue; /* 0 pkt size is not what we want */ } - if ((endpoint->bmAttributes & - USB_ENDPOINT_XFERTYPE_MASK) == + if ((endpoint->bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC) { video_ep = endpoint->bEndpointAddress; /* break out of the search */ @@ -972,20 +972,20 @@ static int qcm_probe(struct usb_interface *intf, err("No suitable endpoint was found\n"); return -ENODEV; -good_videoep: - /* disable isochronous stream before doing anything else */ - err = qcm_stv_setb(dev, STV_ISO_ENABLE, 0); +good_videoep: + /* disable isochronous stream before doing anything else */ + err = qcm_stv_setb(dev, STV_ISO_ENABLE, 0); if (err < 0) { err("Failed to disable sensor stream"); return -EIO; } - + /* Check that this is the same unknown sensor that is known to work. This sensor is suspected to be the ST VV6422C001. I'll check the same value that the qc-usb driver checks. This value is probably not even the - sensor ID since it matches the USB dev ID. Oh well. If it doesn't - match, it's probably a diff sensor so exit and apologize. + sensor ID since it matches the USB dev ID. Oh well. If it doesn't + match, it's probably a diff sensor so exit and apologize. */ err = qcm_stv_getw(dev, CMOS_SENSOR_IDREV, &sensor_id); if (err < 0) { @@ -993,27 +993,27 @@ good_videoep: return err; } if (sensor_id != cpu_to_le16(0x08F0)) { - err("Sensor ID %x != %x. Unsupported. Sorry\n", + err("Sensor ID %x != %x. Unsupported. Sorry\n", le16_to_cpu(sensor_id), (0x08F0)); return -ENODEV; } - + uvd = usbvideo_AllocateDevice(cams); - if (!uvd) + if (!uvd) return -ENOMEM; cam = (struct qcm *) uvd->user_data; - /* buf for doing demosaicing */ - cam->scratch = kmalloc(324*2, GFP_KERNEL); + /* buf for doing demosaicing */ + cam->scratch = kmalloc(324*2, GFP_KERNEL); if (!cam->scratch) /* uvd freed in dereg */ - return -ENOMEM; + return -ENOMEM; /* yes, if we fail after here, cam->scratch gets freed by qcm_free_uvd */ err = qcm_alloc_int_urb(cam); - if (err < 0) + if (err < 0) return err; /* yes, if we fail after here, int urb gets freed @@ -1042,11 +1042,11 @@ good_videoep: return err; } - err = usbvideo_RegisterVideoDevice(uvd); + err = usbvideo_RegisterVideoDevice(uvd); if (err) { /* the uvd gets freed in Deregister */ err("usbvideo_RegisterVideoDevice() failed."); return err; - } + } uvd->max_frame_size = (320 * 240 * 3); qcm_register_input(cam, dev); diff --git a/linux/drivers/media/video/usbvideo/quickcam_messenger.h b/linux/drivers/media/video/usbvideo/quickcam_messenger.h index a4bb397ac..baab9c081 100644 --- a/linux/drivers/media/video/usbvideo/quickcam_messenger.h +++ b/linux/drivers/media/video/usbvideo/quickcam_messenger.h @@ -20,7 +20,7 @@ * - this define is taken from the qc-usb-messenger code */ #define STV_ISO_ENABLE 0x1440 -#define ISOC_PACKET_SIZE 1023 +#define ISOC_PACKET_SIZE 1023 /* Chip identification number including revision indicator */ #define CMOS_SENSOR_IDREV 0xE00A @@ -64,9 +64,9 @@ struct cam_size { u8 cmd; }; -static const struct cam_size camera_sizes[] = { - { 160, 120, 0xf }, - { 320, 240, 0x2 }, +static const struct cam_size camera_sizes[] = { + { 160, 120, 0xf }, + { 320, 240, 0x2 }, }; enum frame_sizes { @@ -101,11 +101,11 @@ struct regval { u16 reg; u8 val; }; -/* this table is derived from the +/* this table is derived from the qc-usb-messenger code */ static const struct regval regval_table[] = { - { STV_ISO_ENABLE, 0x00 }, - { 0x1436, 0x00 }, { 0x1432, 0x03 }, + { STV_ISO_ENABLE, 0x00 }, + { 0x1436, 0x00 }, { 0x1432, 0x03 }, { 0x143a, 0xF9 }, { 0x0509, 0x38 }, { 0x050a, 0x38 }, { 0x050b, 0x38 }, { 0x050c, 0x2A }, { 0x050d, 0x01 }, -- cgit v1.2.3