blob: d23b33a1f696b3df21b3a8672f43c5a0b53a3725 [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
Mauro Carvalho Chehabef8c1882007-11-16 16:28:21 -030025
Hans Verkuil9dd659d2007-11-04 11:03:36 -030026#define PREFIX "xc2028"
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030027
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -030028static int debug;
29module_param(debug, int, 0644);
30MODULE_PARM_DESC(debug, "enable verbose debug messages");
31
Mauro Carvalho Chehaba82200f2007-11-15 11:58:00 -030032static char audio_std[8];
33module_param_string(audio_std, audio_std, sizeof(audio_std), 0);
34MODULE_PARM_DESC(audio_std,
35 "Audio standard. XC3028 audio decoder explicitly "
36 "needs to know what audio\n"
37 "standard is needed for some video standards with audio A2 or NICAM.\n"
38 "The valid values are:\n"
39 "A2\n"
40 "A2/A\n"
41 "A2/B\n"
42 "NICAM\n"
43 "NICAM/A\n"
44 "NICAM/B\n");
45
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030046static LIST_HEAD(xc2028_list);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -030047/* struct for storing firmware table */
48struct firmware_description {
49 unsigned int type;
50 v4l2_std_id id;
51 unsigned char *ptr;
52 unsigned int size;
53};
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030054
55struct xc2028_data {
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030056 struct list_head xc2028_list;
57 struct tuner_i2c_props i2c_props;
58 int (*tuner_callback) (void *dev,
59 int command, int arg);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030060 void *video_dev;
61 int count;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -030062 __u32 frequency;
63
64 struct firmware_description *firm;
65 int firm_size;
66
67 __u16 version;
68
69 struct xc2028_ctrl ctrl;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030070
Michel Ludwig701672e2007-07-18 10:29:10 -030071 v4l2_std_id firm_type; /* video stds supported
72 by current firmware */
73 fe_bandwidth_t bandwidth; /* Firmware bandwidth:
74 6M, 7M or 8M */
75 int need_load_generic; /* The generic firmware
76 were loaded? */
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -030077
78 int max_len; /* Max firmware chunk */
79
Michel Ludwig701672e2007-07-18 10:29:10 -030080 enum tuner_mode mode;
81 struct i2c_client *i2c_client;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030082
83 struct mutex lock;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030084};
85
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -030086#define i2c_send(rc, priv, buf, size) do { \
87 rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size); \
88 if (size != rc) \
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -030089 tuner_err("i2c output error: rc = %d (should be %d)\n", \
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -030090 rc, (int)size); \
91} while (0)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030092
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -030093#define i2c_rcv(rc, priv, buf, size) do { \
94 rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, size); \
95 if (size != rc) \
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -030096 tuner_err("i2c input error: rc = %d (should be %d)\n", \
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -030097 rc, (int)size); \
98} while (0)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030099
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300100#define send_seq(priv, data...) do { \
101 int rc; \
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300102 static u8 _val[] = data; \
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300103 if (sizeof(_val) != \
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300104 (rc = tuner_i2c_xfer_send(&priv->i2c_props, \
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300105 _val, sizeof(_val)))) { \
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300106 tuner_err("Error on line %d: %d\n", __LINE__, rc); \
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300107 return -EINVAL; \
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300108 } \
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300109 msleep(10); \
110} while (0)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300111
Mauro Carvalho Chehab80b52202007-11-05 09:07:13 -0300112static unsigned int xc2028_get_reg(struct xc2028_data *priv, u16 reg)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300113{
114 int rc;
Mauro Carvalho Chehabb873e1a2007-11-05 08:41:50 -0300115 unsigned char buf[2];
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300116
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300117 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300118
Mauro Carvalho Chehab80b52202007-11-05 09:07:13 -0300119 buf[0] = reg>>8;
120 buf[1] = (unsigned char) reg;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300121
Mauro Carvalho Chehab80b52202007-11-05 09:07:13 -0300122 i2c_send(rc, priv, buf, 2);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300123 if (rc < 0)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300124 return rc;
125
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300126 i2c_rcv(rc, priv, buf, 2);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300127 if (rc < 0)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300128 return rc;
129
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300130 return (buf[1]) | (buf[0] << 8);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300131}
132
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300133void dump_firm_type(unsigned int type)
134{
135 if (type & BASE)
136 printk("BASE ");
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300137 if (type & INIT1)
138 printk("INIT1 ");
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300139 if (type & F8MHZ)
140 printk("F8MHZ ");
141 if (type & MTS)
142 printk("MTS ");
143 if (type & D2620)
144 printk("D2620 ");
145 if (type & D2633)
146 printk("D2633 ");
147 if (type & DTV6)
148 printk("DTV6 ");
149 if (type & QAM)
150 printk("QAM ");
151 if (type & DTV7)
152 printk("DTV7 ");
153 if (type & DTV78)
154 printk("DTV78 ");
155 if (type & DTV8)
156 printk("DTV8 ");
157 if (type & FM)
158 printk("FM ");
159 if (type & INPUT1)
160 printk("INPUT1 ");
161 if (type & LCD)
162 printk("LCD ");
163 if (type & NOGD)
164 printk("NOGD ");
165 if (type & MONO)
166 printk("MONO ");
167 if (type & ATSC)
168 printk("ATSC ");
169 if (type & IF)
170 printk("IF ");
171 if (type & LG60)
172 printk("LG60 ");
173 if (type & ATI638)
174 printk("ATI638 ");
175 if (type & OREN538)
176 printk("OREN538 ");
177 if (type & OREN36)
178 printk("OREN36 ");
179 if (type & TOYOTA388)
180 printk("TOYOTA388 ");
181 if (type & TOYOTA794)
182 printk("TOYOTA794 ");
183 if (type & DIBCOM52)
184 printk("DIBCOM52 ");
185 if (type & ZARLINK456)
186 printk("ZARLINK456 ");
187 if (type & CHINA)
188 printk("CHINA ");
189 if (type & F6MHZ)
190 printk("F6MHZ ");
191 if (type & INPUT2)
192 printk("INPUT2 ");
193 if (type & SCODE)
194 printk("SCODE ");
195}
196
Mauro Carvalho Chehabef8c1882007-11-16 16:28:21 -0300197static v4l2_std_id parse_audio_std_option(void)
Mauro Carvalho Chehaba82200f2007-11-15 11:58:00 -0300198{
199 if (strcasecmp(audio_std, "A2"))
200 return V4L2_STD_A2;
201 if (strcasecmp(audio_std, "A2/A"))
202 return V4L2_STD_A2_A;
203 if (strcasecmp(audio_std, "A2/B"))
204 return V4L2_STD_A2_B;
205 if (strcasecmp(audio_std, "NICAM"))
206 return V4L2_STD_NICAM;
207 if (strcasecmp(audio_std, "NICAM/A"))
208 return V4L2_STD_NICAM_A;
209 if (strcasecmp(audio_std, "NICAM/B"))
210 return V4L2_STD_NICAM_B;
211
212 return 0;
213}
214
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300215static void free_firmware(struct xc2028_data *priv)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300216{
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300217 int i;
218
219 if (!priv->firm)
220 return;
221
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300222 for (i = 0; i < priv->firm_size; i++)
223 kfree(priv->firm[i].ptr);
224
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300225 kfree(priv->firm);
226
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300227 priv->firm = NULL;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300228 priv->need_load_generic = 1;
229}
230
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300231static int load_all_firmwares(struct dvb_frontend *fe)
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300232{
233 struct xc2028_data *priv = fe->tuner_priv;
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300234 const struct firmware *fw = NULL;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300235 unsigned char *p, *endp;
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300236 int rc = 0;
237 int n, n_array;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300238 char name[33];
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300239
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300240 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300241
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300242 tuner_info("Reading firmware %s\n", priv->ctrl.fname);
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300243 rc = request_firmware(&fw, priv->ctrl.fname,
244 &priv->i2c_props.adap->dev);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300245 if (rc < 0) {
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300246 if (rc == -ENOENT)
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300247 tuner_err("Error: firmware %s not found.\n",
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300248 priv->ctrl.fname);
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300249 else
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300250 tuner_err("Error %d while requesting firmware %s \n",
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300251 rc, priv->ctrl.fname);
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300252
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300253 return rc;
254 }
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300255 p = fw->data;
256 endp = p + fw->size;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300257
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300258 if (fw->size < sizeof(name) - 1 + 2) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300259 tuner_err("Error: firmware size is zero!\n");
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300260 rc = -EINVAL;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300261 goto done;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300262 }
263
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300264 memcpy(name, p, sizeof(name) - 1);
265 name[sizeof(name) - 1] = 0;
266 p += sizeof(name) - 1;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300267
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300268 priv->version = le16_to_cpu(*(__u16 *) p);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300269 p += 2;
270
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300271 tuner_info("Firmware: %s, ver %d.%d\n", name,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300272 priv->version >> 8, priv->version & 0xff);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300273
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300274 if (p + 2 > endp)
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300275 goto corrupt;
276
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300277 n_array = le16_to_cpu(*(__u16 *) p);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300278 p += 2;
279
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300280 tuner_info("There are %d firmwares at %s\n",
281 n_array, priv->ctrl.fname);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300282
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300283 priv->firm = kzalloc(sizeof(*priv->firm) * n_array, GFP_KERNEL);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300284
285 if (!fw) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300286 tuner_err("Not enough memory for reading firmware.\n");
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300287 rc = -ENOMEM;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300288 goto done;
289 }
290
291 priv->firm_size = n_array;
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300292 n = -1;
293 while (p < endp) {
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300294 __u32 type, size;
295 v4l2_std_id id;
296
297 n++;
298 if (n >= n_array) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300299 tuner_err("Too much firmwares at the file\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300300 goto corrupt;
301 }
302
303 /* Checks if there's enough bytes to read */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300304 if (p + sizeof(type) + sizeof(id) + sizeof(size) > endp) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300305 tuner_err("Firmware header is incomplete!\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300306 goto corrupt;
307 }
308
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300309 type = le32_to_cpu(*(__u32 *) p);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300310 p += sizeof(type);
311
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300312 id = le64_to_cpu(*(v4l2_std_id *) p);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300313 p += sizeof(id);
314
Michel Ludwig2fc580f2007-11-16 07:19:35 -0300315 size = le32_to_cpu(*(__u32 *) p);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300316 p += sizeof(size);
317
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300318 if ((!size) || (size + p > endp)) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300319 tuner_err("Firmware type ");
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300320 dump_firm_type(type);
Mauro Carvalho Chehabef8c1882007-11-16 16:28:21 -0300321 printk("(%x), id %llx is corrupted "
322 "(size=%d, expected %d)\n",
323 type, id,
324 (unsigned)(endp - p), size);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300325 goto corrupt;
326 }
327
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300328 priv->firm[n].ptr = kzalloc(size, GFP_KERNEL);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300329 if (!priv->firm[n].ptr) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300330 tuner_err("Not enough memory.\n");
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300331 rc = -ENOMEM;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300332 goto err;
333 }
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300334 tuner_info("Reading firmware type ");
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300335 dump_firm_type(type);
336 printk("(%x), id %lx, size=%d.\n",
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300337 type, (unsigned long)id, size);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300338
339 memcpy(priv->firm[n].ptr, p, size);
340 priv->firm[n].type = type;
341 priv->firm[n].id = id;
342 priv->firm[n].size = size;
343
344 p += size;
345 }
346
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300347 if (n + 1 != priv->firm_size) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300348 tuner_err("Firmware file is incomplete!\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300349 goto corrupt;
350 }
351
352 goto done;
353
354corrupt:
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300355 rc = -EINVAL;
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300356 tuner_err("Error: firmware file is corrupted!\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300357
358err:
359 tuner_info("Releasing loaded firmware file.\n");
360
361 free_firmware(priv);
362
363done:
364 release_firmware(fw);
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300365 tuner_dbg("Firmware files loaded.\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300366
367 return rc;
368}
369
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300370static int seek_firmware(struct dvb_frontend *fe, unsigned int type,
371 v4l2_std_id *id)
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300372{
373 struct xc2028_data *priv = fe->tuner_priv;
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300374 int i;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300375
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300376 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300377
378 if (!priv->firm) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300379 tuner_err("Error! firmware not loaded\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300380 return -EINVAL;
381 }
382
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300383 if (((type & ~SCODE) == 0) && (*id == 0))
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300384 *id = V4L2_STD_PAL;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300385
386 /* Seek for exact match */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300387 for (i = 0; i < priv->firm_size; i++) {
388 if ((type == priv->firm[i].type) && (*id == priv->firm[i].id))
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300389 goto found;
390 }
391
392 /* Seek for generic video standard match */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300393 for (i = 0; i < priv->firm_size; i++) {
394 if ((type == priv->firm[i].type) && (*id & priv->firm[i].id))
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300395 goto found;
396 }
397
398 /*FIXME: Would make sense to seek for type "hint" match ? */
399
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300400 i = -EINVAL;
401 goto ret;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300402
403found:
404 *id = priv->firm[i].id;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300405
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300406ret:
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300407 tuner_dbg("%s firmware for type=", (i < 0)? "Can't find": "Found");
408 if (debug) {
409 dump_firm_type(type);
410 printk("(%x), id %08lx.\n", type, (unsigned long)*id);
411 }
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300412 return i;
413}
414
415static int load_firmware(struct dvb_frontend *fe, unsigned int type,
416 v4l2_std_id *id)
417{
418 struct xc2028_data *priv = fe->tuner_priv;
419 int pos, rc;
420 unsigned char *p, *endp, buf[priv->max_len];
421
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300422 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300423
424 pos = seek_firmware(fe, type, id);
425 if (pos < 0)
426 return pos;
427
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300428 tuner_info("Loading firmware for type=");
429 dump_firm_type(type);
430 printk("(%x), id %08lx.\n", type, (unsigned long)*id);
431
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300432 p = priv->firm[pos].ptr;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300433
434 if (!p) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300435 tuner_err("Firmware pointer were freed!");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300436 return -EINVAL;
437 }
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300438 endp = p + priv->firm[pos].size;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300439
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300440 while (p < endp) {
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300441 __u16 size;
442
443 /* Checks if there's enough bytes to read */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300444 if (p + sizeof(size) > endp) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300445 tuner_err("Firmware chunk size is wrong\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300446 return -EINVAL;
447 }
448
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300449 size = le16_to_cpu(*(__u16 *) p);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300450 p += sizeof(size);
451
452 if (size == 0xffff)
453 return 0;
454
455 if (!size) {
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300456 /* Special callback command received */
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300457 rc = priv->tuner_callback(priv->video_dev,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300458 XC2028_TUNER_RESET, 0);
459 if (rc < 0) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300460 tuner_err("Error at RESET code %d\n",
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300461 (*p) & 0x7f);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300462 return -EINVAL;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300463 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300464 continue;
465 }
Michel Ludwig5403bba2007-11-16 07:49:49 -0300466 if (size >= 0xff00) {
467 switch (size) {
468 case 0xff00:
469 rc = priv->tuner_callback(priv->video_dev,
470 XC2028_RESET_CLK, 0);
471 if (rc < 0) {
472 tuner_err("Error at RESET code %d\n",
473 (*p) & 0x7f);
474 return -EINVAL;
475 }
476 default:
477 tuner_info("Invalid RESET code %d\n",
478 size & 0x7f);
479 return -EINVAL;
480
481 }
Mauro Carvalho Chehab2d4c0ac2007-11-16 09:43:19 -0300482 continue;
Michel Ludwig5403bba2007-11-16 07:49:49 -0300483 }
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300484
485 /* Checks for a sleep command */
486 if (size & 0x8000) {
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300487 msleep(size & 0x7fff);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300488 continue;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300489 }
490
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300491 if ((size + p > endp)) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300492 tuner_err("missing bytes: need %d, have %d\n",
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300493 size, (int)(endp - p));
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300494 return -EINVAL;
495 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300496
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300497 buf[0] = *p;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300498 p++;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300499 size--;
500
501 /* Sends message chunks */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300502 while (size > 0) {
503 int len = (size < priv->max_len - 1) ?
504 size : priv->max_len - 1;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300505
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300506 memcpy(buf + 1, p, len);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300507
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300508 i2c_send(rc, priv, buf, len + 1);
509 if (rc < 0) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300510 tuner_err("%d returned from send\n", rc);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300511 return -EINVAL;
512 }
513
514 p += len;
515 size -= len;
516 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300517 }
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300518 return 0;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300519}
520
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300521static int load_scode(struct dvb_frontend *fe, unsigned int type,
522 v4l2_std_id *id, int scode)
523{
524 struct xc2028_data *priv = fe->tuner_priv;
525 int pos, rc;
526 unsigned char *p;
527
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300528 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300529
530 pos = seek_firmware(fe, type, id);
531 if (pos < 0)
532 return pos;
533
534 p = priv->firm[pos].ptr;
535
536 if (!p) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300537 tuner_err("Firmware pointer were freed!");
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300538 return -EINVAL;
539 }
540
541 if ((priv->firm[pos].size != 12 * 16) || (scode >= 16))
542 return -EINVAL;
543
544 if (priv->version < 0x0202) {
545 send_seq(priv, {0x20, 0x00, 0x00, 0x00});
546 } else {
547 send_seq(priv, {0xa0, 0x00, 0x00, 0x00});
548 }
549
550 i2c_send(rc, priv, p + 12 * scode, 12);
551
552 send_seq(priv, {0x00, 0x8c});
553
554 return 0;
555}
556
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300557static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300558 v4l2_std_id std, fe_bandwidth_t bandwidth)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300559{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300560 struct xc2028_data *priv = fe->tuner_priv;
Mauro Carvalho Chehab80b52202007-11-05 09:07:13 -0300561 int rc, version, hwmodel;
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300562 v4l2_std_id std0 = 0;
563 unsigned int type0 = 0, type = 0;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300564 int change_digital_bandwidth;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300565
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300566 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300567
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300568 if (!priv->firm) {
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300569 if (!priv->ctrl.fname) {
570 tuner_info("xc2028/3028 firmware name not set!\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300571 return -EINVAL;
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300572 }
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300573
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300574 rc = load_all_firmwares(fe);
575 if (rc < 0)
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300576 return rc;
577 }
578
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300579 tuner_dbg("I am in mode %u and I should switch to mode %i\n",
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300580 priv->mode, new_mode);
Michel Ludwig701672e2007-07-18 10:29:10 -0300581
582 /* first of all, determine whether we have switched the mode */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300583 if (new_mode != priv->mode) {
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300584 priv->mode = new_mode;
585 priv->need_load_generic = 1;
Michel Ludwig701672e2007-07-18 10:29:10 -0300586 }
587
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300588 change_digital_bandwidth = (priv->mode == T_DIGITAL_TV
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300589 && bandwidth != priv->bandwidth) ? 1 : 0;
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300590 tuner_dbg("old bandwidth %u, new bandwidth %u\n", priv->bandwidth,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300591 bandwidth);
Michel Ludwig701672e2007-07-18 10:29:10 -0300592
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300593 if (priv->need_load_generic) {
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300594 /* Reset is needed before loading firmware */
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300595 rc = priv->tuner_callback(priv->video_dev,
596 XC2028_TUNER_RESET, 0);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300597 if (rc < 0)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300598 return rc;
599
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300600 type0 = BASE;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300601
602 if (priv->ctrl.type == XC2028_FIRM_MTS)
603 type0 |= MTS;
604
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300605 if (priv->bandwidth == 8)
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300606 type0 |= F8MHZ;
607
608 /* FIXME: How to load FM and FM|INPUT1 firmwares? */
609
610 rc = load_firmware(fe, type0, &std0);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300611 if (rc < 0) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300612 tuner_err("Error %d while loading generic firmware\n",
613 rc);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300614 return rc;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300615 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300616
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300617 priv->need_load_generic = 0;
618 priv->firm_type = 0;
619 if (priv->mode == T_DIGITAL_TV)
620 change_digital_bandwidth = 1;
Michel Ludwig701672e2007-07-18 10:29:10 -0300621 }
622
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300623 tuner_dbg("I should change bandwidth %u\n", change_digital_bandwidth);
Michel Ludwig701672e2007-07-18 10:29:10 -0300624
625 if (change_digital_bandwidth) {
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300626
627 /*FIXME: Should allow selecting between D2620 and D2633 */
628 type |= D2620;
629
630 /* FIXME: When should select a DTV78 firmware?
631 */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300632 switch (bandwidth) {
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300633 case BANDWIDTH_8_MHZ:
634 type |= DTV8;
635 break;
636 case BANDWIDTH_7_MHZ:
637 type |= DTV7;
638 break;
639 case BANDWIDTH_6_MHZ:
640 /* FIXME: Should allow select also ATSC */
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300641 type |= DTV6 | QAM;
Michel Ludwig701672e2007-07-18 10:29:10 -0300642 break;
643
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300644 default:
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300645 tuner_err("error: bandwidth not supported.\n");
Michel Ludwig701672e2007-07-18 10:29:10 -0300646 };
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300647 priv->bandwidth = bandwidth;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300648 }
649
Michel Ludwig5403bba2007-11-16 07:49:49 -0300650 if (!change_digital_bandwidth && priv->mode == T_DIGITAL_TV)
651 return 0;
652
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300653 /* Load INIT1, if needed */
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300654 tuner_dbg("Load init1 firmware, if exists\n");
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300655 type0 = BASE | INIT1;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300656 if (priv->ctrl.type == XC2028_FIRM_MTS)
657 type0 |= MTS;
658
659 /* FIXME: Should handle errors - if INIT1 found */
660 rc = load_firmware(fe, type0, &std0);
661
662 /* FIXME: Should add support for FM radio
663 */
664
665 if (priv->ctrl.type == XC2028_FIRM_MTS)
666 type |= MTS;
667
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300668 if (priv->firm_type & std) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300669 tuner_dbg("Std-specific firmware already loaded.\n");
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300670 return 0;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300671 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300672
Mauro Carvalho Chehaba82200f2007-11-15 11:58:00 -0300673 /* Add audio hack to std mask */
674 std |= parse_audio_std_option();
675
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300676 rc = load_firmware(fe, type, &std);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300677 if (rc < 0)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300678 return rc;
679
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300680 /* Load SCODE firmware, if exists */
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300681 tuner_dbg("Trying to load scode 0\n");
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300682 type |= SCODE;
683
684 rc = load_scode(fe, type, &std, 0);
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300685
Mauro Carvalho Chehab80b52202007-11-05 09:07:13 -0300686 version = xc2028_get_reg(priv, 0x0004);
687 hwmodel = xc2028_get_reg(priv, 0x0008);
688
689 tuner_info("Device is Xceive %d version %d.%d, "
690 "firmware version %d.%d\n",
691 hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8,
692 (version & 0xf0) >> 4, version & 0xf);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300693
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300694 priv->firm_type = std;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300695
696 return 0;
697}
698
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300699static int xc2028_signal(struct dvb_frontend *fe, u16 *strength)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300700{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300701 struct xc2028_data *priv = fe->tuner_priv;
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300702 int frq_lock, signal = 0;
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300703
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300704 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300705
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300706 mutex_lock(&priv->lock);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300707
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300708 *strength = 0;
709
Mauro Carvalho Chehab80b52202007-11-05 09:07:13 -0300710 /* Sync Lock Indicator */
711 frq_lock = xc2028_get_reg(priv, 0x0002);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300712 if (frq_lock <= 0)
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300713 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300714
715 /* Frequency is locked. Return signal quality */
716
Mauro Carvalho Chehab80b52202007-11-05 09:07:13 -0300717 /* Get SNR of the video signal */
718 signal = xc2028_get_reg(priv, 0x0040);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300719
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300720 if (signal <= 0)
721 signal = frq_lock;
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300722
723ret:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300724 mutex_unlock(&priv->lock);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300725
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300726 *strength = signal;
727
728 return 0;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300729}
730
731#define DIV 15625
732
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300733static int generic_set_tv_freq(struct dvb_frontend *fe, u32 freq /* in Hz */ ,
734 enum tuner_mode new_mode,
735 v4l2_std_id std, fe_bandwidth_t bandwidth)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300736{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300737 struct xc2028_data *priv = fe->tuner_priv;
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300738 int rc = -EINVAL;
739 unsigned char buf[5];
740 u32 div, offset = 0;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300741
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300742 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300743
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300744 mutex_lock(&priv->lock);
745
Mauro Carvalho Chehabd4e76682007-07-18 23:14:25 -0300746 /* HACK: It seems that specific firmware need to be reloaded
747 when freq is changed */
Michel Ludwig701672e2007-07-18 10:29:10 -0300748
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300749 priv->firm_type = 0;
Michel Ludwig701672e2007-07-18 10:29:10 -0300750
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300751 /* Reset GPIO 1 */
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300752 rc = priv->tuner_callback(priv->video_dev, XC2028_TUNER_RESET, 0);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300753 if (rc < 0)
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300754 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300755
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300756 msleep(10);
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300757 tuner_dbg("should set frequency %d kHz)\n", freq / 1000);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300758
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300759 if (check_firmware(fe, new_mode, std, bandwidth) < 0)
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300760 goto ret;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300761
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300762 if (new_mode == T_DIGITAL_TV)
Mauro Carvalho Chehabd4e76682007-07-18 23:14:25 -0300763 offset = 2750000;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300764
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300765 div = (freq - offset + DIV / 2) / DIV;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300766
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300767 /* CMD= Set frequency */
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300768
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300769 if (priv->version < 0x0202) {
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300770 send_seq(priv, {0x00, 0x02, 0x00, 0x00});
771 } else {
772 send_seq(priv, {0x80, 0x02, 0x00, 0x00});
773 }
774
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300775 rc = priv->tuner_callback(priv->video_dev, XC2028_RESET_CLK, 1);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300776 if (rc < 0)
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300777 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300778
779 msleep(10);
Michel Ludwig701672e2007-07-18 10:29:10 -0300780
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300781 buf[0] = 0xff & (div >> 24);
782 buf[1] = 0xff & (div >> 16);
783 buf[2] = 0xff & (div >> 8);
784 buf[3] = 0xff & (div);
785 buf[4] = 0;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300786
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300787 i2c_send(rc, priv, buf, sizeof(buf));
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300788 if (rc < 0)
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300789 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300790 msleep(100);
791
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300792 priv->frequency = freq;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300793
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300794 tuner_dbg("divider= %02x %02x %02x %02x (freq=%d.%02d)\n",
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300795 buf[1], buf[2], buf[3], buf[4],
796 freq / 1000000, (freq % 1000000) / 10000);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300797
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300798 rc = 0;
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300799
800ret:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300801 mutex_unlock(&priv->lock);
802
803 return rc;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300804}
805
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300806static int xc2028_set_tv_freq(struct dvb_frontend *fe,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300807 struct analog_parameters *p)
Michel Ludwig701672e2007-07-18 10:29:10 -0300808{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300809 struct xc2028_data *priv = fe->tuner_priv;
Michel Ludwig701672e2007-07-18 10:29:10 -0300810
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300811 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300812
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300813 return generic_set_tv_freq(fe, 62500l * p->frequency, T_ANALOG_TV,
814 p->std, BANDWIDTH_8_MHZ /* NOT USED */);
Michel Ludwig701672e2007-07-18 10:29:10 -0300815}
816
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300817static int xc2028_set_params(struct dvb_frontend *fe,
Michel Ludwig701672e2007-07-18 10:29:10 -0300818 struct dvb_frontend_parameters *p)
819{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300820 struct xc2028_data *priv = fe->tuner_priv;
Michel Ludwig701672e2007-07-18 10:29:10 -0300821
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300822 tuner_dbg("%s called\n", __FUNCTION__);
Michel Ludwig701672e2007-07-18 10:29:10 -0300823
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300824 /* FIXME: Only OFDM implemented */
825 if (fe->ops.info.type != FE_OFDM) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300826 tuner_err("DTV type not implemented.\n");
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300827 return -EINVAL;
828 }
Michel Ludwig701672e2007-07-18 10:29:10 -0300829
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300830 return generic_set_tv_freq(fe, p->frequency, T_DIGITAL_TV,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300831 0 /* NOT USED */,
832 p->u.ofdm.bandwidth);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300833
Michel Ludwig701672e2007-07-18 10:29:10 -0300834}
835
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300836static int xc2028_dvb_release(struct dvb_frontend *fe)
Michel Ludwig701672e2007-07-18 10:29:10 -0300837{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300838 struct xc2028_data *priv = fe->tuner_priv;
Michel Ludwig701672e2007-07-18 10:29:10 -0300839
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300840 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300841
842 priv->count--;
843
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300844 if (!priv->count) {
Mauro Carvalho Chehab1808a692007-10-29 17:38:59 -0300845 list_del(&priv->xc2028_list);
846
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300847 kfree(priv->ctrl.fname);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300848
849 free_firmware(priv);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300850 kfree(priv);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300851 }
Michel Ludwig701672e2007-07-18 10:29:10 -0300852
853 return 0;
854}
855
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300856static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency)
Michel Ludwig701672e2007-07-18 10:29:10 -0300857{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300858 struct xc2028_data *priv = fe->tuner_priv;
859
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300860 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300861
862 *frequency = priv->frequency;
Michel Ludwig701672e2007-07-18 10:29:10 -0300863
864 return 0;
865}
866
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300867static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300868{
869 struct xc2028_data *priv = fe->tuner_priv;
870 struct xc2028_ctrl *p = priv_cfg;
871
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300872 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300873
874 priv->ctrl.type = p->type;
875
876 if (p->fname) {
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300877 kfree(priv->ctrl.fname);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300878
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300879 priv->ctrl.fname = kmalloc(strlen(p->fname) + 1, GFP_KERNEL);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300880 if (!priv->ctrl.fname)
881 return -ENOMEM;
882
883 free_firmware(priv);
884 strcpy(priv->ctrl.fname, p->fname);
885 }
886
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300887 if (p->max_len > 0)
Mauro Carvalho Chehab352fae12007-11-01 16:56:26 -0300888 priv->max_len = p->max_len;
889
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300890 return 0;
891}
892
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300893static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
Michel Ludwig701672e2007-07-18 10:29:10 -0300894 .info = {
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300895 .name = "Xceive XC3028",
896 .frequency_min = 42000000,
897 .frequency_max = 864000000,
898 .frequency_step = 50000,
899 },
Michel Ludwig701672e2007-07-18 10:29:10 -0300900
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300901 .set_config = xc2028_set_config,
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300902 .set_analog_params = xc2028_set_tv_freq,
903 .release = xc2028_dvb_release,
904 .get_frequency = xc2028_get_frequency,
905 .get_rf_strength = xc2028_signal,
906 .set_params = xc2028_set_params,
Michel Ludwig701672e2007-07-18 10:29:10 -0300907
Michel Ludwig701672e2007-07-18 10:29:10 -0300908};
909
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300910void *xc2028_attach(struct dvb_frontend *fe, struct xc2028_config *cfg)
Michel Ludwig701672e2007-07-18 10:29:10 -0300911{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300912 struct xc2028_data *priv;
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300913 void *video_dev;
Michel Ludwig701672e2007-07-18 10:29:10 -0300914
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300915 if (debug)
916 printk(KERN_DEBUG PREFIX "Xcv2028/3028 init called!\n");
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300917
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300918 if (NULL == cfg->video_dev)
919 return NULL;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300920
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300921 if (!fe) {
922 printk(KERN_ERR PREFIX "No frontend!\n");
923 return NULL;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300924 }
925
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300926 video_dev = cfg->video_dev;
927
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300928 list_for_each_entry(priv, &xc2028_list, xc2028_list) {
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300929 if (priv->video_dev == cfg->video_dev) {
930 video_dev = NULL;
931 break;
932 }
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300933 }
934
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300935 if (video_dev) {
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300936 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
937 if (priv == NULL)
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300938 return NULL;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300939
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300940 priv->bandwidth = BANDWIDTH_6_MHZ;
941 priv->need_load_generic = 1;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300942 priv->mode = T_UNINITIALIZED;
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300943 priv->i2c_props.addr = cfg->i2c_addr;
944 priv->i2c_props.adap = cfg->i2c_adap;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300945 priv->video_dev = video_dev;
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300946 priv->tuner_callback = cfg->callback;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300947 priv->max_len = 13;
948
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300949 mutex_init(&priv->lock);
950
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300951 list_add_tail(&priv->xc2028_list, &xc2028_list);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300952 }
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300953
954 fe->tuner_priv = priv;
Mauro Carvalho Chehab1808a692007-10-29 17:38:59 -0300955 priv->count++;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300956
957 memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300958 sizeof(xc2028_dvb_tuner_ops));
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300959
960 tuner_info("type set to %s\n", "XCeive xc2028/xc3028 tuner");
Michel Ludwig701672e2007-07-18 10:29:10 -0300961
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300962 return fe;
Michel Ludwig701672e2007-07-18 10:29:10 -0300963}
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300964
Michel Ludwig701672e2007-07-18 10:29:10 -0300965EXPORT_SYMBOL(xc2028_attach);
966
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300967MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver");
Mauro Carvalho Chehab983d2142007-10-29 23:44:18 -0300968MODULE_AUTHOR("Michel Ludwig <michel.ludwig@gmail.com>");
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300969MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
970MODULE_LICENSE("GPL");