[linux-dvb] Hybrid tuner proposal (V4L/DVB)

Markus Rechberger mrechberger at gmail.com
Thu Apr 5 00:52:30 CEST 2007


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



More information about the linux-dvb mailing list