From: Mauro Carvalho Chehab <mchehab@redhat.com>
To: Devin Heitmueller <dheitmueller@kernellabs.com>
Cc: Linux Media Mailing List <linux-media@vger.kernel.org>,
Hans Verkuil <hverkuil@xs4all.nl>,
Michael Krufky <mkrufky@kernellabs.com>
Subject: Re: [RFC PATCH] tuner-core: fix broken G_TUNER call when tuner is in standby
Date: Wed, 02 Feb 2011 13:21:55 -0200 [thread overview]
Message-ID: <4D497693.5020403@redhat.com> (raw)
In-Reply-To: <AANLkTim3=Xuyantq2zKJ0W8C+-objnBQNbYNaqb9pgc-@mail.gmail.com>
Em 23-01-2011 20:22, Devin Heitmueller escreveu:
> Hello all,
>
> The following patch addresses a V4L2 specification violation where the
> G_TUNER call would return complete garbage in the event that the tuner
> is asleep. Per Hans' suggestion, I have split out the tuner
> operational mode from whether it is in standby, and fixed the G_TUNER
> call to return as much as possible when the tuner is in standby.
>
> Without this change, products that have tuners which support standby
> mode cannot be tuned from within VLC.
>
> I recognize that changes to tuner-core tend to be pretty hairy, so I
> welcome suggestions/feedback on this patch.
>
> Regards,
>
> Devin
>
> -- Devin J. Heitmueller - Kernel Labs http://www.kernellabs.com
>
>
> tuner_standby_mode.patch
>
>
> tuner-core: fix broken G_TUNER call when tuner is in standby
>
> From: Devin Heitmueller <dheitmueller@kernellabs.com>
>
> The logic for determining the supported device modes was combined with the
> logic which dictates whether the tuner was asleep. This resulted in calls
> such as G_TUNER returning complete garbage in the event that the tuner was
> in standby mode (a violation of the V4L2 specification, and causing VLC to
> be broken for such tuners).
>
> This patch reworks the logic so the current tuner mode is maintained separately
> from whether it is in standby (per Hans Verkuil's suggestion). It also
> restructures the G_TUNER call such that all the staticly defined information
> related to the tuner is returned regardless of whether it is in standby (e.g.
> the supported frequency range, etc).
>
> Priority: normal
>
> Signed-off-by: Devin Heitmueller <dheitmueller@kernellabs.com>
> Cc: Hans Verkuil <hverkuil@xs4all.nl>
>
> --- media_build/linux/drivers/media/video/tuner-core.c 2010-10-24 19:34:59.000000000 -0400
> +++ media_build_950qfixes//linux/drivers/media/video/tuner-core.c 2011-01-23 17:18:22.381107568 -0500
> @@ -90,6 +90,7 @@
>
> unsigned int mode;
> unsigned int mode_mask; /* Combination of allowable modes */
> + unsigned int in_standby:1;
>
> unsigned int type; /* chip type id */
> unsigned int config;
> @@ -700,6 +701,7 @@
> static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, char *cmd)
> {
> struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
> + unsigned int orig_mode = t->mode;
>
> if (mode == t->mode)
> return 0;
> @@ -709,7 +711,8 @@
> if (check_mode(t, cmd) == -EINVAL) {
> tuner_dbg("Tuner doesn't support this mode. "
> "Putting tuner to sleep\n");
> - t->mode = T_STANDBY;
> + t->mode = orig_mode;
Hmm... as orig_mode = t->mode, this is:
t->mode = t->mode...
This doesn't make sense ;)
> + t->in_standby = 1;
> if (analog_ops->standby)
> analog_ops->standby(&t->fe);
> return -EINVAL;
> @@ -769,7 +772,7 @@
>
> if (check_mode(t, "s_power") == -EINVAL)
> return 0;
> - t->mode = T_STANDBY;
> + t->in_standby = 1;
> if (analog_ops->standby)
> analog_ops->standby(&t->fe);
> return 0;
> @@ -854,47 +857,54 @@
> struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
> struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
>
> - if (check_mode(t, "g_tuner") == -EINVAL)
> - return 0;
Some of those checks are to allow switching between radio and TV
mode. Had you test to switch between video/radio mode on your tests
(e. g. alternating video streaming on/off with radio tune)?
> switch_v4l2();
>
> + /* First populate everything that doesn't require talking to the
> + actual hardware */
> vt->type = t->mode;
> - if (analog_ops->get_afc)
> - vt->afc = analog_ops->get_afc(&t->fe);
> if (t->mode == V4L2_TUNER_ANALOG_TV)
> + {
> vt->capability |= V4L2_TUNER_CAP_NORM;
> - if (t->mode != V4L2_TUNER_RADIO) {
> vt->rangelow = tv_range[0] * 16;
> vt->rangehigh = tv_range[1] * 16;
> - return 0;
> + } else {
> + /* radio mode */
> + vt->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
> + vt->capability |= V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
> + vt->audmode = t->audmode;
> + vt->rangelow = radio_range[0] * 16000;
> + vt->rangehigh = radio_range[1] * 16000;
> }
>
> - /* radio mode */
> - vt->rxsubchans =
> - V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
> - if (fe_tuner_ops->get_status) {
> - u32 tuner_status;
> + /* If the hardware is in sleep mode, bail out at this point */
> + if (t->in_standby)
> + return 0;
This doesn't seem right. Instead, the proper behaviour should be
to turn tuner on (likely waiting for some time) and then get a status.
>
> - fe_tuner_ops->get_status(&t->fe, &tuner_status);
> - vt->rxsubchans =
> - (tuner_status & TUNER_STATUS_STEREO) ?
> - V4L2_TUNER_SUB_STEREO :
> - V4L2_TUNER_SUB_MONO;
> + /* Now populate the fields that requires the hardware to be alive */
> + if (t->mode == V4L2_TUNER_ANALOG_TV) {
> + if (analog_ops->get_afc)
> + vt->afc = analog_ops->get_afc(&t->fe);
> } else {
> - if (analog_ops->is_stereo) {
> + if (fe_tuner_ops->get_status) {
Hmm... get_status() taking procedence over is_stereo... not sure if this
work as expected. On the other hand:
$ git grep is_stereo drivers/media/
drivers/media/dvb/dvb-core/dvb_frontend.h: int (*is_stereo)(struct dvb_frontend *fe);
drivers/media/radio/radio-zoltrix.c:static int zol_is_stereo(struct zoltrix *zol)
drivers/media/radio/radio-zoltrix.c: if (zol_is_stereo(zol))
drivers/media/video/msp3400-kthreads.c: int is_stereo = status & 0x40;
drivers/media/video/msp3400-kthreads.c: if (is_stereo)
drivers/media/video/msp3400-kthreads.c: status, is_stereo, is_bilingual, state->rxsubchans);
drivers/media/video/tuner-core.c: if (analog_ops->is_stereo)
drivers/media/video/tuner-core.c: analog_ops->is_stereo(fe) ? "yes" : "no");
drivers/media/video/tuner-core.c: if (analog_ops->is_stereo) {
drivers/media/video/tuner-core.c: analog_ops->is_stereo(&t->fe) ?
It seems that nobody is using is_stereo() callback anymore. So, we can just
get rid of it. This is part of an old API that was there before mkrufky patches
that unified analog and digital tuners.
> + u32 tuner_status;
> +
> + fe_tuner_ops->get_status(&t->fe, &tuner_status);
> vt->rxsubchans =
> - analog_ops->is_stereo(&t->fe) ?
> + (tuner_status & TUNER_STATUS_STEREO) ?
> V4L2_TUNER_SUB_STEREO :
> V4L2_TUNER_SUB_MONO;
> + } else {
> + if (analog_ops->is_stereo) {
> + vt->rxsubchans =
> + analog_ops->is_stereo(&t->fe) ?
> + V4L2_TUNER_SUB_STEREO :
> + V4L2_TUNER_SUB_MONO;
> + }
> }
> + if (analog_ops->has_signal)
> + vt->signal = analog_ops->has_signal(&t->fe);
has_signal() seems to be another callback asking for its removal. Only tda8290 has it:
$ git grep has_signal drivers/media/
drivers/media/common/tuners/tda8290.c:static int tda8295_has_signal(struct dvb_frontend *fe)
drivers/media/common/tuners/tda8290.c: if (tda8295_has_signal(fe))
drivers/media/common/tuners/tda8290.c:static int tda8290_has_signal(struct dvb_frontend *fe)
drivers/media/common/tuners/tda8290.c: .has_signal = tda8290_has_signal,
drivers/media/common/tuners/tda8290.c: .has_signal = tda8295_has_signal,
drivers/media/dvb/dvb-core/dvb_frontend.h: int (*has_signal)(struct dvb_frontend *fe);
drivers/media/video/tuner-core.c:static int fe_has_signal(struct dvb_frontend *fe)
drivers/media/video/tuner-core.c: .has_signal = fe_has_signal,
drivers/media/video/tuner-core.c: if (analog_ops->has_signal)
drivers/media/video/tuner-core.c: analog_ops->has_signal(fe));
drivers/media/video/tuner-core.c: if (analog_ops->has_signal)
drivers/media/video/tuner-core.c: vt->signal = analog_ops->has_signal(&t->fe);
Other drivers use get_rf_strength():
$ git grep get_rf_strength drivers/media/
drivers/media/common/tuners/tea5761.c:static int tea5761_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
drivers/media/common/tuners/tea5761.c: .get_rf_strength = tea5761_get_rf_strength,
drivers/media/common/tuners/tea5767.c:static int tea5767_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
drivers/media/common/tuners/tea5767.c: .get_rf_strength = tea5767_get_rf_strength,
drivers/media/common/tuners/tuner-simple.c:static int simple_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
drivers/media/common/tuners/tuner-simple.c: .get_rf_strength = simple_get_rf_strength,
drivers/media/common/tuners/tuner-xc2028.c: .get_rf_strength = xc2028_signal,
drivers/media/dvb/dvb-core/dvb_frontend.h: int (*get_rf_strength)(struct dvb_frontend *fe, u16 *strength);
drivers/media/video/tuner-core.c: if (fe->ops.tuner_ops.get_rf_strength)
drivers/media/video/tuner-core.c: fe->ops.tuner_ops.get_rf_strength(fe, &strength);
So, I think we should replace has_signal() at tda8290, with get_rf_strength(),
and remove its call from tuner-core.
> }
> - if (analog_ops->has_signal)
> - vt->signal = analog_ops->has_signal(&t->fe);
> - vt->capability |=
> - V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
> - vt->audmode = t->audmode;
> - vt->rangelow = radio_range[0] * 16000;
> - vt->rangehigh = radio_range[1] * 16000;
> +
> return 0;
> }
>
> @@ -911,6 +921,11 @@
> /* do nothing unless we're a radio tuner */
> if (t->mode != V4L2_TUNER_RADIO)
> return 0;
> +
> + /* Should this really fail silently if the device is asleep? */
> + if (t->in_standby == 1)
> + return 0;
> +
Same comment as before: it should wake up the device. That's
the expected behaviour by radio applications like console/radio.
> t->audmode = vt->audmode;
> set_radio_freq(client, t->radio_freq);
> return 0;
> @@ -1004,14 +1019,11 @@
> *tv = NULL;
>
> list_for_each_entry(pos, &tuner_list, list) {
> - int mode_mask;
> -
> if (pos->i2c->adapter != adap ||
> strcmp(pos->i2c->driver->driver.name, "tuner"))
> continue;
>
> - mode_mask = pos->mode_mask & ~T_STANDBY;
> - if (*radio == NULL && mode_mask == T_RADIO)
> + if (*radio == NULL && pos->mode_mask == T_RADIO)
> *radio = pos;
> /* Note: currently TDA9887 is the only demod-only
> device. If other devices appear then we need to
> @@ -1063,7 +1075,8 @@
> t->i2c->addr) >= 0) {
> t->type = TUNER_TEA5761;
> t->mode_mask = T_RADIO;
> - t->mode = T_STANDBY;
> + t->mode = T_RADIO;
> + t->in_standby = 1;
> /* Sets freq to FM range */
> t->radio_freq = 87.5 * 16000;
> tuner_lookup(t->i2c->adapter, &radio, &tv);
> @@ -1088,7 +1101,8 @@
> t->type = TUNER_TDA9887;
> t->mode_mask = T_RADIO | T_ANALOG_TV |
> T_DIGITAL_TV;
> - t->mode = T_STANDBY;
> + t->mode = T_ANALOG_TV;
> + t->in_standby = 1;
> goto register_client;
> }
> break;
> @@ -1098,7 +1112,8 @@
> >= 0) {
> t->type = TUNER_TEA5767;
> t->mode_mask = T_RADIO;
> - t->mode = T_STANDBY;
> + t->mode = T_RADIO;
> + t->in_standby = 1;
> /* Sets freq to FM range */
> t->radio_freq = 87.5 * 16000;
> tuner_lookup(t->i2c->adapter, &radio, &tv);
Not sure about the above. In the past, T_STANDBY were used at the
initial setup to tell the core that the tuner is not in usage. I
think we need to do some tests to see if none of the above changes
would break it. Complex devices to probe are those with multiple
tuners, like devices with a tea5767 for FM and a separate tuner for
TV. I think we need to do some tests with those devices.
Cheers,
Mauro
next prev parent reply other threads:[~2011-02-02 15:22 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-01-23 22:22 [RFC PATCH] tuner-core: fix broken G_TUNER call when tuner is in standby Devin Heitmueller
2011-02-02 15:21 ` Mauro Carvalho Chehab [this message]
2011-02-03 23:50 ` Devin Heitmueller
2011-02-04 0:58 ` Mauro Carvalho Chehab
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4D497693.5020403@redhat.com \
--to=mchehab@redhat.com \
--cc=dheitmueller@kernellabs.com \
--cc=hverkuil@xs4all.nl \
--cc=linux-media@vger.kernel.org \
--cc=mkrufky@kernellabs.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.