blob: 03e318d2d4bf1e1bf6ed7f112f5e259b42d6748b [file] [log] [blame]
Mauro Carvalho Chehab25c16462011-07-23 10:59:25 -03001/*
2 * Driver for AzureWave 6007 DVB-C/T USB2.0 and clones
3 *
4 * Copyright (c) Henry Wang <Henry.wang@AzureWave.com>
5 *
6 * This driver was made publicly available by Terratec, at:
7 * http://linux.terratec.de/files/TERRATEC_H7/20110323_TERRATEC_H7_Linux.tar.gz
8 * The original driver's license is GPL, as declared with MODULE_LICENSE()
9 *
10 * Driver modifiyed by Mauro Carvalho Chehab <mchehab@redhat.com> in order
11 * to work with upstream drxk driver, and to fix some bugs.
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation under version 2 of the License.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030021 */
22
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030023#include "drxk.h"
24#include "mt2063.h"
25#include "dvb_ca_en50221.h"
Mauro Carvalho Chehab70fa4442011-07-23 10:55:10 -030026
27#define DVB_USB_LOG_PREFIX "az6007"
Mauro Carvalho Chehabc108a5a2011-07-25 10:38:20 -030028#include "dvb-usb.h"
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030029
30/* debug */
31int dvb_usb_az6007_debug;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -030032module_param_named(debug, dvb_usb_az6007_debug, int, 0644);
33MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))."
34 DVB_USB_DEBUG_STATUS);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030035
Mauro Carvalho Chehab70fa4442011-07-23 10:55:10 -030036#define deb_info(args...) dprintk(dvb_usb_az6007_debug, 0x01, args)
37#define deb_xfer(args...) dprintk(dvb_usb_az6007_debug, 0x02, args)
38#define deb_rc(args...) dprintk(dvb_usb_az6007_debug, 0x04, args)
39#define deb_fe(args...) dprintk(dvb_usb_az6007_debug, 0x08, args)
40
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030041DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
42
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030043struct az6007_device_state {
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -030044 struct dvb_ca_en50221 ca;
45 struct mutex ca_mutex;
46 u8 power_state;
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030047
48 /* Due to DRX-K - probably need changes */
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -030049 int (*gate_ctrl) (struct dvb_frontend *, int);
50 struct semaphore pll_mutex;
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030051 bool dont_attach_fe1;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030052};
53
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -030054static struct drxk_config terratec_h7_drxk = {
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030055 .adr = 0x29,
56 .single_master = 1,
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -030057 .no_i2c_bridge = 0,
Mauro Carvalho Chehabda989e02011-07-24 09:25:39 -030058 .max_size = 64,
Mauro Carvalho Chehab81091142011-07-25 11:07:20 -030059 .microcode_name = "dvb-usb-terratec-h7-drxk.fw",
60 .parallel_ts = 1,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030061};
62
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030063static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
64{
65 struct dvb_usb_adapter *adap = fe->sec_priv;
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -030066 struct az6007_device_state *st;
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -030067 int status = 0;
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030068
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -030069 deb_info("%s: %s\n", __func__, enable ? "enable" : "disable");
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -030070
71 if (!adap)
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030072 return -EINVAL;
73
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -030074 st = adap->priv;
75
76 if (!st)
77 return -EINVAL;
78
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030079 if (enable) {
Mauro Carvalho Chehab22125012011-07-23 09:58:38 -030080#if 0
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030081 down(&st->pll_mutex);
Mauro Carvalho Chehab22125012011-07-23 09:58:38 -030082#endif
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030083 status = st->gate_ctrl(fe, 1);
84 } else {
Mauro Carvalho Chehab22125012011-07-23 09:58:38 -030085#if 0
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030086 status = st->gate_ctrl(fe, 0);
Mauro Carvalho Chehab22125012011-07-23 09:58:38 -030087#endif
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030088 up(&st->pll_mutex);
89 }
90 return status;
91}
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030092
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -030093static struct mt2063_config az6007_mt2063_config = {
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -030094 .tuner_address = 0x60,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030095 .refclock = 36125000,
96};
97
98/* check for mutex FIXME */
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -030099static int az6007_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value,
100 u16 index, u8 *b, int blen)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300101{
102 int ret = -1;
103
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300104 ret = usb_control_msg(d->udev,
105 usb_rcvctrlpipe(d->udev, 0),
106 req,
107 USB_TYPE_VENDOR | USB_DIR_IN,
108 value, index, b, blen, 5000);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300109
110 if (ret < 0) {
111 warn("usb in operation failed. (%d)", ret);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300112 return -EIO;
113 }
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300114
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300115 deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
116 index);
117 debug_dump(b, blen, deb_xfer);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300118
119 return ret;
120}
121
122static int az6007_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
123 u16 index, u8 *b, int blen)
124{
125 int ret;
126
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300127 deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
128 index);
129 debug_dump(b, blen, deb_xfer);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300130
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300131 if (blen > 64) {
Mauro Carvalho Chehabda989e02011-07-24 09:25:39 -0300132 err("az6007: tried to write %d bytes, but I2C max size is 64 bytes\n",
133 blen);
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300134 return -EOPNOTSUPP;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300135 }
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300136
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300137 ret = usb_control_msg(d->udev,
138 usb_sndctrlpipe(d->udev, 0),
139 req,
140 USB_TYPE_VENDOR | USB_DIR_OUT,
141 value, index, b, blen, 5000);
142 if (ret != blen) {
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300143 err("usb out operation failed. (%d)", ret);
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300144 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300145 }
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300146
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300147 return 0;
148}
149
150static int az6007_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
151{
152 return 0;
153}
154
155/* keys for the enclosed remote control */
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300156static struct rc_map_table rc_map_az6007_table[] = {
157 {0x0001, KEY_1},
158 {0x0002, KEY_2},
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300159};
160
161/* remote control stuff (does not work with my box) */
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300162static int az6007_rc_query(struct dvb_usb_device *d, u32 * event, int *state)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300163{
164 return 0;
165#if 0
166 u8 key[10];
167 int i;
168
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300169 /* remove the following return to enabled remote querying */
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300170
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300171 az6007_usb_in_op(d, READ_REMOTE_REQ, 0, 0, key, 10);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300172
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300173 deb_rc("remote query key: %x %d\n", key[1], key[1]);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300174
175 if (key[1] == 0x44) {
176 *state = REMOTE_NO_KEY_PRESSED;
177 return 0;
178 }
179
180 for (i = 0; i < ARRAY_SIZE(az6007_rc_keys); i++)
181 if (az6007_rc_keys[i].custom == key[1]) {
182 *state = REMOTE_KEY_PRESSED;
183 *event = az6007_rc_keys[i].event;
184 break;
185 }
186 return 0;
187#endif
188}
189
190/*
191int az6007_power_ctrl(struct dvb_usb_device *d, int onoff)
192{
193 u8 v = onoff;
194 return az6007_usb_out_op(d,0xBC,v,3,NULL,1);
195}
196*/
197
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300198static int az6007_read_mac_addr(struct dvb_usb_device *d, u8 mac[6])
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300199{
200 az6007_usb_in_op(d, 0xb7, 6, 0, &mac[0], 6);
201 return 0;
202}
203
Mauro Carvalho Chehab81091142011-07-25 11:07:20 -0300204#define AZ6007_POWER 0xbc
205#define FX2_SCON1 0xc0
206#define AZ6007_TS_THROUGH 0xc7
207
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300208static int az6007_frontend_poweron(struct dvb_usb_adapter *adap)
209{
Mauro Carvalho Chehab81091142011-07-25 11:07:20 -0300210 struct dvb_usb_device *d = adap->dev;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300211
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300212 deb_info("az6007_frontend_poweron adap=%p adap->dev=%p\n",
213 adap, adap->dev);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300214
Mauro Carvalho Chehab81091142011-07-25 11:07:20 -0300215 az6007_usb_out_op(d, AZ6007_POWER /* 0xbc */, 0, 2, NULL, 0);
216 msleep(150);
217 az6007_usb_out_op(d, AZ6007_POWER /* 0xbc */, 1, 4, NULL, 0);
218 msleep(100);
219 az6007_usb_out_op(d, AZ6007_POWER /* 0xbc */, 1, 3, NULL, 0);
220 msleep(100);
221 az6007_usb_out_op(d, AZ6007_POWER /* 0xbc */, 1, 4, NULL, 0);
222 msleep(100);
223 az6007_usb_out_op(d, FX2_SCON1 /* 0xc0 */, 0, 3, NULL, 0);
224 msleep (10);
225 az6007_usb_out_op(d, FX2_SCON1 /* 0xc0 */, 1, 3, NULL, 0);
226 msleep (10);
227 az6007_usb_out_op(d, AZ6007_POWER /* 0xbc */, 0, 0, NULL, 0);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300228
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300229 deb_info("az6007_frontend_poweron: OK\n");
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300230
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300231 return 0;
232}
233
234static int az6007_frontend_reset(struct dvb_usb_adapter *adap)
235{
236 int ret;
237 u8 req;
238 u16 value;
239 u16 index;
240 int blen;
241
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300242 deb_info("az6007_frontend_reset adap=%p adap->dev=%p\n", adap, adap->dev);
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300243
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300244 /* reset demodulator */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300245 req = 0xC0;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300246 value = 1; /* high */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300247 index = 3;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300248 blen = 0;
249 ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
250 if (ret != 0) {
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300251 err("az6007_frontend_reset failed 1 !!!");
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300252 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300253 }
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300254
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300255 req = 0xC0;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300256 value = 0; /* low */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300257 index = 3;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300258 blen = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300259 msleep_interruptible(200);
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300260 ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
261 if (ret != 0) {
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300262 err("az6007_frontend_reset failed 2 !!!");
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300263 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300264 }
265 msleep_interruptible(200);
266 req = 0xC0;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300267 value = 1; /* high */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300268 index = 3;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300269 blen = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300270
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300271 ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
272 if (ret != 0) {
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300273 err("az6007_frontend_reset failed 3 !!!");
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300274 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300275 }
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300276
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300277 msleep_interruptible(200);
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300278
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300279 deb_info("reset az6007 frontend\n");
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300280
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300281 return 0;
282}
283
284static int az6007_led_on_off(struct usb_interface *intf, int onoff)
285{
286 int ret = -1;
287 u8 req;
288 u16 value;
289 u16 index;
290 int blen;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300291 /* TS through */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300292 req = 0xBC;
293 value = onoff;
294 index = 0;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300295 blen = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300296
297 ret = usb_control_msg(interface_to_usbdev(intf),
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300298 usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
299 req,
300 USB_TYPE_VENDOR | USB_DIR_OUT,
301 value, index, NULL, blen, 2000);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300302
303 if (ret < 0) {
304 warn("usb in operation failed. (%d)", ret);
305 ret = -EIO;
306 } else
307 ret = 0;
308
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300309 deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
310 index);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300311
312 return ret;
313}
314
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300315static int az6007_frontend_attach(struct dvb_usb_adapter *adap)
316{
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300317 struct az6007_device_state *st = adap->priv;
318
319 int result;
320
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300321 BUG_ON(!st);
322
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300323 az6007_frontend_poweron(adap);
324 az6007_frontend_reset(adap);
325
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300326 info("az6007: attaching demod drxk");
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300327 adap->fe = dvb_attach(drxk_attach, &terratec_h7_drxk,
328 &adap->dev->i2c_adap, &adap->fe2);
329 if (!adap->fe) {
330 result = -EINVAL;
331 goto out_free;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300332 }
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300333
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300334 deb_info("Setting hacks\n");
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300335
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300336 /* FIXME: do we need a pll semaphore? */
337 adap->fe->sec_priv = adap;
338 sema_init(&st->pll_mutex, 1);
339 st->gate_ctrl = adap->fe->ops.i2c_gate_ctrl;
340 adap->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
341 adap->fe2->id = 1;
342
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300343 info("az6007: attaching tuner mt2063");
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300344 /* Attach mt2063 to DVB-C frontend */
345 if (adap->fe->ops.i2c_gate_ctrl)
346 adap->fe->ops.i2c_gate_ctrl(adap->fe, 1);
347 if (!dvb_attach(mt2063_attach, adap->fe, &az6007_mt2063_config,
348 &adap->dev->i2c_adap)) {
349 result = -EINVAL;
350
351 goto out_free;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300352 }
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300353 if (adap->fe->ops.i2c_gate_ctrl)
354 adap->fe->ops.i2c_gate_ctrl(adap->fe, 0);
355
356 /* Hack - needed due to drxk */
357 adap->fe2->tuner_priv = adap->fe->tuner_priv;
358 memcpy(&adap->fe2->ops.tuner_ops,
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300359 &adap->fe->ops.tuner_ops, sizeof(adap->fe->ops.tuner_ops));
Mauro Carvalho Chehabda989e02011-07-24 09:25:39 -0300360
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300361 return 0;
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300362
363out_free:
364 if (adap->fe)
365 dvb_frontend_detach(adap->fe);
366 adap->fe = NULL;
367 adap->fe2 = NULL;
368
369 return result;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300370}
371
Mauro Carvalho Chehab81091142011-07-25 11:07:20 -0300372int az6007_power_ctrl(struct dvb_usb_device *d, int onoff)
373{
374 if (!onoff)
375 return 0;
376
377
378 info("Sending poweron sequence");
379
380 az6007_usb_out_op(d, AZ6007_TS_THROUGH /* 0xc7 */, 0, 0, NULL, 0);
381
382#if 0
383 // Seems to be a poweroff sequence
384 az6007_usb_out_op(d, 0xbc, 1, 3, NULL, 0);
385 az6007_usb_out_op(d, 0xbc, 1, 4, NULL, 0);
386 az6007_usb_out_op(d, 0xc0, 0, 3, NULL, 0);
387 az6007_usb_out_op(d, 0xc0, 1, 3, NULL, 0);
388 az6007_usb_out_op(d, 0xbc, 0, 1, NULL, 0);
389#endif
390 return 0;
391}
392
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300393static struct dvb_usb_device_properties az6007_properties;
394
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300395static void az6007_usb_disconnect(struct usb_interface *intf)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300396{
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300397 dvb_usb_device_exit(intf);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300398}
399
400/* I2C */
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300401static int az6007_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
402 int num)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300403{
404 struct dvb_usb_device *d = i2c_get_adapdata(adap);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300405 int i, j, len;
406 int ret = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300407 u16 index;
408 u16 value;
409 int length;
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300410 u8 req, addr;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300411 u8 data[512];
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300412
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300413 if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
414 return -EAGAIN;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300415
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300416 for (i = 0; i < num; i++) {
417 addr = msgs[i].addr << 1;
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300418 if (((i + 1) < num)
419 && (msgs[i].len == 1)
420 && (!msgs[i].flags & I2C_M_RD)
421 && (msgs[i + 1].flags & I2C_M_RD)
422 && (msgs[i].addr == msgs[i + 1].addr)) {
423 /*
424 * A write + read xfer for the same address, where
425 * the first xfer has just 1 byte length.
426 * Need to join both into one operation
427 */
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300428 if (dvb_usb_az6007_debug & 2)
429 printk(KERN_DEBUG
430 "az6007 I2C xfer write+read addr=0x%x len=%d/%d: ",
431 addr, msgs[i].len, msgs[i + 1].len);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300432 req = 0xb9;
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300433 index = msgs[i].buf[0];
434 value = addr | (1 << 8);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300435 length = 6 + msgs[i + 1].len;
436 len = msgs[i + 1].len;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300437 ret = az6007_usb_in_op(d, req, value, index, data,
438 length);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300439 if (ret >= len) {
440 for (j = 0; j < len; j++) {
441 msgs[i + 1].buf[j] = data[j + 5];
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300442 if (dvb_usb_az6007_debug & 2)
443 printk(KERN_CONT
444 "0x%02x ",
445 msgs[i + 1].buf[j]);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300446 }
447 } else
448 ret = -EIO;
449 i++;
450 } else if (!(msgs[i].flags & I2C_M_RD)) {
451 /* write bytes */
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300452 if (dvb_usb_az6007_debug & 2)
453 printk(KERN_DEBUG
454 "az6007 I2C xfer write addr=0x%x len=%d: ",
455 addr, msgs[i].len);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300456 req = 0xbd;
457 index = msgs[i].buf[0];
458 value = addr | (1 << 8);
459 length = msgs[i].len - 1;
460 len = msgs[i].len - 1;
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300461 if (dvb_usb_az6007_debug & 2)
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300462 printk(KERN_CONT "(0x%02x) ", msgs[i].buf[0]);
463 for (j = 0; j < len; j++) {
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300464 data[j] = msgs[i].buf[j + 1];
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300465 if (dvb_usb_az6007_debug & 2)
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300466 printk(KERN_CONT "0x%02x ", data[j]);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300467 }
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300468 ret = az6007_usb_out_op(d, req, value, index, data,
469 length);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300470 } else {
471 /* read bytes */
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300472 if (dvb_usb_az6007_debug & 2)
473 printk(KERN_DEBUG
474 "az6007 I2C xfer read addr=0x%x len=%d: ",
475 addr, msgs[i].len);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300476 req = 0xb9;
477 index = msgs[i].buf[0];
478 value = addr;
479 length = msgs[i].len + 6;
480 len = msgs[i].len;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300481 ret = az6007_usb_in_op(d, req, value, index, data,
482 length);
483 for (j = 0; j < len; j++) {
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300484 msgs[i].buf[j] = data[j + 5];
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300485 if (dvb_usb_az6007_debug & 2)
486 printk(KERN_CONT
487 "0x%02x ", data[j + 5]);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300488 }
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300489 }
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300490 if (dvb_usb_az6007_debug & 2)
491 printk(KERN_CONT "\n");
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300492 if (ret < 0)
493 goto err;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300494 }
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300495err:
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300496 mutex_unlock(&d->i2c_mutex);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300497
498 if (ret < 0) {
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300499 info("%s ERROR: %i", __func__, ret);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300500 return ret;
501 }
502 return num;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300503}
504
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300505static u32 az6007_i2c_func(struct i2c_adapter *adapter)
506{
507 return I2C_FUNC_I2C;
508}
509
510static struct i2c_algorithm az6007_i2c_algo = {
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300511 .master_xfer = az6007_i2c_xfer,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300512 .functionality = az6007_i2c_func,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300513};
514
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300515int az6007_identify_state(struct usb_device *udev,
516 struct dvb_usb_device_properties *props,
517 struct dvb_usb_device_description **desc, int *cold)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300518{
519 u8 b[16];
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300520 s16 ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
521 0xb7, USB_TYPE_VENDOR | USB_DIR_IN, 6, 0, b,
522 6, USB_CTRL_GET_TIMEOUT);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300523
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300524 deb_info("FW GET_VERSION length: %d\n", ret);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300525
526 *cold = ret <= 0;
527
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300528 deb_info("cold: %d\n", *cold);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300529 return 0;
530}
531
532static int az6007_usb_probe(struct usb_interface *intf,
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300533 const struct usb_device_id *id)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300534{
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300535 az6007_led_on_off(intf, 0);
536
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300537 return dvb_usb_device_init(intf, &az6007_properties,
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300538 THIS_MODULE, NULL, adapter_nr);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300539}
540
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300541static struct usb_device_id az6007_usb_table[] = {
542 {USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_6007)},
543 {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7)},
544 {0},
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300545};
546
547MODULE_DEVICE_TABLE(usb, az6007_usb_table);
548
549static struct dvb_usb_device_properties az6007_properties = {
550 .caps = DVB_USB_IS_AN_I2C_ADAPTER,
551 .usb_ctrl = CYPRESS_FX2,
Mauro Carvalho Chehab81091142011-07-25 11:07:20 -0300552 .firmware = "dvb-usb-terratec-h7-az6007.fw",
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300553 .no_reconnect = 1,
554
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300555 .identify_state = az6007_identify_state,
556 .num_adapters = 1,
557 .adapter = {
558 {
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300559 .streaming_ctrl = az6007_streaming_ctrl,
560 .frontend_attach = az6007_frontend_attach,
561
562 /* parameter for the MPEG2-data transfer */
563 .stream = {
564 .type = USB_BULK,
565 .count = 10,
566 .endpoint = 0x02,
567 .u = {
568 .bulk = {
569 .buffersize = 4096,
570 }
571 }
572 },
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300573 .size_of_priv = sizeof(struct az6007_device_state),
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300574 }
575 },
Mauro Carvalho Chehab81091142011-07-25 11:07:20 -0300576 .power_ctrl = az6007_power_ctrl,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300577 .read_mac_address = az6007_read_mac_addr,
578
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300579 .rc.legacy = {
580 .rc_map_table = rc_map_az6007_table,
581 .rc_map_size = ARRAY_SIZE(rc_map_az6007_table),
582 .rc_interval = 400,
583 .rc_query = az6007_rc_query,
584 },
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300585 .i2c_algo = &az6007_i2c_algo,
586
587 .num_device_descs = 2,
588 .devices = {
589 { .name = "AzureWave DTV StarBox DVB-T/C USB2.0 (az6007)",
590 .cold_ids = { &az6007_usb_table[0], NULL },
591 .warm_ids = { NULL },
592 },
593 { .name = "TerraTec DTV StarBox DVB-T/C USB2.0 (az6007)",
594 .cold_ids = { &az6007_usb_table[1], NULL },
595 .warm_ids = { NULL },
596 },
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300597 { NULL },
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300598 }
599};
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300600
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300601/* usb specific object needed to register this driver with the usb subsystem */
602static struct usb_driver az6007_usb_driver = {
603 .name = "dvb_usb_az6007",
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300604 .probe = az6007_usb_probe,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300605 .disconnect = dvb_usb_device_exit,
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300606 /* .disconnect = az6007_usb_disconnect, */
607 .id_table = az6007_usb_table,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300608};
609
610/* module stuff */
611static int __init az6007_usb_module_init(void)
612{
613 int result;
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300614 deb_info("az6007 usb module init\n");
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300615
616 result = usb_register(&az6007_usb_driver);
617 if (result) {
618 err("usb_register failed. (%d)", result);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300619 return result;
620 }
621
622 return 0;
623}
624
625static void __exit az6007_usb_module_exit(void)
626{
627 /* deregister this driver from the USB subsystem */
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300628 deb_info("az6007 usb module exit\n");
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300629 usb_deregister(&az6007_usb_driver);
630}
631
632module_init(az6007_usb_module_init);
633module_exit(az6007_usb_module_exit);
634
635MODULE_AUTHOR("Henry Wang <Henry.wang@AzureWave.com>");
636MODULE_DESCRIPTION("Driver for AzureWave 6007 DVB-C/T USB2.0 and clones");
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300637MODULE_VERSION("1.1");
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300638MODULE_LICENSE("GPL");