xref: /OK3568_Linux_fs/kernel/drivers/media/usb/dvb-usb-v2/ce6230.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Intel CE6230 DVB USB driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include "ce6230.h"
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
11*4882a593Smuzhiyun 
ce6230_ctrl_msg(struct dvb_usb_device * d,struct usb_req * req)12*4882a593Smuzhiyun static int ce6230_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req)
13*4882a593Smuzhiyun {
14*4882a593Smuzhiyun 	int ret;
15*4882a593Smuzhiyun 	unsigned int pipe;
16*4882a593Smuzhiyun 	u8 request;
17*4882a593Smuzhiyun 	u8 requesttype;
18*4882a593Smuzhiyun 	u16 value;
19*4882a593Smuzhiyun 	u16 index;
20*4882a593Smuzhiyun 	u8 *buf;
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun 	request = req->cmd;
23*4882a593Smuzhiyun 	value = req->value;
24*4882a593Smuzhiyun 	index = req->index;
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun 	switch (req->cmd) {
27*4882a593Smuzhiyun 	case I2C_READ:
28*4882a593Smuzhiyun 	case DEMOD_READ:
29*4882a593Smuzhiyun 	case REG_READ:
30*4882a593Smuzhiyun 		requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
31*4882a593Smuzhiyun 		break;
32*4882a593Smuzhiyun 	case I2C_WRITE:
33*4882a593Smuzhiyun 	case DEMOD_WRITE:
34*4882a593Smuzhiyun 	case REG_WRITE:
35*4882a593Smuzhiyun 		requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
36*4882a593Smuzhiyun 		break;
37*4882a593Smuzhiyun 	default:
38*4882a593Smuzhiyun 		dev_err(&d->udev->dev, "%s: unknown command=%02x\n",
39*4882a593Smuzhiyun 				KBUILD_MODNAME, req->cmd);
40*4882a593Smuzhiyun 		ret = -EINVAL;
41*4882a593Smuzhiyun 		goto error;
42*4882a593Smuzhiyun 	}
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	buf = kmalloc(req->data_len, GFP_KERNEL);
45*4882a593Smuzhiyun 	if (!buf) {
46*4882a593Smuzhiyun 		ret = -ENOMEM;
47*4882a593Smuzhiyun 		goto error;
48*4882a593Smuzhiyun 	}
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	if (requesttype == (USB_TYPE_VENDOR | USB_DIR_OUT)) {
51*4882a593Smuzhiyun 		/* write */
52*4882a593Smuzhiyun 		memcpy(buf, req->data, req->data_len);
53*4882a593Smuzhiyun 		pipe = usb_sndctrlpipe(d->udev, 0);
54*4882a593Smuzhiyun 	} else {
55*4882a593Smuzhiyun 		/* read */
56*4882a593Smuzhiyun 		pipe = usb_rcvctrlpipe(d->udev, 0);
57*4882a593Smuzhiyun 	}
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	msleep(1); /* avoid I2C errors */
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	ret = usb_control_msg(d->udev, pipe, request, requesttype, value, index,
62*4882a593Smuzhiyun 			buf, req->data_len, CE6230_USB_TIMEOUT);
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	dvb_usb_dbg_usb_control_msg(d->udev, request, requesttype, value, index,
65*4882a593Smuzhiyun 			buf, req->data_len);
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	if (ret < 0)
68*4882a593Smuzhiyun 		dev_err(&d->udev->dev, "%s: usb_control_msg() failed=%d\n",
69*4882a593Smuzhiyun 				KBUILD_MODNAME, ret);
70*4882a593Smuzhiyun 	else
71*4882a593Smuzhiyun 		ret = 0;
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	/* read request, copy returned data to return buf */
74*4882a593Smuzhiyun 	if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
75*4882a593Smuzhiyun 		memcpy(req->data, buf, req->data_len);
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 	kfree(buf);
78*4882a593Smuzhiyun error:
79*4882a593Smuzhiyun 	return ret;
80*4882a593Smuzhiyun }
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun /* I2C */
83*4882a593Smuzhiyun static struct zl10353_config ce6230_zl10353_config;
84*4882a593Smuzhiyun 
ce6230_i2c_master_xfer(struct i2c_adapter * adap,struct i2c_msg msg[],int num)85*4882a593Smuzhiyun static int ce6230_i2c_master_xfer(struct i2c_adapter *adap,
86*4882a593Smuzhiyun 		struct i2c_msg msg[], int num)
87*4882a593Smuzhiyun {
88*4882a593Smuzhiyun 	struct dvb_usb_device *d = i2c_get_adapdata(adap);
89*4882a593Smuzhiyun 	int ret = 0, i = 0;
90*4882a593Smuzhiyun 	struct usb_req req;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	if (num > 2)
93*4882a593Smuzhiyun 		return -EOPNOTSUPP;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	memset(&req, 0, sizeof(req));
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
98*4882a593Smuzhiyun 		return -EAGAIN;
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	while (i < num) {
101*4882a593Smuzhiyun 		if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
102*4882a593Smuzhiyun 			if (msg[i].addr ==
103*4882a593Smuzhiyun 				ce6230_zl10353_config.demod_address) {
104*4882a593Smuzhiyun 				req.cmd = DEMOD_READ;
105*4882a593Smuzhiyun 				req.value = msg[i].addr >> 1;
106*4882a593Smuzhiyun 				req.index = msg[i].buf[0];
107*4882a593Smuzhiyun 				req.data_len = msg[i+1].len;
108*4882a593Smuzhiyun 				req.data = &msg[i+1].buf[0];
109*4882a593Smuzhiyun 				ret = ce6230_ctrl_msg(d, &req);
110*4882a593Smuzhiyun 			} else {
111*4882a593Smuzhiyun 				dev_err(&d->udev->dev, "%s: I2C read not " \
112*4882a593Smuzhiyun 						"implemented\n",
113*4882a593Smuzhiyun 						KBUILD_MODNAME);
114*4882a593Smuzhiyun 				ret = -EOPNOTSUPP;
115*4882a593Smuzhiyun 			}
116*4882a593Smuzhiyun 			i += 2;
117*4882a593Smuzhiyun 		} else {
118*4882a593Smuzhiyun 			if (msg[i].addr ==
119*4882a593Smuzhiyun 				ce6230_zl10353_config.demod_address) {
120*4882a593Smuzhiyun 				req.cmd = DEMOD_WRITE;
121*4882a593Smuzhiyun 				req.value = msg[i].addr >> 1;
122*4882a593Smuzhiyun 				req.index = msg[i].buf[0];
123*4882a593Smuzhiyun 				req.data_len = msg[i].len-1;
124*4882a593Smuzhiyun 				req.data = &msg[i].buf[1];
125*4882a593Smuzhiyun 				ret = ce6230_ctrl_msg(d, &req);
126*4882a593Smuzhiyun 			} else {
127*4882a593Smuzhiyun 				req.cmd = I2C_WRITE;
128*4882a593Smuzhiyun 				req.value = 0x2000 + (msg[i].addr >> 1);
129*4882a593Smuzhiyun 				req.index = 0x0000;
130*4882a593Smuzhiyun 				req.data_len = msg[i].len;
131*4882a593Smuzhiyun 				req.data = &msg[i].buf[0];
132*4882a593Smuzhiyun 				ret = ce6230_ctrl_msg(d, &req);
133*4882a593Smuzhiyun 			}
134*4882a593Smuzhiyun 			i += 1;
135*4882a593Smuzhiyun 		}
136*4882a593Smuzhiyun 		if (ret)
137*4882a593Smuzhiyun 			break;
138*4882a593Smuzhiyun 	}
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	mutex_unlock(&d->i2c_mutex);
141*4882a593Smuzhiyun 	return ret ? ret : i;
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun 
ce6230_i2c_functionality(struct i2c_adapter * adapter)144*4882a593Smuzhiyun static u32 ce6230_i2c_functionality(struct i2c_adapter *adapter)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun 	return I2C_FUNC_I2C;
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun static struct i2c_algorithm ce6230_i2c_algorithm = {
150*4882a593Smuzhiyun 	.master_xfer   = ce6230_i2c_master_xfer,
151*4882a593Smuzhiyun 	.functionality = ce6230_i2c_functionality,
152*4882a593Smuzhiyun };
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun /* Callbacks for DVB USB */
155*4882a593Smuzhiyun static struct zl10353_config ce6230_zl10353_config = {
156*4882a593Smuzhiyun 	.demod_address = 0x1e,
157*4882a593Smuzhiyun 	.adc_clock = 450000,
158*4882a593Smuzhiyun 	.if2 = 45700,
159*4882a593Smuzhiyun 	.no_tuner = 1,
160*4882a593Smuzhiyun 	.parallel_ts = 1,
161*4882a593Smuzhiyun 	.clock_ctl_1 = 0x34,
162*4882a593Smuzhiyun 	.pll_0 = 0x0e,
163*4882a593Smuzhiyun };
164*4882a593Smuzhiyun 
ce6230_zl10353_frontend_attach(struct dvb_usb_adapter * adap)165*4882a593Smuzhiyun static int ce6230_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
166*4882a593Smuzhiyun {
167*4882a593Smuzhiyun 	struct dvb_usb_device *d = adap_to_d(adap);
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	dev_dbg(&d->udev->dev, "%s:\n", __func__);
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	adap->fe[0] = dvb_attach(zl10353_attach, &ce6230_zl10353_config,
172*4882a593Smuzhiyun 			&d->i2c_adap);
173*4882a593Smuzhiyun 	if (adap->fe[0] == NULL)
174*4882a593Smuzhiyun 		return -ENODEV;
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	return 0;
177*4882a593Smuzhiyun }
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun static struct mxl5005s_config ce6230_mxl5003s_config = {
180*4882a593Smuzhiyun 	.i2c_address     = 0xc6,
181*4882a593Smuzhiyun 	.if_freq         = IF_FREQ_4570000HZ,
182*4882a593Smuzhiyun 	.xtal_freq       = CRYSTAL_FREQ_16000000HZ,
183*4882a593Smuzhiyun 	.agc_mode        = MXL_SINGLE_AGC,
184*4882a593Smuzhiyun 	.tracking_filter = MXL_TF_DEFAULT,
185*4882a593Smuzhiyun 	.rssi_enable     = MXL_RSSI_ENABLE,
186*4882a593Smuzhiyun 	.cap_select      = MXL_CAP_SEL_ENABLE,
187*4882a593Smuzhiyun 	.div_out         = MXL_DIV_OUT_4,
188*4882a593Smuzhiyun 	.clock_out       = MXL_CLOCK_OUT_DISABLE,
189*4882a593Smuzhiyun 	.output_load     = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
190*4882a593Smuzhiyun 	.top		 = MXL5005S_TOP_25P2,
191*4882a593Smuzhiyun 	.mod_mode        = MXL_DIGITAL_MODE,
192*4882a593Smuzhiyun 	.if_mode         = MXL_ZERO_IF,
193*4882a593Smuzhiyun 	.AgcMasterByte   = 0x00,
194*4882a593Smuzhiyun };
195*4882a593Smuzhiyun 
ce6230_mxl5003s_tuner_attach(struct dvb_usb_adapter * adap)196*4882a593Smuzhiyun static int ce6230_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
197*4882a593Smuzhiyun {
198*4882a593Smuzhiyun 	struct dvb_usb_device *d = adap_to_d(adap);
199*4882a593Smuzhiyun 	int ret;
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	dev_dbg(&d->udev->dev, "%s:\n", __func__);
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	ret = dvb_attach(mxl5005s_attach, adap->fe[0], &d->i2c_adap,
204*4882a593Smuzhiyun 			&ce6230_mxl5003s_config) == NULL ? -ENODEV : 0;
205*4882a593Smuzhiyun 	return ret;
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun 
ce6230_power_ctrl(struct dvb_usb_device * d,int onoff)208*4882a593Smuzhiyun static int ce6230_power_ctrl(struct dvb_usb_device *d, int onoff)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun 	int ret;
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff);
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	/* InterfaceNumber 1 / AlternateSetting 0     idle
215*4882a593Smuzhiyun 	   InterfaceNumber 1 / AlternateSetting 1     streaming */
216*4882a593Smuzhiyun 	ret = usb_set_interface(d->udev, 1, onoff);
217*4882a593Smuzhiyun 	if (ret)
218*4882a593Smuzhiyun 		dev_err(&d->udev->dev, "%s: usb_set_interface() failed=%d\n",
219*4882a593Smuzhiyun 				KBUILD_MODNAME, ret);
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	return ret;
222*4882a593Smuzhiyun }
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun /* DVB USB Driver stuff */
225*4882a593Smuzhiyun static struct dvb_usb_device_properties ce6230_props = {
226*4882a593Smuzhiyun 	.driver_name = KBUILD_MODNAME,
227*4882a593Smuzhiyun 	.owner = THIS_MODULE,
228*4882a593Smuzhiyun 	.adapter_nr = adapter_nr,
229*4882a593Smuzhiyun 	.bInterfaceNumber = 1,
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	.i2c_algo = &ce6230_i2c_algorithm,
232*4882a593Smuzhiyun 	.power_ctrl = ce6230_power_ctrl,
233*4882a593Smuzhiyun 	.frontend_attach = ce6230_zl10353_frontend_attach,
234*4882a593Smuzhiyun 	.tuner_attach = ce6230_mxl5003s_tuner_attach,
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	.num_adapters = 1,
237*4882a593Smuzhiyun 	.adapter = {
238*4882a593Smuzhiyun 		{
239*4882a593Smuzhiyun 			.stream = {
240*4882a593Smuzhiyun 				.type = USB_BULK,
241*4882a593Smuzhiyun 				.count = 6,
242*4882a593Smuzhiyun 				.endpoint = 0x82,
243*4882a593Smuzhiyun 				.u = {
244*4882a593Smuzhiyun 					.bulk = {
245*4882a593Smuzhiyun 						.buffersize = (16 * 512),
246*4882a593Smuzhiyun 					}
247*4882a593Smuzhiyun 				}
248*4882a593Smuzhiyun 			},
249*4882a593Smuzhiyun 		}
250*4882a593Smuzhiyun 	},
251*4882a593Smuzhiyun };
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun static const struct usb_device_id ce6230_id_table[] = {
254*4882a593Smuzhiyun 	{ DVB_USB_DEVICE(USB_VID_INTEL, USB_PID_INTEL_CE9500,
255*4882a593Smuzhiyun 		&ce6230_props, "Intel CE9500 reference design", NULL) },
256*4882a593Smuzhiyun 	{ DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A310,
257*4882a593Smuzhiyun 		&ce6230_props, "AVerMedia A310 USB 2.0 DVB-T tuner", NULL) },
258*4882a593Smuzhiyun 	{ }
259*4882a593Smuzhiyun };
260*4882a593Smuzhiyun MODULE_DEVICE_TABLE(usb, ce6230_id_table);
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun static struct usb_driver ce6230_usb_driver = {
263*4882a593Smuzhiyun 	.name = KBUILD_MODNAME,
264*4882a593Smuzhiyun 	.id_table = ce6230_id_table,
265*4882a593Smuzhiyun 	.probe = dvb_usbv2_probe,
266*4882a593Smuzhiyun 	.disconnect = dvb_usbv2_disconnect,
267*4882a593Smuzhiyun 	.suspend = dvb_usbv2_suspend,
268*4882a593Smuzhiyun 	.resume = dvb_usbv2_resume,
269*4882a593Smuzhiyun 	.reset_resume = dvb_usbv2_reset_resume,
270*4882a593Smuzhiyun 	.no_dynamic_id = 1,
271*4882a593Smuzhiyun 	.soft_unbind = 1,
272*4882a593Smuzhiyun };
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun module_usb_driver(ce6230_usb_driver);
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
277*4882a593Smuzhiyun MODULE_DESCRIPTION("Intel CE6230 driver");
278*4882a593Smuzhiyun MODULE_LICENSE("GPL");
279