[media] dvb_frontend: Fix a regression when switching back to DVB-S
[pandora-kernel.git] / drivers / media / dvb / dvb-core / dvb_frontend.c
index b15db4f..0f64d71 100644 (file)
@@ -143,10 +143,12 @@ struct dvb_frontend_private {
 static void dvb_frontend_wakeup(struct dvb_frontend *fe);
 static int dtv_get_frontend(struct dvb_frontend *fe,
                            struct dvb_frontend_parameters *p_out);
+static int dtv_property_legacy_params_sync(struct dvb_frontend *fe,
+                                          struct dvb_frontend_parameters *p);
 
 static bool has_get_frontend(struct dvb_frontend *fe)
 {
-       return fe->ops.get_frontend;
+       return fe->ops.get_frontend != NULL;
 }
 
 /*
@@ -655,6 +657,8 @@ restart:
                                        dprintk("%s: Retune requested, FESTATE_RETUNE\n", __func__);
                                        re_tune = true;
                                        fepriv->state = FESTATE_TUNED;
+                               } else {
+                                       re_tune = false;
                                }
 
                                if (fe->ops.tune)
@@ -695,6 +699,7 @@ restart:
                                        fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
                                        fepriv->delay = HZ / 2;
                                }
+                               dtv_property_legacy_params_sync(fe, &fepriv->parameters_out);
                                fe->ops.read_status(fe, &s);
                                if (s != fepriv->status) {
                                        dvb_frontend_add_event(fe, s); /* update event list */
@@ -904,8 +909,11 @@ static int dvb_frontend_clear_cache(struct dvb_frontend *fe)
 {
        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
        int i;
+       u32 delsys;
 
+       delsys = c->delivery_system;
        memset(c, 0, sizeof(struct dtv_frontend_properties));
+       c->delivery_system = delsys;
 
        c->state = DTV_CLEAR;
 
@@ -1009,25 +1017,6 @@ static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = {
        _DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 1, 0),
        _DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 1, 0),
 
-       _DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 0, 0),
-       _DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 0, 0),
-       _DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID, 0, 0),
-       _DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX, 0, 0),
-       _DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT, 0, 0),
-       _DTV_CMD(DTV_ISDBT_LAYER_ENABLED, 0, 0),
-       _DTV_CMD(DTV_ISDBT_LAYERA_FEC, 0, 0),
-       _DTV_CMD(DTV_ISDBT_LAYERA_MODULATION, 0, 0),
-       _DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT, 0, 0),
-       _DTV_CMD(DTV_ISDBT_LAYERA_TIME_INTERLEAVING, 0, 0),
-       _DTV_CMD(DTV_ISDBT_LAYERB_FEC, 0, 0),
-       _DTV_CMD(DTV_ISDBT_LAYERB_MODULATION, 0, 0),
-       _DTV_CMD(DTV_ISDBT_LAYERB_SEGMENT_COUNT, 0, 0),
-       _DTV_CMD(DTV_ISDBT_LAYERB_TIME_INTERLEAVING, 0, 0),
-       _DTV_CMD(DTV_ISDBT_LAYERC_FEC, 0, 0),
-       _DTV_CMD(DTV_ISDBT_LAYERC_MODULATION, 0, 0),
-       _DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 0, 0),
-       _DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 0, 0),
-
        _DTV_CMD(DTV_ISDBS_TS_ID, 1, 0),
        _DTV_CMD(DTV_DVBT2_PLP_ID, 1, 0),
 
@@ -1413,6 +1402,15 @@ static int set_delivery_system(struct dvb_frontend *fe, u32 desired_system)
        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
        enum dvbv3_emulation_type type;
 
+       /*
+        * It was reported that some old DVBv5 applications were
+        * filling delivery_system with SYS_UNDEFINED. If this happens,
+        * assume that the application wants to use the first supported
+        * delivery system.
+        */
+       if (c->delivery_system == SYS_UNDEFINED)
+               c->delivery_system = fe->ops.delsys[0];
+
        if (desired_system == SYS_UNDEFINED) {
                /*
                 * A DVBv3 call doesn't know what's the desired system.
@@ -1448,6 +1446,28 @@ static int set_delivery_system(struct dvb_frontend *fe, u32 desired_system)
                                __func__);
                        return -EINVAL;
                }
+               /*
+                * Get a delivery system that is compatible with DVBv3
+                * NOTE: in order for this to work with softwares like Kaffeine that
+                *      uses a DVBv5 call for DVB-S2 and a DVBv3 call to go back to
+                *      DVB-S, drivers that support both should put the SYS_DVBS entry
+                *      before the SYS_DVBS2, otherwise it won't switch back to DVB-S.
+                *      The real fix is that userspace applications should not use DVBv3
+                *      and not trust on calling FE_SET_FRONTEND to switch the delivery
+                *      system.
+                */
+               ncaps = 0;
+               while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
+                       if (fe->ops.delsys[ncaps] == desired_system) {
+                               delsys = desired_system;
+                               break;
+                       }
+                       ncaps++;
+               }
+               if (delsys == SYS_UNDEFINED) {
+                       dprintk("%s() Couldn't find a delivery system that matches %d\n",
+                               __func__, desired_system);
+               }
        } else {
                /*
                 * This is a DVBv5 call. So, it likely knows the supported
@@ -1496,9 +1516,10 @@ static int set_delivery_system(struct dvb_frontend *fe, u32 desired_system)
                                __func__);
                        return -EINVAL;
                }
-               c->delivery_system = delsys;
        }
 
+       c->delivery_system = delsys;
+
        /*
         * The DVBv3 or DVBv5 call is requesting a different system. So,
         * emulation is needed.
@@ -1732,6 +1753,7 @@ static int dvb_frontend_ioctl_properties(struct file *file,
 {
        struct dvb_device *dvbdev = file->private_data;
        struct dvb_frontend *fe = dvbdev->priv;
+       struct dvb_frontend_private *fepriv = fe->frontend_priv;
        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
        int err = 0;
 
@@ -1798,9 +1820,14 @@ static int dvb_frontend_ioctl_properties(struct file *file,
 
                /*
                 * Fills the cache out struct with the cache contents, plus
-                * the data retrieved from get_frontend.
+                * the data retrieved from get_frontend, if the frontend
+                * is not idle. Otherwise, returns the cached content
                 */
-               dtv_get_frontend(fe, NULL);
+               if (fepriv->state != FESTATE_IDLE) {
+                       err = dtv_get_frontend(fe, NULL);
+                       if (err < 0)
+                               goto out;
+               }
                for (i = 0; i < tvps->num; i++) {
                        err = dtv_property_process_get(fe, c, tvp + i, file);
                        if (err < 0)
@@ -1831,6 +1858,13 @@ static int dtv_set_frontend(struct dvb_frontend *fe)
        if (dvb_frontend_check_parameters(fe) < 0)
                return -EINVAL;
 
+       /*
+        * Initialize output parameters to match the values given by
+        * the user. FE_SET_FRONTEND triggers an initial frontend event
+        * with status = 0, which copies output parameters to userspace.
+        */
+       dtv_property_legacy_params_sync(fe, &fepriv->parameters_out);
+
        /*
         * Be sure that the bandwidth will be filled for all
         * non-satellite systems, as tuners need to know what