[media] dvb_frontend: Fix DVBv3 emulation
authorMauro Carvalho Chehab <mchehab@redhat.com>
Sun, 1 Jan 2012 19:11:14 +0000 (16:11 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Wed, 4 Jan 2012 19:29:50 +0000 (17:29 -0200)
For frontends with ISDB-T, DVB-T2, CMDBTH, etc, some code is
needed, in order to provide emulation. Add such code, and check
if the desired delivery system is supported by the frontend.

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/dvb/dvb-core/dvb_frontend.c

index 7f6ce06c7e46ee3fa6f466d0561d07fa99024535..c1b3b30a2e6dd5fe49b9bd4e74c356a79c7a2bf5 100644 (file)
@@ -1422,6 +1422,139 @@ static int dtv_property_process_get(struct dvb_frontend *fe,
 
 static int dtv_set_frontend(struct dvb_frontend *fe);
 
+static bool is_dvbv3_delsys(u32 delsys)
+{
+       bool status;
+
+       status = (delsys == SYS_DVBT) || (delsys == SYS_DVBC_ANNEX_A) ||
+                (delsys == SYS_DVBS) || (delsys == SYS_ATSC);
+
+       return status;
+}
+
+static int set_delivery_system(struct dvb_frontend *fe, u32 desired_system)
+{
+       int ncaps, i;
+       u32 delsys = SYS_UNDEFINED;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       enum dvbv3_emulation_type type;
+
+       if (desired_system == SYS_UNDEFINED) {
+               /*
+                * A DVBv3 call doesn't know what's the desired system.
+                * So, don't change the current delivery system. Instead,
+                * find the closest DVBv3 system that matches the delivery
+                * system.
+                */
+               if (is_dvbv3_delsys(c->delivery_system)) {
+                       dprintk("%s() Using delivery system to %d\n",
+                               __func__, c->delivery_system);
+                       return 0;
+               }
+               type = dvbv3_type(c->delivery_system);
+               switch (type) {
+               case DVBV3_QPSK:
+                       desired_system = FE_QPSK;
+                       break;
+               case DVBV3_QAM:
+                       desired_system = FE_QAM;
+                       break;
+               case DVBV3_ATSC:
+                       desired_system = FE_ATSC;
+                       break;
+               case DVBV3_OFDM:
+                       desired_system = FE_OFDM;
+                       break;
+               default:
+                       dprintk("%s(): This frontend doesn't support DVBv3 calls\n",
+                               __func__);
+                       return -EINVAL;
+               }
+               delsys = c->delivery_system;
+       } else {
+               /*
+                * Check if the desired delivery system is supported
+                */
+               ncaps = 0;
+               while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
+                       if (fe->ops.delsys[ncaps] == desired_system) {
+                               c->delivery_system = desired_system;
+                               dprintk("%s() Changing delivery system to %d\n",
+                                       __func__, desired_system);
+                               return 0;
+                       }
+               }
+               type = dvbv3_type(desired_system);
+
+               /*
+                * The delivery system is not supported. See if it can be
+                * emulated.
+                * The emulation only works if the desired system is one of the
+                * DVBv3 delivery systems
+                */
+               if (!is_dvbv3_delsys(desired_system)) {
+                       dprintk("%s() can't use a DVBv3 FE_SET_FRONTEND call on this frontend\n",
+                               __func__);
+                       return -EINVAL;
+               }
+
+               /*
+                * Get the last non-DVBv3 delivery system that has the same type
+                * of the desired system
+                */
+               ncaps = 0;
+               while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
+                       if ((dvbv3_type(fe->ops.delsys[ncaps]) == type) &&
+                           !is_dvbv3_delsys(fe->ops.delsys[ncaps]))
+                               delsys = fe->ops.delsys[ncaps];
+                       ncaps++;
+               }
+               /* There's nothing compatible with the desired delivery system */
+               if (delsys == SYS_UNDEFINED) {
+                       dprintk("%s() Incompatible DVBv3 FE_SET_FRONTEND call for this frontend\n",
+                               __func__);
+                       return -EINVAL;
+               }
+               c->delivery_system = delsys;
+       }
+
+       /*
+        * Emulate newer delivery systems like ISDBT, DVBT and DMBTH
+        * for older DVBv5 applications. The emulation will try to use
+        * the auto mode for most things, and will assume that the desired
+        * delivery system is the last one at the ops.delsys[] array
+        */
+       dprintk("%s() Using delivery system %d emulated as if it were a %d\n",
+               __func__, delsys, desired_system);
+
+       /*
+        * For now, uses it for ISDB-T, DMBTH and DVB-T2
+        * For DVB-S2 and DVB-TURBO, assumes that the DVB-S parameters are enough.
+        */
+       if (type == DVBV3_OFDM) {
+               c->modulation = QAM_AUTO;
+               c->code_rate_HP = FEC_AUTO;
+               c->code_rate_LP = FEC_AUTO;
+               c->transmission_mode = TRANSMISSION_MODE_AUTO;
+               c->guard_interval = GUARD_INTERVAL_AUTO;
+               c->hierarchy = HIERARCHY_AUTO;
+
+               c->isdbt_partial_reception = -1;
+               c->isdbt_sb_mode = -1;
+               c->isdbt_sb_subchannel = -1;
+               c->isdbt_sb_segment_idx = -1;
+               c->isdbt_sb_segment_count = -1;
+               c->isdbt_layer_enabled = 0x7;
+               for (i = 0; i < 3; i++) {
+                       c->layer[i].fec = FEC_AUTO;
+                       c->layer[i].modulation = QAM_AUTO;
+                       c->layer[i].interleaving = -1;
+                       c->layer[i].segment_count = -1;
+               }
+       }
+       return 0;
+}
+
 static int dtv_property_process_set(struct dvb_frontend *fe,
                                    struct dtv_property *tvp,
                                    struct file *file)
@@ -1484,7 +1617,7 @@ static int dtv_property_process_set(struct dvb_frontend *fe,
                c->rolloff = tvp->u.data;
                break;
        case DTV_DELIVERY_SYSTEM:
-               c->delivery_system = tvp->u.data;
+               r = set_delivery_system(fe, tvp->u.data);
                break;
        case DTV_VOLTAGE:
                c->voltage = tvp->u.data;
@@ -2043,6 +2176,11 @@ static int dvb_frontend_ioctl_legacy(struct file *file,
                /* Synchronise DVBv5 parameters from DVBv3 */
                memcpy (&fepriv->parameters_in, parg,
                        sizeof (struct dvb_frontend_parameters));
+
+               err = set_delivery_system(fe, SYS_UNDEFINED);
+               if (err)
+                       break;
+
                err = dtv_property_cache_sync(fe, c, &fepriv->parameters_in);
                if (err)
                        break;