Merge with rsync://fileserver/linux
[linux-2.6] / drivers / media / dvb / dvb-usb / cxusb.c
1 /* DVB USB compliant linux driver for Conexant USB reference design.
2  *
3  * The Conexant reference design I saw on their website was only for analogue
4  * capturing (using the cx25842). The box I took to write this driver (reverse
5  * engineered) is the one labeled Medion MD95700. In addition to the cx25842
6  * for analogue capturing it also has a cx22702 DVB-T demodulator on the main
7  * board. Besides it has a atiremote (X10) and a USB2.0 hub onboard.
8  *
9  * Maybe it is a little bit premature to call this driver cxusb, but I assume
10  * the USB protocol is identical or at least inherited from the reference
11  * design, so it can be reused for the "analogue-only" device (if it will
12  * appear at all).
13  *
14  * TODO: check if the cx25840-driver (from ivtv) can be used for the analogue
15  * part
16  *
17  * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de)
18  *
19  *      This program is free software; you can redistribute it and/or modify it
20  *      under the terms of the GNU General Public License as published by the Free
21  *      Software Foundation, version 2.
22  *
23  * see Documentation/dvb/README.dvb-usb for more information
24  */
25 #include "cxusb.h"
26
27 #include "cx22702.h"
28
29 /* debug */
30 int dvb_usb_cxusb_debug;
31 module_param_named(debug,dvb_usb_cxusb_debug, int, 0644);
32 MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
33
34 static int cxusb_ctrl_msg(struct dvb_usb_device *d,
35                 u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
36 {
37         int wo = (rbuf == NULL || rlen == 0); /* write-only */
38         u8 sndbuf[1+wlen];
39         memset(sndbuf,0,1+wlen);
40
41         sndbuf[0] = cmd;
42         memcpy(&sndbuf[1],wbuf,wlen);
43         if (wo)
44                 dvb_usb_generic_write(d,sndbuf,1+wlen);
45         else
46                 dvb_usb_generic_rw(d,sndbuf,1+wlen,rbuf,rlen,0);
47
48         return 0;
49 }
50
51 /* I2C */
52 static void cxusb_set_i2c_path(struct dvb_usb_device *d, enum cxusb_i2c_pathes path)
53 {
54         struct cxusb_state *st = d->priv;
55         u8 o[2],i;
56
57         if (path == st->cur_i2c_path)
58                 return;
59
60         o[0] = IOCTL_SET_I2C_PATH;
61         switch (path) {
62                 case PATH_CX22702:
63                         o[1] = 0;
64                         break;
65                 case PATH_TUNER_OTHER:
66                         o[1] = 1;
67                         break;
68                 default:
69                         err("unkown i2c path");
70                         return;
71         }
72         cxusb_ctrl_msg(d,CMD_IOCTL,o,2,&i,1);
73
74         if (i != 0x01)
75                 deb_info("i2c_path setting failed.\n");
76
77         st->cur_i2c_path = path;
78 }
79
80 static int cxusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
81 {
82         struct dvb_usb_device *d = i2c_get_adapdata(adap);
83         int i;
84
85         if (down_interruptible(&d->i2c_sem) < 0)
86                 return -EAGAIN;
87
88         if (num > 2)
89                 warn("more than 2 i2c messages at a time is not handled yet. TODO.");
90
91         for (i = 0; i < num; i++) {
92
93                 switch (msg[i].addr) {
94                         case 0x63:
95                                 cxusb_set_i2c_path(d,PATH_CX22702);
96                                 break;
97                         default:
98                                 cxusb_set_i2c_path(d,PATH_TUNER_OTHER);
99                                 break;
100                 }
101
102                 /* read request */
103                 if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
104                         u8 obuf[3+msg[i].len], ibuf[1+msg[i+1].len];
105                         obuf[0] = msg[i].len;
106                         obuf[1] = msg[i+1].len;
107                         obuf[2] = msg[i].addr;
108                         memcpy(&obuf[3],msg[i].buf,msg[i].len);
109
110                         if (cxusb_ctrl_msg(d, CMD_I2C_READ,
111                                                 obuf, 3+msg[i].len,
112                                                 ibuf, 1+msg[i+1].len) < 0)
113                                 break;
114
115                         if (ibuf[0] != 0x08)
116                                 deb_info("i2c read could have been failed\n");
117
118                         memcpy(msg[i+1].buf,&ibuf[1],msg[i+1].len);
119
120                         i++;
121                 } else { /* write */
122                         u8 obuf[2+msg[i].len], ibuf;
123                         obuf[0] = msg[i].addr;
124                         obuf[1] = msg[i].len;
125                         memcpy(&obuf[2],msg[i].buf,msg[i].len);
126
127                         if (cxusb_ctrl_msg(d,CMD_I2C_WRITE, obuf, 2+msg[i].len, &ibuf,1) < 0)
128                                 break;
129                         if (ibuf != 0x08)
130                                 deb_info("i2c write could have been failed\n");
131                 }
132         }
133
134         up(&d->i2c_sem);
135         return i;
136 }
137
138 static u32 cxusb_i2c_func(struct i2c_adapter *adapter)
139 {
140         return I2C_FUNC_I2C;
141 }
142
143 static struct i2c_algorithm cxusb_i2c_algo = {
144         .name          = "Conexant USB I2C algorithm",
145         .id            = I2C_ALGO_BIT,
146         .master_xfer   = cxusb_i2c_xfer,
147         .functionality = cxusb_i2c_func,
148 };
149
150 static int cxusb_power_ctrl(struct dvb_usb_device *d, int onoff)
151 {
152         return 0;
153 }
154
155 static int cxusb_streaming_ctrl(struct dvb_usb_device *d, int onoff)
156 {
157         u8 buf[2] = { 0x03, 0x00 };
158         if (onoff)
159                 cxusb_ctrl_msg(d,0x36, buf, 2, NULL, 0);
160         else
161                 cxusb_ctrl_msg(d,0x37, NULL, 0, NULL, 0);
162
163         return 0;
164 }
165
166 struct cx22702_config cxusb_cx22702_config = {
167         .demod_address = 0x63,
168
169         .output_mode = CX22702_PARALLEL_OUTPUT,
170
171         .pll_init = dvb_usb_pll_init_i2c,
172         .pll_set  = dvb_usb_pll_set_i2c,
173 };
174
175 /* Callbacks for DVB USB */
176 static int cxusb_tuner_attach(struct dvb_usb_device *d)
177 {
178         u8 bpll[4] = { 0x0b, 0xdc, 0x9c, 0xa0 };
179         d->pll_addr = 0x61;
180         memcpy(d->pll_init,bpll,4);
181         d->pll_desc = &dvb_pll_fmd1216me;
182         return 0;
183 }
184
185 static int cxusb_frontend_attach(struct dvb_usb_device *d)
186 {
187         u8 buf[2] = { 0x03, 0x00 };
188         u8 b = 0;
189
190         if (usb_set_interface(d->udev,0,0) < 0)
191                 err("set interface to alts=0 failed");
192
193         cxusb_ctrl_msg(d,0xde,&b,0,NULL,0);
194         cxusb_set_i2c_path(d,PATH_TUNER_OTHER);
195         cxusb_ctrl_msg(d,CMD_POWER_OFF, NULL, 0, &b, 1);
196
197         if (usb_set_interface(d->udev,0,6) < 0)
198                 err("set interface failed");
199
200         cxusb_ctrl_msg(d,0x36, buf, 2, NULL, 0);
201         cxusb_set_i2c_path(d,PATH_CX22702);
202         cxusb_ctrl_msg(d,CMD_POWER_ON, NULL, 0, &b, 1);
203
204         if ((d->fe = cx22702_attach(&cxusb_cx22702_config, &d->i2c_adap)) != NULL)
205                 return 0;
206
207         return -EIO;
208 }
209
210 /* DVB USB Driver stuff */
211 static struct dvb_usb_properties cxusb_properties;
212
213 static int cxusb_probe(struct usb_interface *intf,
214                 const struct usb_device_id *id)
215 {
216         return dvb_usb_device_init(intf,&cxusb_properties,THIS_MODULE);
217 }
218
219 static struct usb_device_id cxusb_table [] = {
220                 { USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700) },
221                 {}              /* Terminating entry */
222 };
223 MODULE_DEVICE_TABLE (usb, cxusb_table);
224
225 static struct dvb_usb_properties cxusb_properties = {
226         .caps = DVB_USB_IS_AN_I2C_ADAPTER,
227
228         .usb_ctrl = CYPRESS_FX2,
229
230         .size_of_priv     = sizeof(struct cxusb_state),
231
232         .streaming_ctrl   = cxusb_streaming_ctrl,
233         .power_ctrl       = cxusb_power_ctrl,
234         .frontend_attach  = cxusb_frontend_attach,
235         .tuner_attach     = cxusb_tuner_attach,
236
237         .i2c_algo         = &cxusb_i2c_algo,
238
239         .generic_bulk_ctrl_endpoint = 0x01,
240         /* parameter for the MPEG2-data transfer */
241         .urb = {
242                 .type = DVB_USB_ISOC,
243                 .count = 5,
244                 .endpoint = 0x02,
245                 .u = {
246                         .isoc = {
247                                 .framesperurb = 32,
248                                 .framesize = 940,
249                                 .interval = 5,
250                         }
251                 }
252         },
253
254         .num_device_descs = 1,
255         .devices = {
256                 {   "Medion MD95700 (MDUSBTV-HYBRID)",
257                         { NULL },
258                         { &cxusb_table[0], NULL },
259                 },
260         }
261 };
262
263 static struct usb_driver cxusb_driver = {
264         .owner          = THIS_MODULE,
265         .name           = "dvb_usb_cxusb",
266         .probe          = cxusb_probe,
267         .disconnect = dvb_usb_device_exit,
268         .id_table       = cxusb_table,
269 };
270
271 /* module stuff */
272 static int __init cxusb_module_init(void)
273 {
274         int result;
275         if ((result = usb_register(&cxusb_driver))) {
276                 err("usb_register failed. Error number %d",result);
277                 return result;
278         }
279
280         return 0;
281 }
282
283 static void __exit cxusb_module_exit(void)
284 {
285         /* deregister this driver from the USB subsystem */
286         usb_deregister(&cxusb_driver);
287 }
288
289 module_init (cxusb_module_init);
290 module_exit (cxusb_module_exit);
291
292 MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
293 MODULE_DESCRIPTION("Driver for Conexant USB2.0 hybrid reference design");
294 MODULE_VERSION("1.0-alpha");
295 MODULE_LICENSE("GPL");