Пример #1
0
static int em28xx_dvb_suspend(struct em28xx *dev)
{
	int ret = 0;

	if (dev->is_audio_only)
		return 0;

	if (!dev->board.has_dvb)
		return 0;

	em28xx_info("Suspending DVB extension");
	if (dev->dvb) {
		struct em28xx_dvb *dvb = dev->dvb;

		if (dvb->fe[0]) {
			ret = dvb_frontend_suspend(dvb->fe[0]);
			em28xx_info("fe0 suspend %d", ret);
		}
		if (dvb->fe[1]) {
			dvb_frontend_suspend(dvb->fe[1]);
			em28xx_info("fe1 suspend %d", ret);
		}
	}

	return 0;
}
int dvb_usbv2_suspend(struct usb_interface *intf, pm_message_t msg)
{
	struct dvb_usb_device *d = usb_get_intfdata(intf);
	int ret = 0, i, active_fe;
	struct dvb_frontend *fe;
	dev_dbg(&d->udev->dev, "%s:\n", __func__);

	/* stop remote controller poll */
	if (d->rc_polling_active)
		cancel_delayed_work_sync(&d->rc_query_work);

	for (i = MAX_NO_OF_ADAPTER_PER_DEVICE - 1; i >= 0; i--) {
		active_fe = d->adapter[i].active_fe;
		if (d->adapter[i].dvb_adap.priv && active_fe != -1) {
			fe = d->adapter[i].fe[active_fe];
			d->adapter[i].suspend_resume_active = true;

			if (d->props->streaming_ctrl)
				d->props->streaming_ctrl(fe, 0);

			/* stop usb streaming */
			usb_urb_killv2(&d->adapter[i].stream);

			ret = dvb_frontend_suspend(fe);
		}
	}

	return ret;
}
Пример #3
0
static int em28xx_dvb_init(struct em28xx *dev)
{
	int result = 0, mfe_shared = 0;
	struct em28xx_dvb *dvb;

	if (dev->is_audio_only) {
		/* Shouldn't initialize IR for this interface */
		return 0;
	}

	if (!dev->board.has_dvb) {
		/* This device does not support the extension */
		return 0;
	}

	em28xx_info("Binding DVB extension\n");

	dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL);
	if (dvb == NULL) {
		em28xx_info("em28xx_dvb: memory allocation failed\n");
		return -ENOMEM;
	}
	dev->dvb = dvb;
	dvb->fe[0] = dvb->fe[1] = NULL;

	/* pre-allocate DVB usb transfer buffers */
	if (dev->dvb_xfer_bulk) {
		result = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE,
					   dev->dvb_xfer_bulk,
					   EM28XX_DVB_NUM_BUFS,
					   512,
					   EM28XX_DVB_BULK_PACKET_MULTIPLIER);
	} else {
		result = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE,
					   dev->dvb_xfer_bulk,
					   EM28XX_DVB_NUM_BUFS,
					   dev->dvb_max_pkt_size_isoc,
					   EM28XX_DVB_NUM_ISOC_PACKETS);
	}
	if (result) {
		em28xx_errdev("em28xx_dvb: failed to pre-allocate USB transfer buffers for DVB.\n");
		kfree(dvb);
		dev->dvb = NULL;
		return result;
	}

	mutex_lock(&dev->lock);
	em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
	/* init frontend */
	switch (dev->model) {
	case EM2874_BOARD_LEADERSHIP_ISDBT:
		dvb->fe[0] = dvb_attach(s921_attach,
				&sharp_isdbt, &dev->i2c_adap[dev->def_i2c_bus]);

		if (!dvb->fe[0]) {
			result = -EINVAL;
			goto out_free;
		}

		break;
	case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
	case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
	case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
	case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
		dvb->fe[0] = dvb_attach(lgdt330x_attach,
					   &em2880_lgdt3303_dev,
					   &dev->i2c_adap[dev->def_i2c_bus]);
		if (em28xx_attach_xc3028(0x61, dev) < 0) {
			result = -EINVAL;
			goto out_free;
		}
		break;
	case EM2880_BOARD_KWORLD_DVB_310U:
		dvb->fe[0] = dvb_attach(zl10353_attach,
					   &em28xx_zl10353_with_xc3028,
					   &dev->i2c_adap[dev->def_i2c_bus]);
		if (em28xx_attach_xc3028(0x61, dev) < 0) {
			result = -EINVAL;
			goto out_free;
		}
		break;
	case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
	case EM2882_BOARD_TERRATEC_HYBRID_XS:
	case EM2880_BOARD_EMPIRE_DUAL_TV:
		dvb->fe[0] = dvb_attach(zl10353_attach,
					   &em28xx_zl10353_xc3028_no_i2c_gate,
					   &dev->i2c_adap[dev->def_i2c_bus]);
		if (em28xx_attach_xc3028(0x61, dev) < 0) {
			result = -EINVAL;
			goto out_free;
		}
		break;
	case EM2880_BOARD_TERRATEC_HYBRID_XS:
	case EM2880_BOARD_TERRATEC_HYBRID_XS_FR:
	case EM2881_BOARD_PINNACLE_HYBRID_PRO:
	case EM2882_BOARD_DIKOM_DK300:
	case EM2882_BOARD_KWORLD_VS_DVBT:
		dvb->fe[0] = dvb_attach(zl10353_attach,
					   &em28xx_zl10353_xc3028_no_i2c_gate,
					   &dev->i2c_adap[dev->def_i2c_bus]);
		if (dvb->fe[0] == NULL) {
			/* This board could have either a zl10353 or a mt352.
			   If the chip id isn't for zl10353, try mt352 */
			dvb->fe[0] = dvb_attach(mt352_attach,
						   &terratec_xs_mt352_cfg,
						   &dev->i2c_adap[dev->def_i2c_bus]);
		}

		if (em28xx_attach_xc3028(0x61, dev) < 0) {
			result = -EINVAL;
			goto out_free;
		}
		break;
	case EM2870_BOARD_KWORLD_355U:
		dvb->fe[0] = dvb_attach(zl10353_attach,
					   &em28xx_zl10353_no_i2c_gate_dev,
					   &dev->i2c_adap[dev->def_i2c_bus]);
		if (dvb->fe[0] != NULL)
			dvb_attach(qt1010_attach, dvb->fe[0],
				   &dev->i2c_adap[dev->def_i2c_bus], &em28xx_qt1010_config);
		break;
	case EM2883_BOARD_KWORLD_HYBRID_330U:
	case EM2882_BOARD_EVGA_INDTUBE:
		dvb->fe[0] = dvb_attach(s5h1409_attach,
					   &em28xx_s5h1409_with_xc3028,
					   &dev->i2c_adap[dev->def_i2c_bus]);
		if (em28xx_attach_xc3028(0x61, dev) < 0) {
			result = -EINVAL;
			goto out_free;
		}
		break;
	case EM2882_BOARD_KWORLD_ATSC_315U:
		dvb->fe[0] = dvb_attach(lgdt330x_attach,
					   &em2880_lgdt3303_dev,
					   &dev->i2c_adap[dev->def_i2c_bus]);
		if (dvb->fe[0] != NULL) {
			if (!dvb_attach(simple_tuner_attach, dvb->fe[0],
				&dev->i2c_adap[dev->def_i2c_bus], 0x61, TUNER_THOMSON_DTT761X)) {
				result = -EINVAL;
				goto out_free;
			}
		}
		break;
	case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
	case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E:
		dvb->fe[0] = dvb_attach(drxd_attach, &em28xx_drxd, NULL,
					   &dev->i2c_adap[dev->def_i2c_bus], &dev->udev->dev);
		if (em28xx_attach_xc3028(0x61, dev) < 0) {
			result = -EINVAL;
			goto out_free;
		}
		break;
	case EM2870_BOARD_REDDO_DVB_C_USB_BOX:
		/* Philips CU1216L NIM (Philips TDA10023 + Infineon TUA6034) */
		dvb->fe[0] = dvb_attach(tda10023_attach,
			&em28xx_tda10023_config,
			&dev->i2c_adap[dev->def_i2c_bus], 0x48);
		if (dvb->fe[0]) {
			if (!dvb_attach(simple_tuner_attach, dvb->fe[0],
				&dev->i2c_adap[dev->def_i2c_bus], 0x60, TUNER_PHILIPS_CU1216L)) {
				result = -EINVAL;
				goto out_free;
			}
		}
		break;
	case EM2870_BOARD_KWORLD_A340:
		dvb->fe[0] = dvb_attach(lgdt3305_attach,
					   &em2870_lgdt3304_dev,
					   &dev->i2c_adap[dev->def_i2c_bus]);
		if (!dvb->fe[0]) {
			result = -EINVAL;
			goto out_free;
		}
		if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
			&dev->i2c_adap[dev->def_i2c_bus],
			&kworld_a340_config)) {
				dvb_frontend_detach(dvb->fe[0]);
				result = -EINVAL;
				goto out_free;
		}
		break;
	case EM28174_BOARD_PCTV_290E:
		/* set default GPIO0 for LNA, used if GPIOLIB is undefined */
		dvb->lna_gpio = CXD2820R_GPIO_E | CXD2820R_GPIO_O |
				CXD2820R_GPIO_L;
		dvb->fe[0] = dvb_attach(cxd2820r_attach,
					&em28xx_cxd2820r_config,
					&dev->i2c_adap[dev->def_i2c_bus],
					&dvb->lna_gpio);
		if (dvb->fe[0]) {
			/* FE 0 attach tuner */
			if (!dvb_attach(tda18271_attach,
					dvb->fe[0],
					0x60,
					&dev->i2c_adap[dev->def_i2c_bus],
					&em28xx_cxd2820r_tda18271_config)) {

				dvb_frontend_detach(dvb->fe[0]);
				result = -EINVAL;
				goto out_free;
			}

#ifdef CONFIG_GPIOLIB
			/* enable LNA for DVB-T, DVB-T2 and DVB-C */
			result = gpio_request_one(dvb->lna_gpio,
					GPIOF_OUT_INIT_LOW, NULL);
			if (result)
				em28xx_errdev("gpio request failed %d\n",
						result);
			else
				gpio_free(dvb->lna_gpio);

			result = 0; /* continue even set LNA fails */
#endif
			dvb->fe[0]->ops.set_lna = em28xx_pctv_290e_set_lna;
		}

		break;
	case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C:
	{
		struct xc5000_config cfg;
		hauppauge_hvr930c_init(dev);

		dvb->fe[0] = dvb_attach(drxk_attach,
					&hauppauge_930c_drxk, &dev->i2c_adap[dev->def_i2c_bus]);
		if (!dvb->fe[0]) {
			result = -EINVAL;
			goto out_free;
		}
		/* FIXME: do we need a pll semaphore? */
		dvb->fe[0]->sec_priv = dvb;
		sema_init(&dvb->pll_mutex, 1);
		dvb->gate_ctrl = dvb->fe[0]->ops.i2c_gate_ctrl;
		dvb->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl;

		/* Attach xc5000 */
		memset(&cfg, 0, sizeof(cfg));
		cfg.i2c_address  = 0x61;
		cfg.if_khz = 4000;

		if (dvb->fe[0]->ops.i2c_gate_ctrl)
			dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1);
		if (!dvb_attach(xc5000_attach, dvb->fe[0], &dev->i2c_adap[dev->def_i2c_bus],
				&cfg)) {
			result = -EINVAL;
			goto out_free;
		}
		if (dvb->fe[0]->ops.i2c_gate_ctrl)
			dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 0);

		break;
	}
	case EM2884_BOARD_TERRATEC_H5:
		terratec_h5_init(dev);

		dvb->fe[0] = dvb_attach(drxk_attach, &terratec_h5_drxk, &dev->i2c_adap[dev->def_i2c_bus]);
		if (!dvb->fe[0]) {
			result = -EINVAL;
			goto out_free;
		}
		/* FIXME: do we need a pll semaphore? */
		dvb->fe[0]->sec_priv = dvb;
		sema_init(&dvb->pll_mutex, 1);
		dvb->gate_ctrl = dvb->fe[0]->ops.i2c_gate_ctrl;
		dvb->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl;

		/* Attach tda18271 to DVB-C frontend */
		if (dvb->fe[0]->ops.i2c_gate_ctrl)
			dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1);
		if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], &dev->i2c_adap[dev->def_i2c_bus], 0x60)) {
			result = -EINVAL;
			goto out_free;
		}
		if (dvb->fe[0]->ops.i2c_gate_ctrl)
			dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 0);

		break;
	case EM2884_BOARD_C3TECH_DIGITAL_DUO:
		dvb->fe[0] = dvb_attach(mb86a20s_attach,
					   &c3tech_duo_mb86a20s_config,
					   &dev->i2c_adap[dev->def_i2c_bus]);
		if (dvb->fe[0] != NULL)
			dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
				   &dev->i2c_adap[dev->def_i2c_bus],
				   &c3tech_duo_tda18271_config);
		break;
	case EM28174_BOARD_PCTV_460E:
		/* attach demod */
		dvb->fe[0] = dvb_attach(tda10071_attach,
			&em28xx_tda10071_config, &dev->i2c_adap[dev->def_i2c_bus]);

		/* attach SEC */
		if (dvb->fe[0])
			dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap[dev->def_i2c_bus],
				&em28xx_a8293_config);
		break;
	case EM2874_BOARD_DELOCK_61959:
	case EM2874_BOARD_MAXMEDIA_UB425_TC:
		/* attach demodulator */
		dvb->fe[0] = dvb_attach(drxk_attach, &maxmedia_ub425_tc_drxk,
				&dev->i2c_adap[dev->def_i2c_bus]);

		if (dvb->fe[0]) {
			/* disable I2C-gate */
			dvb->fe[0]->ops.i2c_gate_ctrl = NULL;

			/* attach tuner */
			if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
					&dev->i2c_adap[dev->def_i2c_bus],
					&em28xx_cxd2820r_tda18271_config)) {
				dvb_frontend_detach(dvb->fe[0]);
				result = -EINVAL;
				goto out_free;
			}
		}
		break;
	case EM2884_BOARD_PCTV_510E:
	case EM2884_BOARD_PCTV_520E:
		pctv_520e_init(dev);

		/* attach demodulator */
		dvb->fe[0] = dvb_attach(drxk_attach, &pctv_520e_drxk,
				&dev->i2c_adap[dev->def_i2c_bus]);

		if (dvb->fe[0]) {
			/* attach tuner */
			if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
					&dev->i2c_adap[dev->def_i2c_bus],
					&em28xx_cxd2820r_tda18271_config)) {
				dvb_frontend_detach(dvb->fe[0]);
				result = -EINVAL;
				goto out_free;
			}
		}
		break;
	case EM2884_BOARD_CINERGY_HTC_STICK:
		terratec_htc_stick_init(dev);

		/* attach demodulator */
		dvb->fe[0] = dvb_attach(drxk_attach, &terratec_htc_stick_drxk,
					&dev->i2c_adap[dev->def_i2c_bus]);
		if (!dvb->fe[0]) {
			result = -EINVAL;
			goto out_free;
		}

		/* Attach the demodulator. */
		if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
				&dev->i2c_adap[dev->def_i2c_bus],
				&em28xx_cxd2820r_tda18271_config)) {
			result = -EINVAL;
			goto out_free;
		}
		break;
	case EM2884_BOARD_TERRATEC_HTC_USB_XS:
		terratec_htc_usb_xs_init(dev);

		/* attach demodulator */
		dvb->fe[0] = dvb_attach(drxk_attach, &terratec_htc_stick_drxk,
					&dev->i2c_adap[dev->def_i2c_bus]);
		if (!dvb->fe[0]) {
			result = -EINVAL;
			goto out_free;
		}

		/* Attach the demodulator. */
		if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
				&dev->i2c_adap[dev->def_i2c_bus],
				&em28xx_cxd2820r_tda18271_config)) {
			result = -EINVAL;
			goto out_free;
		}
		break;
	case EM2874_BOARD_KWORLD_UB435Q_V2:
		dvb->fe[0] = dvb_attach(lgdt3305_attach,
					&em2874_lgdt3305_dev,
					&dev->i2c_adap[dev->def_i2c_bus]);
		if (!dvb->fe[0]) {
			result = -EINVAL;
			goto out_free;
		}

		/* Attach the demodulator. */
		if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
				&dev->i2c_adap[dev->def_i2c_bus],
				&kworld_ub435q_v2_config)) {
			result = -EINVAL;
			goto out_free;
		}
		break;
	case EM2874_BOARD_KWORLD_UB435Q_V3:
	{
		struct i2c_client *client;
		struct i2c_adapter *adapter = &dev->i2c_adap[dev->def_i2c_bus];
		struct i2c_board_info board_info = {
			.type = "tda18212",
			.addr = 0x60,
			.platform_data = &kworld_ub435q_v3_config,
		};

		dvb->fe[0] = dvb_attach(lgdt3305_attach,
					&em2874_lgdt3305_nogate_dev,
					&dev->i2c_adap[dev->def_i2c_bus]);
		if (!dvb->fe[0]) {
			result = -EINVAL;
			goto out_free;
		}

		/* attach tuner */
		kworld_ub435q_v3_config.fe = dvb->fe[0];
		request_module("tda18212");
		client = i2c_new_device(adapter, &board_info);
		if (client == NULL || client->dev.driver == NULL) {
			dvb_frontend_detach(dvb->fe[0]);
			result = -ENODEV;
			goto out_free;
		}

		if (!try_module_get(client->dev.driver->owner)) {
			i2c_unregister_device(client);
			dvb_frontend_detach(dvb->fe[0]);
			result = -ENODEV;
			goto out_free;
		}

		dvb->i2c_client_tuner = client;
		break;
	}
	case EM2874_BOARD_PCTV_HD_MINI_80E:
		dvb->fe[0] = dvb_attach(drx39xxj_attach, &dev->i2c_adap[dev->def_i2c_bus]);
		if (dvb->fe[0] != NULL) {
			dvb->fe[0] = dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
						&dev->i2c_adap[dev->def_i2c_bus],
						&pinnacle_80e_dvb_config);
			if (!dvb->fe[0]) {
				result = -EINVAL;
				goto out_free;
			}
		}
		break;
	case EM28178_BOARD_PCTV_461E:
		{
			/* demod I2C adapter */
			struct i2c_adapter *i2c_adapter;
			struct i2c_client *client;
			struct i2c_board_info info;
			struct m88ts2022_config m88ts2022_config = {
				.clock = 27000000,
			};
			memset(&info, 0, sizeof(struct i2c_board_info));

			/* attach demod */
			dvb->fe[0] = dvb_attach(m88ds3103_attach,
					&pctv_461e_m88ds3103_config,
					&dev->i2c_adap[dev->def_i2c_bus],
					&i2c_adapter);
			if (dvb->fe[0] == NULL) {
				result = -ENODEV;
				goto out_free;
			}

			/* attach tuner */
			m88ts2022_config.fe = dvb->fe[0];
			strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE);
			info.addr = 0x60;
			info.platform_data = &m88ts2022_config;
			request_module("m88ts2022");
			client = i2c_new_device(i2c_adapter, &info);
			if (client == NULL || client->dev.driver == NULL) {
				dvb_frontend_detach(dvb->fe[0]);
				result = -ENODEV;
				goto out_free;
			}

			if (!try_module_get(client->dev.driver->owner)) {
				i2c_unregister_device(client);
				dvb_frontend_detach(dvb->fe[0]);
				result = -ENODEV;
				goto out_free;
			}

			/* delegate signal strength measurement to tuner */
			dvb->fe[0]->ops.read_signal_strength =
					dvb->fe[0]->ops.tuner_ops.get_rf_strength;

			/* attach SEC */
			if (!dvb_attach(a8293_attach, dvb->fe[0],
					&dev->i2c_adap[dev->def_i2c_bus],
					&em28xx_a8293_config)) {
				module_put(client->dev.driver->owner);
				i2c_unregister_device(client);
				dvb_frontend_detach(dvb->fe[0]);
				result = -ENODEV;
				goto out_free;
			}

			dvb->i2c_client_tuner = client;
		}
		break;
	case EM28178_BOARD_PCTV_292E:
		{
			struct i2c_adapter *adapter;
			struct i2c_client *client;
			struct i2c_board_info info;
			struct si2168_config si2168_config;
			struct si2157_config si2157_config;

			/* attach demod */
			si2168_config.i2c_adapter = &adapter;
			si2168_config.fe = &dvb->fe[0];
			si2168_config.ts_mode = SI2168_TS_PARALLEL;
			memset(&info, 0, sizeof(struct i2c_board_info));
			strlcpy(info.type, "si2168", I2C_NAME_SIZE);
			info.addr = 0x64;
			info.platform_data = &si2168_config;
			request_module(info.type);
			client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info);
			if (client == NULL || client->dev.driver == NULL) {
				result = -ENODEV;
				goto out_free;
			}

			if (!try_module_get(client->dev.driver->owner)) {
				i2c_unregister_device(client);
				result = -ENODEV;
				goto out_free;
			}

			dvb->i2c_client_demod = client;

			/* attach tuner */
			memset(&si2157_config, 0, sizeof(si2157_config));
			si2157_config.fe = dvb->fe[0];
			memset(&info, 0, sizeof(struct i2c_board_info));
			strlcpy(info.type, "si2157", I2C_NAME_SIZE);
			info.addr = 0x60;
			info.platform_data = &si2157_config;
			request_module(info.type);
			client = i2c_new_device(adapter, &info);
			if (client == NULL || client->dev.driver == NULL) {
				module_put(dvb->i2c_client_demod->dev.driver->owner);
				i2c_unregister_device(dvb->i2c_client_demod);
				result = -ENODEV;
				goto out_free;
			}

			if (!try_module_get(client->dev.driver->owner)) {
				i2c_unregister_device(client);
				module_put(dvb->i2c_client_demod->dev.driver->owner);
				i2c_unregister_device(dvb->i2c_client_demod);
				result = -ENODEV;
				goto out_free;
			}

			dvb->i2c_client_tuner = client;
			dvb->fe[0]->ops.set_lna = em28xx_pctv_292e_set_lna;
		}
		break;
	default:
		em28xx_errdev("/2: The frontend of your DVB/ATSC card"
				" isn't supported yet\n");
		break;
	}
	if (NULL == dvb->fe[0]) {
		em28xx_errdev("/2: frontend initialization failed\n");
		result = -EINVAL;
		goto out_free;
	}
	/* define general-purpose callback pointer */
	dvb->fe[0]->callback = em28xx_tuner_callback;
	if (dvb->fe[1])
		dvb->fe[1]->callback = em28xx_tuner_callback;

	/* register everything */
	result = em28xx_register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);

	if (result < 0)
		goto out_free;

	/* MFE lock */
	dvb->adapter.mfe_shared = mfe_shared;

	em28xx_info("DVB extension successfully initialized\n");

	kref_get(&dev->ref);

ret:
	em28xx_set_mode(dev, EM28XX_SUSPEND);
	mutex_unlock(&dev->lock);
	return result;

out_free:
	kfree(dvb);
	dev->dvb = NULL;
	goto ret;
}

static inline void prevent_sleep(struct dvb_frontend_ops *ops)
{
	ops->set_voltage = NULL;
	ops->sleep = NULL;
	ops->tuner_ops.sleep = NULL;
}

static int em28xx_dvb_fini(struct em28xx *dev)
{
	struct em28xx_dvb *dvb;
	struct i2c_client *client;

	if (dev->is_audio_only) {
		/* Shouldn't initialize IR for this interface */
		return 0;
	}

	if (!dev->board.has_dvb) {
		/* This device does not support the extension */
		return 0;
	}

	if (!dev->dvb)
		return 0;

	em28xx_info("Closing DVB extension\n");

	dvb = dev->dvb;
	client = dvb->i2c_client_tuner;

	em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE);

	if (dev->disconnected) {
		/* We cannot tell the device to sleep
		 * once it has been unplugged. */
		if (dvb->fe[0]) {
			prevent_sleep(&dvb->fe[0]->ops);
			dvb->fe[0]->exit = DVB_FE_DEVICE_REMOVED;
		}
		if (dvb->fe[1]) {
			prevent_sleep(&dvb->fe[1]->ops);
			dvb->fe[1]->exit = DVB_FE_DEVICE_REMOVED;
		}
	}

	/* remove I2C tuner */
	if (client) {
		module_put(client->dev.driver->owner);
		i2c_unregister_device(client);
	}

	/* remove I2C demod */
	client = dvb->i2c_client_demod;
	if (client) {
		module_put(client->dev.driver->owner);
		i2c_unregister_device(client);
	}

	em28xx_unregister_dvb(dvb);
	kfree(dvb);
	dev->dvb = NULL;
	kref_put(&dev->ref, em28xx_free_device);

	return 0;
}

static int em28xx_dvb_suspend(struct em28xx *dev)
{
	int ret = 0;

	if (dev->is_audio_only)
		return 0;

	if (!dev->board.has_dvb)
		return 0;

	em28xx_info("Suspending DVB extension\n");
	if (dev->dvb) {
		struct em28xx_dvb *dvb = dev->dvb;

		if (dvb->fe[0]) {
			ret = dvb_frontend_suspend(dvb->fe[0]);
			em28xx_info("fe0 suspend %d\n", ret);
		}
		if (dvb->fe[1]) {
			dvb_frontend_suspend(dvb->fe[1]);
			em28xx_info("fe1 suspend %d\n", ret);
		}
	}

	return 0;
}