diff options
30 files changed, 1970 insertions, 1334 deletions
diff --git a/v4l/ChangeLog b/v4l/ChangeLog index 5d5251da7..834bee3d6 100644 --- a/v4l/ChangeLog +++ b/v4l/ChangeLog @@ -1,3 +1,76 @@ +2006-01-01 08:17 mcisely + + * v4l_experimental/pvrusb2/Kbuild: + * v4l_experimental/pvrusb2/README: + * v4l_experimental/pvrusb2/pvrusb2-audio.c: (set_stereo), + (check_stereo), (msp3400_check), (msp3400_update), + (get_audio_status), (pvr2_msp3400_detach), + (pvr2_i2c_msp3400_setup): + * v4l_experimental/pvrusb2/pvrusb2-audio.h: + * v4l_experimental/pvrusb2/pvrusb2-context.c: + (pvr2_context_trigger_poll), (pvr2_context_poll), + (pvr2_context_setup), (pvr2_context_create), (pvr2_context_exit): + * v4l_experimental/pvrusb2/pvrusb2-context.h: + * v4l_experimental/pvrusb2/pvrusb2-debug.h: + * v4l_experimental/pvrusb2/pvrusb2-debugifc.c: + (pvr2_debugifc_print_info): + * v4l_experimental/pvrusb2/pvrusb2-demod.c: (set_config), + (demod_check), (demod_update), (demod_detach), + (pvr2_i2c_demod_setup): + * v4l_experimental/pvrusb2/pvrusb2-demod.h: + * v4l_experimental/pvrusb2/pvrusb2-encoder.c: + * v4l_experimental/pvrusb2/pvrusb2-hdw-internal.h: + * v4l_experimental/pvrusb2/pvrusb2-hdw.c: + (pvr2_hdw_subsys_bit_chg_no_lock), (get_default_tuner_type), + (pvr2_hdw_setup_low), (pvr2_hdw_destroy), + (pvr2_hdw_commit_ctl_internal), (pvr2_hdw_poll), + (pvr2_hdw_setup_poll_trigger), (pvr2_hdw_poll_trigger_unlocked), + (pvr2_hdw_poll_trigger), (pvr2_hdw_get_signal_status_internal): + * v4l_experimental/pvrusb2/pvrusb2-hdw.h: + * v4l_experimental/pvrusb2/pvrusb2-i2c-chips-v4l2.c: + (pvr2_i2c_probe), (pvr2_i2c_get_op): + * v4l_experimental/pvrusb2/pvrusb2-i2c-cmd-v4l2.c: (set_standard), + (check_standard), (set_bcsh), (check_bcsh), (set_volume), + (check_volume), (set_frequency), (check_frequency), (set_size), + (check_size): + * v4l_experimental/pvrusb2/pvrusb2-i2c-cmd-v4l2.h: + * v4l_experimental/pvrusb2/pvrusb2-i2c-core.c: (pvr2_i2c_write), + (pvr2_i2c_read), (pvr2_i2c_xfer), (pvr2_i2c_control), + (pvr2_i2c_functionality), (pvr2_i2c_core_singleton), + (pvr2_i2c_client_cmd), (pvr2_i2c_core_cmd), (handler_check), + (pvr2_i2c_core_sync), (pvr2_i2c_core_check_stale), + (pvr2_i2c_client_describe), (pvr2_i2c_report), + (pvr2_i2c_attach_inform), (pvr2_i2c_detach_inform), (do_i2c_scan), + (pvr2_i2c_core_init), (pvr2_i2c_core_done): + * v4l_experimental/pvrusb2/pvrusb2-i2c-core.h: + * v4l_experimental/pvrusb2/pvrusb2-i2c.c: + * v4l_experimental/pvrusb2/pvrusb2-i2c.h: + * v4l_experimental/pvrusb2/pvrusb2-main.c: + * v4l_experimental/pvrusb2/pvrusb2-tuner.c: (set_type), + (tuner_check), (tuner_update), (pvr2_tuner_detach), + (pvr2_i2c_tuner_setup): + * v4l_experimental/pvrusb2/pvrusb2-tuner.h: + * v4l_experimental/pvrusb2/pvrusb2-v4l2.c: + (pvr2_v4l2_destroy_no_lock), (pvr2_v4l2_release): + * v4l_experimental/pvrusb2/pvrusb2-version.h: + * v4l_experimental/pvrusb2/pvrusb2-video-v4l.c: (set_input), + (check_input), (set_audio), (check_audio), (decoder_detach), + (decoder_check), (decoder_update), (decoder_detect), + (decoder_enable), (decoder_is_tuned), (pvr2_i2c_decoder_v4l_setup): + * v4l_experimental/pvrusb2/pvrusb2-video-v4l.h: + * v4l_experimental/pvrusb2/pvrusb2-video.c: + * v4l_experimental/pvrusb2/pvrusb2-video.h: + + - New mechanism for handling all the I2C support modules. + Broadcast is used for V4L2 commands to the I2C clients, + identification and adoption of attached I2C clients is + centralized to a single place, on-the-fly update of reattached + modules is handled uniformly, and code overall is reworked to + hopefully improve maintainability and remove a lot of the uglier + hackish aspects of the previous implementation. + + Signed-off-by: Mike Isely <isely@pobox.com> + 2006-01-01 01:12 hverkuil * linux/Documentation/video4linux/cx2341x/fw-calling.txt: diff --git a/v4l_experimental/pvrusb2/Kbuild b/v4l_experimental/pvrusb2/Kbuild index 2166db3e7..635e58b28 100644 --- a/v4l_experimental/pvrusb2/Kbuild +++ b/v4l_experimental/pvrusb2/Kbuild @@ -1,12 +1,14 @@ pvrusb2-objs := \ + pvrusb2-i2c-core.o \ + pvrusb2-i2c-cmd-v4l2.o \ pvrusb2-audio.o \ + pvrusb2-i2c-chips-v4l2.o \ pvrusb2-encoder.o \ - pvrusb2-video.o \ pvrusb2-video-v4l.o \ pvrusb2-eeprom.o \ pvrusb2-tuner.o \ - pvrusb2-i2c.o \ + pvrusb2-demod.o \ pvrusb2-main.o \ pvrusb2-hdw.o \ pvrusb2-v4l2.o \ diff --git a/v4l_experimental/pvrusb2/README b/v4l_experimental/pvrusb2/README index dc93b7750..073491307 100644 --- a/v4l_experimental/pvrusb2/README +++ b/v4l_experimental/pvrusb2/README @@ -1,5 +1,5 @@ -$Id: README,v 1.1 2005/11/14 13:31:24 mchehab Exp $ +$Id: README,v 1.2 2006/01/01 08:26:03 mcisely Exp $ Mike Isely <isely@pobox.com> pvrusb2 driver @@ -132,13 +132,33 @@ Source file list / functional overview: (Policy, instantiation, and arbitration of pvrusb2 devices fall within the jurisdiction of pvrusb-context not here). - pvrusb2-i2c.[ch] - This module provides an implementation of a + pvrusb2-i2c-chips-*.c - These modules implement the glue logic to + tie together and configure various I2C modules as they attach to + the I2C bus. There are two versions of this file. The "v4l2" + version is intended to be used in-tree alongside V4L, where we + implement just the logic that makes sense for a pure V4L + environment. The "all" version is intended for use outside of + V4L, where we might encounter other possibly "challenging" modules + from ivtv or older kernel snapshots (or even the support modules + in the standalone snapshot). + + pvrusb2-i2c-cmd-v4l1.[ch] - This module implements generic V4L1 + compatible commands to the I2C modules. It is here where state + changes inside the pvrusb2 driver are translated into V4L1 + commands that are in turn send to the various I2C modules. + + pvrusb2-i2c-cmd-v4l2.[ch] - This module implements generic V4L2 + compatible commands to the I2C modules. It is here where state + changes inside the pvrusb2 driver are translated into V4L2 + commands that are in turn send to the various I2C modules. + + pvrusb2-i2c-core.[ch] - This module provides an implementation of a kernel-friendly I2C adaptor driver, through which other external I2C client drivers (e.g. msp3400, tuner, lirc) may connect and operate corresponding chips within the the pvrusb2 device. It is through here that other V4L modules can reach into this driver to operate specific pieces (and those modules are in turn driven by - glue logic here which is coordinated by pvrusb2-hdw, doled out by + glue logic which is coordinated by pvrusb2-hdw, doled out by pvrusb2-context, and then ultimately made available to users through one of the high level interfaces). @@ -176,10 +196,11 @@ Source file list / functional overview: ways. Note that **ALL** V4L functionality is published only through here and nowhere else. - pvrusb2-video.[ch] - This is glue logic that resides between this + pvrusb2-video-*.[ch] - This is glue logic that resides between this driver and the saa711x.ko I2C client driver (which is found elsewhere in V4L). Note that saa711x.ko used to be known as - saa7115.ko in ivtv. + saa7115.ko in ivtv. There are two versions of this; one is + selected depending on the particular saa711[5x].ko that is found. pvrusb2.h - This header contains compile time tunable parameters (and at the moment the driver has very little that needs to be diff --git a/v4l_experimental/pvrusb2/pvrusb2-audio.c b/v4l_experimental/pvrusb2/pvrusb2-audio.c index 8ba8827be..81408e1a6 100644 --- a/v4l_experimental/pvrusb2/pvrusb2-audio.c +++ b/v4l_experimental/pvrusb2/pvrusb2-audio.c @@ -1,6 +1,6 @@ /* * - * $Id: pvrusb2-audio.c,v 1.2 2005/11/29 14:10:44 mchehab Exp $ + * $Id: pvrusb2-audio.c,v 1.3 2006/01/01 08:26:03 mcisely Exp $ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> @@ -20,7 +20,6 @@ * */ -#include "pvrusb2-i2c.h" #include "pvrusb2-audio.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" @@ -28,6 +27,15 @@ #include <linux/videodev.h> #include <media/audiochip.h> +struct pvr2_msp3400_handler { + struct pvr2_hdw *hdw; + struct pvr2_i2c_client *client; + struct pvr2_i2c_handler i2c_handler; + struct pvr2_audio_stat astat; + unsigned long stale_mask; +}; + + /* MCI <isely@pobox.com> 10-Mar-2005 - Rather than operate the msp34xx @@ -60,48 +68,22 @@ static int xlat_audiomode_to_v4l2(int id) return V4L2_TUNER_MODE_STEREO; } -/* Relay the video standard into the msp3400 module so that it can use - that information as additional clue(s) for correctly detecting - audio. */ -int pvr2_audio_set_standard(struct pvr2_hdw *hdw) -{ - struct video_channel vc; - memset(&vc,0,sizeof(vc)); - switch (hdw->controls[PVR2_CID_VIDEOSTANDARD].value) { - default: - case PVR2_CVAL_VIDEOSTANDARD_NTSC_M: - case PVR2_CVAL_VIDEOSTANDARD_PAL_M: /* Hack */ - vc.norm = VIDEO_MODE_NTSC; - break; - case PVR2_CVAL_VIDEOSTANDARD_SECAM_L: - vc.norm = VIDEO_MODE_SECAM; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_BG: - case PVR2_CVAL_VIDEOSTANDARD_PAL_I: - case PVR2_CVAL_VIDEOSTANDARD_PAL_DK: - vc.norm = VIDEO_MODE_PAL; - break; - } - hdw->subsys_enabled_mask |= PVR2_SUBSYS_AUDIO_CFG_STD; - return pvr2_i2c_msp3400_cmd(hdw,VIDIOCSCHAN,&vc); -} /* This function selects the correct audio input source */ -int pvr2_audio_set_stereo(struct pvr2_hdw *hdw) +static void set_stereo(struct pvr2_msp3400_handler *ctxt) { - int stat; + struct pvr2_hdw *hdw = ctxt->hdw; unsigned short sarg = 0; struct msp_matrix mspm; - pvr2_trace(PVR2_TRACE_AUDIO,"pvr_audio_set_stereo"); + pvr2_trace(PVR2_TRACE_CHIPS,"i2c msp3400 set_stereo"); 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); - stat = pvr2_i2c_msp3400_cmd(hdw,VIDIOC_S_TUNER,&vt); - if (stat < 0) return stat; + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_TUNER,&vt); } sarg = AUDIO_TUNER; @@ -119,8 +101,7 @@ int pvr2_audio_set_stereo(struct pvr2_hdw *hdw) sarg = AUDIO_EXTERN; break; } - stat = pvr2_i2c_msp3400_cmd(hdw,AUDC_SET_INPUT,&sarg); - if (stat < 0) return stat; + pvr2_i2c_client_cmd(ctxt->client,AUDC_SET_INPUT,&sarg); /* The above should have been enough to do the job, however msp3400.ko does an incomplete job of handling the scart @@ -144,82 +125,117 @@ int pvr2_audio_set_stereo(struct pvr2_hdw *hdw) break; } mspm.output = 1; - stat = pvr2_i2c_msp3400_cmd(hdw,MSP_SET_MATRIX,&mspm); - if (stat < 0) return stat; + pvr2_i2c_client_cmd(ctxt->client,MSP_SET_MATRIX,&mspm); +} - hdw->subsys_enabled_mask |= PVR2_SUBSYS_AUDIO_CFG_MODE; - return 0; +static int check_stereo(struct pvr2_msp3400_handler *ctxt) +{ + struct pvr2_hdw *hdw = ctxt->hdw; + return hdw->controls[PVR2_CID_INPUT].dirty != 0; } -/* This sets the audio volume parameters */ -int pvr2_audio_setvolume(struct pvr2_hdw *hdw) + +struct pvr2_msp3400_ops { + void (*update)(struct pvr2_msp3400_handler *); + int (*check)(struct pvr2_msp3400_handler *); +}; + + +static const struct pvr2_msp3400_ops msp3400_ops[] = { + { .update = set_stereo, .check = check_stereo}, +}; + + +static int msp3400_check(struct pvr2_msp3400_handler *ctxt) { - struct video_audio vt; - memset(&vt,0,sizeof(vt)); - vt.flags = (VIDEO_AUDIO_VOLUME | - VIDEO_AUDIO_BALANCE | - VIDEO_AUDIO_BASS | - VIDEO_AUDIO_TREBLE | - VIDEO_AUDIO_MUTABLE); - if (hdw->controls[PVR2_CID_MUTE].value) vt.flags |= VIDEO_AUDIO_MUTE; - vt.volume = hdw->controls[PVR2_CID_VOLUME].value; - vt.balance = hdw->controls[PVR2_CID_BALANCE].value; - vt.bass = hdw->controls[PVR2_CID_BASS].value; - vt.treble = hdw->controls[PVR2_CID_TREBLE].value; - pvr2_trace(PVR2_TRACE_AUDIO, - "pvr_audio_setvolume(vol=%d bal=%d bas=%d treb=%d mute=%d)", - vt.volume,vt.balance,vt.bass,vt.treble, - (vt.flags & VIDEO_AUDIO_MUTE) != 0); - - hdw->subsys_enabled_mask |= PVR2_SUBSYS_AUDIO_CFG_VBBTM; - - return pvr2_i2c_msp3400_cmd(hdw,VIDIOCSAUDIO,&vt); + unsigned long msk; + unsigned int idx; + + for (idx = 0; idx < sizeof(msp3400_ops)/sizeof(msp3400_ops[0]); + idx++) { + msk = 1 << idx; + if (ctxt->stale_mask & msk) continue; + if (msp3400_ops[idx].check(ctxt)) { + ctxt->stale_mask |= msk; + } + } + return ctxt->stale_mask != 0; } + +static void msp3400_update(struct pvr2_msp3400_handler *ctxt) +{ + unsigned long msk; + unsigned int idx; + + for (idx = 0; idx < sizeof(msp3400_ops)/sizeof(msp3400_ops[0]); + idx++) { + msk = 1 << idx; + if (!(ctxt->stale_mask & msk)) continue; + ctxt->stale_mask &= ~msk; + msp3400_ops[idx].update(ctxt); + } +} + + /* This reads back the current volume parameters and signal type */ -int pvr2_audio_update_status(struct pvr2_hdw *hdw) +static int get_audio_status(struct pvr2_msp3400_handler *ctxt) { struct video_audio vt; int stat; - stat = pvr2_i2c_msp3400_cmd(hdw,VIDIOCGAUDIO,&vt); + memset(&vt,0,sizeof(vt)); + stat = pvr2_i2c_client_cmd(ctxt->client,VIDIOCGAUDIO,&vt); if (stat < 0) return stat; -#ifdef notdef - if (vt.flags & VIDEO_AUDIO_MUTABLE) { - hdw->controls[PVR2_CID_MUTE].value = - (vt.flags & VIDEO_AUDIO_MUTE) ? 1 : 0; - } - if (vt.flags & VIDEO_AUDIO_VOLUME) { - pvr2_trace(PVR2_TRACE_AUDIO, - "pvr_audio_update_status: got volume"); - hdw->controls[PVR2_CID_VOLUME].value = vt.volume; - } - if (vt.flags & VIDEO_AUDIO_BASS) { - pvr2_trace(PVR2_TRACE_AUDIO, - "pvr_audio_update_status: got bass"); - hdw->controls[PVR2_CID_BASS].value = vt.bass; - } - if (vt.flags & VIDEO_AUDIO_TREBLE) { - pvr2_trace(PVR2_TRACE_AUDIO, - "pvr_audio_update_status: got treble"); - hdw->controls[PVR2_CID_TREBLE].value = vt.treble; - } - if (vt.flags & (VIDEO_AUDIO_BALANCE|VIDEO_AUDIO_VOLUME)) { - pvr2_trace(PVR2_TRACE_AUDIO, - "pvr_audio_update_status: got balance"); - hdw->controls[PVR2_CID_BALANCE].value = vt.balance; - } -#endif - - hdw->flag_stereo = (vt.mode & VIDEO_SOUND_STEREO) != 0; - hdw->flag_bilingual = (vt.mode & - (VIDEO_SOUND_LANG1|VIDEO_SOUND_LANG2)) != 0; + ctxt->hdw->flag_stereo = (vt.mode & VIDEO_SOUND_STEREO) != 0; + ctxt->hdw->flag_bilingual = + (vt.mode & (VIDEO_SOUND_LANG1|VIDEO_SOUND_LANG2)) != 0; return 0; } +static void pvr2_msp3400_detach(struct pvr2_msp3400_handler *ctxt) +{ + ctxt->client->handler = 0; + ctxt->hdw->audio_stat = 0; + kfree(ctxt); +} + + +const static struct pvr2_i2c_handler_functions msp3400_funcs = { + .detach = (void (*)(void *))pvr2_msp3400_detach, + .check = (int (*)(void *))msp3400_check, + .update = (void (*)(void *))msp3400_update, +}; + + +int pvr2_i2c_msp3400_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) +{ + struct pvr2_msp3400_handler *ctxt; + if (hdw->audio_stat) return 0; + if (cp->handler) return 0; + + ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + if (!ctxt) return 0; + memset(ctxt,0,sizeof(*ctxt)); + + ctxt->i2c_handler.func_data = ctxt; + ctxt->i2c_handler.func_table = &msp3400_funcs; + ctxt->client = cp; + ctxt->hdw = hdw; + ctxt->astat.ctxt = ctxt; + ctxt->astat.status = (int (*)(void *))get_audio_status; + ctxt->astat.detach = (void (*)(void *))pvr2_msp3400_detach; + ctxt->stale_mask = 1 << ((sizeof(msp3400_ops)/ + sizeof(msp3400_ops[0])) - 1); + cp->handler = &ctxt->i2c_handler; + hdw->audio_stat = &ctxt->astat; + return !0; +} + + /* Stuff for Emacs to see, in order to encourage consistent editing style: *** Local Variables: *** diff --git a/v4l_experimental/pvrusb2/pvrusb2-audio.h b/v4l_experimental/pvrusb2/pvrusb2-audio.h index 3800438f7..0a278927e 100644 --- a/v4l_experimental/pvrusb2/pvrusb2-audio.h +++ b/v4l_experimental/pvrusb2/pvrusb2-audio.h @@ -1,6 +1,6 @@ /* * - * $Id: pvrusb2-audio.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $ + * $Id: pvrusb2-audio.h,v 1.2 2006/01/01 08:26:03 mcisely Exp $ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> @@ -23,21 +23,9 @@ #ifndef __PVRUSB2_AUDIO_H #define __PVRUSB2_AUDIO_H -/* - - This module connects the pvrusb2 driver to the I2C chip level - driver which handles device audio processing. This interface is - used internally by the driver; higher level code should only - interact through the interface provided by pvrusb2-hdw.h. - -*/ - -struct pvr2_hdw; +#include "pvrusb2-i2c-core.h" -int pvr2_audio_setvolume(struct pvr2_hdw *); -int pvr2_audio_set_stereo(struct pvr2_hdw *); -int pvr2_audio_set_standard(struct pvr2_hdw *); -int pvr2_audio_update_status(struct pvr2_hdw *); +int pvr2_i2c_msp3400_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); #endif /* __PVRUSB2_AUDIO_H */ diff --git a/v4l_experimental/pvrusb2/pvrusb2-context.c b/v4l_experimental/pvrusb2/pvrusb2-context.c index b0faa9a44..483aba179 100644 --- a/v4l_experimental/pvrusb2/pvrusb2-context.c +++ b/v4l_experimental/pvrusb2/pvrusb2-context.c @@ -1,5 +1,5 @@ /* - * $Id: pvrusb2-context.c,v 1.1 2005/11/14 13:31:24 mchehab Exp $ + * $Id: pvrusb2-context.c,v 1.2 2006/01/01 08:26:03 mcisely Exp $ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -38,12 +38,31 @@ static void pvr2_context_destroy(struct pvr2_context *mp) } +static void pvr2_context_trigger_poll(struct pvr2_context *mp) +{ + queue_work(mp->workqueue,&mp->workpoll); +} + + +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"); +} + + static void pvr2_context_setup(struct pvr2_context *mp) { pvr2_context_enter(mp); do { - (mp->kthread_ref_count)--; if (!pvr2_hdw_dev_ok(mp->hdw)) break; pvr2_hdw_setup(mp->hdw); + pvr2_hdw_setup_poll_trigger( + mp->hdw, + (void (*)(void *))pvr2_context_trigger_poll, + mp); if (!pvr2_hdw_dev_ok(mp->hdw)) break; if (!pvr2_hdw_init_ok(mp->hdw)) break; mp->video_stream.stream = pvr2_hdw_get_video_stream(mp->hdw); @@ -72,8 +91,8 @@ struct pvr2_context *pvr2_context_create( } mp->workqueue = create_singlethread_workqueue("pvrusb2"); - (mp->kthread_ref_count)++; INIT_WORK(&mp->workinit,(void (*)(void*))pvr2_context_setup,mp); + INIT_WORK(&mp->workpoll,(void (*)(void*))pvr2_context_poll,mp); queue_work(mp->workqueue,&mp->workinit); done: return mp; @@ -90,9 +109,7 @@ void pvr2_context_enter(struct pvr2_context *mp) void pvr2_context_exit(struct pvr2_context *mp) { int destroy_flag = 0; - if (!(mp->mc_first || - (!mp->disconnect_flag) || - mp->kthread_ref_count)) { + if (!(mp->mc_first || !mp->disconnect_flag)) { destroy_flag = !0; } pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_exit(id=%p) outside",mp); diff --git a/v4l_experimental/pvrusb2/pvrusb2-context.h b/v4l_experimental/pvrusb2/pvrusb2-context.h index 230c2828f..2f453c908 100644 --- a/v4l_experimental/pvrusb2/pvrusb2-context.h +++ b/v4l_experimental/pvrusb2/pvrusb2-context.h @@ -1,5 +1,5 @@ /* - * $Id: pvrusb2-context.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $ + * $Id: pvrusb2-context.h,v 1.2 2006/01/01 08:26:03 mcisely Exp $ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -44,7 +44,6 @@ struct pvr2_context { struct pvr2_context_stream video_stream; struct semaphore sem; int disconnect_flag; - int kthread_ref_count; /* Called after pvr2_context initialization is complete */ void (*setup_func)(struct pvr2_context *); @@ -52,7 +51,7 @@ struct pvr2_context { /* Work queue overhead for out-of-line processing */ struct workqueue_struct *workqueue; struct work_struct workinit; - + struct work_struct workpoll; }; struct pvr2_channel { diff --git a/v4l_experimental/pvrusb2/pvrusb2-debug.h b/v4l_experimental/pvrusb2/pvrusb2-debug.h index f970be2c2..9928d883d 100644 --- a/v4l_experimental/pvrusb2/pvrusb2-debug.h +++ b/v4l_experimental/pvrusb2/pvrusb2-debug.h @@ -1,5 +1,5 @@ /* - * $Id: pvrusb2-debug.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $ + * $Id: pvrusb2-debug.h,v 1.2 2006/01/01 08:26:03 mcisely Exp $ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -39,17 +39,18 @@ extern int debug; #define PVR2_TRACE_CREG 0x00000400 // Main critical region entry / exit #define PVR2_TRACE_SYSFS 0x00000800 // Sysfs driven I/O #define PVR2_TRACE_FIRMWARE 0x00001000 // firmware upload actions -#define PVR2_TRACE_TUNER 0x00002000 // tuner operation +#define PVR2_TRACE_CHIPS 0x00002000 // chip broadcast operation #define PVR2_TRACE_I2C 0x00004000 // I2C related stuff -#define PVR2_TRACE_V4LIOCTL 0x00008000 // v4l ioctl details -#define PVR2_TRACE_AUDIO 0x00010000 // audio operation -#define PVR2_TRACE_DECODER 0x00020000 // video capture operation -#define PVR2_TRACE_ENCODER 0x00040000 // mpeg2 encoder operation -#define PVR2_TRACE_BUF_POOL 0x00080000 // Track buffer pool management -#define PVR2_TRACE_BUF_FLOW 0x00100000 // Track buffer flow in system -#define PVR2_TRACE_DATA_FLOW 0x00200000 // Track data flow -#define PVR2_TRACE_DEBUGIFC 0x00400000 // Debug interface actions -#define PVR2_TRACE_GPIO 0x00800000 // GPIO state bit changes +#define PVR2_TRACE_I2C_CMD 0x00008000 // Software commands to I2C modules +#define PVR2_TRACE_I2C_CORE 0x00010000 // I2C core debugging +#define PVR2_TRACE_I2C_TRAF 0x00020000 // I2C traffic through the adapter +#define PVR2_TRACE_V4LIOCTL 0x00040000 // v4l ioctl details +#define PVR2_TRACE_ENCODER 0x00080000 // mpeg2 encoder operation +#define PVR2_TRACE_BUF_POOL 0x00100000 // Track buffer pool management +#define PVR2_TRACE_BUF_FLOW 0x00200000 // Track buffer flow in system +#define PVR2_TRACE_DATA_FLOW 0x00400000 // Track data flow +#define PVR2_TRACE_DEBUGIFC 0x00800000 // Debug interface actions +#define PVR2_TRACE_GPIO 0x01000000 // GPIO state bit changes #endif /* __PVRUSB2_HDW_INTERNAL_H */ /* diff --git a/v4l_experimental/pvrusb2/pvrusb2-debugifc.c b/v4l_experimental/pvrusb2/pvrusb2-debugifc.c index a29bafb85..f56b96f56 100644 --- a/v4l_experimental/pvrusb2/pvrusb2-debugifc.c +++ b/v4l_experimental/pvrusb2/pvrusb2-debugifc.c @@ -1,6 +1,6 @@ /* * - * $Id: pvrusb2-debugifc.c,v 1.1 2005/11/14 13:31:24 mchehab Exp $ + * $Id: pvrusb2-debugifc.c,v 1.2 2006/01/01 08:26:03 mcisely Exp $ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -24,6 +24,7 @@ #include "pvrusb2-debugifc.h" #include "pvrusb2-hdw.h" #include "pvrusb2-debug.h" +#include "pvrusb2-i2c-core.h" struct debugifc_mask_item { const char *name; @@ -32,16 +33,6 @@ struct debugifc_mask_item { static struct debugifc_mask_item mask_items[] = { {"ENC_FIRMWARE",PVR2_SUBSYS_ENC_FIRMWARE}, - {"TUN_STD",PVR2_SUBSYS_TUNER_CFG_STD}, - {"TUN_FREQ",PVR2_SUBSYS_TUNER_CFG_FREQ}, - {"AUD_VBBTM",PVR2_SUBSYS_AUDIO_CFG_VBBTM}, - {"AUD_STD",PVR2_SUBSYS_AUDIO_CFG_STD}, - {"AUD_MODE",PVR2_SUBSYS_AUDIO_CFG_MODE}, - {"DIG_NORM",PVR2_SUBSYS_DIGITIZER_CFG_NORM}, - {"DIG_INPUT",PVR2_SUBSYS_DIGITIZER_CFG_INPUT}, - {"DIG_SIZE",PVR2_SUBSYS_DIGITIZER_CFG_SIZE}, - {"DIG_AUDIO",PVR2_SUBSYS_DIGITIZER_CFG_AUDIO}, - {"DIG_BCSH",PVR2_SUBSYS_DIGITIZER_CFG_BCSH}, {"ENC_CFG",PVR2_SUBSYS_ENC_CFG}, {"DIG_RUN",PVR2_SUBSYS_DIGITIZER_RUN}, {"USB_RUN",PVR2_SUBSYS_USBSTREAM_RUN}, @@ -285,6 +276,11 @@ int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt) ccnt = scnprintf(buf,acnt,"\n"); bcnt += ccnt; acnt -= ccnt; buf += ccnt; + ccnt = scnprintf(buf,acnt,"Attached I2C modules:\n"); + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + ccnt = pvr2_i2c_report(hdw,buf,acnt); + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + return bcnt; } diff --git a/v4l_experimental/pvrusb2/pvrusb2-demod.c b/v4l_experimental/pvrusb2/pvrusb2-demod.c new file mode 100644 index 000000000..f9f1fc844 --- /dev/null +++ b/v4l_experimental/pvrusb2/pvrusb2-demod.c @@ -0,0 +1,117 @@ +/* + * + * $Id: pvrusb2-demod.c,v 1.1 2006/01/01 08:26:03 mcisely Exp $ + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> + * + * This program is free software; you can redistribute it and/or 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.h" +#include "pvrusb2-util.h" +#include "pvrusb2-demod.h" +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" +#include "compat.h" +#include <linux/videodev.h> +#include <media/tuner.h> + + +struct pvr2_demod_handler { + struct pvr2_hdw *hdw; + struct pvr2_i2c_client *client; + struct pvr2_i2c_handler i2c_handler; + int type_update_fl; +}; + + +static void set_config(struct pvr2_demod_handler *ctxt) +{ + struct pvr2_hdw *hdw = ctxt->hdw; + int cfg = 0; + + switch (hdw->tuner_type) { + case TUNER_PHILIPS_FM1216ME_MK3: + case TUNER_PHILIPS_FM1236_MK3: + cfg = TDA9887_PORT1_ACTIVE|TDA9887_PORT2_ACTIVE; + break; + default: + break; + } + pvr2_trace(PVR2_TRACE_CHIPS,"i2c demod set_config(0x%x)",cfg); + pvr2_i2c_client_cmd(ctxt->client,TDA9887_SET_CONFIG,&cfg); + ctxt->type_update_fl = 0; +} + + +static int demod_check(struct pvr2_demod_handler *ctxt) +{ + struct pvr2_hdw *hdw = ctxt->hdw; + if (hdw->tuner_updated) ctxt->type_update_fl = !0; + return ctxt->type_update_fl != 0; +} + + +static void demod_update(struct pvr2_demod_handler *ctxt) +{ + if (ctxt->type_update_fl) set_config(ctxt); +} + + +static void demod_detach(struct pvr2_demod_handler *ctxt) +{ + ctxt->client->handler = 0; + kfree(ctxt); +} + + +const static struct pvr2_i2c_handler_functions tuner_funcs = { + .detach = (void (*)(void *))demod_detach, + .check = (int (*)(void *))demod_check, + .update = (void (*)(void *))demod_update, +}; + + +int pvr2_i2c_demod_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) +{ + struct pvr2_demod_handler *ctxt; + if (cp->handler) return 0; + + ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + if (!ctxt) return 0; + memset(ctxt,0,sizeof(*ctxt)); + + ctxt->i2c_handler.func_data = ctxt; + ctxt->i2c_handler.func_table = &tuner_funcs; + ctxt->type_update_fl = !0; + ctxt->client = cp; + ctxt->hdw = hdw; + cp->handler = &ctxt->i2c_handler; + 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/v4l_experimental/pvrusb2/pvrusb2-i2c.h b/v4l_experimental/pvrusb2/pvrusb2-demod.h index 668baac81..0067872ea 100644 --- a/v4l_experimental/pvrusb2/pvrusb2-i2c.h +++ b/v4l_experimental/pvrusb2/pvrusb2-demod.h @@ -1,6 +1,6 @@ /* * - * $Id: pvrusb2-i2c.h,v 1.2 2005/12/07 06:53:52 mcisely Exp $ + * $Id: pvrusb2-demod.h,v 1.1 2006/01/01 08:26:03 mcisely Exp $ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -18,27 +18,20 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#ifndef __PVRUSB2_I2C_H -#define __PVRUSB2_I2C_H +#ifndef __PVRUSB2_DEMOD_H +#define __PVRUSB2_DEMOD_H -struct pvr2_hdw; +#include "pvrusb2-i2c-core.h" -void pvr2_i2c_init(struct pvr2_hdw *); -void pvr2_i2c_done(struct pvr2_hdw *); -int pvr2_i2c_tuner_cmd(struct pvr2_hdw *,unsigned int,void *); -int pvr2_i2c_ifhandler_cmd(struct pvr2_hdw *,unsigned int,void *); -int pvr2_i2c_msp3400_cmd(struct pvr2_hdw *,unsigned int,void *); -int pvr2_i2c_saa7115_cmd(struct pvr2_hdw *,unsigned int,void *); -int pvr2_i2c_tveeprom_cmd(struct pvr2_hdw *,unsigned int,void *); - -#endif /* __PVRUSB2_I2C_H */ +int pvr2_i2c_demod_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); +#endif /* __PVRUSB2_DEMOD_H */ /* Stuff for Emacs to see, in order to encourage consistent editing style: *** Local Variables: *** *** mode: c *** - *** fill-column: 75 *** + *** fill-column: 70 *** *** tab-width: 8 *** *** c-basic-offset: 8 *** *** End: *** diff --git a/v4l_experimental/pvrusb2/pvrusb2-encoder.c b/v4l_experimental/pvrusb2/pvrusb2-encoder.c index 293997b36..91e03b2bc 100644 --- a/v4l_experimental/pvrusb2/pvrusb2-encoder.c +++ b/v4l_experimental/pvrusb2/pvrusb2-encoder.c @@ -1,6 +1,6 @@ /* * - * $Id: pvrusb2-encoder.c,v 1.3 2005/11/29 19:49:09 mchehab Exp $ + * $Id: pvrusb2-encoder.c,v 1.4 2006/01/01 08:26:03 mcisely Exp $ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> @@ -24,7 +24,6 @@ #include <linux/firmware.h> #include "pvrusb2-util.h" #include "pvrusb2-encoder.h" -#include "pvrusb2-i2c.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" diff --git a/v4l_experimental/pvrusb2/pvrusb2-hdw-internal.h b/v4l_experimental/pvrusb2/pvrusb2-hdw-internal.h index fb274daea..bcdf143f6 100644 --- a/v4l_experimental/pvrusb2/pvrusb2-hdw-internal.h +++ b/v4l_experimental/pvrusb2/pvrusb2-hdw-internal.h @@ -1,6 +1,6 @@ /* * - * $Id: pvrusb2-hdw-internal.h,v 1.3 2005/12/07 06:53:52 mcisely Exp $ + * $Id: pvrusb2-hdw-internal.h,v 1.4 2006/01/01 08:26:03 mcisely Exp $ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -58,6 +58,29 @@ struct pvr2_ctl_state { int dirty; }; +struct pvr2_audio_stat { + void *ctxt; + void (*detach)(void *); + int (*status)(void *); +}; + +struct pvr2_decoder_ctrl { + void *ctxt; + void (*detach)(void *); + void (*enable)(void *,int); + int (*tuned)(void *); +}; + +#define PVR2_I2C_PEND_DETECT 0x01 /* Need to detect a client type */ +#define PVR2_I2C_PEND_CLIENT 0x02 /* Client needs a specific update */ +#define PVR2_I2C_PEND_REFRESH 0x04 /* Client has specific pending bits */ +#define PVR2_I2C_PEND_STALE 0x08 /* Broadcast pending bits */ + +#define PVR2_I2C_PEND_ALL (PVR2_I2C_PEND_DETECT |\ + PVR2_I2C_PEND_CLIENT |\ + PVR2_I2C_PEND_REFRESH |\ + PVR2_I2C_PEND_STALE) + /* This structure contains all state data directly needed to manipulate the hardware (as opposed to complying with a kernel interface) */ @@ -73,17 +96,21 @@ struct pvr2_hdw { struct semaphore big_lock_sem; int big_lock_held; /* For debugging */ + void (*poll_trigger_func)(void *); + void *poll_trigger_data; + char name[32]; /* I2C stuff */ struct i2c_adapter i2c_adap; struct i2c_algorithm i2c_algo; int i2c_linked; - struct i2c_client *i2c_tuner_client; - struct i2c_client *i2c_ifhandler_client; - struct i2c_client *i2c_audio_client; - struct i2c_client *i2c_video_client; - struct i2c_client *i2c_tveeprom_client; + unsigned int i2c_pend_types; /* Which types of update are needed */ + unsigned long i2c_pend_mask; /* Change bits we need to scan */ + unsigned long i2c_stale_mask; /* Pending broadcast change bits */ + unsigned long i2c_active_mask; /* All change bits currently in use */ + struct list_head i2c_clients; + struct semaphore i2c_list_lock; /* Frequency table */ unsigned int freqTable[FREQTABLE_SIZE]; @@ -110,6 +137,10 @@ struct pvr2_hdw { int flag_init_ok; // true if structure is fully initialized int flag_streaming_enabled; // true if streaming should be on + int flag_decoder_is_tuned; + + struct pvr2_decoder_ctrl *decoder_ctrl; + // CPU firmware info (used to help find / save firmware data) char *fw_buffer; unsigned int fw_size; @@ -122,11 +153,9 @@ struct pvr2_hdw { /* Tuner / frequency control stuff */ unsigned int tuner_type; + int tuner_updated; unsigned long video_standards; - /* Context for controller video decoder (see pvrusb2-video.h) */ - struct pvr2_decoder *dcp; - int unit_number; /* ID for driver instance */ unsigned long serial_number; /* ID for hardware itself */ @@ -139,6 +168,7 @@ struct pvr2_hdw { /* Information about what audio signal we're hearing */ int flag_stereo; int flag_bilingual; + struct pvr2_audio_stat *audio_stat; /* Every last bit of controllable state */ struct pvr2_ctl_state controls[PVR2_CID_COUNT]; diff --git a/v4l_experimental/pvrusb2/pvrusb2-hdw.c b/v4l_experimental/pvrusb2/pvrusb2-hdw.c index 234617d31..bde8cdfca 100644 --- a/v4l_experimental/pvrusb2/pvrusb2-hdw.c +++ b/v4l_experimental/pvrusb2/pvrusb2-hdw.c @@ -1,6 +1,6 @@ /* * - * $Id: pvrusb2-hdw.c,v 1.5 2005/12/02 13:59:00 mcisely Exp $ + * $Id: pvrusb2-hdw.c,v 1.6 2006/01/01 08:26:03 mcisely Exp $ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -28,12 +28,10 @@ #include "pvrusb2.h" #include "pvrusb2-util.h" #include "pvrusb2-hdw.h" -#include "pvrusb2-i2c.h" +#include "pvrusb2-i2c-core.h" #include "pvrusb2-tuner.h" #include "pvrusb2-eeprom.h" #include "pvrusb2-hdw-internal.h" -#include "pvrusb2-audio.h" -#include "pvrusb2-video.h" #include "pvrusb2-encoder.h" #include "pvrusb2-debug.h" @@ -44,6 +42,7 @@ static int hfull = 1; static int width = 720; static int initusbreset = 1; static int procreload = 0; +static int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 }; module_param(initusbreset, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(initusbreset, "Do USB reset device on probe"); @@ -54,6 +53,8 @@ module_param(hfull, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(hfull, "full height video ?"); module_param(width, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(width, "video width : 720,480,352"); +module_param_array(tuner, int, NULL, 0444); +MODULE_PARM_DESC(tuner,"specify installed tuner type"); #define PVR2_CTL_WRITE_ENDPOINT 0x01 #define PVR2_CTL_READ_ENDPOINT 0x81 @@ -703,8 +704,17 @@ void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, if (vmsk & PVR2_SUBSYS_DIGITIZER_RUN) { pvr2_trace(PVR2_TRACE_CTL, "/*---TRACE_CTL----*/" - " pvr2_decoder_disable_output"); - pvr2_decoder_disable_output(hdw->dcp); + " decoder disable"); + if (hdw->decoder_ctrl) { + hdw->decoder_ctrl->enable( + hdw->decoder_ctrl->ctxt,0); + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "WARNING:" + " No decoder present"); + } + hdw->subsys_enabled_mask &= + ~PVR2_SUBSYS_DIGITIZER_RUN; } if (vmsk & PVR2_SUBSYS_CFG_ALL) { hdw->subsys_enabled_mask &= @@ -726,66 +736,6 @@ void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, break; } } - if (vmsk & PVR2_SUBSYS_TUNER_CFG_STD) { - pvr2_trace(PVR2_TRACE_CTL, - "/*---TRACE_CTL----*/" - " pvr2_tuner_set_standard"); - pvr2_tuner_set_standard(hdw); - } - if (vmsk & PVR2_SUBSYS_TUNER_CFG_FREQ) { - pvr2_trace(PVR2_TRACE_CTL, - "/*---TRACE_CTL----*/" - " pvr2_tuner_set_freq"); - pvr2_tuner_set_freq(hdw); - } - if (vmsk & PVR2_SUBSYS_AUDIO_CFG_STD) { - pvr2_trace(PVR2_TRACE_CTL, - "/*---TRACE_CTL----*/" - " pvr2_audio_set_standard"); - pvr2_audio_set_standard(hdw); - } - if (vmsk & PVR2_SUBSYS_AUDIO_CFG_MODE) { - pvr2_trace(PVR2_TRACE_CTL, - "/*---TRACE_CTL----*/" - " pvr2_audio_set_stereo"); - pvr2_audio_set_stereo(hdw); - } - if (vmsk & PVR2_SUBSYS_AUDIO_CFG_VBBTM) { - pvr2_trace(PVR2_TRACE_CTL, - "/*---TRACE_CTL----*/" - " pvr2_audio_setvolume"); - pvr2_audio_setvolume(hdw); - } - if (vmsk & PVR2_SUBSYS_DIGITIZER_CFG_NORM) { - pvr2_trace(PVR2_TRACE_CTL, - "/*---TRACE_CTL----*/" - " pvr2_decoder_set_norm"); - pvr2_decoder_set_norm(hdw->dcp); - } - if (vmsk & PVR2_SUBSYS_DIGITIZER_CFG_INPUT) { - pvr2_trace(PVR2_TRACE_CTL, - "/*---TRACE_CTL----*/" - " pvr2_decoder_set_input"); - pvr2_decoder_set_input(hdw->dcp); - } - if (vmsk & PVR2_SUBSYS_DIGITIZER_CFG_SIZE) { - pvr2_trace(PVR2_TRACE_CTL, - "/*---TRACE_CTL----*/" - " pvr2_decoder_set_size"); - pvr2_decoder_set_size(hdw->dcp); - } - if (vmsk & PVR2_SUBSYS_DIGITIZER_CFG_AUDIO) { - pvr2_trace(PVR2_TRACE_CTL, - "/*---TRACE_CTL----*/" - " pvr2_decoder_set_audio"); - pvr2_decoder_set_audio(hdw->dcp); - } - if (vmsk & PVR2_SUBSYS_DIGITIZER_CFG_BCSH) { - pvr2_trace(PVR2_TRACE_CTL, - "/*---TRACE_CTL----*/" - " pvr2_decoder_set_bcsh"); - pvr2_decoder_set_bcsh(hdw->dcp); - } if (vmsk & PVR2_SUBSYS_ENC_CFG) { pvr2_trace(PVR2_TRACE_CTL, "/*---TRACE_CTL----*/" @@ -802,8 +752,17 @@ void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, if (vmsk & PVR2_SUBSYS_DIGITIZER_RUN) { pvr2_trace(PVR2_TRACE_CTL, "/*---TRACE_CTL----*/" - " pvr2_decoder_enable_output"); - pvr2_decoder_enable_output(hdw->dcp); + " decoder enable"); + if (hdw->decoder_ctrl) { + hdw->decoder_ctrl->enable( + hdw->decoder_ctrl->ctxt,!0); + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "WARNING:" + " No decoder present"); + } + hdw->subsys_enabled_mask |= + PVR2_SUBSYS_DIGITIZER_RUN; } if (vmsk & PVR2_SUBSYS_USBSTREAM_RUN) { pvr2_trace(PVR2_TRACE_CTL, @@ -949,6 +908,19 @@ int pvr2_hdw_set_stream_type(struct pvr2_hdw *hdw,enum pvr2_config config) } +static int get_default_tuner_type(struct pvr2_hdw *hdw) +{ + int unit_number = hdw->unit_number; + int tp = -1; + if ((unit_number >= 0) && (unit_number < PVR_NUM)) { + tp = tuner[unit_number]; + } + if (tp < 0) return -EINVAL; + hdw->tuner_type = tp; + return 0; +} + + static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) { unsigned int idx; @@ -965,7 +937,7 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) } if (!pvr2_hdw_dev_ok(hdw)) return; - pvr2_i2c_init(hdw); + pvr2_i2c_core_init(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; if (pvr2_upload_firmware2(hdw)){ @@ -983,18 +955,19 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) pvr2_reset_ctl_endpoints(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; - hdw->dcp = pvr2_decoder_create(hdw); - pvr2_eeprom_analyze(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; - if (!pvr2_tuner_get_default_type(hdw)) { + if (!get_default_tuner_type(hdw)) { pvr2_trace(PVR2_TRACE_INIT, "pvr2_hdw_setup: Tuner type overridden to %d", hdw->tuner_type); } - pvr2_tuner_set_type(hdw); + hdw->tuner_updated = !0; + pvr2_i2c_core_check_stale(hdw); + hdw->tuner_updated = 0; + if (!pvr2_hdw_dev_ok(hdw)) return; pvr2_eeprom_set_default_standard(hdw); @@ -1013,6 +986,12 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) } if (!pvr2_hdw_dev_ok(hdw)) return; + + /* Make sure everything is up to date */ + pvr2_i2c_core_sync(hdw); + + if (!pvr2_hdw_dev_ok(hdw)) return; + hdw->flag_init_ok = !0; } @@ -1188,9 +1167,13 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) pvr2_stream_destroy(hdw->vid_stream); hdw->vid_stream = 0; } - pvr2_decoder_destroy(hdw->dcp); - hdw->dcp = 0; - pvr2_i2c_done(hdw); + if (hdw->audio_stat) { + hdw->audio_stat->detach(hdw->audio_stat->ctxt); + } + if (hdw->decoder_ctrl) { + hdw->decoder_ctrl->detach(hdw->decoder_ctrl->ctxt); + } + pvr2_i2c_core_done(hdw); pvr2_hdw_remove_usb_stuff(hdw); down(&pvr2_unit_sem); do { if ((hdw->unit_number >= 0) && @@ -1380,13 +1363,12 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) 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 this. 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. */ + 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. */ @@ -1460,29 +1442,6 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) } } - if (hdw->controls[PVR2_CID_VIDEOSTANDARD].dirty) { - hdw->controls[PVR2_CID_FREQUENCY].dirty = !0; - stale_subsys_mask |= (PVR2_SUBSYS_DIGITIZER_CFG_NORM | - PVR2_SUBSYS_TUNER_CFG_STD | - PVR2_SUBSYS_AUDIO_CFG_STD); - } - - if (hdw->controls[PVR2_CID_HRES].dirty || - hdw->controls[PVR2_CID_VRES].dirty) { - stale_subsys_mask |= PVR2_SUBSYS_DIGITIZER_CFG_SIZE; - } - - if (hdw->controls[PVR2_CID_BRIGHTNESS].dirty || - hdw->controls[PVR2_CID_CONTRAST].dirty || - hdw->controls[PVR2_CID_SATURATION].dirty || - hdw->controls[PVR2_CID_HUE].dirty) { - stale_subsys_mask |= PVR2_SUBSYS_DIGITIZER_CFG_BCSH; - } - - if (hdw->controls[PVR2_CID_SRATE].dirty) { - stale_subsys_mask |= PVR2_SUBSYS_DIGITIZER_CFG_AUDIO; - } - if (hdw->controls[PVR2_CID_VIDEOSTANDARD].dirty || hdw->controls[PVR2_CID_VRES].dirty || hdw->controls[PVR2_CID_HRES].dirty || @@ -1495,43 +1454,25 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) hdw->controls[PVR2_CID_AUDIOLAYER].dirty || hdw->controls[PVR2_CID_AUDIOCRC].dirty || hdw->controls[PVR2_CID_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; - } - - if (hdw->controls[PVR2_CID_AUDIOMODE].dirty || - hdw->controls[PVR2_CID_INPUT].dirty) { - stale_subsys_mask |= PVR2_SUBSYS_AUDIO_CFG_MODE; - } - - if (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) { - stale_subsys_mask |= PVR2_SUBSYS_AUDIO_CFG_VBBTM; - } - - if (hdw->controls[PVR2_CID_INPUT].dirty) { - stale_subsys_mask |= PVR2_SUBSYS_DIGITIZER_CFG_INPUT; - } - - if (hdw->controls[PVR2_CID_FREQUENCY].dirty) { - stale_subsys_mask |= PVR2_SUBSYS_TUNER_CFG_FREQ; - } - - if (stale_subsys_mask & (PVR2_SUBSYS_DIGITIZER_CFG_NORM | - PVR2_SUBSYS_TUNER_CFG_STD | - PVR2_SUBSYS_AUDIO_CFG_STD | - PVR2_SUBSYS_DIGITIZER_CFG_SIZE | - PVR2_SUBSYS_DIGITIZER_CFG_AUDIO | - PVR2_SUBSYS_ENC_CFG)) { stale_subsys_mask |= hdw->subsys_stream_mask; } + /* Scan i2c core at this point - before we clear all the dirty + bits. Various parts of the i2c core will notice dirty bits as + appropriate and arrange to broadcast or directly send updates to + the client drivers in order to keep everything in sync */ + pvr2_i2c_core_check_stale(hdw); + for (idx = 0; idx < PVR2_CID_COUNT; idx++) { hdw->controls[idx].dirty = 0; } + /* Now execute i2c core update */ + pvr2_i2c_core_sync(hdw); + pvr2_hdw_subsys_bit_chg_no_lock(hdw,stale_subsys_mask,0); pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,saved_subsys_mask); @@ -1548,6 +1489,41 @@ int pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw) } +void pvr2_hdw_poll(struct pvr2_hdw *hdw) +{ + LOCK_TAKE(hdw->big_lock); do { + pvr2_i2c_core_sync(hdw); + } while (0); LOCK_GIVE(hdw->big_lock); +} + + +void pvr2_hdw_setup_poll_trigger(struct pvr2_hdw *hdw, + void (*func)(void *), + void *data) +{ + LOCK_TAKE(hdw->big_lock); do { + hdw->poll_trigger_func = func; + hdw->poll_trigger_data = data; + } while (0); LOCK_GIVE(hdw->big_lock); +} + + +void pvr2_hdw_poll_trigger_unlocked(struct pvr2_hdw *hdw) +{ + if (hdw->poll_trigger_func) { + hdw->poll_trigger_func(hdw->poll_trigger_data); + } +} + + +void pvr2_hdw_poll_trigger(struct pvr2_hdw *hdw) +{ + LOCK_TAKE(hdw->big_lock); do { + pvr2_hdw_poll_trigger_unlocked(hdw); + } while (0); LOCK_GIVE(hdw->big_lock); +} + + /* 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) @@ -1563,9 +1539,11 @@ unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *hdw) switch (hdw->controls[PVR2_CID_INPUT].value) { case PVR2_CVAL_INPUT_TV: case PVR2_CVAL_INPUT_RADIO: - if (pvr2_decoder_is_tuned(hdw->dcp) > 0) { + if (hdw->decoder_ctrl && + hdw->decoder_ctrl->tuned(hdw->decoder_ctrl->ctxt)) { msk |= PVR2_SIGNAL_OK; - if (pvr2_audio_update_status(hdw) == 0) { + if (hdw->audio_stat && + hdw->audio_stat->status(hdw->audio_stat->ctxt)) { if (hdw->flag_stereo) { msk |= PVR2_SIGNAL_STEREO; } diff --git a/v4l_experimental/pvrusb2/pvrusb2-hdw.h b/v4l_experimental/pvrusb2/pvrusb2-hdw.h index eaa15c22a..e09735ce6 100644 --- a/v4l_experimental/pvrusb2/pvrusb2-hdw.h +++ b/v4l_experimental/pvrusb2/pvrusb2-hdw.h @@ -1,6 +1,6 @@ /* * - * $Id: pvrusb2-hdw.h,v 1.3 2005/12/28 16:22:33 mcisely Exp $ + * $Id: pvrusb2-hdw.h,v 1.4 2006/01/01 08:26:03 mcisely Exp $ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -139,39 +139,13 @@ this directly (let the driver handle things itself), but it is useful for debugging. */ #define PVR2_SUBSYS_ENC_FIRMWARE 0x00000001 -#define PVR2_SUBSYS_TUNER_CFG_STD 0x00000002 -#define PVR2_SUBSYS_TUNER_CFG_FREQ 0x00000004 -#define PVR2_SUBSYS_AUDIO_CFG_VBBTM 0x00000008 -#define PVR2_SUBSYS_AUDIO_CFG_STD 0x00000010 -#define PVR2_SUBSYS_AUDIO_CFG_MODE 0x00000020 -#define PVR2_SUBSYS_DIGITIZER_CFG_NORM 0x00000040 -#define PVR2_SUBSYS_DIGITIZER_CFG_INPUT 0x00000080 -#define PVR2_SUBSYS_DIGITIZER_CFG_SIZE 0x00000100 -#define PVR2_SUBSYS_DIGITIZER_CFG_AUDIO 0x00000200 -#define PVR2_SUBSYS_DIGITIZER_CFG_BCSH 0x00000400 -#define PVR2_SUBSYS_ENC_CFG 0x00000800 -#define PVR2_SUBSYS_DIGITIZER_RUN 0x00001000 -#define PVR2_SUBSYS_USBSTREAM_RUN 0x00002000 -#define PVR2_SUBSYS_ENC_RUN 0x00004000 - -#define PVR2_SUBSYS_TUNER_CFG_ALL ( \ - PVR2_SUBSYS_TUNER_CFG_STD | \ - PVR2_SUBSYS_TUNER_CFG_FREQ ) -#define PVR2_SUBSYS_AUDIO_CFG_ALL ( \ - PVR2_SUBSYS_AUDIO_CFG_MODE | \ - PVR2_SUBSYS_AUDIO_CFG_STD | \ - PVR2_SUBSYS_AUDIO_CFG_VBBTM ) -#define PVR2_SUBSYS_DIGITIZER_CFG_ALL ( \ - PVR2_SUBSYS_DIGITIZER_CFG_NORM | \ - PVR2_SUBSYS_DIGITIZER_CFG_INPUT | \ - PVR2_SUBSYS_DIGITIZER_CFG_SIZE | \ - PVR2_SUBSYS_DIGITIZER_CFG_AUDIO | \ - PVR2_SUBSYS_DIGITIZER_CFG_BCSH ) +#define PVR2_SUBSYS_ENC_CFG 0x00004000 +#define PVR2_SUBSYS_DIGITIZER_RUN 0x00008000 +#define PVR2_SUBSYS_USBSTREAM_RUN 0x00010000 +#define PVR2_SUBSYS_ENC_RUN 0x00020000 + #define PVR2_SUBSYS_CFG_ALL ( \ PVR2_SUBSYS_ENC_FIRMWARE | \ - PVR2_SUBSYS_TUNER_CFG_ALL | \ - PVR2_SUBSYS_AUDIO_CFG_ALL | \ - PVR2_SUBSYS_DIGITIZER_CFG_ALL | \ PVR2_SUBSYS_ENC_CFG ) #define PVR2_SUBSYS_RUN_ALL ( \ PVR2_SUBSYS_DIGITIZER_RUN | \ @@ -196,6 +170,18 @@ struct pvr2_hdw; hardware */ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf); +/* Poll for background activity (if any) */ +void pvr2_hdw_poll(struct pvr2_hdw *); + +/* Trigger a poll to take place later at a convenient time */ +void pvr2_hdw_poll_trigger(struct pvr2_hdw *); +void pvr2_hdw_poll_trigger_unlocked(struct pvr2_hdw *); + +/* Register a callback used to trigger a future poll */ +void pvr2_hdw_setup_poll_trigger(struct pvr2_hdw *, + void (*func)(void *), + void *data); + /* Get pointer to structure given unit number */ struct pvr2_hdw *pvr2_hdw_find(int unit_number); diff --git a/v4l_experimental/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/v4l_experimental/pvrusb2/pvrusb2-i2c-chips-v4l2.c new file mode 100644 index 000000000..03b6872cc --- /dev/null +++ b/v4l_experimental/pvrusb2/pvrusb2-i2c-chips-v4l2.c @@ -0,0 +1,104 @@ +/* + * + * $Id: pvrusb2-i2c-chips-v4l2.c,v 1.1 2006/01/01 08:26:03 mcisely Exp $ + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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 "compat.h" +#include "pvrusb2-i2c-core.h" +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" +#include "pvrusb2-i2c-cmd-v4l2.h" +#include "pvrusb2-audio.h" +#include "pvrusb2-tuner.h" +#include "pvrusb2-demod.h" +#include "pvrusb2-video-v4l.h" + +#define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) + +#define OP_STANDARD 0 +#define OP_BCSH 1 +#define OP_VOLUME 2 +#define OP_FREQ 3 +#define OP_AUDIORATE 4 +#define OP_SIZE 5 + +static const struct pvr2_i2c_op * const ops[] = { + [OP_STANDARD] = &pvr2_i2c_op_v4l2_standard, + [OP_BCSH] = &pvr2_i2c_op_v4l2_bcsh, + [OP_VOLUME] = &pvr2_i2c_op_v4l2_volume, + [OP_FREQ] = &pvr2_i2c_op_v4l2_frequency, + [OP_SIZE] = &pvr2_i2c_op_v4l2_size, +}; + +void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) +{ + int id; + id = cp->client->driver->id; + cp->ctl_mask = ((1 << OP_STANDARD) | + (1 << OP_BCSH) | + (1 << OP_VOLUME) | + (1 << OP_FREQ) | + (1 << OP_SIZE)); + + if (id == I2C_DRIVERID_MSP3400) { + if (pvr2_i2c_msp3400_setup(hdw,cp)) { + return; + } + } + if (id == I2C_DRIVERID_TUNER) { + if (pvr2_i2c_tuner_setup(hdw,cp)) { + return; + } + } +#ifndef PVR2_SUPPRESS_SAA711X + if (id == I2C_DRIVERID_SAA711X) { + if (pvr2_i2c_decoder_v4l_setup(hdw,cp)) { + return; + } + } +#endif + /* This is a really horrid way to identify the tda9887 driver, + however (1) its assigned id is -1 which is completely useless to + us, and (2) perhaps tda9887 is going to disappear soon anyway. + Once it really goes away, then this silly thing here will just + become harmless. */ + if (!strcmp(cp->client->driver->name,"i2c tda9887 driver")) { + if (pvr2_i2c_demod_setup(hdw,cp)) { + return; + } + } +} + + +const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx) +{ + if (idx >= sizeof(ops)/sizeof(ops[0])) return 0; + return ops[idx]; +} + + +/* + 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/v4l_experimental/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/v4l_experimental/pvrusb2/pvrusb2-i2c-cmd-v4l2.c new file mode 100644 index 000000000..92f2dfa6a --- /dev/null +++ b/v4l_experimental/pvrusb2/pvrusb2-i2c-cmd-v4l2.c @@ -0,0 +1,229 @@ +/* + * + * $Id: pvrusb2-i2c-cmd-v4l2.c,v 1.1 2006/01/01 08:26:03 mcisely Exp $ + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> + * + * This program is free software; you can redistribute it and/or 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-i2c-cmd-v4l2.h" +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" +#include <linux/videodev.h> +#include <media/audiochip.h> + + +static void set_standard(struct pvr2_hdw *hdw) +{ + int cvstd = hdw->controls[PVR2_CID_VIDEOSTANDARD].value; + v4l2_std_id vs; + 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; + } + 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; +} + + +const struct pvr2_i2c_op pvr2_i2c_op_v4l2_standard = { + .check = check_standard, + .update = set_standard, + .name = "v4l2_standard", +}; + + +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); + memset(&ctrl,0,sizeof(ctrl)); + ctrl.id = V4L2_CID_BRIGHTNESS; + ctrl.value = hdw->controls[PVR2_CID_BRIGHTNESS].value; + pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); + ctrl.id = V4L2_CID_CONTRAST; + ctrl.value = hdw->controls[PVR2_CID_CONTRAST].value; + pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); + ctrl.id = V4L2_CID_SATURATION; + ctrl.value = hdw->controls[PVR2_CID_SATURATION].value; + pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); + ctrl.id = V4L2_CID_HUE; + ctrl.value = hdw->controls[PVR2_CID_HUE].value; + 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); +} + + +const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh = { + .check = check_bcsh, + .update = set_bcsh, + .name = "v4l2_bcsh", +}; + + +static void set_volume(struct pvr2_hdw *hdw) +{ + struct v4l2_control ctrl; + 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); + memset(&ctrl,0,sizeof(ctrl)); + ctrl.id = V4L2_CID_AUDIO_MUTE; + ctrl.value = hdw->controls[PVR2_CID_MUTE].value ? 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; + pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); + ctrl.id = V4L2_CID_AUDIO_BALANCE; + ctrl.value = hdw->controls[PVR2_CID_BALANCE].value; + pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); + ctrl.id = V4L2_CID_AUDIO_BASS; + ctrl.value = hdw->controls[PVR2_CID_BASS].value; + pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); + ctrl.id = V4L2_CID_AUDIO_TREBLE; + ctrl.value = hdw->controls[PVR2_CID_TREBLE].value; + 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); +} + + +const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume = { + .check = check_volume, + .update = set_volume, + .name = "v4l2_volume", +}; + + +static void set_frequency(struct pvr2_hdw *hdw) +{ + unsigned long fv; + struct v4l2_frequency freq; + fv = hdw->controls[PVR2_CID_FREQUENCY].value; + pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_freq(%lu)",fv); + memset(&freq,0,sizeof(freq)); + freq.frequency = fv / 62500; + freq.tuner = 0; + freq.type = V4L2_TUNER_ANALOG_TV; + pvr2_i2c_core_cmd(hdw,VIDIOC_S_FREQUENCY,&freq); +} + + +static int check_frequency(struct pvr2_hdw *hdw) +{ + return hdw->controls[PVR2_CID_FREQUENCY].dirty != 0; +} + + +const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency = { + .check = check_frequency, + .update = set_frequency, + .name = "v4l2_freq", +}; + + +static void set_size(struct pvr2_hdw *hdw) +{ + struct v4l2_format fmt; + + 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; + + pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_size(%dx%d)", + fmt.fmt.pix.width,fmt.fmt.pix.height); + + pvr2_i2c_core_cmd(hdw,VIDIOC_S_FMT,&fmt); +} + + +static int check_size(struct pvr2_hdw *hdw) +{ + return (hdw->controls[PVR2_CID_HRES].dirty || + hdw->controls[PVR2_CID_VRES].dirty); +} + + +const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size = { + .check = check_size, + .update = set_size, + .name = "v4l2_size", +}; + + +/* + 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/v4l_experimental/pvrusb2/pvrusb2-i2c-cmd-v4l2.h b/v4l_experimental/pvrusb2/pvrusb2-i2c-cmd-v4l2.h new file mode 100644 index 000000000..8c3945e93 --- /dev/null +++ b/v4l_experimental/pvrusb2/pvrusb2-i2c-cmd-v4l2.h @@ -0,0 +1,45 @@ +/* + * + * $Id: pvrusb2-i2c-cmd-v4l2.h,v 1.1 2006/01/01 08:26:03 mcisely Exp $ + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> + * + * This program is free software; you can redistribute it and/or 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_CMD_V4L2_H +#define __PVRUSB2_CMD_V4L2_H + +#include "compat.h" +#include "pvrusb2-i2c-core.h" + +extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_standard; +extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh; +extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume; +extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency; +extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size; + +#endif /* __PVRUSB2_CMD_V4L2_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/v4l_experimental/pvrusb2/pvrusb2-i2c-core.c b/v4l_experimental/pvrusb2/pvrusb2-i2c-core.c new file mode 100644 index 000000000..8375ca7af --- /dev/null +++ b/v4l_experimental/pvrusb2/pvrusb2-i2c-core.c @@ -0,0 +1,737 @@ +/* + * + * $Id: pvrusb2-i2c-core.c,v 1.1 2006/01/01 08:26:03 mcisely Exp $ + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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 "compat.h" +#include "pvrusb2-i2c-core.h" +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" + +#define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) + +/* + + This module attempts to implement a compliant I2C adapter for the pvrusb2 + device. By doing this we can then make use of existing functionality in + V4L (e.g. tuner.c) rather than rolling our own. + +*/ + +static unsigned int i2c_scan = 0; +module_param(i2c_scan, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); + +static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */ + u8 i2c_addr, /* I2C address we're talking to */ + u8 *data, /* Data to write */ + u16 length) /* Size of data to write */ +{ + /* Return value - default 0 means success */ + int ret; + +#ifdef notdef + trace_i2c("pvr2_i2c_write"); +#endif + + if (!data) length = 0; + if (length > (sizeof(hdw->cmd_buffer) - 3)) return -ENOTSUPP; + + LOCK_TAKE(hdw->ctl_lock); + + /* Clear the command buffer (likely to be paranoia) */ + memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer)); + + /* Set up command buffer for an I2C write */ + hdw->cmd_buffer[0] = 0x08; /* write prefix */ + hdw->cmd_buffer[1] = i2c_addr; /* i2c addr of chip */ + hdw->cmd_buffer[2] = length; /* length of what follows */ + if (length) memcpy(hdw->cmd_buffer + 3, data, length); + + /* Do the operation */ + ret = pvr2_send_request(hdw, + hdw->cmd_buffer, + length + 3, + hdw->cmd_buffer, + 1); + if (!ret) { + if (hdw->cmd_buffer[0] != 8) { + ret = -EIO; + if (hdw->cmd_buffer[0] != 7) { + trace_i2c("unexpected status" + " from i2_write[%d]: %d", + i2c_addr,hdw->cmd_buffer[0]); + } + } + } +#ifdef notdef + trace_i2c("i2c_write(%d) len=%d ret=%d stat=%d",i2c_addr,length,ret, + hdw->cmd_buffer[0]); +#endif + + LOCK_GIVE(hdw->ctl_lock); + + return ret; +} + +static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */ + u8 i2c_addr, /* I2C address we're talking to */ + u8 *data, /* Data to write */ + u16 dlen, /* Size of data to write */ + u8 *res, /* Where to put data we read */ + u16 rlen) /* Amount of data to read */ +{ + /* Return value - default 0 means success */ + int ret; + +#ifdef notdef + trace_i2c("pvr2_i2c_read"); +#endif + + if (!data) dlen = 0; + if (dlen > (sizeof(hdw->cmd_buffer) - 4)) return -ENOTSUPP; + if (res && (rlen > (sizeof(hdw->cmd_buffer) - 1))) return -ENOTSUPP; + + LOCK_TAKE(hdw->ctl_lock); + + /* Clear the command buffer (likely to be paranoia) */ + memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer)); + + /* Set up command buffer for an I2C write followed by a read */ + hdw->cmd_buffer[0] = 0x09; /* read prefix */ + hdw->cmd_buffer[1] = dlen; /* arg length */ + hdw->cmd_buffer[2] = rlen; /* answer length. Device will send one + more byte (status). */ + hdw->cmd_buffer[3] = i2c_addr; /* i2c addr of chip */ + if (dlen) memcpy(hdw->cmd_buffer + 4, data, dlen); + + /* Do the operation */ + ret = pvr2_send_request(hdw, + hdw->cmd_buffer, + 4 + dlen, + hdw->cmd_buffer, + rlen + 1); + if (!ret) { + if (hdw->cmd_buffer[0] != 8) { + ret = -EIO; + if (hdw->cmd_buffer[0] != 7) { + trace_i2c("unexpected status" + " from i2_read[%d]: %d", + i2c_addr,hdw->cmd_buffer[0]); + } + } + } + +#ifdef notdef + trace_i2c("i2c_read(%d) wlen=%d rlen=%d ret=%d stat=%d", + i2c_addr,dlen,rlen,ret,hdw->cmd_buffer[0]); +#endif + /* Copy back the result */ + if (res && rlen) { + if (ret) { + /* Error, just blank out the return buffer */ + memset(res, 0, rlen); + } else { + memcpy(res, hdw->cmd_buffer + 1, rlen); + } + } + + LOCK_GIVE(hdw->ctl_lock); + + return ret; +} + +/* 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, + struct i2c_msg msgs[], + int num) +{ + int ret = -ENOTSUPP; + struct pvr2_hdw *hdw = (struct pvr2_hdw *)(i2c_adap->algo_data); + + if ((msgs[0].flags & I2C_M_NOSTART)) { + trace_i2c("i2c refusing I2C_M_NOSTART"); + goto done; + } + + if (num == 1) { + if (msgs[0].flags & I2C_M_RD) { + /* Simple read */ + 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)) { + ret = -EIO; + goto done; + } + ret = 1; + goto done; + } + /* If the read is short enough we'll do the whole + thing atomically. Otherwise we have no choice + but to break apart the reads. */ + tcnt = msgs[0].len; + offs = 0; + while (tcnt) { + bcnt = tcnt; + 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)) { + ret = -EIO; + goto done; + } + offs += bcnt; + tcnt -= bcnt; + } + ret = 1; + goto done; + } else { + /* Simple write */ + ret = 1; + if (pvr2_i2c_write(hdw,msgs[0].addr, + msgs[0].buf,msgs[0].len)) { + ret = -EIO; + } + goto done; + } + } else if (num == 2) { + if ((!((msgs[0].flags & I2C_M_RD))) && + (msgs[1].flags & I2C_M_RD)) { + u16 tcnt,bcnt,wcnt,offs; + /* Write followed by atomic read. If the read + portion is short enough we'll do the whole thing + atomically. Otherwise we have no choice but to + break apart the reads. */ + tcnt = msgs[1].len; + wcnt = msgs[0].len; + offs = 0; + while (tcnt || wcnt) { + bcnt = tcnt; + 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)) { + ret = -EIO; + goto done; + } + offs += bcnt; + tcnt -= bcnt; + wcnt = 0; + } + ret = 2; + goto done; + } else { + trace_i2c("i2c refusing complex transfer" + " read0=%d read1=%d", + (msgs[0].flags & I2C_M_RD), + (msgs[1].flags & I2C_M_RD)); + } + } else { + trace_i2c("i2c refusing %d phase transfer",num); + } + + done: + return ret; +} + +static int pvr2_i2c_control(struct i2c_adapter *adapter, + unsigned int cmd, unsigned long arg) +{ + return 0; +} + +static u32 pvr2_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +static int pvr2_i2c_core_singleton(struct i2c_client *cp, + unsigned int cmd,void *arg) +{ + int stat; + if (!cp) return -EINVAL; + if (!(cp->driver)) return -EINVAL; + if (!(cp->driver->command)) return -EINVAL; + if (!try_module_get(cp->driver->owner)) return -EAGAIN; + stat = cp->driver->command(cp,cmd,arg); + module_put(cp->driver->owner); + return stat; +} + +int pvr2_i2c_client_cmd(struct pvr2_i2c_client *cp,unsigned int cmd,void *arg) +{ + if (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); + } + return pvr2_i2c_core_singleton(cp->client,cmd,arg); +} + +int pvr2_i2c_core_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg) +{ + struct list_head *item,*nc; + struct pvr2_i2c_client *cp; + int stat = -EINVAL; + + if (!hdw) return stat; + + down(&hdw->i2c_list_lock); + list_for_each_safe(item,nc,&hdw->i2c_clients) { + cp = list_entry(item,struct pvr2_i2c_client,list); + if (!cp->recv_enable) continue; + up(&hdw->i2c_list_lock); + stat = pvr2_i2c_client_cmd(cp,cmd,arg); + down(&hdw->i2c_list_lock); + } + up(&hdw->i2c_list_lock); + return stat; +} + + +static int handler_check(struct pvr2_i2c_client *cp) +{ + struct pvr2_i2c_handler *hp = cp->handler; + if (!hp) return 0; + if (!hp->func_table->check) return 0; + return hp->func_table->check(hp->func_data) != 0; +} + +#define BUFSIZE 500 + +void pvr2_i2c_core_sync(struct pvr2_hdw *hdw) +{ + unsigned long msk; + unsigned int idx; + struct list_head *item,*nc; + struct pvr2_i2c_client *cp; + + if (!hdw->i2c_linked) return; + if (!(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL)) { + return; + } + down(&hdw->i2c_list_lock); do { + pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync BEGIN"); + if (hdw->i2c_pend_types & PVR2_I2C_PEND_DETECT) { + /* One or more I2C clients have attached since we + last synced. So scan the list and identify the + new clients. */ + char *buf; + unsigned int cnt; + unsigned long amask = 0; + buf = kmalloc(BUFSIZE,GFP_KERNEL); + pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_DETECT"); + hdw->i2c_pend_types &= ~PVR2_I2C_PEND_DETECT; + list_for_each(item,&hdw->i2c_clients) { + cp = list_entry(item,struct pvr2_i2c_client, + list); + if (!cp->detected_flag) { + cp->ctl_mask = 0; + pvr2_i2c_probe(hdw,cp); + cp->detected_flag = !0; + msk = cp->ctl_mask; + cnt = 0; + if (buf) { + cnt = pvr2_i2c_client_describe( + cp, + PVR2_I2C_DETAIL_ALL, + buf,BUFSIZE); + } + trace_i2c("Probed: %.*s",cnt,buf); + if (handler_check(cp)) { + hdw->i2c_pend_types |= + PVR2_I2C_PEND_CLIENT; + } + cp->pend_mask = msk; + hdw->i2c_pend_mask |= msk; + hdw->i2c_pend_types |= + PVR2_I2C_PEND_REFRESH; + } + amask |= cp->ctl_mask; + } + hdw->i2c_active_mask = amask; + if (buf) kfree(buf); + } + if (hdw->i2c_pend_types & PVR2_I2C_PEND_STALE) { + /* Need to do one or more global updates. Arrange + for this to happen. */ + unsigned long m2; + pvr2_trace(PVR2_TRACE_I2C_CORE, + "i2c: PEND_STALE (0x%lx)", + hdw->i2c_stale_mask); + hdw->i2c_pend_types &= ~PVR2_I2C_PEND_STALE; + list_for_each(item,&hdw->i2c_clients) { + cp = list_entry(item,struct pvr2_i2c_client, + list); + m2 = hdw->i2c_stale_mask; + m2 &= cp->ctl_mask; + m2 &= ~cp->pend_mask; + if (m2) { + pvr2_trace(PVR2_TRACE_I2C_CORE, + "i2c: cp=%p setting 0x%lx", + cp,m2); + cp->pend_mask |= m2; + } + } + hdw->i2c_pend_mask |= hdw->i2c_stale_mask; + hdw->i2c_stale_mask = 0; + hdw->i2c_pend_types |= PVR2_I2C_PEND_REFRESH; + } + if (hdw->i2c_pend_types & PVR2_I2C_PEND_CLIENT) { + /* One or more client handlers are asking for an + update. Run through the list of known clients + and update each one. */ + pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_CLIENT"); + hdw->i2c_pend_types &= ~PVR2_I2C_PEND_CLIENT; + list_for_each_safe(item,nc,&hdw->i2c_clients) { + cp = list_entry(item,struct pvr2_i2c_client, + list); + if (!cp->handler) continue; + if (!cp->handler->func_table->update) continue; + pvr2_trace(PVR2_TRACE_I2C_CORE, + "i2c: cp=%p update",cp); + up(&hdw->i2c_list_lock); + cp->handler->func_table->update( + cp->handler->func_data); + down(&hdw->i2c_list_lock); + /* If client's update function set some + additional pending bits, account for that + here. */ + if (cp->pend_mask & ~hdw->i2c_pend_mask) { + hdw->i2c_pend_mask |= cp->pend_mask; + hdw->i2c_pend_types |= + PVR2_I2C_PEND_REFRESH; + } + } + } + if (hdw->i2c_pend_types & PVR2_I2C_PEND_REFRESH) { + const struct pvr2_i2c_op *opf; + unsigned long pm; + /* Some actual updates are pending. Walk through + each update type and perform it. */ + pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_REFRESH" + " (0x%lx)",hdw->i2c_pend_mask); + hdw->i2c_pend_types &= ~PVR2_I2C_PEND_REFRESH; + pm = hdw->i2c_pend_mask; + hdw->i2c_pend_mask = 0; + for (idx = 0, msk = 1; pm; idx++, msk <<= 1) { + if (!(pm & msk)) continue; + pm &= ~msk; + list_for_each(item,&hdw->i2c_clients) { + cp = list_entry(item, + struct pvr2_i2c_client, + list); + if (cp->pend_mask & msk) { + cp->pend_mask &= ~msk; + cp->recv_enable = !0; + } else { + cp->recv_enable = 0; + } + } + opf = pvr2_i2c_get_op(idx); + if (!opf) continue; + up(&hdw->i2c_list_lock); + opf->update(hdw); + down(&hdw->i2c_list_lock); + } + } + pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync END"); + } while (0); up(&hdw->i2c_list_lock); +} + +int pvr2_i2c_core_check_stale(struct pvr2_hdw *hdw) +{ + unsigned long msk,sm,pm; + unsigned int idx; + const struct pvr2_i2c_op *opf; + struct list_head *item; + struct pvr2_i2c_client *cp; + unsigned int pt = 0; + + pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale BEGIN"); + + pm = hdw->i2c_active_mask; + sm = 0; + for (idx = 0, msk = 1; pm; idx++, msk <<= 1) { + if (!(msk & pm)) continue; + pm &= ~msk; + opf = pvr2_i2c_get_op(idx); + if (!opf) continue; + if (opf->check(hdw)) { + sm |= msk; + } + } + if (sm) pt |= PVR2_I2C_PEND_STALE; + + list_for_each(item,&hdw->i2c_clients) { + cp = list_entry(item,struct pvr2_i2c_client,list); + if (!handler_check(cp)) continue; + pt |= PVR2_I2C_PEND_CLIENT; + } + + if (pt) { + down(&hdw->i2c_list_lock); do { + hdw->i2c_pend_types |= pt; + hdw->i2c_stale_mask |= sm; + hdw->i2c_pend_mask |= hdw->i2c_stale_mask; + } while (0); up(&hdw->i2c_list_lock); + } + + pvr2_trace(PVR2_TRACE_I2C_CORE, + "i2c: types=0x%x stale=0x%lx pend=0x%lx", + hdw->i2c_pend_types, + hdw->i2c_stale_mask, + hdw->i2c_pend_mask); + pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale END"); + + return (hdw->i2c_pend_types & PVR2_I2C_PEND_ALL) != 0; +} + +unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp, + unsigned int detail, + char *buf,unsigned int maxlen) +{ + unsigned int ccnt,bcnt; + int spcfl = 0; + const struct pvr2_i2c_op *opf; + + ccnt = 0; + if (detail & PVR2_I2C_DETAIL_DEBUG) { + bcnt = scnprintf(buf,maxlen, + "ctxt=%p ctl_mask=0x%lx", + cp,cp->ctl_mask); + ccnt += bcnt; buf += bcnt; maxlen -= bcnt; + spcfl = !0; + } + bcnt = scnprintf(buf,maxlen, + "%s%s @ 0x%x", + (spcfl ? " " : ""), + cp->client->name, + cp->client->addr); + ccnt += bcnt; buf += bcnt; maxlen -= bcnt; + if ((detail & PVR2_I2C_DETAIL_HANDLER) && + cp->handler && cp->handler->func_table->describe) { + bcnt = scnprintf(buf,maxlen," ("); + ccnt += bcnt; buf += bcnt; maxlen -= bcnt; + bcnt = cp->handler->func_table->describe( + cp->handler->func_data,buf,maxlen); + ccnt += bcnt; buf += bcnt; maxlen -= bcnt; + bcnt = scnprintf(buf,maxlen,")"); + ccnt += bcnt; buf += bcnt; maxlen -= bcnt; + } + if ((detail & PVR2_I2C_DETAIL_CTLMASK) && cp->ctl_mask) { + unsigned int idx; + unsigned long msk,sm; + int spcfl; + bcnt = scnprintf(buf,maxlen," ["); + ccnt += bcnt; buf += bcnt; maxlen -= bcnt; + sm = 0; + spcfl = 0; + for (idx = 0, msk = 1; msk; idx++, msk <<= 1) { + if (!(cp->ctl_mask & msk)) continue; + opf = pvr2_i2c_get_op(idx); + if (opf) { + bcnt = scnprintf(buf,maxlen,"%s%s", + spcfl ? " " : "", + opf->name); + ccnt += bcnt; buf += bcnt; maxlen -= bcnt; + spcfl = !0; + } else { + sm |= msk; + } + } + if (sm) { + bcnt = scnprintf(buf,maxlen,"%s%lx", + idx != 0 ? " " : "",sm); + ccnt += bcnt; buf += bcnt; maxlen -= bcnt; + } + bcnt = scnprintf(buf,maxlen,"]"); + ccnt += bcnt; buf += bcnt; maxlen -= bcnt; + } + return ccnt; +} + +unsigned int pvr2_i2c_report(struct pvr2_hdw *hdw, + char *buf,unsigned int maxlen) +{ + unsigned int ccnt,bcnt; + struct list_head *item; + struct pvr2_i2c_client *cp; + ccnt = 0; + down(&hdw->i2c_list_lock); do { + list_for_each(item,&hdw->i2c_clients) { + cp = list_entry(item,struct pvr2_i2c_client,list); + bcnt = pvr2_i2c_client_describe( + cp, + (PVR2_I2C_DETAIL_HANDLER| + PVR2_I2C_DETAIL_CTLMASK), + buf,maxlen); + ccnt += bcnt; buf += bcnt; maxlen -= bcnt; + bcnt = scnprintf(buf,maxlen,"\n"); + ccnt += bcnt; buf += bcnt; maxlen -= bcnt; + } + } while (0); up(&hdw->i2c_list_lock); + return ccnt; +} + +static int pvr2_i2c_attach_inform(struct i2c_client *client) +{ + struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data); + struct pvr2_i2c_client *cp; + int fl = !(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL); + cp = kmalloc(sizeof(*cp),GFP_KERNEL); + trace_i2c("i2c_attach [client=%s @ 0x%x ctxt=%p]", + client->name, + client->addr,cp); + if (!cp) return -ENOMEM; + memset(cp,0,sizeof(*cp)); + INIT_LIST_HEAD(&cp->list); + cp->client = client; + down(&hdw->i2c_list_lock); do { + list_add_tail(&cp->list,&hdw->i2c_clients); + hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT; + } while (0); up(&hdw->i2c_list_lock); + if (fl) pvr2_hdw_poll_trigger_unlocked(hdw); + return 0; +} + +static int pvr2_i2c_detach_inform(struct i2c_client *client) +{ + struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data); + struct pvr2_i2c_client *cp; + struct list_head *item,*nc; + unsigned long amask = 0; + int foundfl = 0; + down(&hdw->i2c_list_lock); do { + list_for_each_safe(item,nc,&hdw->i2c_clients) { + cp = list_entry(item,struct pvr2_i2c_client,list); + if (cp->client == client) { + trace_i2c("pvr2_i2c_detach" + " [client=%s @ 0x%x ctxt=%p]", + client->name, + client->addr,cp); + if (cp->handler && + cp->handler->func_table->detach) { + cp->handler->func_table->detach( + cp->handler->func_data); + } + list_del(&cp->list); + kfree(cp); + foundfl = !0; + continue; + } + amask |= cp->ctl_mask; + } + hdw->i2c_active_mask = amask; + } while (0); up(&hdw->i2c_list_lock); + if (!foundfl) { + trace_i2c("pvr2_i2c_detach [client=%s @ 0x%x ctxt=<unknown>]", + client->name, + client->addr); + } + return 0; +} + +static struct i2c_algorithm pvr2_i2c_algo_template = { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) + .id = I2C_ALGO_BIT | I2C_HW_B_BT848, +#endif + .master_xfer = pvr2_i2c_xfer, + .algo_control = pvr2_i2c_control, + .functionality = pvr2_i2c_functionality, +}; + +static struct i2c_adapter pvr2_i2c_adap_template = { + .owner = THIS_MODULE, + .class = I2C_CLASS_TV_ANALOG, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) + .id = I2C_ALGO_BIT | I2C_HW_B_BT848, +#else + .id = I2C_HW_B_BT848, +#endif + .client_register = pvr2_i2c_attach_inform, + .client_unregister = pvr2_i2c_detach_inform, +}; + +static void do_i2c_scan(struct pvr2_hdw *hdw) +{ + struct i2c_msg msg[1]; + int i,rc; + msg[0].addr = 0; + msg[0].flags = I2C_M_RD; + msg[0].len = 0; + msg[0].buf = 0; + printk("%s: i2c scan beginning\n",hdw->name); + for (i = 0; i < 128; i++) { + msg[0].addr = i; + rc = i2c_transfer(&hdw->i2c_adap,msg, + sizeof(msg)/sizeof(msg[0])); + if (rc != 1) continue; + printk("%s: i2c scan: found device @ 0x%x\n",hdw->name,i); + } + printk("%s: i2c scan done.\n",hdw->name); +} + +void pvr2_i2c_core_init(struct pvr2_hdw *hdw) +{ + 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)); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) + strlcpy(hdw->i2c_algo.name,hdw->name,sizeof(hdw->i2c_algo.name)); +#endif + hdw->i2c_adap.algo = &hdw->i2c_algo; + hdw->i2c_adap.algo_data = hdw; + hdw->i2c_pend_mask = 0; + hdw->i2c_stale_mask = 0; + hdw->i2c_active_mask = 0; + INIT_LIST_HEAD(&hdw->i2c_clients); + init_MUTEX(&hdw->i2c_list_lock); + hdw->i2c_linked = !0; + i2c_add_adapter(&hdw->i2c_adap); + if (i2c_scan) do_i2c_scan(hdw); +} + +void pvr2_i2c_core_done(struct pvr2_hdw *hdw) +{ + if (hdw->i2c_linked) { + i2c_del_adapter(&hdw->i2c_adap); + hdw->i2c_linked = 0; + } +} + +/* + 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/v4l_experimental/pvrusb2/pvrusb2-i2c-core.h b/v4l_experimental/pvrusb2/pvrusb2-i2c-core.h new file mode 100644 index 000000000..3e8f71962 --- /dev/null +++ b/v4l_experimental/pvrusb2/pvrusb2-i2c-core.h @@ -0,0 +1,96 @@ +/* + * + * $Id: pvrusb2-i2c-core.h,v 1.1 2006/01/01 08:26:03 mcisely Exp $ + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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_I2C_CORE_H +#define __PVRUSB2_I2C_CORE_H + +#include <linux/list.h> +#include <linux/i2c.h> + +struct pvr2_hdw; +struct pvr2_i2c_client; +struct pvr2_i2c_handler; +struct pvr2_i2c_handler_functions; +struct pvr2_i2c_op; +struct pvr2_i2c_op_functions; + +struct pvr2_i2c_client { + struct i2c_client *client; + struct pvr2_i2c_handler *handler; + struct list_head list; + int detected_flag; + int recv_enable; + unsigned long pend_mask; + unsigned long ctl_mask; +}; + +struct pvr2_i2c_handler { + void *func_data; + const struct pvr2_i2c_handler_functions *func_table; +}; + +struct pvr2_i2c_handler_functions { + void (*detach)(void *); + int (*check)(void *); + void (*update)(void *); + unsigned int (*describe)(void *,char *,unsigned int); +}; + +struct pvr2_i2c_op { + int (*check)(struct pvr2_hdw *); + void (*update)(struct pvr2_hdw *); + const char *name; +}; + +void pvr2_i2c_core_init(struct pvr2_hdw *); +void pvr2_i2c_core_done(struct pvr2_hdw *); + +int pvr2_i2c_client_cmd(struct pvr2_i2c_client *,unsigned int cmd,void *arg); +int pvr2_i2c_core_cmd(struct pvr2_hdw *,unsigned int cmd,void *arg); + +int pvr2_i2c_core_check_stale(struct pvr2_hdw *); +void pvr2_i2c_core_sync(struct pvr2_hdw *); +unsigned int pvr2_i2c_report(struct pvr2_hdw *,char *buf,unsigned int maxlen); +#define PVR2_I2C_DETAIL_DEBUG 0x0001 +#define PVR2_I2C_DETAIL_HANDLER 0x0002 +#define PVR2_I2C_DETAIL_CTLMASK 0x0004 +#define PVR2_I2C_DETAIL_ALL (\ + PVR2_I2C_DETAIL_DEBUG |\ + PVR2_I2C_DETAIL_HANDLER |\ + PVR2_I2C_DETAIL_CTLMASK) +unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *, + unsigned int detail_mask, + char *buf,unsigned int maxlen); + +void pvr2_i2c_probe(struct pvr2_hdw *,struct pvr2_i2c_client *); +const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx); + +#endif /* __PVRUSB2_I2C_CORE_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/v4l_experimental/pvrusb2/pvrusb2-i2c.c b/v4l_experimental/pvrusb2/pvrusb2-i2c.c deleted file mode 100644 index a8830dcc8..000000000 --- a/v4l_experimental/pvrusb2/pvrusb2-i2c.c +++ /dev/null @@ -1,473 +0,0 @@ -/* - * - * $Id: pvrusb2-i2c.c,v 1.3 2005/12/07 06:53:52 mcisely Exp $ - * - * Copyright (C) 2005 Mike Isely <isely@pobox.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You 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 "compat.h" -#include "pvrusb2-i2c.h" -#include "pvrusb2-hdw-internal.h" -#include "pvrusb2-tuner.h" -#include "pvrusb2-debug.h" - -#define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) - -#ifndef I2C_DRIVERID_SAA7115 -// Using temporary hack for missing I2C driver-ID for saa7115 -#define I2C_DRIVERID_SAA7115 I2C_DRIVERID_EXP1 -#endif - -#ifndef I2C_DRIVERID_TVEEPROM -// Using temporary hack for missing I2C driver-ID for tveeprom -#define I2C_DRIVERID_TVEEPROM I2C_DRIVERID_EXP2 -#endif - -/* - - This module attempts to implement a compliant I2C adapter for the pvrusb2 - device. By doing this we can then make use of existing functionality in - V4L (e.g. tuner.c) rather than rolling our own. - -*/ - -static unsigned int i2c_scan = 0; -module_param(i2c_scan, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); - -static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */ - u8 i2c_addr, /* I2C address we're talking to */ - u8 *data, /* Data to write */ - u16 length) /* Size of data to write */ -{ - /* Return value - default 0 means success */ - int ret; - -#ifdef notdef - trace_i2c("pvr2_i2c_write"); -#endif - - if (!data) length = 0; - if (length > (sizeof(hdw->cmd_buffer) - 3)) return -ENOTSUPP; - - LOCK_TAKE(hdw->ctl_lock); - - /* Clear the command buffer (likely to be paranoia) */ - memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer)); - - /* Set up command buffer for an I2C write */ - hdw->cmd_buffer[0] = 0x08; /* write prefix */ - hdw->cmd_buffer[1] = i2c_addr; /* i2c addr of chip */ - hdw->cmd_buffer[2] = length; /* length of what follows */ - if (length) memcpy(hdw->cmd_buffer + 3, data, length); - - /* Do the operation */ - ret = pvr2_send_request(hdw, - hdw->cmd_buffer, - length + 3, - hdw->cmd_buffer, - 1); - if (!ret) { - if (hdw->cmd_buffer[0] != 8) { - ret = -EIO; - if (hdw->cmd_buffer[0] != 7) { - trace_i2c("unexpected status" - " from i2_write[%d]: %d", - i2c_addr,hdw->cmd_buffer[0]); - } - } - } -#ifdef notdef - trace_i2c("i2c_write(%d) len=%d ret=%d stat=%d",i2c_addr,length,ret, - hdw->cmd_buffer[0]); -#endif - - LOCK_GIVE(hdw->ctl_lock); - - return ret; -} - -static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */ - u8 i2c_addr, /* I2C address we're talking to */ - u8 *data, /* Data to write */ - u16 dlen, /* Size of data to write */ - u8 *res, /* Where to put data we read */ - u16 rlen) /* Amount of data to read */ -{ - /* Return value - default 0 means success */ - int ret; - -#ifdef notdef - trace_i2c("pvr2_i2c_read"); -#endif - - if (!data) dlen = 0; - if (dlen > (sizeof(hdw->cmd_buffer) - 4)) return -ENOTSUPP; - if (res && (rlen > (sizeof(hdw->cmd_buffer) - 1))) return -ENOTSUPP; - - LOCK_TAKE(hdw->ctl_lock); - - /* Clear the command buffer (likely to be paranoia) */ - memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer)); - - /* Set up command buffer for an I2C write followed by a read */ - hdw->cmd_buffer[0] = 0x09; /* read prefix */ - hdw->cmd_buffer[1] = dlen; /* arg length */ - hdw->cmd_buffer[2] = rlen; /* answer length. Device will send one - more byte (status). */ - hdw->cmd_buffer[3] = i2c_addr; /* i2c addr of chip */ - if (dlen) memcpy(hdw->cmd_buffer + 4, data, dlen); - - /* Do the operation */ - ret = pvr2_send_request(hdw, - hdw->cmd_buffer, - 4 + dlen, - hdw->cmd_buffer, - rlen + 1); - if (!ret) { - if (hdw->cmd_buffer[0] != 8) { - ret = -EIO; - if (hdw->cmd_buffer[0] != 7) { - trace_i2c("unexpected status" - " from i2_read[%d]: %d", - i2c_addr,hdw->cmd_buffer[0]); - } - } - } - -#ifdef notdef - trace_i2c("i2c_read(%d) wlen=%d rlen=%d ret=%d stat=%d", - i2c_addr,dlen,rlen,ret,hdw->cmd_buffer[0]); -#endif - /* Copy back the result */ - if (res && rlen) { - if (ret) { - /* Error, just blank out the return buffer */ - memset(res, 0, rlen); - } else { - memcpy(res, hdw->cmd_buffer + 1, rlen); - } - } - - LOCK_GIVE(hdw->ctl_lock); - - return ret; -} - -/* 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, - struct i2c_msg msgs[], - int num) -{ - int ret = -ENOTSUPP; - struct pvr2_hdw *hdw = (struct pvr2_hdw *)(i2c_adap->algo_data); - - if ((msgs[0].flags & I2C_M_NOSTART)) { - trace_i2c("i2c refusing I2C_M_NOSTART"); - goto done; - } - - if (num == 1) { - if (msgs[0].flags & I2C_M_RD) { - /* Simple read */ - u16 tcnt,bcnt,offs; - /* If the read is short enough we'll do the whole - thing atomically. Otherwise we have no choice - but to break apart the reads. */ - tcnt = msgs[0].len; - offs = 0; - while (tcnt) { - bcnt = tcnt; - 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)) { - ret = -EIO; - goto done; - } - offs += bcnt; - tcnt -= bcnt; - } - ret = 1; - goto done; - } else { - /* Simple write */ - ret = 1; - if (pvr2_i2c_write(hdw,msgs[0].addr, - msgs[0].buf,msgs[0].len)) { - ret = -EIO; - } - goto done; - } - } else if (num == 2) { - if ((!((msgs[0].flags & I2C_M_RD))) && - (msgs[1].flags & I2C_M_RD)) { - u16 tcnt,bcnt,wcnt,offs; - /* Write followed by atomic read. If the read - portion is short enough we'll do the whole thing - atomically. Otherwise we have no choice but to - break apart the reads. */ - tcnt = msgs[1].len; - wcnt = msgs[0].len; - offs = 0; - while (tcnt || wcnt) { - bcnt = tcnt; - 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)) { - ret = -EIO; - goto done; - } - offs += bcnt; - tcnt -= bcnt; - wcnt = 0; - } - ret = 2; - goto done; - } else { - trace_i2c("i2c refusing complex transfer" - " read0=%d read1=%d", - (msgs[0].flags & I2C_M_RD), - (msgs[1].flags & I2C_M_RD)); - } - } else { - trace_i2c("i2c refusing %d phase transfer",num); - } - - done: - return ret; -} - -static int pvr2_i2c_control(struct i2c_adapter *adapter, - unsigned int cmd, unsigned long arg) -{ - return 0; -} - -static u32 pvr2_i2c_functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_SMBUS_EMUL; -} - -static int pvr2_i2c_attach_inform(struct i2c_client *client) -{ - struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data); - int id; - id = client->driver->id; - trace_i2c("i2c_attach [client=%s @ 0x%x id=0x%x]", - client->name, - client->addr,id); - if (!(hdw->i2c_audio_client) && (id == I2C_DRIVERID_MSP3400)) { - trace_i2c("attaching msp3400 I2C client"); - hdw->i2c_audio_client = client; - } - if (!(hdw->i2c_tuner_client) && (id == I2C_DRIVERID_TUNER)) { - trace_i2c("attaching tuner I2C client"); - hdw->i2c_tuner_client = client; - pvr2_tuner_set_type(hdw); - } - if (!(hdw->i2c_ifhandler_client)) { - /* This is a really horrid way to identify the tda9887 - driver, however (1) its assigned id is -1 which is - completely useless to us, and (2) I'm hoping that - tda9887 is going to disappear soon anyway. Once it - really goes away, then this silly thing here will just - become harmless. */ - if (!strcmp(client->driver->name,"i2c tda9887 driver")) { - trace_i2c("attaching tda9887 I2C client"); - hdw->i2c_ifhandler_client = client; - } - } -#ifdef PVR2_ENABLE_SAA7115 - if (!(hdw->i2c_video_client) && (id == I2C_DRIVERID_SAA7115)) { - trace_i2c("attaching saa7115 I2C client"); - hdw->i2c_video_client = client; - } -#endif -#ifndef PVR2_SUPPRESS_SAA711X - if (!(hdw->i2c_video_client) && (id == I2C_DRIVERID_SAA711X)) { - trace_i2c("attaching saa711x I2C client"); - hdw->i2c_video_client = client; - } -#endif - if (!(hdw->i2c_tveeprom_client) && (id == I2C_DRIVERID_TVEEPROM)) { - trace_i2c("attaching tveeprom I2C client"); - hdw->i2c_tveeprom_client = client; - } - - return 0; -} - -int pvr2_i2c_cmd(struct i2c_client *cp,unsigned int cmd,void *arg) -{ - int stat; - if (!cp) return -EINVAL; - if (!(cp->driver)) return -EINVAL; - if (!(cp->driver->command)) return -EINVAL; - if (!try_module_get(cp->driver->owner)) return -EAGAIN; - stat = cp->driver->command(cp,cmd,arg); - module_put(cp->driver->owner); - return stat; -} - -int pvr2_i2c_tuner_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg) -{ - int stat = pvr2_i2c_cmd(hdw->i2c_tuner_client,cmd,arg); - if (stat < 0) trace_i2c("pvr2_i2c_tuner_cmd failed with status %d", - stat); - return stat; -} - -int pvr2_i2c_ifhandler_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg) -{ - int stat = pvr2_i2c_cmd(hdw->i2c_ifhandler_client,cmd,arg); - if (stat < 0) trace_i2c("pvr2_i2c_ifhandler_cmd failed with status %d", - stat); - return stat; -} - -int pvr2_i2c_msp3400_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg) -{ - int stat = pvr2_i2c_cmd(hdw->i2c_audio_client,cmd,arg); - if (stat < 0) trace_i2c("pvr2_i2c_msp3400_cmd failed with status %d", - stat); - return stat; -} - -int pvr2_i2c_saa7115_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg) -{ - int stat = pvr2_i2c_cmd(hdw->i2c_video_client,cmd,arg); - if (stat < 0) trace_i2c("pvr2_i2c_saa7115_cmd failed with status %d", - stat); - return stat; -} - -int pvr2_i2c_tveeprom_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg) -{ - int stat = pvr2_i2c_cmd(hdw->i2c_tveeprom_client,cmd,arg); - if (stat < 0) { - trace_i2c("pvr2_i2c_tveeprom_cmd failed with status %d",stat); - } - return stat; -} - -static int pvr2_i2c_detach_inform(struct i2c_client *client) -{ - struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data); - trace_i2c("pvr2_i2c_detach [client=%s @ 0x%x]", - client->name, - client->addr); - if (hdw->i2c_ifhandler_client == client) { - trace_i2c("detaching tda9887 I2C client"); - hdw->i2c_ifhandler_client = 0; - } - if (hdw->i2c_audio_client == client) { - trace_i2c("detaching msp3400 I2C client"); - hdw->i2c_audio_client = 0; - } - if (hdw->i2c_tuner_client == client) { - trace_i2c("detaching tuner I2C client"); - hdw->i2c_tuner_client = 0; - } - if (hdw->i2c_video_client == client) { - trace_i2c("detaching saa7115 / saa711x I2C client"); - hdw->i2c_video_client = 0; - } - if (hdw->i2c_tveeprom_client == client) { - trace_i2c("detaching tveeprom I2C client"); - hdw->i2c_tveeprom_client = 0; - } - - return 0; -} - -static struct i2c_algorithm pvr2_i2c_algo_template = { -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) - .id = I2C_ALGO_BIT | I2C_HW_B_BT848, -#endif - .master_xfer = pvr2_i2c_xfer, - .algo_control = pvr2_i2c_control, - .functionality = pvr2_i2c_functionality, -}; - -static struct i2c_adapter pvr2_i2c_adap_template = { - .owner = THIS_MODULE, - .class = I2C_CLASS_TV_ANALOG, -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) - .id = I2C_ALGO_BIT | I2C_HW_B_BT848, -#else - .id = I2C_HW_B_BT848, -#endif - .client_register = pvr2_i2c_attach_inform, - .client_unregister = pvr2_i2c_detach_inform, -}; - -static void do_i2c_scan(struct pvr2_hdw *hdw) -{ - struct i2c_msg msg[1]; - int i,rc; - msg[0].addr = 0; - msg[0].flags = I2C_M_RD; - msg[0].len = 0; - msg[0].buf = 0; - for (i = 0; i < 128; i++) { - msg[0].addr = i; - rc = i2c_transfer(&hdw->i2c_adap,msg, - sizeof(msg)/sizeof(msg[0])); - if (rc < 0) continue; - printk("%s: i2c scan: found device @ 0x%x\n",hdw->name,i); - } -} - -void pvr2_i2c_init(struct pvr2_hdw *hdw) -{ - 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)); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) - strlcpy(hdw->i2c_algo.name,hdw->name,sizeof(hdw->i2c_algo.name)); -#endif - hdw->i2c_adap.algo = &hdw->i2c_algo; - hdw->i2c_adap.algo_data = hdw; - i2c_add_adapter(&hdw->i2c_adap); - hdw->i2c_linked = !0; - if (i2c_scan) do_i2c_scan(hdw); -} - -void pvr2_i2c_done(struct pvr2_hdw *hdw) -{ - if (hdw->i2c_linked) { - i2c_del_adapter(&hdw->i2c_adap); - hdw->i2c_linked = 0; - } -} - -/* - 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/v4l_experimental/pvrusb2/pvrusb2-main.c b/v4l_experimental/pvrusb2/pvrusb2-main.c index 2afc3de28..ffaaa4409 100644 --- a/v4l_experimental/pvrusb2/pvrusb2-main.c +++ b/v4l_experimental/pvrusb2/pvrusb2-main.c @@ -1,6 +1,6 @@ /* * - * $Id: pvrusb2-main.c,v 1.2 2005/12/07 06:53:52 mcisely Exp $ + * $Id: pvrusb2-main.c,v 1.3 2006/01/01 08:26:03 mcisely Exp $ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> @@ -48,6 +48,7 @@ PVR2_TRACE_EEPROM | \ PVR2_TRACE_INIT | \ PVR2_TRACE_I2C | \ + PVR2_TRACE_CHIPS | \ PVR2_TRACE_START_STOP | \ PVR2_TRACE_CTL | \ PVR2_TRACE_DEBUGIFC | \ diff --git a/v4l_experimental/pvrusb2/pvrusb2-tuner.c b/v4l_experimental/pvrusb2/pvrusb2-tuner.c index c84200ab6..232f083fd 100644 --- a/v4l_experimental/pvrusb2/pvrusb2-tuner.c +++ b/v4l_experimental/pvrusb2/pvrusb2-tuner.c @@ -1,6 +1,6 @@ /* * - * $Id: pvrusb2-tuner.c,v 1.6 2005/12/07 06:53:52 mcisely Exp $ + * $Id: pvrusb2-tuner.c,v 1.7 2006/01/01 08:26:03 mcisely Exp $ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> @@ -24,137 +24,91 @@ #include "pvrusb2-util.h" #include "pvrusb2-tuner.h" #include "pvrusb2-hdw-internal.h" -#include "pvrusb2-i2c.h" #include "pvrusb2-debug.h" #include "compat.h" #include <linux/videodev.h> #include <media/tuner.h> -#define PVR_STD_I2C_ADDR 0x43 -static int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 }; - -module_param_array(tuner, int, NULL, 0444); -MODULE_PARM_DESC(tuner,"specify installed tuner type"); - -static u32 pvr_tbl_standard[] = { - [PVR2_CVAL_VIDEOSTANDARD_PAL_BG] = 0x00d47009, /* PVR_STD_PAL_BG */ - [PVR2_CVAL_VIDEOSTANDARD_PAL_I] = 0x00d4700a, /* PVR_STD_PAL_I */ - [PVR2_CVAL_VIDEOSTANDARD_PAL_DK] = 0x00d4700b, /* PVR_STD_PAL_DK */ - [PVR2_CVAL_VIDEOSTANDARD_SECAM_L] = 0x00c4100b, /* PVR_STD_SECAM */ - [PVR2_CVAL_VIDEOSTANDARD_NTSC_M] = 0x00163044, /* PVR_STD_NTSC */ - [PVR2_CVAL_VIDEOSTANDARD_PAL_M] = 0x00163044 /* PVR_STD_PAL_M */ +struct pvr2_tuner_handler { + struct pvr2_hdw *hdw; + struct pvr2_i2c_client *client; + struct pvr2_i2c_handler i2c_handler; + int type_update_fl; }; -#define trace_tuner(...) pvr2_trace(PVR2_TRACE_TUNER,__VA_ARGS__) - -int pvr2_tuner_get_default_type(struct pvr2_hdw *hdw) -{ - int unit_number = hdw->unit_number; - int tp = -1; - if ((unit_number >= 0) && (unit_number < PVR_NUM)) { - tp = tuner[unit_number]; - } - if (tp < 0) return -EINVAL; - hdw->tuner_type = tp; - return 0; -} - -int pvr2_tuner_set_freq(struct pvr2_hdw *hdw) -{ - int stat; - struct v4l2_frequency freq; - memset(&freq,0,sizeof(freq)); - freq.frequency = hdw->controls[PVR2_CID_FREQUENCY].value / 62500; - freq.tuner = 0; - freq.type = V4L2_TUNER_ANALOG_TV; - trace_tuner("pvr2_tuner_set_freq(%d)",freq.frequency); - stat = pvr2_i2c_tuner_cmd(hdw,VIDIOC_S_FREQUENCY,&freq); - if (stat < 0) return stat; - /* This kicks the audio controller... */ - stat = pvr2_i2c_msp3400_cmd(hdw,VIDIOCSFREQ,0); - if (stat < 0) return stat; - /* Smack around tda9887 as well, if it is lurking nearby */ - if (hdw->i2c_ifhandler_client) { - pvr2_i2c_ifhandler_cmd(hdw,VIDIOC_S_FREQUENCY,&freq); - } - hdw->subsys_enabled_mask |= PVR2_SUBSYS_TUNER_CFG_FREQ; - return 0; -} -int pvr2_tuner_set_type(struct pvr2_hdw *hdw) +static void set_type(struct pvr2_tuner_handler *ctxt) { + struct pvr2_hdw *hdw = ctxt->hdw; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13) struct tuner_setup setup; #endif - trace_tuner("pvr2_tuner_set_type(%d)",hdw->tuner_type); - if (((int)(hdw->tuner_type)) < 0) { - trace_tuner("tuner type not set yet"); - return 0; - } + pvr2_trace(PVR2_TRACE_CHIPS,"i2c tuner set_type(%d)",hdw->tuner_type); + if (((int)(hdw->tuner_type)) < 0) return; + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13) setup.addr = ADDR_UNSET; setup.type = hdw->tuner_type; setup.mode_mask = T_RADIO | T_ANALOG_TV; /* We may really want mode_mask to be T_ANALOG_TV for now */ - return pvr2_i2c_tuner_cmd(hdw,TUNER_SET_TYPE_ADDR,&setup); + pvr2_i2c_client_cmd(ctxt->client,TUNER_SET_TYPE_ADDR,&setup); #else - return pvr2_i2c_tuner_cmd(hdw,TUNER_SET_TYPE,&hdw->tuner_type); + pvr2_i2c_tuner_cmd(ctxt->client,TUNER_SET_TYPE,&hdw->tuner_type); #endif + ctxt->type_update_fl = 0; +} + + +static int tuner_check(struct pvr2_tuner_handler *ctxt) +{ + struct pvr2_hdw *hdw = ctxt->hdw; + if (hdw->tuner_updated) ctxt->type_update_fl = !0; + return ctxt->type_update_fl != 0; +} + + +static void tuner_update(struct pvr2_tuner_handler *ctxt) +{ + if (ctxt->type_update_fl) set_type(ctxt); } -int pvr2_tuner_set_standard(struct pvr2_hdw *hdw) + +static void pvr2_tuner_detach(struct pvr2_tuner_handler *ctxt) { - struct i2c_msg msg[1]; - u8 buff [4]; - v4l2_std_id vs; - int cvstd = hdw->controls[PVR2_CID_VIDEOSTANDARD].value; - - trace_tuner("pvr2_tuner_set_standard(%d)", - hdw->controls[PVR2_CID_VIDEOSTANDARD].value); - - /* FIXME: Might be tuner dependant ? Need more hardware info... */ - /* MCI 15-May-2005: I have absolutely no idea what this is doing - or who it is talking to. */ - PVR2_DECOMPOSE_BE( - buff, 0,pvr_tbl_standard[cvstd]); - msg[0].addr = PVR_STD_I2C_ADDR; - msg[0].flags = 0; - msg[0].len = sizeof(buff); - msg[0].buf = buff; - i2c_transfer(&hdw->i2c_adap,msg,sizeof(msg)/sizeof(msg[0])); - - /* Also tell tuner.ko about the video standard. */ - 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; - } - pvr2_i2c_tuner_cmd(hdw,VIDIOC_S_STD,&vs); - if (hdw->i2c_ifhandler_client) { - pvr2_i2c_ifhandler_cmd(hdw,VIDIOC_S_STD,&vs); - } - - hdw->subsys_enabled_mask |= PVR2_SUBSYS_TUNER_CFG_STD; - return 0; + ctxt->client->handler = 0; + kfree(ctxt); } + +const static struct pvr2_i2c_handler_functions tuner_funcs = { + .detach = (void (*)(void *))pvr2_tuner_detach, + .check = (int (*)(void *))tuner_check, + .update = (void (*)(void *))tuner_update, +}; + + +int pvr2_i2c_tuner_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) +{ + struct pvr2_tuner_handler *ctxt; + if (cp->handler) return 0; + + ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + if (!ctxt) return 0; + memset(ctxt,0,sizeof(*ctxt)); + + ctxt->i2c_handler.func_data = ctxt; + ctxt->i2c_handler.func_table = &tuner_funcs; + ctxt->type_update_fl = !0; + ctxt->client = cp; + ctxt->hdw = hdw; + cp->handler = &ctxt->i2c_handler; + return !0; +} + + + + /* Stuff for Emacs to see, in order to encourage consistent editing style: *** Local Variables: *** diff --git a/v4l_experimental/pvrusb2/pvrusb2-tuner.h b/v4l_experimental/pvrusb2/pvrusb2-tuner.h index 501f2af48..83d92d06d 100644 --- a/v4l_experimental/pvrusb2/pvrusb2-tuner.h +++ b/v4l_experimental/pvrusb2/pvrusb2-tuner.h @@ -1,6 +1,6 @@ /* * - * $Id: pvrusb2-tuner.h,v 1.1 2005/11/14 13:31:24 mchehab Exp $ + * $Id: pvrusb2-tuner.h,v 1.2 2006/01/01 08:26:03 mcisely Exp $ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -21,12 +21,9 @@ #ifndef __PVRUSB2_TUNER_H #define __PVRUSB2_TUNER_H -struct pvr2_hdw; +#include "pvrusb2-i2c-core.h" -int pvr2_tuner_get_default_type(struct pvr2_hdw *); -int pvr2_tuner_set_freq(struct pvr2_hdw *); -int pvr2_tuner_set_type(struct pvr2_hdw *); -int pvr2_tuner_set_standard(struct pvr2_hdw *); +int pvr2_i2c_tuner_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); #endif /* __PVRUSB2_TUNER_H */ diff --git a/v4l_experimental/pvrusb2/pvrusb2-v4l2.c b/v4l_experimental/pvrusb2/pvrusb2-v4l2.c index 71590e400..6d0f8621e 100644 --- a/v4l_experimental/pvrusb2/pvrusb2-v4l2.c +++ b/v4l_experimental/pvrusb2/pvrusb2-v4l2.c @@ -1,6 +1,6 @@ /* * - * $Id: pvrusb2-v4l2.c,v 1.4 2005/12/02 13:59:00 mcisely Exp $ + * $Id: pvrusb2-v4l2.c,v 1.5 2006/01/01 08:26:03 mcisely Exp $ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> @@ -941,6 +941,7 @@ static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip) static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp) { + pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw,-1); pvr2_v4l2_dev_destroy(&vp->video_dev); pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp); @@ -992,8 +993,6 @@ int pvr2_v4l2_release(struct inode *inode, struct file *file) v4l2_prio_close(&vp->prio, &fhp->prio); file->private_data = NULL; - pvr2_hdw_v4l_store_minor_number(fhp->channel.mc_head->hdw,-1); - pvr2_context_enter(mp); do { if (fhp->vnext) { fhp->vnext->vprev = fhp->vprev; diff --git a/v4l_experimental/pvrusb2/pvrusb2-version.h b/v4l_experimental/pvrusb2/pvrusb2-version.h index 1ce53a4bd..9978219d4 100644 --- a/v4l_experimental/pvrusb2/pvrusb2-version.h +++ b/v4l_experimental/pvrusb2/pvrusb2-version.h @@ -1,4 +1,4 @@ #ifndef _INCLUDED_PVRUSB2_DRIVER_VERSION #define _INCLUDED_PVRUSB2_DRIVER_VERSION -#define DRIVER_VERSION "20051202 (from v4l tree)" +#define DRIVER_VERSION "20051231 (from v4l tree)" #endif diff --git a/v4l_experimental/pvrusb2/pvrusb2-video-v4l.c b/v4l_experimental/pvrusb2/pvrusb2-video-v4l.c index 037ba5a71..bf91e3d2a 100644 --- a/v4l_experimental/pvrusb2/pvrusb2-video-v4l.c +++ b/v4l_experimental/pvrusb2/pvrusb2-video-v4l.c @@ -1,6 +1,6 @@ /* * - * $Id: pvrusb2-video-v4l.c,v 1.5 2005/12/28 16:28:02 mcisely Exp $ + * $Id: pvrusb2-video-v4l.c,v 1.6 2006/01/01 08:26:03 mcisely Exp $ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> @@ -22,11 +22,6 @@ /* - This module connects the pvrusb2 driver to the I2C chip level - driver which handles device video processing. This interface is - used internally by the driver; higher level code should only - interact through the interface provided by pvrusb2-hdw.h. - This source file is specifically designed to interface with the saa711x support that is available in the v4l available starting with linux 2.6.15. @@ -37,7 +32,6 @@ #ifndef PVR2_SUPPRESS_SAA711X -#include "pvrusb2-i2c.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" #include <linux/videodev.h> @@ -45,41 +39,21 @@ #include <linux/errno.h> #include <linux/slab.h> - -#define pvr2_decoder_trace(...) pvr2_trace(PVR2_TRACE_DECODER,__VA_ARGS__) - - -static int pvr2_decoder_v4l_enable_output(struct pvr2_decoder *dcp) -{ - int status; - struct pvr2_hdw *hdw = dcp->hdw; - pvr2_decoder_trace("pvr2_decoder_enable_output"); - status = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_STREAMON,0); - if (status) return status; - hdw->subsys_enabled_mask |= PVR2_SUBSYS_DIGITIZER_RUN; - return 0; -} - - -static int pvr2_decoder_v4l_disable_output(struct pvr2_decoder *dcp) -{ - int status; - struct pvr2_hdw *hdw = dcp->hdw; - pvr2_decoder_trace("pvr2_decoder_disable_output"); - status = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_STREAMOFF,0); - if (status) return status; - hdw->subsys_enabled_mask &= ~PVR2_SUBSYS_DIGITIZER_RUN; - return 0; -} +struct pvr2_v4l_decoder { + struct pvr2_i2c_handler handler; + struct pvr2_decoder_ctrl ctrl; + struct pvr2_i2c_client *client; + struct pvr2_hdw *hdw; + unsigned long stale_mask; +}; -static int pvr2_decoder_v4l_set_input(struct pvr2_decoder *dcp) +static void set_input(struct pvr2_v4l_decoder *ctxt) { - int status; - struct pvr2_hdw *hdw = dcp->hdw; + struct pvr2_hdw *hdw = ctxt->hdw; int v = 0; - pvr2_decoder_trace("pvr2_decoder_set_input(%d)", - hdw->controls[PVR2_CID_INPUT].value); + pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_input(%d)", + hdw->controls[PVR2_CID_INPUT].value); switch(hdw->controls[PVR2_CID_INPUT].value){ case PVR2_CVAL_INPUT_TV: v = 4; @@ -93,179 +67,166 @@ static int pvr2_decoder_v4l_set_input(struct pvr2_decoder *dcp) case PVR2_CVAL_INPUT_RADIO: // ????? No idea yet what to do here default: - return -EINVAL; - } - status = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_S_INPUT,&v); - if (!status) { - hdw->subsys_enabled_mask |= PVR2_SUBSYS_DIGITIZER_CFG_INPUT; + return; } - return status; + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_INPUT,&v); } -static int pvr2_decoder_v4l_set_bcsh(struct pvr2_decoder *dcp) +static int check_input(struct pvr2_v4l_decoder *ctxt) { - struct v4l2_control ctrl; - struct pvr2_hdw *hdw = dcp->hdw; - int ret; - memset(&ctrl,0,sizeof(ctrl)); - - pvr2_decoder_trace("pvr2_decoder_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); - - ctrl.id = V4L2_CID_BRIGHTNESS; - ctrl.value = hdw->controls[PVR2_CID_BRIGHTNESS].value; - ret = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_S_CTRL,&ctrl); - if (ret) return ret; - ctrl.id = V4L2_CID_CONTRAST; - ctrl.value = hdw->controls[PVR2_CID_CONTRAST].value; - ret = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_S_CTRL,&ctrl); - if (ret) return ret; - ctrl.id = V4L2_CID_SATURATION; - ctrl.value = hdw->controls[PVR2_CID_SATURATION].value; - ret = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_S_CTRL,&ctrl); - if (ret) return ret; - ctrl.id = V4L2_CID_HUE; - ctrl.value = hdw->controls[PVR2_CID_HUE].value; - ret = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_S_CTRL,&ctrl); - if (ret) return ret; - - hdw->subsys_enabled_mask |= PVR2_SUBSYS_DIGITIZER_CFG_BCSH; - return 0; + struct pvr2_hdw *hdw = ctxt->hdw; + return hdw->controls[PVR2_CID_INPUT].dirty != 0; } -static int pvr2_decoder_v4l_is_tuned(struct pvr2_decoder *dcp) +static void set_audio(struct pvr2_v4l_decoder *ctxt) { - struct v4l2_tuner vt; - struct pvr2_hdw *hdw = dcp->hdw; - int ret; + u32 val; + struct pvr2_hdw *hdw = ctxt->hdw; - memset(&vt,0,sizeof(vt)); - ret = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_G_TUNER,&vt); - if (ret < 0) return -EINVAL; - return vt.signal ? 1 : 0; + pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 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 pvr2_decoder_v4l_set_size(struct pvr2_decoder *dcp) +static int check_audio(struct pvr2_v4l_decoder *ctxt) { - struct v4l2_format fmt; - struct pvr2_hdw *hdw = dcp->hdw; - int ret; + struct pvr2_hdw *hdw = ctxt->hdw; + return hdw->controls[PVR2_CID_SRATE].dirty != 0; +} - 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; +struct pvr2_v4l_decoder_ops { + void (*update)(struct pvr2_v4l_decoder *); + int (*check)(struct pvr2_v4l_decoder *); +}; - pvr2_decoder_trace("pvr2_decoder_set_size(%dx%d)", - fmt.fmt.pix.width,fmt.fmt.pix.height); - ret = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_S_FMT,&fmt); - if (ret) return ret; +static const struct pvr2_v4l_decoder_ops decoder_ops[] = { + { .update = set_input, .check = check_input}, + { .update = set_audio, .check = check_audio}, +}; - hdw->subsys_enabled_mask |= PVR2_SUBSYS_DIGITIZER_CFG_SIZE; - return 0; + +static void decoder_detach(struct pvr2_v4l_decoder *ctxt) +{ + ctxt->client->handler = 0; + ctxt->hdw->decoder_ctrl = 0; + kfree(ctxt); } -static int pvr2_decoder_v4l_set_audio(struct pvr2_decoder *dcp) +static int decoder_check(struct pvr2_v4l_decoder *ctxt) { - int ret; - struct pvr2_hdw *hdw = dcp->hdw; - u32 val; - - pvr2_decoder_trace("pvr2_decoder_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; + 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; + } } - ret = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val); - if (ret) return ret; - hdw->subsys_enabled_mask |= PVR2_SUBSYS_DIGITIZER_CFG_AUDIO; - return 0; + return ctxt->stale_mask != 0; } -static int pvr2_decoder_v4l_set_norm(struct pvr2_decoder *dcp) +static void decoder_update(struct pvr2_v4l_decoder *ctxt) { - v4l2_std_id std; - struct pvr2_hdw *hdw = dcp->hdw; - int status; - pvr2_decoder_trace("pvr2_decoder_set_norm %d", - hdw->controls[PVR2_CID_VIDEOSTANDARD].value); - switch (hdw->controls[PVR2_CID_VIDEOSTANDARD].value) { - default: - case PVR2_CVAL_VIDEOSTANDARD_NTSC_M: - std = V4L2_STD_NTSC_M; - break; - case PVR2_CVAL_VIDEOSTANDARD_SECAM_L: - std = V4L2_STD_SECAM; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_I: - std = V4L2_STD_PAL_I; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_DK: - std = V4L2_STD_PAL_DK; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_BG: - std = V4L2_STD_PAL_BG; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_M: - std = V4L2_STD_PAL_M; - break; + 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); } - status = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_S_STD,&std); - if (status) return status; - hdw->subsys_enabled_mask |= PVR2_SUBSYS_DIGITIZER_CFG_NORM; - return 0; } -static const struct pvr2_decoder_func_table pvr2_decoder_v4l_table = { - (pvr2_decoder_func)0, - (pvr2_decoder_func)pvr2_decoder_v4l_set_norm, - (pvr2_decoder_func)pvr2_decoder_v4l_set_input, - (pvr2_decoder_func)pvr2_decoder_v4l_set_size, - (pvr2_decoder_func)pvr2_decoder_v4l_set_audio, - (pvr2_decoder_func)pvr2_decoder_v4l_set_bcsh, - (pvr2_decoder_func)pvr2_decoder_v4l_is_tuned, - (pvr2_decoder_func)pvr2_decoder_v4l_enable_output, - (pvr2_decoder_func)pvr2_decoder_v4l_disable_output, -}; +static int decoder_detect(struct pvr2_i2c_client *cp) +{ + /* Attempt to query the decoder - let's see if it will answer */ + struct v4l2_tuner vt; + int ret; + memset(&vt,0,sizeof(vt)); + ret = pvr2_i2c_client_cmd(cp,VIDIOC_G_TUNER,&vt); + return ret == 0; /* Return true if it answered */ +} -struct pvr2_decoder *pvr2_decoder_v4l_create(struct pvr2_hdw *hdw) + +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); +} + + +static int decoder_is_tuned(struct pvr2_v4l_decoder *ctxt) { - /* Attempt to query the decoder - let's see if it will answer */ struct v4l2_tuner vt; int ret; - struct pvr2_decoder *dcp; memset(&vt,0,sizeof(vt)); - ret = pvr2_i2c_saa7115_cmd(hdw,VIDIOC_G_TUNER,&vt); - if (ret < 0) return 0; /* Nope, not there */ - - /* Yes, it's there. So initialize... */ - dcp = kmalloc(sizeof(*dcp),GFP_KERNEL); - if (!dcp) return 0; - memset(dcp,0,sizeof(*dcp)); - dcp->hdw = hdw; - dcp->func_table = &pvr2_decoder_v4l_table; - return dcp; + ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt); + if (ret < 0) return -EINVAL; + return vt.signal ? 1 : 0; } + +const static struct pvr2_i2c_handler_functions hfuncs = { + .detach = (void (*)(void *))decoder_detach, + .check = (int (*)(void *))decoder_check, + .update = (void (*)(void *))decoder_update, +}; + + +int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *hdw, + struct pvr2_i2c_client *cp) +{ + struct pvr2_v4l_decoder *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->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; + return !0; +} + + #endif /* PVR2_SUPPRESS_SAA711X */ diff --git a/v4l_experimental/pvrusb2/pvrusb2-video-v4l.h b/v4l_experimental/pvrusb2/pvrusb2-video-v4l.h index e3ef12602..fe4679f48 100644 --- a/v4l_experimental/pvrusb2/pvrusb2-video-v4l.h +++ b/v4l_experimental/pvrusb2/pvrusb2-video-v4l.h @@ -1,6 +1,6 @@ /* * - * $Id: pvrusb2-video-v4l.h,v 1.1 2005/11/27 23:01:16 mcisely Exp $ + * $Id: pvrusb2-video-v4l.h,v 1.2 2006/01/01 08:26:03 mcisely Exp $ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> @@ -33,11 +33,12 @@ */ #include "compat.h" -#include "pvrusb2-video.h" #ifndef PVR2_SUPPRESS_SAA711X -struct pvr2_decoder *pvr2_decoder_v4l_create(struct pvr2_hdw *); +#include "pvrusb2-i2c-core.h" + +int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); #endif /* PVR2_SUPPRESS_SAA711X */ diff --git a/v4l_experimental/pvrusb2/pvrusb2-video.c b/v4l_experimental/pvrusb2/pvrusb2-video.c deleted file mode 100644 index 3dc18ec55..000000000 --- a/v4l_experimental/pvrusb2/pvrusb2-video.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * - * $Id: pvrusb2-video.c,v 1.2 2005/11/27 23:01:16 mcisely Exp $ - * - * Copyright (C) 2005 Mike Isely <isely@pobox.com> - * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> - * - * This program is free software; you can redistribute it and/or 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 module connects the pvrusb2 driver to the I2C chip level - driver which handles device video processing. This interface is - used internally by the driver; higher level code should only - interact through the interface provided by pvrusb2-hdw.h. - - Since there are several possible choices of low level drivers, we - go through a layer of indirection in an attempt to select the right - choice. This module implements that indirection. - -*/ - -#include "compat.h" -#include "pvrusb2-video.h" -#include "pvrusb2-debug.h" -#ifndef PVR2_SUPPRESS_SAA711X -#include "pvrusb2-video-v4l.h" -#endif -#ifdef PVR2_ENABLE_SAA7115 -#include "pvrusb2-video-ivtv.h" -#endif -#include "pvrusb2-debug.h" -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/slab.h> - - -struct pvr2_decoder *pvr2_decoder_create(struct pvr2_hdw *hdw) -{ - struct pvr2_decoder *dcp; - -#ifndef PVR2_SUPPRESS_SAA711X - dcp = pvr2_decoder_v4l_create(hdw); - if (dcp) { - pvr2_trace(PVR2_TRACE_INIT,"Using v4l video decoder driver"); - return dcp; - } -#endif - -#ifdef PVR2_ENABLE_SAA7115 - dcp = pvr2_decoder_ivtv_create(hdw); - if (dcp) { - pvr2_trace(PVR2_TRACE_INIT,"Using ivtv video decoder driver"); - return dcp; - } -#endif - - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Failed to select a viable v4l video decoder driver"); - return 0; -} - - -void pvr2_decoder_destroy(struct pvr2_decoder *dcp) -{ - if (!dcp) return; - if (dcp->func_table->destroy_func) { - dcp->func_table->destroy_func(dcp); - } else { - kfree(dcp); - } -} - - -int pvr2_decoder_set_norm(struct pvr2_decoder *dcp) -{ - if (!dcp) return -EINVAL; - return dcp->func_table->set_norm_func(dcp); -} - - -int pvr2_decoder_set_input(struct pvr2_decoder *dcp) -{ - if (!dcp) return -EINVAL; - return dcp->func_table->set_input_func(dcp); -} - - -int pvr2_decoder_set_size(struct pvr2_decoder *dcp) -{ - if (!dcp) return -EINVAL; - return dcp->func_table->set_size_func(dcp); -} - - -int pvr2_decoder_set_audio(struct pvr2_decoder *dcp) -{ - if (!dcp) return -EINVAL; - return dcp->func_table->set_audio_func(dcp); -} - - -int pvr2_decoder_set_bcsh(struct pvr2_decoder *dcp) -{ - if (!dcp) return -EINVAL; - return dcp->func_table->set_bcsh_func(dcp); -} - - -int pvr2_decoder_is_tuned(struct pvr2_decoder *dcp) -{ - if (!dcp) return -EINVAL; - return dcp->func_table->is_tuned_func(dcp); -} - - -int pvr2_decoder_enable_output(struct pvr2_decoder *dcp) -{ - if (!dcp) return -EINVAL; - return dcp->func_table->enable_func(dcp); -} - - -int pvr2_decoder_disable_output(struct pvr2_decoder *dcp) -{ - if (!dcp) return -EINVAL; - return dcp->func_table->disable_func(dcp); -} - - -/* - 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/v4l_experimental/pvrusb2/pvrusb2-video.h b/v4l_experimental/pvrusb2/pvrusb2-video.h deleted file mode 100644 index f08c3671d..000000000 --- a/v4l_experimental/pvrusb2/pvrusb2-video.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * - * $Id: pvrusb2-video.h,v 1.2 2005/11/27 23:01:16 mcisely Exp $ - * - * Copyright (C) 2005 Mike Isely <isely@pobox.com> - * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> - * - * This program is free software; you can redistribute it and/or 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_VIDEO_H -#define __PVRUSB2_VIDEO_H - -/* - - This module connects the pvrusb2 driver to the I2C chip level - driver which handles device video processing. This interface is - used internally by the driver; higher level code should only - interact through the interface provided by pvrusb2-hdw.h. - -*/ - -struct pvr2_hdw; -struct pvr2_decoder; - -typedef int (*pvr2_decoder_func)(struct pvr2_decoder *); - -struct pvr2_decoder_func_table { - pvr2_decoder_func destroy_func; - pvr2_decoder_func set_norm_func; - pvr2_decoder_func set_input_func; - pvr2_decoder_func set_size_func; - pvr2_decoder_func set_audio_func; - pvr2_decoder_func set_bcsh_func; - pvr2_decoder_func is_tuned_func; - pvr2_decoder_func enable_func; - pvr2_decoder_func disable_func; -}; - -struct pvr2_decoder { - struct pvr2_hdw *hdw; - const struct pvr2_decoder_func_table *func_table; -}; - -struct pvr2_decoder *pvr2_decoder_create(struct pvr2_hdw *); -void pvr2_decoder_destroy(struct pvr2_decoder *); -int pvr2_decoder_set_norm(struct pvr2_decoder *); -int pvr2_decoder_set_input(struct pvr2_decoder *); -int pvr2_decoder_set_size(struct pvr2_decoder *); -int pvr2_decoder_set_audio(struct pvr2_decoder *); -int pvr2_decoder_set_bcsh(struct pvr2_decoder *); -int pvr2_decoder_is_tuned(struct pvr2_decoder *); -int pvr2_decoder_enable_output(struct pvr2_decoder *); -int pvr2_decoder_disable_output(struct pvr2_decoder *); - - -#endif /* __PVRUSB2_VIDEO_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: *** - */ |