Commit | Line | Data |
---|---|---|
c942fddf | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
53af158a AP |
2 | /* |
3 | * DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0. | |
5decdd27 | 4 | * |
4c7e3ea9 | 5 | * Copyright (C) 2006 Antti Palosaari <crope@iki.fi> |
5decdd27 AP |
6 | */ |
7 | ||
8 | #include "au6610.h" | |
5decdd27 AP |
9 | #include "zl10353.h" |
10 | #include "qt1010.h" | |
11 | ||
78e92006 JG |
12 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); |
13 | ||
5decdd27 AP |
14 | static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr, |
15 | u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) | |
16 | { | |
17 | int ret; | |
18 | u16 index; | |
2a596f84 FM |
19 | u8 *usb_buf; |
20 | ||
21 | /* | |
22 | * allocate enough for all known requests, | |
23 | * read returns 5 and write 6 bytes | |
24 | */ | |
25 | usb_buf = kmalloc(6, GFP_KERNEL); | |
26 | if (!usb_buf) | |
27 | return -ENOMEM; | |
28 | ||
5decdd27 AP |
29 | switch (wlen) { |
30 | case 1: | |
31 | index = wbuf[0] << 8; | |
32 | break; | |
33 | case 2: | |
34 | index = wbuf[0] << 8; | |
35 | index += wbuf[1]; | |
36 | break; | |
37 | default: | |
a835ae67 AP |
38 | dev_err(&d->udev->dev, "%s: wlen=%d, aborting\n", |
39 | KBUILD_MODNAME, wlen); | |
2a596f84 FM |
40 | ret = -EINVAL; |
41 | goto error; | |
5decdd27 AP |
42 | } |
43 | ||
44 | ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), operation, | |
b6b2d268 | 45 | USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index, |
2a596f84 | 46 | usb_buf, 6, AU6610_USB_TIMEOUT); |
d89b9369 AP |
47 | |
48 | dvb_usb_dbg_usb_control_msg(d->udev, operation, | |
49 | (USB_TYPE_VENDOR|USB_DIR_IN), addr << 1, index, | |
50 | usb_buf, 6); | |
51 | ||
5decdd27 | 52 | if (ret < 0) |
2a596f84 | 53 | goto error; |
5decdd27 AP |
54 | |
55 | switch (operation) { | |
56 | case AU6610_REQ_I2C_READ: | |
57 | case AU6610_REQ_USB_READ: | |
58 | /* requested value is always 5th byte in buffer */ | |
59 | rbuf[0] = usb_buf[4]; | |
60 | } | |
2a596f84 FM |
61 | error: |
62 | kfree(usb_buf); | |
5decdd27 AP |
63 | return ret; |
64 | } | |
65 | ||
38d0629f MK |
66 | static int au6610_i2c_msg(struct dvb_usb_device *d, u8 addr, |
67 | u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) | |
5decdd27 AP |
68 | { |
69 | u8 request; | |
70 | u8 wo = (rbuf == NULL || rlen == 0); /* write-only */ | |
71 | ||
72 | if (wo) { | |
73 | request = AU6610_REQ_I2C_WRITE; | |
74 | } else { /* rw */ | |
75 | request = AU6610_REQ_I2C_READ; | |
76 | } | |
77 | ||
78 | return au6610_usb_msg(d, request, addr, wbuf, wlen, rbuf, rlen); | |
79 | } | |
80 | ||
81 | ||
82 | /* I2C */ | |
38d0629f MK |
83 | static int au6610_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], |
84 | int num) | |
5decdd27 AP |
85 | { |
86 | struct dvb_usb_device *d = i2c_get_adapdata(adap); | |
87 | int i; | |
88 | ||
5decdd27 AP |
89 | if (num > 2) |
90 | return -EINVAL; | |
91 | ||
c80296d7 RK |
92 | if (mutex_lock_interruptible(&d->i2c_mutex) < 0) |
93 | return -EAGAIN; | |
94 | ||
5decdd27 AP |
95 | for (i = 0; i < num; i++) { |
96 | /* write/read request */ | |
97 | if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { | |
98 | if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf, | |
38d0629f MK |
99 | msg[i].len, msg[i+1].buf, |
100 | msg[i+1].len) < 0) | |
5decdd27 AP |
101 | break; |
102 | i++; | |
103 | } else if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf, | |
104 | msg[i].len, NULL, 0) < 0) | |
105 | break; | |
106 | } | |
107 | ||
108 | mutex_unlock(&d->i2c_mutex); | |
109 | return i; | |
110 | } | |
111 | ||
112 | ||
113 | static u32 au6610_i2c_func(struct i2c_adapter *adapter) | |
114 | { | |
115 | return I2C_FUNC_I2C; | |
116 | } | |
117 | ||
118 | static struct i2c_algorithm au6610_i2c_algo = { | |
119 | .master_xfer = au6610_i2c_xfer, | |
120 | .functionality = au6610_i2c_func, | |
121 | }; | |
122 | ||
123 | /* Callbacks for DVB USB */ | |
5decdd27 | 124 | static struct zl10353_config au6610_zl10353_config = { |
26a154c3 | 125 | .demod_address = 0x0f, |
5decdd27 AP |
126 | .no_tuner = 1, |
127 | .parallel_ts = 1, | |
128 | }; | |
129 | ||
5decdd27 AP |
130 | static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap) |
131 | { | |
cb4f77a3 | 132 | adap->fe[0] = dvb_attach(zl10353_attach, &au6610_zl10353_config, |
a1da8146 | 133 | &adap_to_d(adap)->i2c_adap); |
cb4f77a3 | 134 | if (adap->fe[0] == NULL) |
53af158a | 135 | return -ENODEV; |
5decdd27 | 136 | |
b6b2d268 | 137 | return 0; |
5decdd27 AP |
138 | } |
139 | ||
4c7e3ea9 | 140 | static struct qt1010_config au6610_qt1010_config = { |
26a154c3 | 141 | .i2c_address = 0x62 |
4c7e3ea9 AP |
142 | }; |
143 | ||
144 | static int au6610_qt1010_tuner_attach(struct dvb_usb_adapter *adap) | |
145 | { | |
a1da8146 AP |
146 | return dvb_attach(qt1010_attach, adap->fe[0], |
147 | &adap_to_d(adap)->i2c_adap, | |
148 | &au6610_qt1010_config) == NULL ? -ENODEV : 0; | |
4c7e3ea9 AP |
149 | } |
150 | ||
cb4f77a3 | 151 | static int au6610_init(struct dvb_usb_device *d) |
5decdd27 | 152 | { |
cb4f77a3 AP |
153 | /* TODO: this functionality belongs likely to the streaming control */ |
154 | /* bInterfaceNumber 0, bAlternateSetting 5 */ | |
155 | return usb_set_interface(d->udev, 0, 5); | |
5decdd27 AP |
156 | } |
157 | ||
cb4f77a3 AP |
158 | static struct dvb_usb_device_properties au6610_props = { |
159 | .driver_name = KBUILD_MODNAME, | |
160 | .owner = THIS_MODULE, | |
161 | .adapter_nr = adapter_nr, | |
53af158a | 162 | |
cb4f77a3 AP |
163 | .i2c_algo = &au6610_i2c_algo, |
164 | .frontend_attach = au6610_zl10353_frontend_attach, | |
165 | .tuner_attach = au6610_qt1010_tuner_attach, | |
166 | .init = au6610_init, | |
53af158a | 167 | |
5decdd27 AP |
168 | .num_adapters = 1, |
169 | .adapter = { | |
170 | { | |
a1da8146 | 171 | .stream = DVB_USB_STREAM_ISOC(0x82, 5, 40, 942, 1), |
cb4f77a3 | 172 | }, |
5decdd27 | 173 | }, |
cb4f77a3 | 174 | }; |
53af158a | 175 | |
cb4f77a3 AP |
176 | static const struct usb_device_id au6610_id_table[] = { |
177 | { DVB_USB_DEVICE(USB_VID_ALCOR_MICRO, USB_PID_SIGMATEK_DVB_110, | |
178 | &au6610_props, "Sigmatek DVB-110", NULL) }, | |
179 | { } | |
5decdd27 | 180 | }; |
cb4f77a3 | 181 | MODULE_DEVICE_TABLE(usb, au6610_id_table); |
5decdd27 AP |
182 | |
183 | static struct usb_driver au6610_driver = { | |
cb4f77a3 AP |
184 | .name = KBUILD_MODNAME, |
185 | .id_table = au6610_id_table, | |
186 | .probe = dvb_usbv2_probe, | |
187 | .disconnect = dvb_usbv2_disconnect, | |
188 | .suspend = dvb_usbv2_suspend, | |
189 | .resume = dvb_usbv2_resume, | |
f5a8a78a | 190 | .reset_resume = dvb_usbv2_reset_resume, |
cb4f77a3 AP |
191 | .no_dynamic_id = 1, |
192 | .soft_unbind = 1, | |
5decdd27 AP |
193 | }; |
194 | ||
ecb3b2b3 | 195 | module_usb_driver(au6610_driver); |
5decdd27 AP |
196 | |
197 | MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); | |
53af158a | 198 | MODULE_DESCRIPTION("Driver for Alcor Micro AU6610 DVB-T USB2.0"); |
5decdd27 AP |
199 | MODULE_VERSION("0.1"); |
200 | MODULE_LICENSE("GPL"); |