blob: 8140d8ad0792b3b50cc8523ce2fd628db1c9c04c [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);
Chris Pascoeaa501be2007-11-19 04:45:38 -030047static DEFINE_MUTEX(xc2028_list_mutex);
48
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -030049/* struct for storing firmware table */
50struct firmware_description {
51 unsigned int type;
52 v4l2_std_id id;
53 unsigned char *ptr;
54 unsigned int size;
55};
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030056
Chris Pascoee0f0b372007-11-19 11:22:03 -030057struct firmware_properties {
58 unsigned int type;
59 v4l2_std_id id;
60 v4l2_std_id std_req;
61 unsigned int scode_table;
62 int scode_nr;
63};
64
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030065struct xc2028_data {
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030066 struct list_head xc2028_list;
67 struct tuner_i2c_props i2c_props;
68 int (*tuner_callback) (void *dev,
69 int command, int arg);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030070 void *video_dev;
71 int count;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -030072 __u32 frequency;
73
74 struct firmware_description *firm;
75 int firm_size;
Chris Pascoe06fd82d2007-11-19 06:06:08 -030076 __u16 firm_version;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -030077
78 struct xc2028_ctrl ctrl;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030079
Chris Pascoee0f0b372007-11-19 11:22:03 -030080 struct firmware_properties cur_fw;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030081
82 struct mutex lock;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030083};
84
Chris Pascoe47cc5b72007-11-19 04:14:23 -030085#define i2c_send(priv, buf, size) ({ \
86 int _rc; \
87 _rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size); \
88 if (size != _rc) \
89 tuner_info("i2c output error: rc = %d (should be %d)\n",\
90 _rc, (int)size); \
91 _rc; \
92})
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030093
Chris Pascoe47cc5b72007-11-19 04:14:23 -030094#define i2c_rcv(priv, buf, size) ({ \
95 int _rc; \
96 _rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, size); \
97 if (size != _rc) \
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -030098 tuner_err("i2c input error: rc = %d (should be %d)\n", \
Chris Pascoe47cc5b72007-11-19 04:14:23 -030099 _rc, (int)size); \
100 _rc; \
101})
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300102
Chris Pascoe7d58d112007-11-19 04:31:58 -0300103#define i2c_send_recv(priv, obuf, osize, ibuf, isize) ({ \
104 int _rc; \
105 _rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, obuf, osize, \
106 ibuf, isize); \
107 if (isize != _rc) \
108 tuner_err("i2c input error: rc = %d (should be %d)\n", \
109 _rc, (int)isize); \
110 _rc; \
111})
112
Chris Pascoe47cc5b72007-11-19 04:14:23 -0300113#define send_seq(priv, data...) ({ \
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300114 static u8 _val[] = data; \
Chris Pascoe47cc5b72007-11-19 04:14:23 -0300115 int _rc; \
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300116 if (sizeof(_val) != \
Chris Pascoe47cc5b72007-11-19 04:14:23 -0300117 (_rc = tuner_i2c_xfer_send(&priv->i2c_props, \
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300118 _val, sizeof(_val)))) { \
Chris Pascoe47cc5b72007-11-19 04:14:23 -0300119 tuner_err("Error on line %d: %d\n", __LINE__, _rc); \
120 } else \
121 msleep(10); \
122 _rc; \
123})
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300124
Chris Pascoe7d58d112007-11-19 04:31:58 -0300125static unsigned int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300126{
Mauro Carvalho Chehabb873e1a2007-11-05 08:41:50 -0300127 unsigned char buf[2];
Chris Pascoe7d58d112007-11-19 04:31:58 -0300128 unsigned char ibuf[2];
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300129
Chris Pascoe7d58d112007-11-19 04:31:58 -0300130 tuner_dbg("%s %04x called\n", __FUNCTION__, reg);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300131
Chris Pascoe7d58d112007-11-19 04:31:58 -0300132 buf[0] = reg >> 8;
Mauro Carvalho Chehab80b52202007-11-05 09:07:13 -0300133 buf[1] = (unsigned char) reg;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300134
Chris Pascoe7d58d112007-11-19 04:31:58 -0300135 if (i2c_send_recv(priv, buf, 2, ibuf, 2) != 2)
136 return -EIO;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300137
Chris Pascoe7d58d112007-11-19 04:31:58 -0300138 *val = (ibuf[1]) | (ibuf[0] << 8);
139 return 0;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300140}
141
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300142void dump_firm_type(unsigned int type)
143{
144 if (type & BASE)
145 printk("BASE ");
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300146 if (type & INIT1)
147 printk("INIT1 ");
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300148 if (type & F8MHZ)
149 printk("F8MHZ ");
150 if (type & MTS)
151 printk("MTS ");
152 if (type & D2620)
153 printk("D2620 ");
154 if (type & D2633)
155 printk("D2633 ");
156 if (type & DTV6)
157 printk("DTV6 ");
158 if (type & QAM)
159 printk("QAM ");
160 if (type & DTV7)
161 printk("DTV7 ");
162 if (type & DTV78)
163 printk("DTV78 ");
164 if (type & DTV8)
165 printk("DTV8 ");
166 if (type & FM)
167 printk("FM ");
168 if (type & INPUT1)
169 printk("INPUT1 ");
170 if (type & LCD)
171 printk("LCD ");
172 if (type & NOGD)
173 printk("NOGD ");
174 if (type & MONO)
175 printk("MONO ");
176 if (type & ATSC)
177 printk("ATSC ");
178 if (type & IF)
179 printk("IF ");
180 if (type & LG60)
181 printk("LG60 ");
182 if (type & ATI638)
183 printk("ATI638 ");
184 if (type & OREN538)
185 printk("OREN538 ");
186 if (type & OREN36)
187 printk("OREN36 ");
188 if (type & TOYOTA388)
189 printk("TOYOTA388 ");
190 if (type & TOYOTA794)
191 printk("TOYOTA794 ");
192 if (type & DIBCOM52)
193 printk("DIBCOM52 ");
194 if (type & ZARLINK456)
195 printk("ZARLINK456 ");
196 if (type & CHINA)
197 printk("CHINA ");
198 if (type & F6MHZ)
199 printk("F6MHZ ");
200 if (type & INPUT2)
201 printk("INPUT2 ");
202 if (type & SCODE)
203 printk("SCODE ");
204}
205
Mauro Carvalho Chehabef8c1882007-11-16 16:28:21 -0300206static v4l2_std_id parse_audio_std_option(void)
Mauro Carvalho Chehaba82200f2007-11-15 11:58:00 -0300207{
Chris Pascoee155d902007-11-19 04:16:47 -0300208 if (strcasecmp(audio_std, "A2") == 0)
Mauro Carvalho Chehaba82200f2007-11-15 11:58:00 -0300209 return V4L2_STD_A2;
Chris Pascoee155d902007-11-19 04:16:47 -0300210 if (strcasecmp(audio_std, "A2/A") == 0)
Mauro Carvalho Chehaba82200f2007-11-15 11:58:00 -0300211 return V4L2_STD_A2_A;
Chris Pascoee155d902007-11-19 04:16:47 -0300212 if (strcasecmp(audio_std, "A2/B") == 0)
Mauro Carvalho Chehaba82200f2007-11-15 11:58:00 -0300213 return V4L2_STD_A2_B;
Chris Pascoee155d902007-11-19 04:16:47 -0300214 if (strcasecmp(audio_std, "NICAM") == 0)
Mauro Carvalho Chehaba82200f2007-11-15 11:58:00 -0300215 return V4L2_STD_NICAM;
Chris Pascoee155d902007-11-19 04:16:47 -0300216 if (strcasecmp(audio_std, "NICAM/A") == 0)
Mauro Carvalho Chehaba82200f2007-11-15 11:58:00 -0300217 return V4L2_STD_NICAM_A;
Chris Pascoee155d902007-11-19 04:16:47 -0300218 if (strcasecmp(audio_std, "NICAM/B") == 0)
Mauro Carvalho Chehaba82200f2007-11-15 11:58:00 -0300219 return V4L2_STD_NICAM_B;
220
221 return 0;
222}
223
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300224static void free_firmware(struct xc2028_data *priv)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300225{
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300226 int i;
227
228 if (!priv->firm)
229 return;
230
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300231 for (i = 0; i < priv->firm_size; i++)
232 kfree(priv->firm[i].ptr);
233
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300234 kfree(priv->firm);
235
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300236 priv->firm = NULL;
Chris Pascoe06fd82d2007-11-19 06:06:08 -0300237 priv->firm_size = 0;
Chris Pascoee0f0b372007-11-19 11:22:03 -0300238
239 memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300240}
241
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300242static int load_all_firmwares(struct dvb_frontend *fe)
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300243{
244 struct xc2028_data *priv = fe->tuner_priv;
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300245 const struct firmware *fw = NULL;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300246 unsigned char *p, *endp;
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300247 int rc = 0;
248 int n, n_array;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300249 char name[33];
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300250
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300251 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300252
Chris Pascoe06fd82d2007-11-19 06:06:08 -0300253 tuner_dbg("Reading firmware %s\n", priv->ctrl.fname);
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300254 rc = request_firmware(&fw, priv->ctrl.fname,
255 &priv->i2c_props.adap->dev);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300256 if (rc < 0) {
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300257 if (rc == -ENOENT)
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300258 tuner_err("Error: firmware %s not found.\n",
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300259 priv->ctrl.fname);
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300260 else
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300261 tuner_err("Error %d while requesting firmware %s \n",
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300262 rc, priv->ctrl.fname);
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300263
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300264 return rc;
265 }
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300266 p = fw->data;
267 endp = p + fw->size;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300268
Chris Pascoe06fd82d2007-11-19 06:06:08 -0300269 if (fw->size < sizeof(name) - 1 + 2 + 2) {
270 tuner_err("Error: firmware file %s has invalid size!\n",
271 priv->ctrl.fname);
272 goto corrupt;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300273 }
274
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300275 memcpy(name, p, sizeof(name) - 1);
276 name[sizeof(name) - 1] = 0;
277 p += sizeof(name) - 1;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300278
Chris Pascoe06fd82d2007-11-19 06:06:08 -0300279 priv->firm_version = le16_to_cpu(*(__u16 *) p);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300280 p += 2;
281
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300282 n_array = le16_to_cpu(*(__u16 *) p);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300283 p += 2;
284
Chris Pascoe06fd82d2007-11-19 06:06:08 -0300285 tuner_info("Loading %d firmware images from %s, type: %s, ver %d.%d\n",
286 n_array, priv->ctrl.fname, name,
287 priv->firm_version >> 8, priv->firm_version & 0xff);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300288
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300289 priv->firm = kzalloc(sizeof(*priv->firm) * n_array, GFP_KERNEL);
Chris Pascoe06fd82d2007-11-19 06:06:08 -0300290 if (priv->firm == NULL) {
291 tuner_err("Not enough memory to load firmware file.\n");
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300292 rc = -ENOMEM;
Chris Pascoe06fd82d2007-11-19 06:06:08 -0300293 goto err;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300294 }
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300295 priv->firm_size = n_array;
Chris Pascoe06fd82d2007-11-19 06:06:08 -0300296
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300297 n = -1;
298 while (p < endp) {
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300299 __u32 type, size;
300 v4l2_std_id id;
301
302 n++;
303 if (n >= n_array) {
Chris Pascoe06fd82d2007-11-19 06:06:08 -0300304 tuner_err("More firmware images in file than "
305 "were expected!\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300306 goto corrupt;
307 }
308
309 /* Checks if there's enough bytes to read */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300310 if (p + sizeof(type) + sizeof(id) + sizeof(size) > endp) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300311 tuner_err("Firmware header is incomplete!\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300312 goto corrupt;
313 }
314
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300315 type = le32_to_cpu(*(__u32 *) p);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300316 p += sizeof(type);
317
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300318 id = le64_to_cpu(*(v4l2_std_id *) p);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300319 p += sizeof(id);
320
Michel Ludwig2fc580f2007-11-16 07:19:35 -0300321 size = le32_to_cpu(*(__u32 *) p);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300322 p += sizeof(size);
323
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300324 if ((!size) || (size + p > endp)) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300325 tuner_err("Firmware type ");
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300326 dump_firm_type(type);
Mauro Carvalho Chehabef8c1882007-11-16 16:28:21 -0300327 printk("(%x), id %llx is corrupted "
328 "(size=%d, expected %d)\n",
Chris Pascoe91240dd2007-11-19 04:38:53 -0300329 type, (unsigned long long)id,
Mauro Carvalho Chehabef8c1882007-11-16 16:28:21 -0300330 (unsigned)(endp - p), size);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300331 goto corrupt;
332 }
333
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300334 priv->firm[n].ptr = kzalloc(size, GFP_KERNEL);
Chris Pascoe06fd82d2007-11-19 06:06:08 -0300335 if (priv->firm[n].ptr == NULL) {
336 tuner_err("Not enough memory to load firmware file.\n");
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300337 rc = -ENOMEM;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300338 goto err;
339 }
Chris Pascoe06fd82d2007-11-19 06:06:08 -0300340 tuner_dbg("Reading firmware type ");
341 if (debug) {
342 dump_firm_type(type);
343 printk("(%x), id %llx, size=%d.\n",
344 type, (unsigned long long)id, size);
345 }
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300346
347 memcpy(priv->firm[n].ptr, p, size);
348 priv->firm[n].type = type;
349 priv->firm[n].id = id;
350 priv->firm[n].size = size;
351
352 p += size;
353 }
354
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300355 if (n + 1 != priv->firm_size) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300356 tuner_err("Firmware file is incomplete!\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300357 goto corrupt;
358 }
359
360 goto done;
361
362corrupt:
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300363 rc = -EINVAL;
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300364 tuner_err("Error: firmware file is corrupted!\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300365
366err:
Chris Pascoe06fd82d2007-11-19 06:06:08 -0300367 tuner_info("Releasing partially loaded firmware file.\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300368 free_firmware(priv);
369
370done:
371 release_firmware(fw);
Chris Pascoe06fd82d2007-11-19 06:06:08 -0300372 if (rc == 0)
373 tuner_dbg("Firmware files loaded.\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300374
375 return rc;
376}
377
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300378static int seek_firmware(struct dvb_frontend *fe, unsigned int type,
379 v4l2_std_id *id)
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300380{
381 struct xc2028_data *priv = fe->tuner_priv;
Chris Pascoeb1535292007-11-19 10:04:06 -0300382 int i, best_i = -1, best_nr_matches = 0;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300383
Chris Pascoeb1535292007-11-19 10:04:06 -0300384 tuner_dbg("%s called, want type=", __FUNCTION__);
385 if (debug) {
386 dump_firm_type(type);
387 printk("(%x), id %016llx.\n", type, (unsigned long long)*id);
388 }
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300389
390 if (!priv->firm) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300391 tuner_err("Error! firmware not loaded\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300392 return -EINVAL;
393 }
394
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300395 if (((type & ~SCODE) == 0) && (*id == 0))
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300396 *id = V4L2_STD_PAL;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300397
Chris Pascoee0f0b372007-11-19 11:22:03 -0300398 if (type & BASE)
399 type &= BASE_TYPES;
400 else if (type & SCODE)
401 type &= SCODE_TYPES;
402 else if (type & DTV_TYPES)
403 type = type & DTV_TYPES;
404
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300405 /* Seek for exact match */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300406 for (i = 0; i < priv->firm_size; i++) {
407 if ((type == priv->firm[i].type) && (*id == priv->firm[i].id))
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300408 goto found;
409 }
410
411 /* Seek for generic video standard match */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300412 for (i = 0; i < priv->firm_size; i++) {
Chris Pascoeb1535292007-11-19 10:04:06 -0300413 v4l2_std_id match_mask;
414 int nr_matches;
415
416 if (type != priv->firm[i].type)
417 continue;
418
419 match_mask = *id & priv->firm[i].id;
420 if (!match_mask)
421 continue;
422
423 if ((*id & match_mask) == *id)
424 goto found; /* Supports all the requested standards */
425
426 nr_matches = hweight64(match_mask);
427 if (nr_matches > best_nr_matches) {
428 best_nr_matches = nr_matches;
429 best_i = i;
430 }
431 }
432
433 if (best_nr_matches > 0) {
434 tuner_dbg("Selecting best matching firmware (%d bits) for "
435 "type=", best_nr_matches);
436 dump_firm_type(type);
437 printk("(%x), id %016llx:\n", type, (unsigned long long)*id);
438 i = best_i;
439 goto found;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300440 }
441
442 /*FIXME: Would make sense to seek for type "hint" match ? */
443
Chris Pascoeb1535292007-11-19 10:04:06 -0300444 i = -ENOENT;
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300445 goto ret;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300446
447found:
448 *id = priv->firm[i].id;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300449
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300450ret:
Chris Pascoeb1535292007-11-19 10:04:06 -0300451 tuner_dbg("%s firmware for type=", (i < 0) ? "Can't find" : "Found");
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300452 if (debug) {
453 dump_firm_type(type);
Chris Pascoe91240dd2007-11-19 04:38:53 -0300454 printk("(%x), id %016llx.\n", type, (unsigned long long)*id);
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300455 }
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300456 return i;
457}
458
459static int load_firmware(struct dvb_frontend *fe, unsigned int type,
460 v4l2_std_id *id)
461{
462 struct xc2028_data *priv = fe->tuner_priv;
463 int pos, rc;
Chris Pascoe0a196b62007-11-19 09:29:59 -0300464 unsigned char *p, *endp, buf[priv->ctrl.max_len];
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300465
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300466 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300467
468 pos = seek_firmware(fe, type, id);
469 if (pos < 0)
470 return pos;
471
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300472 tuner_info("Loading firmware for type=");
Chris Pascoeb1535292007-11-19 10:04:06 -0300473 dump_firm_type(priv->firm[pos].type);
474 printk("(%x), id %016llx.\n", priv->firm[pos].type,
475 (unsigned long long)*id);
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300476
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300477 p = priv->firm[pos].ptr;
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300478 endp = p + priv->firm[pos].size;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300479
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300480 while (p < endp) {
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300481 __u16 size;
482
483 /* Checks if there's enough bytes to read */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300484 if (p + sizeof(size) > endp) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300485 tuner_err("Firmware chunk size is wrong\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300486 return -EINVAL;
487 }
488
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300489 size = le16_to_cpu(*(__u16 *) p);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300490 p += sizeof(size);
491
492 if (size == 0xffff)
493 return 0;
494
495 if (!size) {
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300496 /* Special callback command received */
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300497 rc = priv->tuner_callback(priv->video_dev,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300498 XC2028_TUNER_RESET, 0);
499 if (rc < 0) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300500 tuner_err("Error at RESET code %d\n",
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300501 (*p) & 0x7f);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300502 return -EINVAL;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300503 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300504 continue;
505 }
Michel Ludwig5403bba2007-11-16 07:49:49 -0300506 if (size >= 0xff00) {
507 switch (size) {
508 case 0xff00:
509 rc = priv->tuner_callback(priv->video_dev,
510 XC2028_RESET_CLK, 0);
511 if (rc < 0) {
512 tuner_err("Error at RESET code %d\n",
513 (*p) & 0x7f);
514 return -EINVAL;
515 }
Chris Pascoeb32f9fb2007-11-19 04:53:50 -0300516 break;
Michel Ludwig5403bba2007-11-16 07:49:49 -0300517 default:
518 tuner_info("Invalid RESET code %d\n",
519 size & 0x7f);
520 return -EINVAL;
521
522 }
Mauro Carvalho Chehab2d4c0ac2007-11-16 09:43:19 -0300523 continue;
Michel Ludwig5403bba2007-11-16 07:49:49 -0300524 }
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300525
526 /* Checks for a sleep command */
527 if (size & 0x8000) {
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300528 msleep(size & 0x7fff);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300529 continue;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300530 }
531
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300532 if ((size + p > endp)) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300533 tuner_err("missing bytes: need %d, have %d\n",
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300534 size, (int)(endp - p));
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300535 return -EINVAL;
536 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300537
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300538 buf[0] = *p;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300539 p++;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300540 size--;
541
542 /* Sends message chunks */
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300543 while (size > 0) {
Chris Pascoe0a196b62007-11-19 09:29:59 -0300544 int len = (size < priv->ctrl.max_len - 1) ?
545 size : priv->ctrl.max_len - 1;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300546
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300547 memcpy(buf + 1, p, len);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300548
Chris Pascoe47cc5b72007-11-19 04:14:23 -0300549 rc = i2c_send(priv, buf, len + 1);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300550 if (rc < 0) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300551 tuner_err("%d returned from send\n", rc);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300552 return -EINVAL;
553 }
554
555 p += len;
556 size -= len;
557 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300558 }
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300559 return 0;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300560}
561
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300562static int load_scode(struct dvb_frontend *fe, unsigned int type,
563 v4l2_std_id *id, int scode)
564{
565 struct xc2028_data *priv = fe->tuner_priv;
566 int pos, rc;
567 unsigned char *p;
568
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300569 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300570
571 pos = seek_firmware(fe, type, id);
572 if (pos < 0)
573 return pos;
574
575 p = priv->firm[pos].ptr;
576
Chris Pascoed7b22c52007-11-19 10:12:45 -0300577 /* 16 SCODE entries per file; each SCODE entry is 12 bytes and
578 * has a 2-byte size header in the firmware format. */
579 if (priv->firm[pos].size != 14 * 16 || scode >= 16 ||
580 le16_to_cpu(*(__u16 *)(p + 14 * scode)) != 12)
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300581 return -EINVAL;
582
Chris Pascoed7b22c52007-11-19 10:12:45 -0300583 tuner_info("Loading SCODE for type=");
584 dump_firm_type(priv->firm[pos].type);
585 printk("(%x), id %016llx.\n", priv->firm[pos].type,
586 (unsigned long long)*id);
587
Chris Pascoe06fd82d2007-11-19 06:06:08 -0300588 if (priv->firm_version < 0x0202)
Chris Pascoe47cc5b72007-11-19 04:14:23 -0300589 rc = send_seq(priv, {0x20, 0x00, 0x00, 0x00});
590 else
591 rc = send_seq(priv, {0xa0, 0x00, 0x00, 0x00});
592 if (rc < 0)
593 return -EIO;
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300594
Chris Pascoed7b22c52007-11-19 10:12:45 -0300595 rc = i2c_send(priv, p + 14 * scode + 2, 12);
Chris Pascoe47cc5b72007-11-19 04:14:23 -0300596 if (rc < 0)
597 return -EIO;
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300598
Chris Pascoe47cc5b72007-11-19 04:14:23 -0300599 rc = send_seq(priv, {0x00, 0x8c});
600 if (rc < 0)
601 return -EIO;
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300602
603 return 0;
604}
605
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300606static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300607 v4l2_std_id std, fe_bandwidth_t bandwidth)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300608{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300609 struct xc2028_data *priv = fe->tuner_priv;
Chris Pascoee0f0b372007-11-19 11:22:03 -0300610 int rc = 0;
611 unsigned int type = 0;
612 struct firmware_properties new_fw;
Chris Pascoe7d58d112007-11-19 04:31:58 -0300613 u16 version, hwmodel;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300614
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300615 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300616
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300617 if (!priv->firm) {
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300618 if (!priv->ctrl.fname) {
619 tuner_info("xc2028/3028 firmware name not set!\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300620 return -EINVAL;
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300621 }
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300622
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300623 rc = load_all_firmwares(fe);
624 if (rc < 0)
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300625 return rc;
626 }
627
Chris Pascoee0f0b372007-11-19 11:22:03 -0300628 if (priv->ctrl.type == XC2028_FIRM_MTS)
629 type |= MTS;
630 if (bandwidth == BANDWIDTH_7_MHZ || bandwidth == BANDWIDTH_8_MHZ)
631 type |= F8MHZ;
Michel Ludwig701672e2007-07-18 10:29:10 -0300632
Chris Pascoee0f0b372007-11-19 11:22:03 -0300633 /* FIXME: How to load FM and FM|INPUT1 firmwares? */
Michel Ludwig701672e2007-07-18 10:29:10 -0300634
Chris Pascoee0f0b372007-11-19 11:22:03 -0300635 if (new_mode == T_DIGITAL_TV) {
Chris Pascoe59a636e2007-11-19 10:23:17 -0300636 if (priv->ctrl.d2633)
637 type |= D2633;
638 else
639 type |= D2620;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300640
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300641 switch (bandwidth) {
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300642 case BANDWIDTH_8_MHZ:
643 type |= DTV8;
644 break;
645 case BANDWIDTH_7_MHZ:
646 type |= DTV7;
647 break;
648 case BANDWIDTH_6_MHZ:
649 /* FIXME: Should allow select also ATSC */
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300650 type |= DTV6 | QAM;
Michel Ludwig701672e2007-07-18 10:29:10 -0300651 break;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300652 default:
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300653 tuner_err("error: bandwidth not supported.\n");
Michel Ludwig701672e2007-07-18 10:29:10 -0300654 };
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300655 }
656
Chris Pascoee0f0b372007-11-19 11:22:03 -0300657 new_fw.type = type;
658 new_fw.id = std;
659 new_fw.std_req = std;
660 new_fw.scode_table = SCODE | priv->ctrl.scode_table;
661 new_fw.scode_nr = 0;
662
663 tuner_dbg("checking firmware, user requested type=");
664 if (debug) {
665 dump_firm_type(new_fw.type);
666 printk("(%x), id %016llx, scode_tbl ", new_fw.type,
667 (unsigned long long)new_fw.std_req);
668 dump_firm_type(priv->ctrl.scode_table);
669 printk("(%x), scode_nr %d\n", priv->ctrl.scode_table,
670 new_fw.scode_nr);
671 }
672
673 /* No need to reload base firmware if it matches */
674 if (((BASE | new_fw.type) & BASE_TYPES) ==
675 (priv->cur_fw.type & BASE_TYPES)) {
676 tuner_dbg("BASE firmware not changed.\n");
677 goto skip_base;
678 }
679
680 /* Updating BASE - forget about all currently loaded firmware */
681 memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
682
683 /* Reset is needed before loading firmware */
684 rc = priv->tuner_callback(priv->video_dev,
685 XC2028_TUNER_RESET, 0);
686 if (rc < 0)
687 goto fail;
688
689 rc = load_firmware(fe, BASE | new_fw.type, &new_fw.id);
690 if (rc < 0) {
691 tuner_err("Error %d while loading base firmware\n",
692 rc);
693 goto fail;
694 }
Michel Ludwig5403bba2007-11-16 07:49:49 -0300695
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300696 /* Load INIT1, if needed */
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300697 tuner_dbg("Load init1 firmware, if exists\n");
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300698
Chris Pascoee0f0b372007-11-19 11:22:03 -0300699 rc = load_firmware(fe, BASE | INIT1 | new_fw.type, &new_fw.id);
700 if (rc < 0 && rc != -ENOENT) {
701 tuner_err("Error %d while loading init1 firmware\n",
702 rc);
703 goto fail;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300704 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300705
Chris Pascoee0f0b372007-11-19 11:22:03 -0300706skip_base:
707 /*
708 * No need to reload standard specific firmware if base firmware
709 * was not reloaded and requested video standards have not changed.
710 */
711 if (priv->cur_fw.type == (BASE | new_fw.type) &&
712 priv->cur_fw.std_req == std) {
713 tuner_dbg("Std-specific firmware already loaded.\n");
714 goto skip_std_specific;
715 }
Mauro Carvalho Chehaba82200f2007-11-15 11:58:00 -0300716
Chris Pascoee0f0b372007-11-19 11:22:03 -0300717 /* Reloading std-specific firmware forces a SCODE update */
718 priv->cur_fw.scode_table = 0;
719
720 /* Add audio hack to std mask */
721 if (new_mode == T_ANALOG_TV)
722 new_fw.id |= parse_audio_std_option();
723
724 rc = load_firmware(fe, new_fw.type, &new_fw.id);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300725 if (rc < 0)
Chris Pascoee0f0b372007-11-19 11:22:03 -0300726 goto fail;
727
728skip_std_specific:
729 if (priv->cur_fw.scode_table == new_fw.scode_table &&
730 priv->cur_fw.scode_nr == new_fw.scode_nr) {
731 tuner_dbg("SCODE firmware already loaded.\n");
732 goto check_device;
733 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300734
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300735 /* Load SCODE firmware, if exists */
Chris Pascoee0f0b372007-11-19 11:22:03 -0300736 tuner_dbg("Trying to load scode %d\n", new_fw.scode_nr);
Mauro Carvalho Chehabf380e1d2007-11-15 08:43:53 -0300737
Chris Pascoee0f0b372007-11-19 11:22:03 -0300738 rc = load_scode(fe, new_fw.type | new_fw.scode_table,
739 &new_fw.id, new_fw.scode_nr);
Mauro Carvalho Chehab43efe702007-11-14 19:30:28 -0300740
Chris Pascoee0f0b372007-11-19 11:22:03 -0300741check_device:
Chris Pascoe7d58d112007-11-19 04:31:58 -0300742 xc2028_get_reg(priv, 0x0004, &version);
743 xc2028_get_reg(priv, 0x0008, &hwmodel);
Mauro Carvalho Chehab80b52202007-11-05 09:07:13 -0300744
745 tuner_info("Device is Xceive %d version %d.%d, "
746 "firmware version %d.%d\n",
747 hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8,
748 (version & 0xf0) >> 4, version & 0xf);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300749
Chris Pascoee0f0b372007-11-19 11:22:03 -0300750 memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw));
751
752 /*
753 * By setting BASE in cur_fw.type only after successfully loading all
754 * firmwares, we can:
755 * 1. Identify that BASE firmware with type=0 has been loaded;
756 * 2. Tell whether BASE firmware was just changed the next time through.
757 */
758 priv->cur_fw.type |= BASE;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300759
760 return 0;
Chris Pascoee0f0b372007-11-19 11:22:03 -0300761
762fail:
763 memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
764 if (rc == -ENOENT)
765 rc = -EINVAL;
766 return rc;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300767}
768
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300769static int xc2028_signal(struct dvb_frontend *fe, u16 *strength)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300770{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300771 struct xc2028_data *priv = fe->tuner_priv;
Chris Pascoe7d58d112007-11-19 04:31:58 -0300772 u16 frq_lock, signal = 0;
773 int rc;
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300774
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300775 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300776
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300777 mutex_lock(&priv->lock);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300778
Mauro Carvalho Chehab80b52202007-11-05 09:07:13 -0300779 /* Sync Lock Indicator */
Chris Pascoe7d58d112007-11-19 04:31:58 -0300780 rc = xc2028_get_reg(priv, 0x0002, &frq_lock);
781 if (rc < 0 || frq_lock == 0)
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300782 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300783
784 /* Frequency is locked. Return signal quality */
785
Mauro Carvalho Chehab80b52202007-11-05 09:07:13 -0300786 /* Get SNR of the video signal */
Chris Pascoe7d58d112007-11-19 04:31:58 -0300787 rc = xc2028_get_reg(priv, 0x0040, &signal);
788 if (rc < 0)
789 signal = -frq_lock;
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300790
791ret:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300792 mutex_unlock(&priv->lock);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300793
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300794 *strength = signal;
795
Chris Pascoe7d58d112007-11-19 04:31:58 -0300796 return rc;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300797}
798
799#define DIV 15625
800
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300801static int generic_set_tv_freq(struct dvb_frontend *fe, u32 freq /* in Hz */ ,
802 enum tuner_mode new_mode,
803 v4l2_std_id std, fe_bandwidth_t bandwidth)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300804{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300805 struct xc2028_data *priv = fe->tuner_priv;
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300806 int rc = -EINVAL;
Chris Pascoe2ce4b3a2007-11-19 06:20:17 -0300807 unsigned char buf[4];
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300808 u32 div, offset = 0;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300809
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300810 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300811
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300812 mutex_lock(&priv->lock);
813
Mauro Carvalho Chehabd4e76682007-07-18 23:14:25 -0300814 /* HACK: It seems that specific firmware need to be reloaded
Chris Pascoee0f0b372007-11-19 11:22:03 -0300815 when watching analog TV and freq is changed */
816 if (new_mode != T_DIGITAL_TV)
817 priv->cur_fw.type = 0;
Michel Ludwig701672e2007-07-18 10:29:10 -0300818
Chris Pascoe2ce4b3a2007-11-19 06:20:17 -0300819 tuner_dbg("should set frequency %d kHz\n", freq / 1000);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300820
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300821 if (check_firmware(fe, new_mode, std, bandwidth) < 0)
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300822 goto ret;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300823
Chris Pascoea44f1c42007-11-19 06:35:26 -0300824 if (new_mode == T_DIGITAL_TV) {
Mauro Carvalho Chehabd4e76682007-07-18 23:14:25 -0300825 offset = 2750000;
Chris Pascoee0f0b372007-11-19 11:22:03 -0300826 if (priv->cur_fw.type & DTV7)
Chris Pascoea44f1c42007-11-19 06:35:26 -0300827 offset -= 500000;
828 }
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300829
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300830 div = (freq - offset + DIV / 2) / DIV;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300831
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300832 /* CMD= Set frequency */
Chris Pascoe06fd82d2007-11-19 06:06:08 -0300833 if (priv->firm_version < 0x0202)
Chris Pascoe47cc5b72007-11-19 04:14:23 -0300834 rc = send_seq(priv, {0x00, 0x02, 0x00, 0x00});
835 else
836 rc = send_seq(priv, {0x80, 0x02, 0x00, 0x00});
837 if (rc < 0)
838 goto ret;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300839
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300840 rc = priv->tuner_callback(priv->video_dev, XC2028_RESET_CLK, 1);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300841 if (rc < 0)
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300842 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300843
844 msleep(10);
Michel Ludwig701672e2007-07-18 10:29:10 -0300845
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300846 buf[0] = 0xff & (div >> 24);
847 buf[1] = 0xff & (div >> 16);
848 buf[2] = 0xff & (div >> 8);
849 buf[3] = 0xff & (div);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300850
Chris Pascoe47cc5b72007-11-19 04:14:23 -0300851 rc = i2c_send(priv, buf, sizeof(buf));
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300852 if (rc < 0)
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300853 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300854 msleep(100);
855
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300856 priv->frequency = freq;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300857
Chris Pascoe2ce4b3a2007-11-19 06:20:17 -0300858 tuner_dbg("divisor= %02x %02x %02x %02x (freq=%d.%03d)\n",
859 buf[0], buf[1], buf[2], buf[3],
860 freq / 1000000, (freq % 1000000) / 1000);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300861
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300862 rc = 0;
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300863
864ret:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300865 mutex_unlock(&priv->lock);
866
867 return rc;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300868}
869
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300870static int xc2028_set_tv_freq(struct dvb_frontend *fe,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300871 struct analog_parameters *p)
Michel Ludwig701672e2007-07-18 10:29:10 -0300872{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300873 struct xc2028_data *priv = fe->tuner_priv;
Michel Ludwig701672e2007-07-18 10:29:10 -0300874
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300875 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300876
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300877 return generic_set_tv_freq(fe, 62500l * p->frequency, T_ANALOG_TV,
Chris Pascoeddf1c5f2007-11-19 06:33:16 -0300878 p->std, BANDWIDTH_8_MHZ);
879 /* XXX Are some analog standards 6MHz? */
Michel Ludwig701672e2007-07-18 10:29:10 -0300880}
881
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300882static int xc2028_set_params(struct dvb_frontend *fe,
Michel Ludwig701672e2007-07-18 10:29:10 -0300883 struct dvb_frontend_parameters *p)
884{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300885 struct xc2028_data *priv = fe->tuner_priv;
Michel Ludwig701672e2007-07-18 10:29:10 -0300886
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300887 tuner_dbg("%s called\n", __FUNCTION__);
Michel Ludwig701672e2007-07-18 10:29:10 -0300888
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300889 /* FIXME: Only OFDM implemented */
890 if (fe->ops.info.type != FE_OFDM) {
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300891 tuner_err("DTV type not implemented.\n");
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300892 return -EINVAL;
893 }
Michel Ludwig701672e2007-07-18 10:29:10 -0300894
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300895 return generic_set_tv_freq(fe, p->frequency, T_DIGITAL_TV,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300896 0 /* NOT USED */,
897 p->u.ofdm.bandwidth);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300898
Michel Ludwig701672e2007-07-18 10:29:10 -0300899}
900
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300901static int xc2028_dvb_release(struct dvb_frontend *fe)
Michel Ludwig701672e2007-07-18 10:29:10 -0300902{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300903 struct xc2028_data *priv = fe->tuner_priv;
Michel Ludwig701672e2007-07-18 10:29:10 -0300904
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300905 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300906
Chris Pascoeaa501be2007-11-19 04:45:38 -0300907 mutex_lock(&xc2028_list_mutex);
908
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300909 priv->count--;
910
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300911 if (!priv->count) {
Mauro Carvalho Chehab1808a692007-10-29 17:38:59 -0300912 list_del(&priv->xc2028_list);
913
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300914 kfree(priv->ctrl.fname);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300915
916 free_firmware(priv);
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300917 kfree(priv);
Chris Pascoe06fd82d2007-11-19 06:06:08 -0300918 fe->tuner_priv = NULL;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300919 }
Michel Ludwig701672e2007-07-18 10:29:10 -0300920
Chris Pascoeaa501be2007-11-19 04:45:38 -0300921 mutex_unlock(&xc2028_list_mutex);
922
Michel Ludwig701672e2007-07-18 10:29:10 -0300923 return 0;
924}
925
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300926static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency)
Michel Ludwig701672e2007-07-18 10:29:10 -0300927{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300928 struct xc2028_data *priv = fe->tuner_priv;
929
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300930 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300931
932 *frequency = priv->frequency;
Michel Ludwig701672e2007-07-18 10:29:10 -0300933
934 return 0;
935}
936
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300937static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300938{
939 struct xc2028_data *priv = fe->tuner_priv;
940 struct xc2028_ctrl *p = priv_cfg;
Chris Pascoe0a196b62007-11-19 09:29:59 -0300941 int rc = 0;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300942
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300943 tuner_dbg("%s called\n", __FUNCTION__);
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300944
Chris Pascoe06fd82d2007-11-19 06:06:08 -0300945 mutex_lock(&priv->lock);
946
Chris Pascoe0a196b62007-11-19 09:29:59 -0300947 kfree(priv->ctrl.fname);
948 free_firmware(priv);
949
950 memcpy(&priv->ctrl, p, sizeof(priv->ctrl));
951 priv->ctrl.fname = NULL;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300952
953 if (p->fname) {
Chris Pascoe0a196b62007-11-19 09:29:59 -0300954 priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL);
955 if (priv->ctrl.fname == NULL)
956 rc = -ENOMEM;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300957 }
958
Chris Pascoe0a196b62007-11-19 09:29:59 -0300959 if (priv->ctrl.max_len < 9)
960 priv->ctrl.max_len = 13;
Mauro Carvalho Chehab352fae12007-11-01 16:56:26 -0300961
Chris Pascoe06fd82d2007-11-19 06:06:08 -0300962 mutex_unlock(&priv->lock);
963
Chris Pascoe0a196b62007-11-19 09:29:59 -0300964 return rc;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300965}
966
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300967static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
Michel Ludwig701672e2007-07-18 10:29:10 -0300968 .info = {
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -0300969 .name = "Xceive XC3028",
970 .frequency_min = 42000000,
971 .frequency_max = 864000000,
972 .frequency_step = 50000,
973 },
Michel Ludwig701672e2007-07-18 10:29:10 -0300974
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -0300975 .set_config = xc2028_set_config,
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300976 .set_analog_params = xc2028_set_tv_freq,
977 .release = xc2028_dvb_release,
978 .get_frequency = xc2028_get_frequency,
979 .get_rf_strength = xc2028_signal,
980 .set_params = xc2028_set_params,
Michel Ludwig701672e2007-07-18 10:29:10 -0300981
Michel Ludwig701672e2007-07-18 10:29:10 -0300982};
983
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300984void *xc2028_attach(struct dvb_frontend *fe, struct xc2028_config *cfg)
Michel Ludwig701672e2007-07-18 10:29:10 -0300985{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300986 struct xc2028_data *priv;
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300987 void *video_dev;
Michel Ludwig701672e2007-07-18 10:29:10 -0300988
Mauro Carvalho Chehab83fb3402007-11-15 09:44:30 -0300989 if (debug)
Chris Pascoe3157ece2007-11-19 04:34:29 -0300990 printk(KERN_DEBUG PREFIX ": Xcv2028/3028 init called!\n");
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300991
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300992 if (NULL == cfg->video_dev)
993 return NULL;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300994
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300995 if (!fe) {
Chris Pascoe3157ece2007-11-19 04:34:29 -0300996 printk(KERN_ERR PREFIX ": No frontend!\n");
Michel Ludwiga37b4c92007-11-16 07:46:14 -0300997 return NULL;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300998 }
999
Michel Ludwiga37b4c92007-11-16 07:46:14 -03001000 video_dev = cfg->video_dev;
1001
Chris Pascoeaa501be2007-11-19 04:45:38 -03001002 mutex_lock(&xc2028_list_mutex);
1003
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -03001004 list_for_each_entry(priv, &xc2028_list, xc2028_list) {
Michel Ludwiga37b4c92007-11-16 07:46:14 -03001005 if (priv->video_dev == cfg->video_dev) {
1006 video_dev = NULL;
1007 break;
1008 }
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -03001009 }
1010
Michel Ludwiga37b4c92007-11-16 07:46:14 -03001011 if (video_dev) {
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -03001012 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
Chris Pascoeaa501be2007-11-19 04:45:38 -03001013 if (priv == NULL) {
1014 mutex_unlock(&xc2028_list_mutex);
Michel Ludwiga37b4c92007-11-16 07:46:14 -03001015 return NULL;
Chris Pascoeaa501be2007-11-19 04:45:38 -03001016 }
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -03001017
Michel Ludwiga37b4c92007-11-16 07:46:14 -03001018 priv->i2c_props.addr = cfg->i2c_addr;
1019 priv->i2c_props.adap = cfg->i2c_adap;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -03001020 priv->video_dev = video_dev;
Michel Ludwiga37b4c92007-11-16 07:46:14 -03001021 priv->tuner_callback = cfg->callback;
Chris Pascoe0a196b62007-11-19 09:29:59 -03001022 priv->ctrl.max_len = 13;
Mauro Carvalho Chehabde3fe212007-10-24 09:22:08 -03001023
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -03001024 mutex_init(&priv->lock);
1025
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -03001026 list_add_tail(&priv->xc2028_list, &xc2028_list);
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -03001027 }
Michel Ludwiga37b4c92007-11-16 07:46:14 -03001028
1029 fe->tuner_priv = priv;
Mauro Carvalho Chehab1808a692007-10-29 17:38:59 -03001030 priv->count++;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -03001031
1032 memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops,
Mauro Carvalho Chehabab0b9fc2007-11-01 17:47:42 -03001033 sizeof(xc2028_dvb_tuner_ops));
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -03001034
1035 tuner_info("type set to %s\n", "XCeive xc2028/xc3028 tuner");
Michel Ludwig701672e2007-07-18 10:29:10 -03001036
Chris Pascoeaa501be2007-11-19 04:45:38 -03001037 mutex_unlock(&xc2028_list_mutex);
1038
Michel Ludwiga37b4c92007-11-16 07:46:14 -03001039 return fe;
Michel Ludwig701672e2007-07-18 10:29:10 -03001040}
Michel Ludwiga37b4c92007-11-16 07:46:14 -03001041
Michel Ludwig701672e2007-07-18 10:29:10 -03001042EXPORT_SYMBOL(xc2028_attach);
1043
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -03001044MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver");
Mauro Carvalho Chehab983d2142007-10-29 23:44:18 -03001045MODULE_AUTHOR("Michel Ludwig <michel.ludwig@gmail.com>");
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -03001046MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
1047MODULE_LICENSE("GPL");