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