I sent this proposal to several individual developers already around 1-2 weeks ago, and received several comments, maybe I should give it a try to push a very last discussion about it. Video4Linux/DVB Hybrid Tuner Support ==================================== During the last few years some hybrid tuners came up which support digital and analogue signals. Currently there are 2 approaches how to use such tuners. The first way is to simply send a few bytes through i2c to a tuner for setting up a frequency, this way splits up the specs into 2 halfs, the analogue tuner interface supports analogue tuning and the dtv part only supports setting up dtv channels. Pro: the code is tight, since these tuners are easy to program. Contra: all devices would need to reimplement that algorithm for setting up the channel, there's no global point for such an approach. Another way would be to inherit the v4l tuner interface like it's done in the saa7134-dvb tuner the driver just reimplements set_params and calls VIDIOC_S_FREQUENCY. This way if a device only supports DVB it would also have a dependency to the video4linux tuner framework. Pro: code can be reused, only one tuner implementation has to be written. Cons: a dvb only driver would require video4linux tuner bindings. How is it currently implemented into the DVB framework? ------------------------------------------------------- there's already a pluggable tuner mechanism implemented into the DVB framework which makes use of dvb_attach(). Every external module should have a struct that defines what method are provided by the tuner, currently the arguments of these methods are bound to struct dvb_frontend * which carries a priv pointer which is used to stored private data of the module. Here an example of such a struct (taken from mt2060.c) static const struct dvb_tuner_ops mt2060_tuner_ops = { .info = { .name = "Microtune MT2060", .frequency_min = 48000000, .frequency_max = 860000000, .frequency_step = 50000, }, .release = mt2060_release, .init = mt2060_init, .sleep = mt2060_sleep, .set_params = mt2060_set_params, .get_frequency = mt2060_get_frequency, .get_bandwidth = mt2060_get_bandwidth }; The current interface structure is defined as: struct dvb_tuner_ops { struct dvb_tuner_info info; int (*release)(struct dvb_frontend *fe); int (*init)(struct dvb_frontend *fe); int (*sleep)(struct dvb_frontend *fe); /** This is for simple PLLs - set all parameters in one go. */ int (*set_params)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p); /** This is support for demods like the mt352 - fills out the supplied buffer with what to write. */ int (*calc_regs)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p, u8 *buf, int buf_len); int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency); int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth); #define TUNER_STATUS_LOCKED 1 int (*get_status)(struct dvb_frontend *fe, u32 *status); /** These are provided seperately from set_params in order to facilitate silicon * tuners which require sophisticated tuning loops, controlling each parameter seperately. */ int (*set_frequency)(struct dvb_frontend *fe, u32 frequency); int (*set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth); }; these functionpointers are set up during the call of the specific {dvbtuner}_attach function memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(struct dvb_tuner_ops)); This approach is heavily bound to an initialized dvb_frontend structure. The current video4linux approach -------------------------------- the tuner-core doesn't support any external modules for real, instead all modules are directly linked into tuner.ko. The tuner-core calls a detection/setuproutine during either tuner_attach or TUNER_SET_TYPE_ADDR; during the i2c scan you can also override several settings by calling TUNER_SET_TYPE_ADDR within an attach_inform callback to adjust some settings (for example setting a different tuner type on a specific i2c address) This control option also supports changing the tuner type at a later time (for example for radio initialization etc) The new way =========== The newer way I'd like to propose here is to unify a pluggable tuner mechanism which then can be reused by other drivers as well for upcoming tuners. proposed changes to the dvb-framework: changes to the dvb-framework are intrusive, they'd require the change of dvb_tuner_ops. I'd suggest to defer dvb_frontend and add several new pointers to the dvb_tuner_ops structure. The dvb_tuner_ops structure should become a neutral structure that can easily be reused by anything that requires a tuner. the structure could be: struct v4l_dvb_tuner { /* wrapper */ void *priv; /* some privat data for internal use */ void *dev; /* v4l private data for tuner-core */ struct dvb_frontend *fe; /* dvb_frontend, for dvb only drivers, internal use */ int (*ioctl)(struct v4l_dvb_tuner *dev, int cmd, int arg); struct tuner_info info; int (*release)(struct v4l_dvb_tuner *dev); int (*init)(struct v4l_dvb_tuner *dev); int (*sleep)(struct v4l_dvb_tuner *dev); /** This is for simple PLLs - set all parameters in one go. */ int (*set_params)(struct v4l_dvb_tuner *dev, struct tuner_parameters *p); /** This is support for demods like the mt352 - fills out the supplied buffer with what to write. */ int (*calc_regs)(struct v4l_dvb_tuner *dev, struct tuner_parameters *p, u8 *buf, int buf_len); int (*get_frequency)(struct v4l_dvb_tuner *dev, u32 *frequency); int (*get_bandwidth)(struct v4l_dvb_tuner *dev, u32 *bandwidth); #define TUNER_STATUS_LOCKED 1 int (*get_status)(struct v4l_dvb_tuner *dev, u32 *status); /** These are provided seperately from set_params in order to facilitate silicon * tuners which require sophisticated tuning loops, controlling each parameter seperately. */ int (*set_frequency)(struct v4l_dvb_tuner *dev, u32 frequency); int (*set_bandwidth)(struct v4l_dvb_tuner *dev, u32 bandwidth); int (*set_mode)(struct v4l_dvb_tuner *dev, struct tuner_parameters *params); }; in that case dvb_frontend *fe just gets deferred as an element of that structure, if any dvb only would still require dvb_frontend *fe it could easily still make use of it by just refering to its own structure dev->fe; As for hybrid tuners they'd have to avoid the usage of dvb_frontend *fe and they should only rely on its own structure which can be extended even in future. Features like i2c_gate_ctrl might also be added in a unified way to that structure. if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); tuner-core changes: the tuner-core itself relies on i2c, to use the unified external modules we have to add an approach which supports loading these external modules. I added support for loading the xc3028-tuner module within set_type() using the symbol_request approach When tuner.ko gets loaded it scans for i2c addresses and attaches itself to some predefined addresses. if a tuner was found on a certain address the i2c framework calls an attach_inform function which in case of the em28xx and several other drivers is privatly defined in the driver itself. this happens before: register_client: tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name); i2c_attach_client (&t->i2c); set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback, TUNER_ATTACH); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) MOD_INC_USE_COUNT; #endif return 0; } set_type gets called, at that point you can override the tuner to any tuner type you want by calling TUNER_SET_TYPE_ADDR within the attach_inform callback. In case of the em28xx it sets the tuner to TUNER_XC3028, so that the tuner framework knows that it has to request the xc3028 tuner and initialize the necessary function pointers with the unified xc3028 tuner structure. I already had a previous tuner modularization approach which relied on the same way it was done in saa7134 by exporting a symbol which gets called by the external module to copy some function pointers. There were some drawbacks with that approach which are felt with saa7134 cx88 and em28xx already it's not possible to request external modules which rely on a symbol that's not yet exported This becomes an important point if you do some tuner detection, it's not possible to load external drivers before the tuner-core got loaded completly. So I didn't make much noise that this approach is broken especially during the initialization of a tuner.
Mauro wrote that requesting the tuner module first and afterwards call an init function within the driver which requested that module, this might be a solution for using initialization functions similar to usb_register/usb_deregister. on the other side this would break the dvb_attach approach and it might require much bigger changes in the dvb framework (?) This approach is currently partly implemented in v4l-dvb-kernel on mcentral (http://mcentral.de/hg/~mrec/v4l-dvb-kernel)
a current implementation which uses the unified module approach and a similar approach like dvb_attach can be found at: http://mcentral.de/hg/~mrec/v4l-dvb-experimental (see tuner-core.c, v4l_dvb_tuner.h) Markus _______________________________________________ linux-dvb mailing list linux-dvb@xxxxxxxxxxx http://www.linuxtv.org/cgi-bin/mailman/listinfo/linux-dvb