blob: 523972f9ef56eafa739557b75bd8d4bdecb63d37 [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#include "dvb-usb.h"
27
28#define DVB_USB_LOG_PREFIX "az6007"
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,
59// .microcode_name = "dvb-usb-terratec-h5-drxk.fw",
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030060};
61
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030062static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
63{
64 struct dvb_usb_adapter *adap = fe->sec_priv;
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -030065 struct az6007_device_state *st;
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -030066 int status = 0;
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030067
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -030068 deb_info("%s: %s\n", __func__, enable ? "enable" : "disable");
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -030069
70 if (!adap)
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030071 return -EINVAL;
72
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -030073 st = adap->priv;
74
75 if (!st)
76 return -EINVAL;
77
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030078 if (enable) {
Mauro Carvalho Chehab22125012011-07-23 09:58:38 -030079#if 0
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030080 down(&st->pll_mutex);
Mauro Carvalho Chehab22125012011-07-23 09:58:38 -030081#endif
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030082 status = st->gate_ctrl(fe, 1);
83 } else {
Mauro Carvalho Chehab22125012011-07-23 09:58:38 -030084#if 0
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030085 status = st->gate_ctrl(fe, 0);
Mauro Carvalho Chehab22125012011-07-23 09:58:38 -030086#endif
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -030087 up(&st->pll_mutex);
88 }
89 return status;
90}
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030091
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -030092static struct mt2063_config az6007_mt2063_config = {
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -030093 .tuner_address = 0x60,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -030094 .refclock = 36125000,
95};
96
97/* check for mutex FIXME */
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -030098static int az6007_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value,
99 u16 index, u8 *b, int blen)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300100{
101 int ret = -1;
102
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300103 ret = usb_control_msg(d->udev,
104 usb_rcvctrlpipe(d->udev, 0),
105 req,
106 USB_TYPE_VENDOR | USB_DIR_IN,
107 value, index, b, blen, 5000);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300108
109 if (ret < 0) {
110 warn("usb in operation failed. (%d)", ret);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300111 return -EIO;
112 }
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300113
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300114 deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
115 index);
116 debug_dump(b, blen, deb_xfer);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300117
118 return ret;
119}
120
121static int az6007_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value,
122 u16 index, u8 *b, int blen)
123{
124 int ret;
125
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300126 deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
127 index);
128 debug_dump(b, blen, deb_xfer);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300129
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300130 if (blen > 64) {
Mauro Carvalho Chehabda989e02011-07-24 09:25:39 -0300131 err("az6007: tried to write %d bytes, but I2C max size is 64 bytes\n",
132 blen);
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300133 return -EOPNOTSUPP;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300134 }
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300135
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300136 ret = usb_control_msg(d->udev,
137 usb_sndctrlpipe(d->udev, 0),
138 req,
139 USB_TYPE_VENDOR | USB_DIR_OUT,
140 value, index, b, blen, 5000);
141 if (ret != blen) {
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300142 err("usb out operation failed. (%d)", ret);
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300143 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300144 }
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300145
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300146 return 0;
147}
148
149static int az6007_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
150{
151 return 0;
152}
153
154/* keys for the enclosed remote control */
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300155static struct rc_map_table rc_map_az6007_table[] = {
156 {0x0001, KEY_1},
157 {0x0002, KEY_2},
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300158};
159
160/* remote control stuff (does not work with my box) */
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300161static int az6007_rc_query(struct dvb_usb_device *d, u32 * event, int *state)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300162{
163 return 0;
164#if 0
165 u8 key[10];
166 int i;
167
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300168 /* remove the following return to enabled remote querying */
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300169
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300170 az6007_usb_in_op(d, READ_REMOTE_REQ, 0, 0, key, 10);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300171
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300172 deb_rc("remote query key: %x %d\n", key[1], key[1]);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300173
174 if (key[1] == 0x44) {
175 *state = REMOTE_NO_KEY_PRESSED;
176 return 0;
177 }
178
179 for (i = 0; i < ARRAY_SIZE(az6007_rc_keys); i++)
180 if (az6007_rc_keys[i].custom == key[1]) {
181 *state = REMOTE_KEY_PRESSED;
182 *event = az6007_rc_keys[i].event;
183 break;
184 }
185 return 0;
186#endif
187}
188
189/*
190int az6007_power_ctrl(struct dvb_usb_device *d, int onoff)
191{
192 u8 v = onoff;
193 return az6007_usb_out_op(d,0xBC,v,3,NULL,1);
194}
195*/
196
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300197static int az6007_read_mac_addr(struct dvb_usb_device *d, u8 mac[6])
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300198{
199 az6007_usb_in_op(d, 0xb7, 6, 0, &mac[0], 6);
200 return 0;
201}
202
203static int az6007_frontend_poweron(struct dvb_usb_adapter *adap)
204{
205 int ret;
206 u8 req;
207 u16 value;
208 u16 index;
209 int blen;
210
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300211 deb_info("az6007_frontend_poweron adap=%p adap->dev=%p\n",
212 adap, adap->dev);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300213
214 req = 0xBC;
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300215 value = 1; /* power on */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300216 index = 3;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300217 blen = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300218
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300219 ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
220 if (ret != 0) {
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300221 err("az6007_frontend_poweron failed!!!");
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300222 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300223 }
224
225 msleep_interruptible(200);
226
227 req = 0xBC;
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300228 value = 0; /* power off */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300229 index = 3;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300230 blen = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300231
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300232 ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
233 if (ret != 0) {
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300234 err("az6007_frontend_poweron failed!!!");
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300235 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300236 }
237
238 msleep_interruptible(200);
239
240 req = 0xBC;
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300241 value = 1; /* power on */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300242 index = 3;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300243 blen = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300244
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300245 ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
246 if (ret != 0) {
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300247 err("az6007_frontend_poweron failed!!!");
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300248 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300249 }
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300250 deb_info("az6007_frontend_poweron: OK\n");
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300251
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300252 return 0;
253}
254
255static int az6007_frontend_reset(struct dvb_usb_adapter *adap)
256{
257 int ret;
258 u8 req;
259 u16 value;
260 u16 index;
261 int blen;
262
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300263 deb_info("az6007_frontend_reset adap=%p adap->dev=%p\n", adap, adap->dev);
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300264
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300265 /* reset demodulator */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300266 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;
270 ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
271 if (ret != 0) {
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300272 err("az6007_frontend_reset failed 1 !!!");
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300273 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300274 }
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300275
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300276 req = 0xC0;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300277 value = 0; /* low */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300278 index = 3;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300279 blen = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300280 msleep_interruptible(200);
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300281 ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
282 if (ret != 0) {
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300283 err("az6007_frontend_reset failed 2 !!!");
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300284 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300285 }
286 msleep_interruptible(200);
287 req = 0xC0;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300288 value = 1; /* high */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300289 index = 3;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300290 blen = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300291
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300292 ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
293 if (ret != 0) {
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300294 err("az6007_frontend_reset failed 3 !!!");
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300295 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300296 }
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300297
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300298 msleep_interruptible(200);
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300299
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300300 deb_info("reset az6007 frontend\n");
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300301
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300302 return 0;
303}
304
305static int az6007_led_on_off(struct usb_interface *intf, int onoff)
306{
307 int ret = -1;
308 u8 req;
309 u16 value;
310 u16 index;
311 int blen;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300312 /* TS through */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300313 req = 0xBC;
314 value = onoff;
315 index = 0;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300316 blen = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300317
318 ret = usb_control_msg(interface_to_usbdev(intf),
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300319 usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
320 req,
321 USB_TYPE_VENDOR | USB_DIR_OUT,
322 value, index, NULL, blen, 2000);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300323
324 if (ret < 0) {
325 warn("usb in operation failed. (%d)", ret);
326 ret = -EIO;
327 } else
328 ret = 0;
329
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300330 deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
331 index);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300332
333 return ret;
334}
335
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300336static int az6007_frontend_tsbypass(struct dvb_usb_adapter *adap, int onoff)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300337{
338 int ret;
339 u8 req;
340 u16 value;
341 u16 index;
342 int blen;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300343 /* TS through */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300344 req = 0xC7;
345 value = onoff;
346 index = 0;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300347 blen = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300348
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300349 ret = az6007_usb_out_op(adap->dev, req, value, index, NULL, blen);
350 if (ret != 0)
351 return -EIO;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300352 return 0;
353}
354
355static int az6007_frontend_attach(struct dvb_usb_adapter *adap)
356{
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300357 struct az6007_device_state *st = adap->priv;
358
359 int result;
360
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300361 BUG_ON(!st);
362
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300363 az6007_frontend_poweron(adap);
364 az6007_frontend_reset(adap);
365
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300366 info("az6007: attaching demod drxk");
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300367 adap->fe = dvb_attach(drxk_attach, &terratec_h7_drxk,
368 &adap->dev->i2c_adap, &adap->fe2);
369 if (!adap->fe) {
370 result = -EINVAL;
371 goto out_free;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300372 }
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300373
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300374 deb_info("Setting hacks\n");
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300375
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300376 /* FIXME: do we need a pll semaphore? */
377 adap->fe->sec_priv = adap;
378 sema_init(&st->pll_mutex, 1);
379 st->gate_ctrl = adap->fe->ops.i2c_gate_ctrl;
380 adap->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
381 adap->fe2->id = 1;
382
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300383 info("az6007: attaching tuner mt2063");
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300384 /* Attach mt2063 to DVB-C frontend */
385 if (adap->fe->ops.i2c_gate_ctrl)
386 adap->fe->ops.i2c_gate_ctrl(adap->fe, 1);
387 if (!dvb_attach(mt2063_attach, adap->fe, &az6007_mt2063_config,
388 &adap->dev->i2c_adap)) {
389 result = -EINVAL;
390
391 goto out_free;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300392 }
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300393 if (adap->fe->ops.i2c_gate_ctrl)
394 adap->fe->ops.i2c_gate_ctrl(adap->fe, 0);
395
396 /* Hack - needed due to drxk */
397 adap->fe2->tuner_priv = adap->fe->tuner_priv;
398 memcpy(&adap->fe2->ops.tuner_ops,
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300399 &adap->fe->ops.tuner_ops, sizeof(adap->fe->ops.tuner_ops));
Mauro Carvalho Chehabda989e02011-07-24 09:25:39 -0300400
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300401 return 0;
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300402
403out_free:
404 if (adap->fe)
405 dvb_frontend_detach(adap->fe);
406 adap->fe = NULL;
407 adap->fe2 = NULL;
408
409 return result;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300410}
411
412static struct dvb_usb_device_properties az6007_properties;
413
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300414static void az6007_usb_disconnect(struct usb_interface *intf)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300415{
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300416 dvb_usb_device_exit(intf);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300417}
418
419/* I2C */
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300420static int az6007_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
421 int num)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300422{
423 struct dvb_usb_device *d = i2c_get_adapdata(adap);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300424 int i, j, len;
425 int ret = 0;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300426 u16 index;
427 u16 value;
428 int length;
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300429 u8 req, addr;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300430 u8 data[512];
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300431
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300432 if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
433 return -EAGAIN;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300434
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300435 for (i = 0; i < num; i++) {
436 addr = msgs[i].addr << 1;
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300437 if (((i + 1) < num)
438 && (msgs[i].len == 1)
439 && (!msgs[i].flags & I2C_M_RD)
440 && (msgs[i + 1].flags & I2C_M_RD)
441 && (msgs[i].addr == msgs[i + 1].addr)) {
442 /*
443 * A write + read xfer for the same address, where
444 * the first xfer has just 1 byte length.
445 * Need to join both into one operation
446 */
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300447 if (dvb_usb_az6007_debug & 2)
448 printk(KERN_DEBUG
449 "az6007 I2C xfer write+read addr=0x%x len=%d/%d: ",
450 addr, msgs[i].len, msgs[i + 1].len);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300451 req = 0xb9;
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300452 index = msgs[i].buf[0];
453 value = addr | (1 << 8);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300454 length = 6 + msgs[i + 1].len;
455 len = msgs[i + 1].len;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300456 ret = az6007_usb_in_op(d, req, value, index, data,
457 length);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300458 if (ret >= len) {
459 for (j = 0; j < len; j++) {
460 msgs[i + 1].buf[j] = data[j + 5];
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300461 if (dvb_usb_az6007_debug & 2)
462 printk(KERN_CONT
463 "0x%02x ",
464 msgs[i + 1].buf[j]);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300465 }
466 } else
467 ret = -EIO;
468 i++;
469 } else if (!(msgs[i].flags & I2C_M_RD)) {
470 /* write bytes */
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300471 if (dvb_usb_az6007_debug & 2)
472 printk(KERN_DEBUG
473 "az6007 I2C xfer write addr=0x%x len=%d: ",
474 addr, msgs[i].len);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300475 req = 0xbd;
476 index = msgs[i].buf[0];
477 value = addr | (1 << 8);
478 length = msgs[i].len - 1;
479 len = msgs[i].len - 1;
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300480 if (dvb_usb_az6007_debug & 2)
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300481 printk(KERN_CONT "(0x%02x) ", msgs[i].buf[0]);
482 for (j = 0; j < len; j++) {
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300483 data[j] = msgs[i].buf[j + 1];
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300484 if (dvb_usb_az6007_debug & 2)
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300485 printk(KERN_CONT "0x%02x ", data[j]);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300486 }
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300487 ret = az6007_usb_out_op(d, req, value, index, data,
488 length);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300489 } else {
490 /* read bytes */
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300491 if (dvb_usb_az6007_debug & 2)
492 printk(KERN_DEBUG
493 "az6007 I2C xfer read addr=0x%x len=%d: ",
494 addr, msgs[i].len);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300495 req = 0xb9;
496 index = msgs[i].buf[0];
497 value = addr;
498 length = msgs[i].len + 6;
499 len = msgs[i].len;
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300500 ret = az6007_usb_in_op(d, req, value, index, data,
501 length);
502 for (j = 0; j < len; j++) {
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300503 msgs[i].buf[j] = data[j + 5];
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300504 if (dvb_usb_az6007_debug & 2)
505 printk(KERN_CONT
506 "0x%02x ", data[j + 5]);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300507 }
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300508 }
Mauro Carvalho Chehabd20a7f72011-07-23 09:51:12 -0300509 if (dvb_usb_az6007_debug & 2)
510 printk(KERN_CONT "\n");
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300511 if (ret < 0)
512 goto err;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300513 }
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300514err:
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300515 mutex_unlock(&d->i2c_mutex);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300516
517 if (ret < 0) {
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300518 info("%s ERROR: %i", __func__, ret);
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300519 return ret;
520 }
521 return num;
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300522}
523
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300524static u32 az6007_i2c_func(struct i2c_adapter *adapter)
525{
526 return I2C_FUNC_I2C;
527}
528
529static struct i2c_algorithm az6007_i2c_algo = {
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300530 .master_xfer = az6007_i2c_xfer,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300531 .functionality = az6007_i2c_func,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300532};
533
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300534int az6007_identify_state(struct usb_device *udev,
535 struct dvb_usb_device_properties *props,
536 struct dvb_usb_device_description **desc, int *cold)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300537{
538 u8 b[16];
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300539 s16 ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
540 0xb7, USB_TYPE_VENDOR | USB_DIR_IN, 6, 0, b,
541 6, USB_CTRL_GET_TIMEOUT);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300542
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300543 deb_info("FW GET_VERSION length: %d\n", ret);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300544
545 *cold = ret <= 0;
546
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300547 deb_info("cold: %d\n", *cold);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300548 return 0;
549}
550
551static int az6007_usb_probe(struct usb_interface *intf,
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300552 const struct usb_device_id *id)
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300553{
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300554 az6007_led_on_off(intf, 0);
555
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300556 return dvb_usb_device_init(intf, &az6007_properties,
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300557 THIS_MODULE, NULL, adapter_nr);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300558}
559
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300560static struct usb_device_id az6007_usb_table[] = {
561 {USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_6007)},
562 {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7)},
563 {0},
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300564};
565
566MODULE_DEVICE_TABLE(usb, az6007_usb_table);
567
568static struct dvb_usb_device_properties az6007_properties = {
569 .caps = DVB_USB_IS_AN_I2C_ADAPTER,
570 .usb_ctrl = CYPRESS_FX2,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300571 .firmware = "dvb-usb-az6007-03.fw",
572 .no_reconnect = 1,
573
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300574 .identify_state = az6007_identify_state,
575 .num_adapters = 1,
576 .adapter = {
577 {
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300578 .streaming_ctrl = az6007_streaming_ctrl,
579 .frontend_attach = az6007_frontend_attach,
580
581 /* parameter for the MPEG2-data transfer */
582 .stream = {
583 .type = USB_BULK,
584 .count = 10,
585 .endpoint = 0x02,
586 .u = {
587 .bulk = {
588 .buffersize = 4096,
589 }
590 }
591 },
Mauro Carvalho Chehabcaa1a702011-07-22 10:31:25 -0300592 .size_of_priv = sizeof(struct az6007_device_state),
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300593 }
594 },
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300595 /* .power_ctrl = az6007_power_ctrl, */
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300596 .read_mac_address = az6007_read_mac_addr,
597
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300598 .rc.legacy = {
599 .rc_map_table = rc_map_az6007_table,
600 .rc_map_size = ARRAY_SIZE(rc_map_az6007_table),
601 .rc_interval = 400,
602 .rc_query = az6007_rc_query,
603 },
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300604 .i2c_algo = &az6007_i2c_algo,
605
606 .num_device_descs = 2,
607 .devices = {
608 { .name = "AzureWave DTV StarBox DVB-T/C USB2.0 (az6007)",
609 .cold_ids = { &az6007_usb_table[0], NULL },
610 .warm_ids = { NULL },
611 },
612 { .name = "TerraTec DTV StarBox DVB-T/C USB2.0 (az6007)",
613 .cold_ids = { &az6007_usb_table[1], NULL },
614 .warm_ids = { NULL },
615 },
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300616 { NULL },
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300617 }
618};
Mauro Carvalho Chehab6da34702011-07-21 18:31:14 -0300619
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300620/* usb specific object needed to register this driver with the usb subsystem */
621static struct usb_driver az6007_usb_driver = {
622 .name = "dvb_usb_az6007",
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300623 .probe = az6007_usb_probe,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300624 .disconnect = dvb_usb_device_exit,
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300625 /* .disconnect = az6007_usb_disconnect, */
626 .id_table = az6007_usb_table,
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300627};
628
629/* module stuff */
630static int __init az6007_usb_module_init(void)
631{
632 int result;
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300633 deb_info("az6007 usb module init\n");
Mauro Carvalho Chehab93b32122011-07-23 10:40:08 -0300634
635 result = usb_register(&az6007_usb_driver);
636 if (result) {
637 err("usb_register failed. (%d)", result);
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300638 return result;
639 }
640
641 return 0;
642}
643
644static void __exit az6007_usb_module_exit(void)
645{
646 /* deregister this driver from the USB subsystem */
Mauro Carvalho Chehabf2ba9e52011-07-23 11:54:40 -0300647 deb_info("az6007 usb module exit\n");
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300648 usb_deregister(&az6007_usb_driver);
649}
650
651module_init(az6007_usb_module_init);
652module_exit(az6007_usb_module_exit);
653
654MODULE_AUTHOR("Henry Wang <Henry.wang@AzureWave.com>");
655MODULE_DESCRIPTION("Driver for AzureWave 6007 DVB-C/T USB2.0 and clones");
Mauro Carvalho Chehab35753582011-07-23 10:12:12 -0300656MODULE_VERSION("1.1");
Mauro Carvalho Chehab71d67632011-07-21 17:46:41 -0300657MODULE_LICENSE("GPL");