Re: [PATCH REVIEW] e4000: convert DVB tuner to I2C driver model

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Oct 16, 2013 at 1:01 PM, Michael Krufky <mkrufky@xxxxxxxxxxx> wrote:
> On Wed, Oct 16, 2013 at 12:43 PM, Antti Palosaari <crope@xxxxxx> wrote:
>> On 16.10.2013 18:54, Michael Krufky wrote:
>>>
>>> This kinda makes me a bit nervous.  The patch itself looks OK but the
>>> cascading effects that it will have across the DVB subsystem need to
>>> be discussed.
>>
>>
>> Basically, the only effect is that you must use i2c_new_device() or
>> i2c_new_probed_device() instead of old proprietary dvb_attach(). Convert old
>> proprietary method with Kernel standard one.
>>
>> But thats not all. It was only first step. My final goal is to study, test
>> and finally make radio tuners as a own "standalone" I2C drivers. This means
>> that I will remove DVB frontend dependency next from the given tuner driver.
>> And before you ask why: I need some more sophisticated access to tuner in
>> order to meet SDR needs. Also I don't see any reason why those tuners should
>> be a part of DVB API or part of V4L2 API (or even both in case of "hybrid"
>> model). Actual reasons are likely payload from the history, but as of today
>> view it is stupid design :]
>>
>>
>>> Is there a discussion about this kind of conversion on the mailing
>>> list somewhere that I've missed?
>>
>>
>> There has been many times around the years. i2c_new_probed_device() function
>> [1] seems to be added for somehow for out needs too...
>>
>> [1] http://lists.lm-sensors.org/pipermail/i2c/2007-March/000990.html
>>
>>
>> regards
>> Antti
>>
>>
>>>
>>> -Mike
>>>
>>> On Tue, Oct 15, 2013 at 7:33 PM, Mauro Carvalho Chehab
>>> <m.chehab@xxxxxxxxxxx> wrote:
>>>>
>>>> Em Wed, 16 Oct 2013 01:31:04 +0300
>>>> Antti Palosaari <crope@xxxxxx> escreveu:
>>>>
>>>>> Initial driver conversion from proprietary DVB tuner model to more
>>>>> general I2C driver model.
>>>>>
>>>>> That commit has just basic binding stuff and driver itself still
>>>>> needs to be converted more complete later.
>>>>>
>>>>> Cc: Jean Delvare <khali@xxxxxxxxxxxx>
>>>>> Signed-off-by: Antti Palosaari <crope@xxxxxx>
>>>>> ---
>>>>>   drivers/media/tuners/e4000.c            | 73
>>>>> ++++++++++++++++++++++-----------
>>>>>   drivers/media/tuners/e4000.h            |  9 +++-
>>>>>   drivers/media/tuners/e4000_priv.h       |  4 +-
>>>>>   drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 31 +++++++++-----
>>>>>   4 files changed, 78 insertions(+), 39 deletions(-)
>>>>>
>>>>> diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c
>>>>> index 54e2d8a..f6a5dbd 100644
>>>>> --- a/drivers/media/tuners/e4000.c
>>>>> +++ b/drivers/media/tuners/e4000.c
>>>>> @@ -27,7 +27,7 @@ static int e4000_wr_regs(struct e4000_priv *priv, u8
>>>>> reg, u8 *val, int len)
>>>>>        u8 buf[1 + len];
>>>>>        struct i2c_msg msg[1] = {
>>>>>                {
>>>>> -                     .addr = priv->cfg->i2c_addr,
>>>>> +                     .addr = priv->i2c_addr,
>>>>>                        .flags = 0,
>>>>>                        .len = sizeof(buf),
>>>>>                        .buf = buf,
>>>>> @@ -56,12 +56,12 @@ static int e4000_rd_regs(struct e4000_priv *priv, u8
>>>>> reg, u8 *val, int len)
>>>>>        u8 buf[len];
>>>>>        struct i2c_msg msg[2] = {
>>>>>                {
>>>>> -                     .addr = priv->cfg->i2c_addr,
>>>>> +                     .addr = priv->i2c_addr,
>>>>>                        .flags = 0,
>>>>>                        .len = 1,
>>>>>                        .buf = &reg,
>>>>>                }, {
>>>>> -                     .addr = priv->cfg->i2c_addr,
>>>>> +                     .addr = priv->i2c_addr,
>>>>>                        .flags = I2C_M_RD,
>>>>>                        .len = sizeof(buf),
>>>>>                        .buf = buf,
>>>>> @@ -233,8 +233,8 @@ static int e4000_set_params(struct dvb_frontend *fe)
>>>>>         * or more.
>>>>>         */
>>>>>        f_vco = c->frequency * e4000_pll_lut[i].mul;
>>>>> -     sigma_delta = div_u64(0x10000ULL * (f_vco % priv->cfg->clock),
>>>>> priv->cfg->clock);
>>>>> -     buf[0] = f_vco / priv->cfg->clock;
>>>>> +     sigma_delta = div_u64(0x10000ULL * (f_vco % priv->clock),
>>>>> priv->clock);
>>>>> +     buf[0] = f_vco / priv->clock;
>>>>>        buf[1] = (sigma_delta >> 0) & 0xff;
>>>>>        buf[2] = (sigma_delta >> 8) & 0xff;
>>>>>        buf[3] = 0x00;
>>>>> @@ -358,17 +358,6 @@ static int e4000_get_if_frequency(struct
>>>>> dvb_frontend *fe, u32 *frequency)
>>>>>        return 0;
>>>>>   }
>>>>>
>>>>> -static int e4000_release(struct dvb_frontend *fe)
>>>>> -{
>>>>> -     struct e4000_priv *priv = fe->tuner_priv;
>>>>> -
>>>>> -     dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
>>>>> -
>>>>> -     kfree(fe->tuner_priv);
>>>>> -
>>>>> -     return 0;
>>>>> -}
>>>>> -
>>>>>   static const struct dvb_tuner_ops e4000_tuner_ops = {
>>>>>        .info = {
>>>>>                .name           = "Elonics E4000",
>>>>> @@ -376,8 +365,6 @@ static const struct dvb_tuner_ops e4000_tuner_ops =
>>>>> {
>>>>>                .frequency_max  = 862000000,
>>>>>        },
>>>>>
>>>>> -     .release = e4000_release,
>>>>> -
>>>>>        .init = e4000_init,
>>>>>        .sleep = e4000_sleep,
>>>>>        .set_params = e4000_set_params,
>>>>> @@ -385,9 +372,12 @@ static const struct dvb_tuner_ops e4000_tuner_ops =
>>>>> {
>>>>>        .get_if_frequency = e4000_get_if_frequency,
>>>>>   };
>>>>>
>>>>> -struct dvb_frontend *e4000_attach(struct dvb_frontend *fe,
>>>>> -             struct i2c_adapter *i2c, const struct e4000_config *cfg)
>>>>> +static int e4000_probe(struct i2c_client *client,
>>>>> +             const struct i2c_device_id *id)
>>>>>   {
>>>>> +     struct e4000_config *cfg = client->dev.platform_data;
>>>>> +     struct dvb_frontend *fe = cfg->fe;
>>>>> +     struct i2c_adapter *i2c = client->adapter;
>>>>>        struct e4000_priv *priv;
>>>>>        int ret;
>>>>>        u8 chip_id;
>>>>> @@ -402,7 +392,9 @@ struct dvb_frontend *e4000_attach(struct
>>>>> dvb_frontend *fe,
>>>>>                goto err;
>>>>>        }
>>>>>
>>>>> -     priv->cfg = cfg;
>>>>> +     priv->i2c_addr = cfg->i2c_addr;
>>>>> +     priv->clock = cfg->clock;
>>>>> +     priv->i2c_client = client;
>>>>>        priv->i2c = i2c;
>>>>>
>>>>>        /* check if the tuner is there */
>>>>> @@ -412,8 +404,10 @@ struct dvb_frontend *e4000_attach(struct
>>>>> dvb_frontend *fe,
>>>>>
>>>>>        dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__,
>>>>> chip_id);
>>>>>
>>>>> -     if (chip_id != 0x40)
>>>>> +     if (chip_id != 0x40) {
>>>>> +             ret = -ENODEV;
>>>>>                goto err;
>>>>> +     }
>>>>>
>>>>>        /* put sleep as chip seems to be in normal mode by default */
>>>>>        ret = e4000_wr_reg(priv, 0x00, 0x00);
>>>>> @@ -431,16 +425,45 @@ struct dvb_frontend *e4000_attach(struct
>>>>> dvb_frontend *fe,
>>>>>        if (fe->ops.i2c_gate_ctrl)
>>>>>                fe->ops.i2c_gate_ctrl(fe, 0);
>>>>>
>>>>> -     return fe;
>>>>> +     i2c_set_clientdata(client, priv);
>>>>> +
>>>>> +     return 0;
>>>>>   err:
>>>>>        if (fe->ops.i2c_gate_ctrl)
>>>>>                fe->ops.i2c_gate_ctrl(fe, 0);
>>>>>
>>>>>        dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
>>>>>        kfree(priv);
>>>>> -     return NULL;
>>>>> +     return ret;
>>>>>   }
>>>>> -EXPORT_SYMBOL(e4000_attach);
>>>>> +
>>>>> +static int e4000_remove(struct i2c_client *client)
>>>>> +{
>>>>> +     struct e4000_priv *priv = i2c_get_clientdata(client);
>>>>> +
>>>>> +     dev_dbg(&client->dev, "%s:\n", __func__);
>>>>> +
>>>>> +     kfree(priv);
>>>>> +     return 0;
>>>>> +}
>>>>> +
>>>>> +static const struct i2c_device_id e4000_id[] = {
>>>>> +     {"e4000", 0},
>>>>> +     {}
>>>>> +};
>>>>> +MODULE_DEVICE_TABLE(i2c, e4000_id);
>>>>> +
>>>>> +static struct i2c_driver e4000_driver = {
>>>>> +     .driver = {
>>>>> +             .owner  = THIS_MODULE,
>>>>> +             .name   = "e4000",
>>>>> +     },
>>>>> +     .probe          = e4000_probe,
>>>>> +     .remove         = e4000_remove,
>>>>> +     .id_table       = e4000_id,
>>>>> +};
>>>>> +
>>>>> +module_i2c_driver(e4000_driver);
>>>>>
>>>>>   MODULE_DESCRIPTION("Elonics E4000 silicon tuner driver");
>>>>>   MODULE_AUTHOR("Antti Palosaari <crope@xxxxxx>");
>>>>> diff --git a/drivers/media/tuners/e4000.h b/drivers/media/tuners/e4000.h
>>>>> index 25ee7c0..760b206 100644
>>>>> --- a/drivers/media/tuners/e4000.h
>>>>> +++ b/drivers/media/tuners/e4000.h
>>>>> @@ -26,6 +26,11 @@
>>>>>
>>>>>   struct e4000_config {
>>>>>        /*
>>>>> +      * frontend
>>>>> +      */
>>>>> +     struct dvb_frontend *fe;
>>>>> +
>>>>> +     /*
>>>>>         * I2C address
>>>>>         * 0x64, 0x65, 0x66, 0x67
>>>>>         */
>>>>> @@ -39,10 +44,10 @@ struct e4000_config {
>>>>>
>>>>>   #if IS_ENABLED(CONFIG_MEDIA_TUNER_E4000)
>>>>
>>>>
>>>> You can get rid of #if IS_ENABLED, by replacing it by a generic attach
>>>> function, using the subdev model. In other words, this is how a tuner
>>>> subdev is defined:
>>>>
>>>>          struct v4l2_subdev_tuner_ops {
>>>>                  int (*s_radio)(struct v4l2_subdev *sd);
>>>>                  int (*s_frequency)(struct v4l2_subdev *sd, const struct
>>>> v4l2_frequency *freq);
>>>>                  int (*g_frequency)(struct v4l2_subdev *sd, struct
>>>> v4l2_frequency *freq);
>>>>                  int (*g_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner
>>>> *vt);
>>>>                  int (*s_tuner)(struct v4l2_subdev *sd, const struct
>>>> v4l2_tuner *vt);
>>>>                  int (*g_modulator)(struct v4l2_subdev *sd, struct
>>>> v4l2_modulator *vm);
>>>>                  int (*s_modulator)(struct v4l2_subdev *sd, const struct
>>>> v4l2_modulator *vm);
>>>>                  int (*s_type_addr)(struct v4l2_subdev *sd, struct
>>>> tuner_setup *type);
>>>>                  int (*s_config)(struct v4l2_subdev *sd, const struct
>>>> v4l2_priv_tun_config *config);
>>>>          };
>>>>
>>>> As the presence of the device is known, there's no need to actually
>>>> attach
>>>> anything, but you need to pass the configuration for the subdevice. This
>>>> is
>>>> done by calling the subdev s_config ops to setup them:
>>>>
>>>>          v4l2_subdev_call(sd, tuner, s_config, vfh, arg);
>>>>
>>>> A new callback will be needed there, in order to put the device into DVB
>>>> mode,
>>>> e. g. passing the DVB specific parameters.
>>>>
>>>> As the subdev callbacks are defined on a generic .h header and they
>>>> always
>>>> exist, if the driver is not compiled, i2c_new_device() won't find the
>>>> driver,
>>>> and the bridge driver won't call s_config() for this I2C client.
>>>>
>>>> That's IMHO, one of the biggest advantages of this conversion: as a
>>>> bonus,
>>>> we get rid of several Kconfig issues. Also, with this change, lsmod will
>>>> properly show that the tuner is bound to the bridge driver.
>>>>
>>>>>   extern struct dvb_frontend *e4000_attach(struct dvb_frontend *fe,
>>>>> -             struct i2c_adapter *i2c, const struct e4000_config *cfg);
>>>>> +             struct i2c_adapter *i2c, struct e4000_config *cfg);
>>>>>   #else
>>>>>   static inline struct dvb_frontend *e4000_attach(struct dvb_frontend
>>>>> *fe,
>>>>> -             struct i2c_adapter *i2c, const struct e4000_config *cfg)
>>>>> +             struct i2c_adapter *i2c, struct e4000_config *cfg)
>>>>>   {
>>>>>        dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n",
>>>>> __func__);
>>>>>        return NULL;
>>>>> diff --git a/drivers/media/tuners/e4000_priv.h
>>>>> b/drivers/media/tuners/e4000_priv.h
>>>>> index a385505..7f47068 100644
>>>>> --- a/drivers/media/tuners/e4000_priv.h
>>>>> +++ b/drivers/media/tuners/e4000_priv.h
>>>>> @@ -24,8 +24,10 @@
>>>>>   #include "e4000.h"
>>>>>
>>>>>   struct e4000_priv {
>>>>> -     const struct e4000_config *cfg;
>>>>>        struct i2c_adapter *i2c;
>>>>> +     struct i2c_client *i2c_client;
>>>>> +     u8 i2c_addr;
>>>>> +     u32 clock;
>>>>>   };
>>>>>
>>>>>   struct e4000_pll {
>>>>> diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
>>>>> b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
>>>>> index defc491..573805a 100644
>>>>> --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
>>>>> +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
>>>>> @@ -843,11 +843,6 @@ err:
>>>>>        return ret;
>>>>>   }
>>>>>
>>>>> -static const struct e4000_config rtl2832u_e4000_config = {
>>>>> -     .i2c_addr = 0x64,
>>>>> -     .clock = 28800000,
>>>>> -};
>>>>> -
>>>>>   static const struct fc2580_config rtl2832u_fc2580_config = {
>>>>>        .i2c_addr = 0x56,
>>>>>        .clock = 16384000,
>>>>> @@ -874,10 +869,14 @@ static int rtl2832u_tuner_attach(struct
>>>>> dvb_usb_adapter *adap)
>>>>>        int ret;
>>>>>        struct dvb_usb_device *d = adap_to_d(adap);
>>>>>        struct rtl28xxu_priv *priv = d_to_priv(d);
>>>>> -     struct dvb_frontend *fe;
>>>>> +     struct dvb_frontend *fe = NULL;
>>>>> +     struct i2c_client *client = NULL;
>>>>> +     struct i2c_board_info info;
>>>>>
>>>>>        dev_dbg(&d->udev->dev, "%s:\n", __func__);
>>>>>
>>>>> +     memset(&info, 0, sizeof(struct i2c_board_info));
>>>>> +
>>>>>        switch (priv->tuner) {
>>>>>        case TUNER_RTL2832_FC0012:
>>>>>                fe = dvb_attach(fc0012_attach, adap->fe[0],
>>>>> @@ -897,9 +896,20 @@ static int rtl2832u_tuner_attach(struct
>>>>> dvb_usb_adapter *adap)
>>>>>                adap->fe[0]->ops.read_signal_strength =
>>>>>
>>>>> adap->fe[0]->ops.tuner_ops.get_rf_strength;
>>>>>                return 0;
>>>>> -     case TUNER_RTL2832_E4000:
>>>>> -             fe = dvb_attach(e4000_attach, adap->fe[0], &d->i2c_adap,
>>>>> -                             &rtl2832u_e4000_config);
>>>>> +     case TUNER_RTL2832_E4000: {
>>>>> +                     struct e4000_config e4000_config = {
>>>>> +                             .fe = adap->fe[0],
>>>>> +                             .i2c_addr = 0x64,
>>>>> +                             .clock = 28800000,
>>>>> +                     };
>>>>> +
>>>>> +                     strlcpy(info.type, "e4000", I2C_NAME_SIZE);
>>>>> +                     info.addr = 0x64;
>>>>> +                     info.platform_data = &e4000_config;
>>>>> +
>>>>> +                     request_module("e4000");
>>>>> +                     client = i2c_new_device(&d->i2c_adap, &info);
>>>>> +             }
>>>>>                break;
>>>>>        case TUNER_RTL2832_FC2580:
>>>>>                fe = dvb_attach(fc2580_attach, adap->fe[0], &d->i2c_adap,
>>>>> @@ -927,12 +937,11 @@ static int rtl2832u_tuner_attach(struct
>>>>> dvb_usb_adapter *adap)
>>>>>
>>>>> adap->fe[0]->ops.tuner_ops.get_rf_strength;
>>>>>                break;
>>>>>        default:
>>>>> -             fe = NULL;
>>>>>                dev_err(&d->udev->dev, "%s: unknown tuner=%d\n",
>>>>> KBUILD_MODNAME,
>>>>>                                priv->tuner);
>>>>>        }
>>>>>
>>>>> -     if (fe == NULL) {
>>>>> +     if (fe == NULL && (client == NULL || client->driver == NULL)) {
>>>>>                ret = -ENODEV;
>>>>>                goto err;
>>>>>        }
>>>>
>>>>
>>>>
>>>> --
>>>>
>>>> Cheers,
>>>> Mauro
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe linux-media" in
>>>> the body of a message to majordomo@xxxxxxxxxxxxxxx
>>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
>>
>>
>> --
>> http://palosaari.fi/
>
>
>
> Ugh, apologies for my previous toppost(again) ... I wish gmail had an
> option for replies below quoted text, instead of the default.  (If
> someone knows a way - please let me know ;-) )
>
> I only ask because I'm used to seeing an RFC proposal on the mailing
> list for this type of change - I can't spend all day on irc like I had
> in the past.
>
> Anyway, cool... I have been wanting to do something like this for
> years, too... but not exactly this way.  Meanwhile, it does indeed
> seem like i2c_new_probed_device() will achieve what we're looking for,
> so perhaps this will indeed be a good solution.
>
> Are we sure that this leaves total control of the driver attachment to
> the driver itself?  I mean, no worries of the wrong i2c tuner driver
> trying to attach?  No worries about automatic probing that could
> damage the hardware?  It seems OK to me but somebody needs to ask
> those questions ;-)
>
> -Mike


YIKES!!  i2c_new_probed_device() does indeed probe the hardware --
this is unacceptable, as such an action can damage the ic.

Is there some additional information that I'm missing that lets this
perform an attach without probe?
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux