blob: 492181d8de870773ece785fd81031638edc75742 [file] [log] [blame]
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -03001/* tuner-xc2028
2 *
3 * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org)
Mauro Carvalho Chehab983d2142007-10-29 23:44:18 -03004 *
Michel Ludwig701672e2007-07-18 10:29:10 -03005 * Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com)
6 * - frontend interface
Mauro Carvalho Chehab983d2142007-10-29 23:44:18 -03007 *
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -03008 * This code is placed under the terms of the GNU General Public License v2
9 */
10
11#include <linux/i2c.h>
12#include <asm/div64.h>
13#include <linux/firmware.h>
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -030014#include <linux/videodev2.h>
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030015#include <linux/delay.h>
Michel Ludwig701672e2007-07-18 10:29:10 -030016#include <media/tuner.h>
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -030017#include <linux/mutex.h>
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030018#include "tuner-i2c.h"
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030019#include "tuner-xc2028.h"
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -030020#include "tuner-xc2028-types.h"
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030021
Michel Ludwig701672e2007-07-18 10:29:10 -030022#include <linux/dvb/frontend.h>
23#include "dvb_frontend.h"
24
Hans Verkuil9dd659d2007-11-04 11:03:36 -030025#define PREFIX "xc2028"
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030026
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -030027static int debug;
28module_param(debug, int, 0644);
29MODULE_PARM_DESC(debug, "enable verbose debug messages");
30
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030031static LIST_HEAD(xc2028_list);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -030032/* struct for storing firmware table */
33struct firmware_description {
34 unsigned int type;
35 v4l2_std_id id;
36 unsigned char *ptr;
37 unsigned int size;
38};
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030039
40struct xc2028_data {
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030041 struct list_head xc2028_list;
42 struct tuner_i2c_props i2c_props;
43 int (*tuner_callback) (void *dev,
44 int command, int arg);
45 struct device *dev;
46 void *video_dev;
47 int count;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -030048 __u32 frequency;
49
50 struct firmware_description *firm;
51 int firm_size;
52
53 __u16 version;
54
55 struct xc2028_ctrl ctrl;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030056
Michel Ludwig701672e2007-07-18 10:29:10 -030057 v4l2_std_id firm_type; /* video stds supported
58 by current firmware */
59 fe_bandwidth_t bandwidth; /* Firmware bandwidth:
60 6M, 7M or 8M */
61 int need_load_generic; /* The generic firmware
62 were loaded? */
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -030063
64 int max_len; /* Max firmware chunk */
65
Michel Ludwig701672e2007-07-18 10:29:10 -030066 enum tuner_mode mode;
67 struct i2c_client *i2c_client;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030068
69 struct mutex lock;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030070};
71
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -030072#define i2c_send(rc, priv, buf, size) do { \
73 rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size); \
74 if (size != rc) \
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -030075 tuner_err("i2c output error: rc = %d (should be %d)\n", \
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -030076 rc, (int)size); \
77} while (0)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030078
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -030079#define i2c_rcv(rc, priv, buf, size) do { \
80 rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, size); \
81 if (size != rc) \
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -030082 tuner_err("i2c input error: rc = %d (should be %d)\n", \
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -030083 rc, (int)size); \
84} while (0)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030085
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -030086#define send_seq(priv, data...) do { \
87 int rc; \
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030088 static u8 _val[] = data; \
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030089 if (sizeof(_val) != \
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -030090 (rc = tuner_i2c_xfer_send(&priv->i2c_props, \
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030091 _val, sizeof(_val)))) { \
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -030092 tuner_err("Error on line %d: %d\n", __LINE__, rc); \
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -030093 return -EINVAL; \
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030094 } \
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -030095 msleep(10); \
96} while (0)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030097
Mauro Carvalho Chehab80b52202007-11-05 09:07:13 -030098static unsigned int xc2028_get_reg(struct xc2028_data *priv, u16 reg)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030099{
100 int rc;
Mauro Carvalho Chehabb873e1a2007-11-05 08:41:50 -0300101 unsigned char buf[2];
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300102
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300103 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300104
Mauro Carvalho Chehab80b52202007-11-05 09:07:13 -0300105 buf[0] = reg>>8;
106 buf[1] = (unsigned char) reg;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300107
Mauro Carvalho Chehab80b52202007-11-05 09:07:13 -0300108 i2c_send(rc, priv, buf, 2);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300109 if (rc < 0)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300110 return rc;
111
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300112 i2c_rcv(rc, priv, buf, 2);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300113 if (rc < 0)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300114 return rc;
115
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300116 return (buf[1]) | (buf[0] << 8);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300117}
118
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300119void dump_firm_type(unsigned int type)
120{
121 if (type & BASE)
122 printk("BASE ");
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300123 if (type & INIT1)
124 printk("INIT1 ");
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300125 if (type & F8MHZ)
126 printk("F8MHZ ");
127 if (type & MTS)
128 printk("MTS ");
129 if (type & D2620)
130 printk("D2620 ");
131 if (type & D2633)
132 printk("D2633 ");
133 if (type & DTV6)
134 printk("DTV6 ");
135 if (type & QAM)
136 printk("QAM ");
137 if (type & DTV7)
138 printk("DTV7 ");
139 if (type & DTV78)
140 printk("DTV78 ");
141 if (type & DTV8)
142 printk("DTV8 ");
143 if (type & FM)
144 printk("FM ");
145 if (type & INPUT1)
146 printk("INPUT1 ");
147 if (type & LCD)
148 printk("LCD ");
149 if (type & NOGD)
150 printk("NOGD ");
151 if (type & MONO)
152 printk("MONO ");
153 if (type & ATSC)
154 printk("ATSC ");
155 if (type & IF)
156 printk("IF ");
157 if (type & LG60)
158 printk("LG60 ");
159 if (type & ATI638)
160 printk("ATI638 ");
161 if (type & OREN538)
162 printk("OREN538 ");
163 if (type & OREN36)
164 printk("OREN36 ");
165 if (type & TOYOTA388)
166 printk("TOYOTA388 ");
167 if (type & TOYOTA794)
168 printk("TOYOTA794 ");
169 if (type & DIBCOM52)
170 printk("DIBCOM52 ");
171 if (type & ZARLINK456)
172 printk("ZARLINK456 ");
173 if (type & CHINA)
174 printk("CHINA ");
175 if (type & F6MHZ)
176 printk("F6MHZ ");
177 if (type & INPUT2)
178 printk("INPUT2 ");
179 if (type & SCODE)
180 printk("SCODE ");
181}
182
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300183static void free_firmware(struct xc2028_data *priv)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300184{
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300185 int i;
186
187 if (!priv->firm)
188 return;
189
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300190 for (i = 0; i < priv->firm_size; i++)
191 kfree(priv->firm[i].ptr);
192
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300193 kfree(priv->firm);
194
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300195 priv->firm = NULL;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300196 priv->need_load_generic = 1;
197}
198
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300199static int load_all_firmwares(struct dvb_frontend *fe)
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300200{
201 struct xc2028_data *priv = fe->tuner_priv;
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300202 const struct firmware *fw = NULL;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300203 unsigned char *p, *endp;
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300204 int rc = 0;
205 int n, n_array;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300206 char name[33];
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300207
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300208 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300209
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300210 tuner_info("Reading firmware %s\n", priv->ctrl.fname);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300211 rc = request_firmware(&fw, priv->ctrl.fname, priv->dev);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300212 if (rc < 0) {
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300213 if (rc == -ENOENT)
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300214 tuner_err("Error: firmware %s not found.\n",
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300215 priv->ctrl.fname);
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300216 else
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300217 tuner_err("Error %d while requesting firmware %s \n",
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300218 rc, priv->ctrl.fname);
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300219
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300220 return rc;
221 }
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300222 p = fw->data;
223 endp = p + fw->size;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300224
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300225 if (fw->size < sizeof(name) - 1 + 2) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300226 tuner_err("Error: firmware size is zero!\n");
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300227 rc = -EINVAL;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300228 goto done;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300229 }
230
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300231 memcpy(name, p, sizeof(name) - 1);
232 name[sizeof(name) - 1] = 0;
233 p += sizeof(name) - 1;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300234
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300235 priv->version = le16_to_cpu(*(__u16 *) p);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300236 p += 2;
237
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300238 tuner_info("Firmware: %s, ver %d.%d\n", name,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300239 priv->version >> 8, priv->version & 0xff);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300240
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300241 if (p + 2 > endp)
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300242 goto corrupt;
243
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300244 n_array = le16_to_cpu(*(__u16 *) p);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300245 p += 2;
246
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300247 tuner_info("There are %d firmwares at %s\n",
248 n_array, priv->ctrl.fname);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300249
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300250 priv->firm = kzalloc(sizeof(*priv->firm) * n_array, GFP_KERNEL);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300251
252 if (!fw) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300253 tuner_err("Not enough memory for reading firmware.\n");
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300254 rc = -ENOMEM;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300255 goto done;
256 }
257
258 priv->firm_size = n_array;
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300259 n = -1;
260 while (p < endp) {
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300261 __u32 type, size;
262 v4l2_std_id id;
263
264 n++;
265 if (n >= n_array) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300266 tuner_err("Too much firmwares at the file\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300267 goto corrupt;
268 }
269
270 /* Checks if there's enough bytes to read */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300271 if (p + sizeof(type) + sizeof(id) + sizeof(size) > endp) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300272 tuner_err("Firmware header is incomplete!\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300273 goto corrupt;
274 }
275
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300276 type = le32_to_cpu(*(__u32 *) p);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300277 p += sizeof(type);
278
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300279 id = le64_to_cpu(*(v4l2_std_id *) p);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300280 p += sizeof(id);
281
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300282 size = le32_to_cpu(*(v4l2_std_id *) p);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300283 p += sizeof(size);
284
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300285 if ((!size) || (size + p > endp)) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300286 tuner_err("Firmware type ");
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300287 dump_firm_type(type);
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300288 printk("(%x), id %lx is corrupted "
289 "(size=%ld, expected %d)\n",
290 type, (unsigned long)id, endp - p, size);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300291 goto corrupt;
292 }
293
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300294 priv->firm[n].ptr = kzalloc(size, GFP_KERNEL);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300295 if (!priv->firm[n].ptr) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300296 tuner_err("Not enough memory.\n");
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300297 rc = -ENOMEM;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300298 goto err;
299 }
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300300 tuner_info("Reading firmware type ");
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300301 dump_firm_type(type);
302 printk("(%x), id %lx, size=%d.\n",
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300303 type, (unsigned long)id, size);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300304
305 memcpy(priv->firm[n].ptr, p, size);
306 priv->firm[n].type = type;
307 priv->firm[n].id = id;
308 priv->firm[n].size = size;
309
310 p += size;
311 }
312
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300313 if (n + 1 != priv->firm_size) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300314 tuner_err("Firmware file is incomplete!\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300315 goto corrupt;
316 }
317
318 goto done;
319
320corrupt:
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300321 rc = -EINVAL;
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300322 tuner_err("Error: firmware file is corrupted!\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300323
324err:
325 tuner_info("Releasing loaded firmware file.\n");
326
327 free_firmware(priv);
328
329done:
330 release_firmware(fw);
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300331 tuner_dbg("Firmware files loaded.\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300332
333 return rc;
334}
335
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300336static int seek_firmware(struct dvb_frontend *fe, unsigned int type,
337 v4l2_std_id *id)
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300338{
339 struct xc2028_data *priv = fe->tuner_priv;
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300340 int i;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300341
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300342 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300343
344 if (!priv->firm) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300345 tuner_err("Error! firmware not loaded\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300346 return -EINVAL;
347 }
348
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300349 if (((type & ~SCODE) == 0) && (*id == 0))
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300350 *id = V4L2_STD_PAL;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300351
352 /* Seek for exact match */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300353 for (i = 0; i < priv->firm_size; i++) {
354 if ((type == priv->firm[i].type) && (*id == priv->firm[i].id))
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300355 goto found;
356 }
357
358 /* Seek for generic video standard match */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300359 for (i = 0; i < priv->firm_size; i++) {
360 if ((type == priv->firm[i].type) && (*id & priv->firm[i].id))
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300361 goto found;
362 }
363
364 /*FIXME: Would make sense to seek for type "hint" match ? */
365
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300366 i = -EINVAL;
367 goto ret;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300368
369found:
370 *id = priv->firm[i].id;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300371
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300372ret:
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300373 tuner_dbg("%s firmware for type=", (i < 0)? "Can't find": "Found");
374 if (debug) {
375 dump_firm_type(type);
376 printk("(%x), id %08lx.\n", type, (unsigned long)*id);
377 }
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300378 return i;
379}
380
381static int load_firmware(struct dvb_frontend *fe, unsigned int type,
382 v4l2_std_id *id)
383{
384 struct xc2028_data *priv = fe->tuner_priv;
385 int pos, rc;
386 unsigned char *p, *endp, buf[priv->max_len];
387
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300388 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300389
390 pos = seek_firmware(fe, type, id);
391 if (pos < 0)
392 return pos;
393
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300394 tuner_info("Loading firmware for type=");
395 dump_firm_type(type);
396 printk("(%x), id %08lx.\n", type, (unsigned long)*id);
397
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300398 p = priv->firm[pos].ptr;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300399
400 if (!p) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300401 tuner_err("Firmware pointer were freed!");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300402 return -EINVAL;
403 }
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300404 endp = p + priv->firm[pos].size;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300405
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300406 while (p < endp) {
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300407 __u16 size;
408
409 /* Checks if there's enough bytes to read */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300410 if (p + sizeof(size) > endp) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300411 tuner_err("Firmware chunk size is wrong\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300412 return -EINVAL;
413 }
414
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300415 size = le16_to_cpu(*(__u16 *) p);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300416 p += sizeof(size);
417
418 if (size == 0xffff)
419 return 0;
420
421 if (!size) {
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300422 /* Special callback command received */
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300423 rc = priv->tuner_callback(priv->video_dev,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300424 XC2028_TUNER_RESET, 0);
425 if (rc < 0) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300426 tuner_err("Error at RESET code %d\n",
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300427 (*p) & 0x7f);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300428 return -EINVAL;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300429 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300430 continue;
431 }
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300432
433 /* Checks for a sleep command */
434 if (size & 0x8000) {
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300435 msleep(size & 0x7fff);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300436 continue;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300437 }
438
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300439 if ((size + p > endp)) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300440 tuner_err("missing bytes: need %d, have %d\n",
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300441 size, (int)(endp - p));
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300442 return -EINVAL;
443 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300444
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300445 buf[0] = *p;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300446 p++;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300447 size--;
448
449 /* Sends message chunks */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300450 while (size > 0) {
451 int len = (size < priv->max_len - 1) ?
452 size : priv->max_len - 1;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300453
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300454 memcpy(buf + 1, p, len);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300455
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300456 i2c_send(rc, priv, buf, len + 1);
457 if (rc < 0) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300458 tuner_err("%d returned from send\n", rc);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300459 return -EINVAL;
460 }
461
462 p += len;
463 size -= len;
464 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300465 }
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300466 return 0;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300467}
468
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300469static int load_scode(struct dvb_frontend *fe, unsigned int type,
470 v4l2_std_id *id, int scode)
471{
472 struct xc2028_data *priv = fe->tuner_priv;
473 int pos, rc;
474 unsigned char *p;
475
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300476 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300477
478 pos = seek_firmware(fe, type, id);
479 if (pos < 0)
480 return pos;
481
482 p = priv->firm[pos].ptr;
483
484 if (!p) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300485 tuner_err("Firmware pointer were freed!");
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300486 return -EINVAL;
487 }
488
489 if ((priv->firm[pos].size != 12 * 16) || (scode >= 16))
490 return -EINVAL;
491
492 if (priv->version < 0x0202) {
493 send_seq(priv, {0x20, 0x00, 0x00, 0x00});
494 } else {
495 send_seq(priv, {0xa0, 0x00, 0x00, 0x00});
496 }
497
498 i2c_send(rc, priv, p + 12 * scode, 12);
499
500 send_seq(priv, {0x00, 0x8c});
501
502 return 0;
503}
504
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300505static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300506 v4l2_std_id std, fe_bandwidth_t bandwidth)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300507{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300508 struct xc2028_data *priv = fe->tuner_priv;
Mauro Carvalho Chehab80b52202007-11-05 09:07:13 -0300509 int rc, version, hwmodel;
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300510 v4l2_std_id std0 = 0;
511 unsigned int type0 = 0, type = 0;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300512 int change_digital_bandwidth;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300513
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300514 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300515
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300516 if (!priv->firm) {
517 if (!priv->ctrl.fname)
518 return -EINVAL;
519
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300520 rc = load_all_firmwares(fe);
521 if (rc < 0)
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300522 return rc;
523 }
524
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300525 tuner_dbg("I am in mode %u and I should switch to mode %i\n",
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300526 priv->mode, new_mode);
Michel Ludwig701672e2007-07-18 10:29:10 -0300527
528 /* first of all, determine whether we have switched the mode */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300529 if (new_mode != priv->mode) {
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300530 priv->mode = new_mode;
531 priv->need_load_generic = 1;
Michel Ludwig701672e2007-07-18 10:29:10 -0300532 }
533
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300534 change_digital_bandwidth = (priv->mode == T_DIGITAL_TV
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300535 && bandwidth != priv->bandwidth) ? 1 : 0;
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300536 tuner_dbg("old bandwidth %u, new bandwidth %u\n", priv->bandwidth,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300537 bandwidth);
Michel Ludwig701672e2007-07-18 10:29:10 -0300538
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300539 if (priv->need_load_generic) {
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300540 /* Reset is needed before loading firmware */
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300541 rc = priv->tuner_callback(priv->video_dev,
542 XC2028_TUNER_RESET, 0);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300543 if (rc < 0)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300544 return rc;
545
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300546 type0 = BASE;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300547
548 if (priv->ctrl.type == XC2028_FIRM_MTS)
549 type0 |= MTS;
550
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300551 if (priv->bandwidth == 8)
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300552 type0 |= F8MHZ;
553
554 /* FIXME: How to load FM and FM|INPUT1 firmwares? */
555
556 rc = load_firmware(fe, type0, &std0);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300557 if (rc < 0) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300558 tuner_err("Error %d while loading generic firmware\n",
559 rc);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300560 return rc;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300561 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300562
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300563 priv->need_load_generic = 0;
564 priv->firm_type = 0;
565 if (priv->mode == T_DIGITAL_TV)
566 change_digital_bandwidth = 1;
Michel Ludwig701672e2007-07-18 10:29:10 -0300567 }
568
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300569 tuner_dbg("I should change bandwidth %u\n", change_digital_bandwidth);
Michel Ludwig701672e2007-07-18 10:29:10 -0300570
571 if (change_digital_bandwidth) {
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300572
573 /*FIXME: Should allow selecting between D2620 and D2633 */
574 type |= D2620;
575
576 /* FIXME: When should select a DTV78 firmware?
577 */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300578 switch (bandwidth) {
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300579 case BANDWIDTH_8_MHZ:
580 type |= DTV8;
581 break;
582 case BANDWIDTH_7_MHZ:
583 type |= DTV7;
584 break;
585 case BANDWIDTH_6_MHZ:
586 /* FIXME: Should allow select also ATSC */
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300587 type |= DTV6 | QAM;
Michel Ludwig701672e2007-07-18 10:29:10 -0300588 break;
589
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300590 default:
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300591 tuner_err("error: bandwidth not supported.\n");
Michel Ludwig701672e2007-07-18 10:29:10 -0300592 };
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300593 priv->bandwidth = bandwidth;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300594 }
595
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300596 /* Load INIT1, if needed */
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300597 tuner_dbg("Load init1 firmware, if exists\n");
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300598 type0 = BASE | INIT1;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300599 if (priv->ctrl.type == XC2028_FIRM_MTS)
600 type0 |= MTS;
601
602 /* FIXME: Should handle errors - if INIT1 found */
603 rc = load_firmware(fe, type0, &std0);
604
605 /* FIXME: Should add support for FM radio
606 */
607
608 if (priv->ctrl.type == XC2028_FIRM_MTS)
609 type |= MTS;
610
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300611 if (priv->firm_type & std) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300612 tuner_dbg("Std-specific firmware already loaded.\n");
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300613 return 0;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300614 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300615
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300616 rc = load_firmware(fe, type, &std);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300617 if (rc < 0)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300618 return rc;
619
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300620 /* Load SCODE firmware, if exists */
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300621 tuner_dbg("Trying to load scode 0\n");
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300622 type |= SCODE;
623
624 rc = load_scode(fe, type, &std, 0);
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300625
Mauro Carvalho Chehab80b52202007-11-05 09:07:13 -0300626 version = xc2028_get_reg(priv, 0x0004);
627 hwmodel = xc2028_get_reg(priv, 0x0008);
628
629 tuner_info("Device is Xceive %d version %d.%d, "
630 "firmware version %d.%d\n",
631 hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8,
632 (version & 0xf0) >> 4, version & 0xf);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300633
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300634 priv->firm_type = std;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300635
636 return 0;
637}
638
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300639static int xc2028_signal(struct dvb_frontend *fe, u16 *strength)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300640{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300641 struct xc2028_data *priv = fe->tuner_priv;
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300642 int frq_lock, signal = 0;
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300643
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300644 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300645
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300646 mutex_lock(&priv->lock);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300647
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300648 *strength = 0;
649
Mauro Carvalho Chehab80b52202007-11-05 09:07:13 -0300650 /* Sync Lock Indicator */
651 frq_lock = xc2028_get_reg(priv, 0x0002);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300652 if (frq_lock <= 0)
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300653 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300654
655 /* Frequency is locked. Return signal quality */
656
Mauro Carvalho Chehab80b52202007-11-05 09:07:13 -0300657 /* Get SNR of the video signal */
658 signal = xc2028_get_reg(priv, 0x0040);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300659
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300660 if (signal <= 0)
661 signal = frq_lock;
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300662
663ret:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300664 mutex_unlock(&priv->lock);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300665
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300666 *strength = signal;
667
668 return 0;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300669}
670
671#define DIV 15625
672
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300673static int generic_set_tv_freq(struct dvb_frontend *fe, u32 freq /* in Hz */ ,
674 enum tuner_mode new_mode,
675 v4l2_std_id std, fe_bandwidth_t bandwidth)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300676{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300677 struct xc2028_data *priv = fe->tuner_priv;
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300678 int rc = -EINVAL;
679 unsigned char buf[5];
680 u32 div, offset = 0;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300681
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300682 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300683
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300684 mutex_lock(&priv->lock);
685
Mauro Carvalho Chehabd4e76682007-07-18 23:14:25 -0300686 /* HACK: It seems that specific firmware need to be reloaded
687 when freq is changed */
Michel Ludwig701672e2007-07-18 10:29:10 -0300688
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300689 priv->firm_type = 0;
Michel Ludwig701672e2007-07-18 10:29:10 -0300690
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300691 /* Reset GPIO 1 */
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300692 rc = priv->tuner_callback(priv->video_dev, XC2028_TUNER_RESET, 0);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300693 if (rc < 0)
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300694 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300695
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300696 msleep(10);
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300697 tuner_dbg("should set frequency %d kHz)\n", freq / 1000);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300698
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300699 if (check_firmware(fe, new_mode, std, bandwidth) < 0)
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300700 goto ret;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300701
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300702 if (new_mode == T_DIGITAL_TV)
Mauro Carvalho Chehabd4e76682007-07-18 23:14:25 -0300703 offset = 2750000;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300704
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300705 div = (freq - offset + DIV / 2) / DIV;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300706
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300707 /* CMD= Set frequency */
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300708
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300709 if (priv->version < 0x0202) {
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300710 send_seq(priv, {0x00, 0x02, 0x00, 0x00});
711 } else {
712 send_seq(priv, {0x80, 0x02, 0x00, 0x00});
713 }
714
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300715 rc = priv->tuner_callback(priv->video_dev, XC2028_RESET_CLK, 1);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300716 if (rc < 0)
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300717 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300718
719 msleep(10);
Michel Ludwig701672e2007-07-18 10:29:10 -0300720
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300721 buf[0] = 0xff & (div >> 24);
722 buf[1] = 0xff & (div >> 16);
723 buf[2] = 0xff & (div >> 8);
724 buf[3] = 0xff & (div);
725 buf[4] = 0;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300726
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300727 i2c_send(rc, priv, buf, sizeof(buf));
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300728 if (rc < 0)
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300729 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300730 msleep(100);
731
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300732 priv->frequency = freq;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300733
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300734 tuner_dbg("divider= %02x %02x %02x %02x (freq=%d.%02d)\n",
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300735 buf[1], buf[2], buf[3], buf[4],
736 freq / 1000000, (freq % 1000000) / 10000);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300737
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300738 rc = 0;
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300739
740ret:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300741 mutex_unlock(&priv->lock);
742
743 return rc;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300744}
745
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300746static int xc2028_set_tv_freq(struct dvb_frontend *fe,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300747 struct analog_parameters *p)
Michel Ludwig701672e2007-07-18 10:29:10 -0300748{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300749 struct xc2028_data *priv = fe->tuner_priv;
Michel Ludwig701672e2007-07-18 10:29:10 -0300750
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300751 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300752
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300753 return generic_set_tv_freq(fe, 62500l * p->frequency, T_ANALOG_TV,
754 p->std, BANDWIDTH_8_MHZ /* NOT USED */);
Michel Ludwig701672e2007-07-18 10:29:10 -0300755}
756
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300757static int xc2028_set_params(struct dvb_frontend *fe,
Michel Ludwig701672e2007-07-18 10:29:10 -0300758 struct dvb_frontend_parameters *p)
759{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300760 struct xc2028_data *priv = fe->tuner_priv;
Michel Ludwig701672e2007-07-18 10:29:10 -0300761
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300762 tuner_dbg("%s called\n", __FUNCTION__);
Michel Ludwig701672e2007-07-18 10:29:10 -0300763
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300764 /* FIXME: Only OFDM implemented */
765 if (fe->ops.info.type != FE_OFDM) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300766 tuner_err("DTV type not implemented.\n");
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300767 return -EINVAL;
768 }
Michel Ludwig701672e2007-07-18 10:29:10 -0300769
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300770 return generic_set_tv_freq(fe, p->frequency, T_DIGITAL_TV,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300771 0 /* NOT USED */,
772 p->u.ofdm.bandwidth);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300773
Michel Ludwig701672e2007-07-18 10:29:10 -0300774}
775
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300776static int xc2028_dvb_release(struct dvb_frontend *fe)
Michel Ludwig701672e2007-07-18 10:29:10 -0300777{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300778 struct xc2028_data *priv = fe->tuner_priv;
Michel Ludwig701672e2007-07-18 10:29:10 -0300779
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300780 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300781
782 priv->count--;
783
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300784 if (!priv->count) {
Mauro Carvalho Chehab1808a692007-10-29 17:38:59 -0300785 list_del(&priv->xc2028_list);
786
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300787 kfree(priv->ctrl.fname);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300788
789 free_firmware(priv);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300790 kfree(priv);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300791 }
Michel Ludwig701672e2007-07-18 10:29:10 -0300792
793 return 0;
794}
795
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300796static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency)
Michel Ludwig701672e2007-07-18 10:29:10 -0300797{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300798 struct xc2028_data *priv = fe->tuner_priv;
799
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300800 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300801
802 *frequency = priv->frequency;
Michel Ludwig701672e2007-07-18 10:29:10 -0300803
804 return 0;
805}
806
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300807static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300808{
809 struct xc2028_data *priv = fe->tuner_priv;
810 struct xc2028_ctrl *p = priv_cfg;
811
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300812 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300813
814 priv->ctrl.type = p->type;
815
816 if (p->fname) {
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300817 kfree(priv->ctrl.fname);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300818
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300819 priv->ctrl.fname = kmalloc(strlen(p->fname) + 1, GFP_KERNEL);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300820 if (!priv->ctrl.fname)
821 return -ENOMEM;
822
823 free_firmware(priv);
824 strcpy(priv->ctrl.fname, p->fname);
825 }
826
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300827 if (p->max_len > 0)
Mauro Carvalho Chehab352fae12007-11-01 16:56:26 -0300828 priv->max_len = p->max_len;
829
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300830 return 0;
831}
832
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300833static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
Michel Ludwig701672e2007-07-18 10:29:10 -0300834 .info = {
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300835 .name = "Xceive XC3028",
836 .frequency_min = 42000000,
837 .frequency_max = 864000000,
838 .frequency_step = 50000,
839 },
Michel Ludwig701672e2007-07-18 10:29:10 -0300840
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300841 .set_config = xc2028_set_config,
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300842 .set_analog_params = xc2028_set_tv_freq,
843 .release = xc2028_dvb_release,
844 .get_frequency = xc2028_get_frequency,
845 .get_rf_strength = xc2028_signal,
846 .set_params = xc2028_set_params,
Michel Ludwig701672e2007-07-18 10:29:10 -0300847
Michel Ludwig701672e2007-07-18 10:29:10 -0300848};
849
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300850int xc2028_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c_adap,
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300851 u8 i2c_addr, struct device *dev, void *video_dev,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300852 int (*tuner_callback) (void *dev, int command, int arg))
Michel Ludwig701672e2007-07-18 10:29:10 -0300853{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300854 struct xc2028_data *priv;
Michel Ludwig701672e2007-07-18 10:29:10 -0300855
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300856 if (debug)
857 printk(KERN_DEBUG PREFIX "Xcv2028/3028 init called!\n");
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300858
859 if (NULL == dev)
860 return -ENODEV;
861
862 if (NULL == video_dev)
863 return -ENODEV;
864
865 if (!tuner_callback) {
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300866 printk(KERN_ERR PREFIX "No tuner callback!\n");
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300867 return -EINVAL;
868 }
869
870 list_for_each_entry(priv, &xc2028_list, xc2028_list) {
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300871 if (priv->dev == dev)
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300872 dev = NULL;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300873 }
874
875 if (dev) {
876 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
877 if (priv == NULL)
878 return -ENOMEM;
879
880 fe->tuner_priv = priv;
881
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300882 priv->bandwidth = BANDWIDTH_6_MHZ;
883 priv->need_load_generic = 1;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300884 priv->mode = T_UNINITIALIZED;
885 priv->i2c_props.addr = i2c_addr;
886 priv->i2c_props.adap = i2c_adap;
887 priv->dev = dev;
888 priv->video_dev = video_dev;
889 priv->tuner_callback = tuner_callback;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300890 priv->max_len = 13;
891
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300892
893 mutex_init(&priv->lock);
894
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300895 list_add_tail(&priv->xc2028_list, &xc2028_list);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300896 }
Mauro Carvalho Chehab1808a692007-10-29 17:38:59 -0300897 priv->count++;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300898
899 memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300900 sizeof(xc2028_dvb_tuner_ops));
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300901
902 tuner_info("type set to %s\n", "XCeive xc2028/xc3028 tuner");
Michel Ludwig701672e2007-07-18 10:29:10 -0300903
904 return 0;
905}
Michel Ludwig701672e2007-07-18 10:29:10 -0300906EXPORT_SYMBOL(xc2028_attach);
907
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300908MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver");
Mauro Carvalho Chehab983d2142007-10-29 23:44:18 -0300909MODULE_AUTHOR("Michel Ludwig <michel.ludwig@gmail.com>");
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300910MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
911MODULE_LICENSE("GPL");