18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * E3C EC168 DVB USB driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "ec168.h"
98c2ecf20Sopenharmony_ci#include "ec100.h"
108c2ecf20Sopenharmony_ci#include "mxl5005s.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ciDVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic int ec168_ctrl_msg(struct dvb_usb_device *d, struct ec168_req *req)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	int ret;
178c2ecf20Sopenharmony_ci	unsigned int pipe;
188c2ecf20Sopenharmony_ci	u8 request, requesttype;
198c2ecf20Sopenharmony_ci	u8 *buf;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	switch (req->cmd) {
228c2ecf20Sopenharmony_ci	case DOWNLOAD_FIRMWARE:
238c2ecf20Sopenharmony_ci	case GPIO:
248c2ecf20Sopenharmony_ci	case WRITE_I2C:
258c2ecf20Sopenharmony_ci	case STREAMING_CTRL:
268c2ecf20Sopenharmony_ci		requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
278c2ecf20Sopenharmony_ci		request = req->cmd;
288c2ecf20Sopenharmony_ci		break;
298c2ecf20Sopenharmony_ci	case READ_I2C:
308c2ecf20Sopenharmony_ci		requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
318c2ecf20Sopenharmony_ci		request = req->cmd;
328c2ecf20Sopenharmony_ci		break;
338c2ecf20Sopenharmony_ci	case GET_CONFIG:
348c2ecf20Sopenharmony_ci		requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
358c2ecf20Sopenharmony_ci		request = CONFIG;
368c2ecf20Sopenharmony_ci		break;
378c2ecf20Sopenharmony_ci	case SET_CONFIG:
388c2ecf20Sopenharmony_ci		requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
398c2ecf20Sopenharmony_ci		request = CONFIG;
408c2ecf20Sopenharmony_ci		break;
418c2ecf20Sopenharmony_ci	case WRITE_DEMOD:
428c2ecf20Sopenharmony_ci		requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
438c2ecf20Sopenharmony_ci		request = DEMOD_RW;
448c2ecf20Sopenharmony_ci		break;
458c2ecf20Sopenharmony_ci	case READ_DEMOD:
468c2ecf20Sopenharmony_ci		requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
478c2ecf20Sopenharmony_ci		request = DEMOD_RW;
488c2ecf20Sopenharmony_ci		break;
498c2ecf20Sopenharmony_ci	default:
508c2ecf20Sopenharmony_ci		dev_err(&d->udev->dev, "%s: unknown command=%02x\n",
518c2ecf20Sopenharmony_ci				KBUILD_MODNAME, req->cmd);
528c2ecf20Sopenharmony_ci		ret = -EINVAL;
538c2ecf20Sopenharmony_ci		goto error;
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	buf = kmalloc(req->size, GFP_KERNEL);
578c2ecf20Sopenharmony_ci	if (!buf) {
588c2ecf20Sopenharmony_ci		ret = -ENOMEM;
598c2ecf20Sopenharmony_ci		goto error;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (requesttype == (USB_TYPE_VENDOR | USB_DIR_OUT)) {
638c2ecf20Sopenharmony_ci		/* write */
648c2ecf20Sopenharmony_ci		memcpy(buf, req->data, req->size);
658c2ecf20Sopenharmony_ci		pipe = usb_sndctrlpipe(d->udev, 0);
668c2ecf20Sopenharmony_ci	} else {
678c2ecf20Sopenharmony_ci		/* read */
688c2ecf20Sopenharmony_ci		pipe = usb_rcvctrlpipe(d->udev, 0);
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	msleep(1); /* avoid I2C errors */
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	ret = usb_control_msg(d->udev, pipe, request, requesttype, req->value,
748c2ecf20Sopenharmony_ci		req->index, buf, req->size, EC168_USB_TIMEOUT);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	dvb_usb_dbg_usb_control_msg(d->udev, request, requesttype, req->value,
778c2ecf20Sopenharmony_ci			req->index, buf, req->size);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (ret < 0)
808c2ecf20Sopenharmony_ci		goto err_dealloc;
818c2ecf20Sopenharmony_ci	else
828c2ecf20Sopenharmony_ci		ret = 0;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/* read request, copy returned data to return buf */
858c2ecf20Sopenharmony_ci	if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
868c2ecf20Sopenharmony_ci		memcpy(req->data, buf, req->size);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	kfree(buf);
898c2ecf20Sopenharmony_ci	return ret;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cierr_dealloc:
928c2ecf20Sopenharmony_ci	kfree(buf);
938c2ecf20Sopenharmony_cierror:
948c2ecf20Sopenharmony_ci	dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
958c2ecf20Sopenharmony_ci	return ret;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/* I2C */
998c2ecf20Sopenharmony_cistatic struct ec100_config ec168_ec100_config;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic int ec168_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
1028c2ecf20Sopenharmony_ci	int num)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct dvb_usb_device *d = i2c_get_adapdata(adap);
1058c2ecf20Sopenharmony_ci	struct ec168_req req;
1068c2ecf20Sopenharmony_ci	int i = 0;
1078c2ecf20Sopenharmony_ci	int ret;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (num > 2)
1108c2ecf20Sopenharmony_ci		return -EINVAL;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
1138c2ecf20Sopenharmony_ci		return -EAGAIN;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	while (i < num) {
1168c2ecf20Sopenharmony_ci		if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
1178c2ecf20Sopenharmony_ci			if (msg[i].addr == ec168_ec100_config.demod_address) {
1188c2ecf20Sopenharmony_ci				if (msg[i].len < 1) {
1198c2ecf20Sopenharmony_ci					i = -EOPNOTSUPP;
1208c2ecf20Sopenharmony_ci					break;
1218c2ecf20Sopenharmony_ci				}
1228c2ecf20Sopenharmony_ci				req.cmd = READ_DEMOD;
1238c2ecf20Sopenharmony_ci				req.value = 0;
1248c2ecf20Sopenharmony_ci				req.index = 0xff00 + msg[i].buf[0]; /* reg */
1258c2ecf20Sopenharmony_ci				req.size = msg[i+1].len; /* bytes to read */
1268c2ecf20Sopenharmony_ci				req.data = &msg[i+1].buf[0];
1278c2ecf20Sopenharmony_ci				ret = ec168_ctrl_msg(d, &req);
1288c2ecf20Sopenharmony_ci				i += 2;
1298c2ecf20Sopenharmony_ci			} else {
1308c2ecf20Sopenharmony_ci				dev_err(&d->udev->dev, "%s: I2C read not " \
1318c2ecf20Sopenharmony_ci						"implemented\n",
1328c2ecf20Sopenharmony_ci						KBUILD_MODNAME);
1338c2ecf20Sopenharmony_ci				ret = -EOPNOTSUPP;
1348c2ecf20Sopenharmony_ci				i += 2;
1358c2ecf20Sopenharmony_ci			}
1368c2ecf20Sopenharmony_ci		} else {
1378c2ecf20Sopenharmony_ci			if (msg[i].addr == ec168_ec100_config.demod_address) {
1388c2ecf20Sopenharmony_ci				if (msg[i].len < 1) {
1398c2ecf20Sopenharmony_ci					i = -EOPNOTSUPP;
1408c2ecf20Sopenharmony_ci					break;
1418c2ecf20Sopenharmony_ci				}
1428c2ecf20Sopenharmony_ci				req.cmd = WRITE_DEMOD;
1438c2ecf20Sopenharmony_ci				req.value = msg[i].buf[1]; /* val */
1448c2ecf20Sopenharmony_ci				req.index = 0xff00 + msg[i].buf[0]; /* reg */
1458c2ecf20Sopenharmony_ci				req.size = 0;
1468c2ecf20Sopenharmony_ci				req.data = NULL;
1478c2ecf20Sopenharmony_ci				ret = ec168_ctrl_msg(d, &req);
1488c2ecf20Sopenharmony_ci				i += 1;
1498c2ecf20Sopenharmony_ci			} else {
1508c2ecf20Sopenharmony_ci				if (msg[i].len < 1) {
1518c2ecf20Sopenharmony_ci					i = -EOPNOTSUPP;
1528c2ecf20Sopenharmony_ci					break;
1538c2ecf20Sopenharmony_ci				}
1548c2ecf20Sopenharmony_ci				req.cmd = WRITE_I2C;
1558c2ecf20Sopenharmony_ci				req.value = msg[i].buf[0]; /* val */
1568c2ecf20Sopenharmony_ci				req.index = 0x0100 + msg[i].addr; /* I2C addr */
1578c2ecf20Sopenharmony_ci				req.size = msg[i].len-1;
1588c2ecf20Sopenharmony_ci				req.data = &msg[i].buf[1];
1598c2ecf20Sopenharmony_ci				ret = ec168_ctrl_msg(d, &req);
1608c2ecf20Sopenharmony_ci				i += 1;
1618c2ecf20Sopenharmony_ci			}
1628c2ecf20Sopenharmony_ci		}
1638c2ecf20Sopenharmony_ci		if (ret)
1648c2ecf20Sopenharmony_ci			goto error;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci	ret = i;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cierror:
1708c2ecf20Sopenharmony_ci	mutex_unlock(&d->i2c_mutex);
1718c2ecf20Sopenharmony_ci	return ret;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic u32 ec168_i2c_func(struct i2c_adapter *adapter)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	return I2C_FUNC_I2C;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic struct i2c_algorithm ec168_i2c_algo = {
1808c2ecf20Sopenharmony_ci	.master_xfer   = ec168_i2c_xfer,
1818c2ecf20Sopenharmony_ci	.functionality = ec168_i2c_func,
1828c2ecf20Sopenharmony_ci};
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci/* Callbacks for DVB USB */
1858c2ecf20Sopenharmony_cistatic int ec168_identify_state(struct dvb_usb_device *d, const char **name)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	int ret;
1888c2ecf20Sopenharmony_ci	u8 reply;
1898c2ecf20Sopenharmony_ci	struct ec168_req req = {GET_CONFIG, 0, 1, sizeof(reply), &reply};
1908c2ecf20Sopenharmony_ci	dev_dbg(&d->udev->dev, "%s:\n", __func__);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	ret = ec168_ctrl_msg(d, &req);
1938c2ecf20Sopenharmony_ci	if (ret)
1948c2ecf20Sopenharmony_ci		goto error;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	dev_dbg(&d->udev->dev, "%s: reply=%02x\n", __func__, reply);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (reply == 0x01)
1998c2ecf20Sopenharmony_ci		ret = WARM;
2008c2ecf20Sopenharmony_ci	else
2018c2ecf20Sopenharmony_ci		ret = COLD;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	return ret;
2048c2ecf20Sopenharmony_cierror:
2058c2ecf20Sopenharmony_ci	dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
2068c2ecf20Sopenharmony_ci	return ret;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic int ec168_download_firmware(struct dvb_usb_device *d,
2108c2ecf20Sopenharmony_ci		const struct firmware *fw)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	int ret, len, remaining;
2138c2ecf20Sopenharmony_ci	struct ec168_req req = {DOWNLOAD_FIRMWARE, 0, 0, 0, NULL};
2148c2ecf20Sopenharmony_ci	dev_dbg(&d->udev->dev, "%s:\n", __func__);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	#define LEN_MAX 2048 /* max packet size */
2178c2ecf20Sopenharmony_ci	for (remaining = fw->size; remaining > 0; remaining -= LEN_MAX) {
2188c2ecf20Sopenharmony_ci		len = remaining;
2198c2ecf20Sopenharmony_ci		if (len > LEN_MAX)
2208c2ecf20Sopenharmony_ci			len = LEN_MAX;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci		req.size = len;
2238c2ecf20Sopenharmony_ci		req.data = (u8 *) &fw->data[fw->size - remaining];
2248c2ecf20Sopenharmony_ci		req.index = fw->size - remaining;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		ret = ec168_ctrl_msg(d, &req);
2278c2ecf20Sopenharmony_ci		if (ret) {
2288c2ecf20Sopenharmony_ci			dev_err(&d->udev->dev,
2298c2ecf20Sopenharmony_ci					"%s: firmware download failed=%d\n",
2308c2ecf20Sopenharmony_ci					KBUILD_MODNAME, ret);
2318c2ecf20Sopenharmony_ci			goto error;
2328c2ecf20Sopenharmony_ci		}
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	req.size = 0;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	/* set "warm"? */
2388c2ecf20Sopenharmony_ci	req.cmd = SET_CONFIG;
2398c2ecf20Sopenharmony_ci	req.value = 0;
2408c2ecf20Sopenharmony_ci	req.index = 0x0001;
2418c2ecf20Sopenharmony_ci	ret = ec168_ctrl_msg(d, &req);
2428c2ecf20Sopenharmony_ci	if (ret)
2438c2ecf20Sopenharmony_ci		goto error;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	/* really needed - no idea what does */
2468c2ecf20Sopenharmony_ci	req.cmd = GPIO;
2478c2ecf20Sopenharmony_ci	req.value = 0;
2488c2ecf20Sopenharmony_ci	req.index = 0x0206;
2498c2ecf20Sopenharmony_ci	ret = ec168_ctrl_msg(d, &req);
2508c2ecf20Sopenharmony_ci	if (ret)
2518c2ecf20Sopenharmony_ci		goto error;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	/* activate tuner I2C? */
2548c2ecf20Sopenharmony_ci	req.cmd = WRITE_I2C;
2558c2ecf20Sopenharmony_ci	req.value = 0;
2568c2ecf20Sopenharmony_ci	req.index = 0x00c6;
2578c2ecf20Sopenharmony_ci	ret = ec168_ctrl_msg(d, &req);
2588c2ecf20Sopenharmony_ci	if (ret)
2598c2ecf20Sopenharmony_ci		goto error;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	return ret;
2628c2ecf20Sopenharmony_cierror:
2638c2ecf20Sopenharmony_ci	dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
2648c2ecf20Sopenharmony_ci	return ret;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic struct ec100_config ec168_ec100_config = {
2688c2ecf20Sopenharmony_ci	.demod_address = 0xff, /* not real address, demod is integrated */
2698c2ecf20Sopenharmony_ci};
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic int ec168_ec100_frontend_attach(struct dvb_usb_adapter *adap)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct dvb_usb_device *d = adap_to_d(adap);
2748c2ecf20Sopenharmony_ci	dev_dbg(&d->udev->dev, "%s:\n", __func__);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	adap->fe[0] = dvb_attach(ec100_attach, &ec168_ec100_config,
2778c2ecf20Sopenharmony_ci			&d->i2c_adap);
2788c2ecf20Sopenharmony_ci	if (adap->fe[0] == NULL)
2798c2ecf20Sopenharmony_ci		return -ENODEV;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	return 0;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic struct mxl5005s_config ec168_mxl5003s_config = {
2858c2ecf20Sopenharmony_ci	.i2c_address     = 0xc6,
2868c2ecf20Sopenharmony_ci	.if_freq         = IF_FREQ_4570000HZ,
2878c2ecf20Sopenharmony_ci	.xtal_freq       = CRYSTAL_FREQ_16000000HZ,
2888c2ecf20Sopenharmony_ci	.agc_mode        = MXL_SINGLE_AGC,
2898c2ecf20Sopenharmony_ci	.tracking_filter = MXL_TF_OFF,
2908c2ecf20Sopenharmony_ci	.rssi_enable     = MXL_RSSI_ENABLE,
2918c2ecf20Sopenharmony_ci	.cap_select      = MXL_CAP_SEL_ENABLE,
2928c2ecf20Sopenharmony_ci	.div_out         = MXL_DIV_OUT_4,
2938c2ecf20Sopenharmony_ci	.clock_out       = MXL_CLOCK_OUT_DISABLE,
2948c2ecf20Sopenharmony_ci	.output_load     = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
2958c2ecf20Sopenharmony_ci	.top		 = MXL5005S_TOP_25P2,
2968c2ecf20Sopenharmony_ci	.mod_mode        = MXL_DIGITAL_MODE,
2978c2ecf20Sopenharmony_ci	.if_mode         = MXL_ZERO_IF,
2988c2ecf20Sopenharmony_ci	.AgcMasterByte   = 0x00,
2998c2ecf20Sopenharmony_ci};
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic int ec168_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	struct dvb_usb_device *d = adap_to_d(adap);
3048c2ecf20Sopenharmony_ci	dev_dbg(&d->udev->dev, "%s:\n", __func__);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	return dvb_attach(mxl5005s_attach, adap->fe[0], &d->i2c_adap,
3078c2ecf20Sopenharmony_ci			&ec168_mxl5003s_config) == NULL ? -ENODEV : 0;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic int ec168_streaming_ctrl(struct dvb_frontend *fe, int onoff)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	struct dvb_usb_device *d = fe_to_d(fe);
3138c2ecf20Sopenharmony_ci	struct ec168_req req = {STREAMING_CTRL, 0x7f01, 0x0202, 0, NULL};
3148c2ecf20Sopenharmony_ci	dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (onoff)
3178c2ecf20Sopenharmony_ci		req.index = 0x0102;
3188c2ecf20Sopenharmony_ci	return ec168_ctrl_msg(d, &req);
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci/* DVB USB Driver stuff */
3228c2ecf20Sopenharmony_ci/* bInterfaceNumber 0 is HID
3238c2ecf20Sopenharmony_ci * bInterfaceNumber 1 is DVB-T */
3248c2ecf20Sopenharmony_cistatic const struct dvb_usb_device_properties ec168_props = {
3258c2ecf20Sopenharmony_ci	.driver_name = KBUILD_MODNAME,
3268c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
3278c2ecf20Sopenharmony_ci	.adapter_nr = adapter_nr,
3288c2ecf20Sopenharmony_ci	.bInterfaceNumber = 1,
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	.identify_state = ec168_identify_state,
3318c2ecf20Sopenharmony_ci	.firmware = EC168_FIRMWARE,
3328c2ecf20Sopenharmony_ci	.download_firmware = ec168_download_firmware,
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	.i2c_algo = &ec168_i2c_algo,
3358c2ecf20Sopenharmony_ci	.frontend_attach = ec168_ec100_frontend_attach,
3368c2ecf20Sopenharmony_ci	.tuner_attach = ec168_mxl5003s_tuner_attach,
3378c2ecf20Sopenharmony_ci	.streaming_ctrl = ec168_streaming_ctrl,
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	.num_adapters = 1,
3408c2ecf20Sopenharmony_ci	.adapter = {
3418c2ecf20Sopenharmony_ci		{
3428c2ecf20Sopenharmony_ci			.stream = DVB_USB_STREAM_BULK(0x82, 6, 32 * 512),
3438c2ecf20Sopenharmony_ci		}
3448c2ecf20Sopenharmony_ci	},
3458c2ecf20Sopenharmony_ci};
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic const struct usb_device_id ec168_id[] = {
3488c2ecf20Sopenharmony_ci	{ DVB_USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168,
3498c2ecf20Sopenharmony_ci		     &ec168_props, "E3C EC168 reference design", NULL)},
3508c2ecf20Sopenharmony_ci	{ DVB_USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_2,
3518c2ecf20Sopenharmony_ci		     &ec168_props, "E3C EC168 reference design", NULL)},
3528c2ecf20Sopenharmony_ci	{ DVB_USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_3,
3538c2ecf20Sopenharmony_ci		     &ec168_props, "E3C EC168 reference design", NULL)},
3548c2ecf20Sopenharmony_ci	{ DVB_USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_4,
3558c2ecf20Sopenharmony_ci		     &ec168_props, "E3C EC168 reference design", NULL)},
3568c2ecf20Sopenharmony_ci	{ DVB_USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_5,
3578c2ecf20Sopenharmony_ci		     &ec168_props, "E3C EC168 reference design", NULL)},
3588c2ecf20Sopenharmony_ci	{}
3598c2ecf20Sopenharmony_ci};
3608c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, ec168_id);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistatic struct usb_driver ec168_driver = {
3638c2ecf20Sopenharmony_ci	.name = KBUILD_MODNAME,
3648c2ecf20Sopenharmony_ci	.id_table = ec168_id,
3658c2ecf20Sopenharmony_ci	.probe = dvb_usbv2_probe,
3668c2ecf20Sopenharmony_ci	.disconnect = dvb_usbv2_disconnect,
3678c2ecf20Sopenharmony_ci	.suspend = dvb_usbv2_suspend,
3688c2ecf20Sopenharmony_ci	.resume = dvb_usbv2_resume,
3698c2ecf20Sopenharmony_ci	.no_dynamic_id = 1,
3708c2ecf20Sopenharmony_ci	.soft_unbind = 1,
3718c2ecf20Sopenharmony_ci};
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cimodule_usb_driver(ec168_driver);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ciMODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
3768c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("E3C EC168 driver");
3778c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
3788c2ecf20Sopenharmony_ciMODULE_FIRMWARE(EC168_FIRMWARE);
379