blob: e4c371896de4b7bc9eb1a320ef61d36a780e5a96 [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)
Michel Ludwig701672e2007-07-18 10:29:10 -03004 * Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com)
5 * - frontend interface
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -03006 * This code is placed under the terms of the GNU General Public License v2
7 */
8
9#include <linux/i2c.h>
10#include <asm/div64.h>
11#include <linux/firmware.h>
12#include <linux/videodev.h>
13#include <linux/delay.h>
Michel Ludwig701672e2007-07-18 10:29:10 -030014#include <media/tuner.h>
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -030015#include <linux/mutex.h>
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030016#include "tuner-i2c.h"
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030017#include "tuner-xc2028.h"
18
Michel Ludwig701672e2007-07-18 10:29:10 -030019#include <linux/dvb/frontend.h>
20#include "dvb_frontend.h"
21
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030022#define PREFIX "xc2028 "
23
24static LIST_HEAD(xc2028_list);
Michel Ludwig701672e2007-07-18 10:29:10 -030025
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030026/* Firmwares used on tm5600/tm6000 + xc2028/xc3028 */
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -030027
28/* Generic firmwares */
29static const char *firmware_INIT0 = "tm_xc3028_MTS_init0.fw";
30static const char *firmware_8MHZ_INIT0 = "tm_xc3028_8M_MTS_init0.fw";
31static const char *firmware_INIT1 = "tm_xc3028_68M_MTS_init1.fw";
32
33/* Standard-specific firmwares */
34static const char *firmware_6M = "tm_xc3028_DTV_6M.fw";
Michel Ludwigff7326d2007-07-27 08:24:39 -030035static const char *firmware_7M = "tm_xc3028_DTV_7M.fw";
36static const char *firmware_8M = "tm_xc3028_DTV_8M.fw";
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -030037static const char *firmware_B = "tm_xc3028_B_PAL.fw";
38static const char *firmware_DK = "tm_xc3028_DK_PAL_MTS.fw";
39static const char *firmware_MN = "tm_xc3028_MN_BTSC.fw";
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030040
41struct xc2028_data {
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030042 struct list_head xc2028_list;
43 struct tuner_i2c_props i2c_props;
44 int (*tuner_callback) (void *dev,
45 int command, int arg);
46 struct device *dev;
47 void *video_dev;
48 int count;
49 u32 frequency;
50
Michel Ludwig701672e2007-07-18 10:29:10 -030051 v4l2_std_id firm_type; /* video stds supported
52 by current firmware */
53 fe_bandwidth_t bandwidth; /* Firmware bandwidth:
54 6M, 7M or 8M */
55 int need_load_generic; /* The generic firmware
56 were loaded? */
57 enum tuner_mode mode;
58 struct i2c_client *i2c_client;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030059
60 struct mutex lock;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030061};
62
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030063#define i2c_send(rc, priv, buf, size) \
64if (size != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size))) \
65 tuner_info("i2c output error: rc = %d (should be %d)\n", \
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030066 rc, (int)size);
67
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030068#define i2c_rcv(rc, priv, buf, size) \
69if (size != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, size))) \
70 tuner_info("i2c input error: rc = %d (should be %d)\n", \
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030071 rc, (int)size);
72
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030073#define send_seq(priv, data...) \
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030074{ int rc; \
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030075 static u8 _val[] = data; \
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030076 if (sizeof(_val) != \
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030077 (rc = tuner_i2c_xfer_send (&priv->i2c_props, \
78 _val, sizeof(_val)))) { \
79 tuner_info("Error on line %d: %d\n",__LINE__,rc); \
80 return -EINVAL; \
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030081 } \
82 msleep (10); \
83}
84
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030085static int xc2028_get_reg(struct xc2028_data *priv, u16 reg)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030086{
87 int rc;
88 unsigned char buf[1];
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030089
90 tuner_info("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030091
92 buf[0]= reg;
93
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030094 i2c_send(rc, priv, buf, sizeof(buf));
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030095 if (rc<0)
96 return rc;
97
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -030098 i2c_rcv(rc, priv, buf, 2);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -030099 if (rc<0)
100 return rc;
101
102 return (buf[1])|(buf[0]<<8);
103}
104
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300105static int load_firmware (struct dvb_frontend *fe, const char *name)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300106{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300107 struct xc2028_data *priv = fe->tuner_priv;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300108 const struct firmware *fw=NULL;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300109 unsigned char *p, *endp;
110 int len=0, rc=0;
111 static const char firmware_ver[] = "tm6000/xcv v1";
112
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300113 tuner_info("%s called\n", __FUNCTION__);
114
115 tuner_info("Loading firmware %s\n", name);
116 rc = request_firmware(&fw, name, priv->dev);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300117 if (rc < 0) {
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300118 if (rc==-ENOENT)
119 tuner_info("Error: firmware %s not found.\n", name);
120 else
121 tuner_info("Error %d while requesting firmware %s \n", rc, name);
122
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300123 return rc;
124 }
125 p=fw->data;
126 endp=p+fw->size;
127
128 if(fw->size==0) {
129 tuner_info("Error: firmware size is zero!\n");
130 rc=-EINVAL;
131 goto err;
132 }
133 if (fw->size<sizeof(firmware_ver)-1) {
134 /* Firmware is incorrect */
135 tuner_info("Error: firmware size is less than header (%d<%d)!\n",
136 (int)fw->size,(int)sizeof(firmware_ver)-1);
137 rc=-EINVAL;
138 goto err;
139 }
140
141 if (memcmp(p,firmware_ver,sizeof(firmware_ver)-1)) {
142 /* Firmware is incorrect */
143 tuner_info("Error: firmware is not for tm5600/6000 + Xcv2028/3028!\n");
144 rc=-EINVAL;
145 goto err;
146 }
147 p+=sizeof(firmware_ver)-1;
148
149 while(p<endp) {
150 if ((*p) & 0x80) {
151 /* Special callback command received */
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300152 rc = priv->tuner_callback(priv->video_dev,
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300153 XC2028_TUNER_RESET, (*p)&0x7f);
154 if (rc<0) {
155 tuner_info("Error at RESET code %d\n",
156 (*p)&0x7f);
157 goto err;
158 }
159 p++;
160 continue;
161 }
162 len=*p;
163 p++;
164 if (p+len+1>endp) {
165 /* Firmware is incorrect */
166 tuner_info("Error: firmware is truncated!\n");
167 rc=-EINVAL;
168 goto err;
169 }
170 if (len<=0) {
171 tuner_info("Error: firmware file is corrupted!\n");
172 rc=-EINVAL;
173 goto err;
174 }
175
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300176 i2c_send(rc, priv, p, len);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300177 if (rc<0)
178 goto err;
179 p+=len;
180
181 if (*p)
182 msleep(*p);
183 p++;
184 }
185
186
187err:
188 release_firmware(fw);
189
190 return rc;
191}
192
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300193static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode,
194 v4l2_std_id std,
Michel Ludwig701672e2007-07-18 10:29:10 -0300195 fe_bandwidth_t bandwidth)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300196{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300197 struct xc2028_data *priv = fe->tuner_priv;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300198 int rc, version;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300199 const char *name;
Michel Ludwig701672e2007-07-18 10:29:10 -0300200 int change_digital_bandwidth;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300201
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300202 tuner_info("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300203
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300204 tuner_info( "I am in mode %u and I should switch to mode %i\n",
205 priv->mode, new_mode);
Michel Ludwig701672e2007-07-18 10:29:10 -0300206
207 /* first of all, determine whether we have switched the mode */
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300208 if(new_mode != priv->mode) {
209 priv->mode = new_mode;
210 priv->need_load_generic = 1;
Michel Ludwig701672e2007-07-18 10:29:10 -0300211 }
212
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300213 change_digital_bandwidth = (priv->mode == T_DIGITAL_TV
214 && bandwidth != priv->bandwidth) ? 1 : 0;
215 tuner_info("old bandwidth %u, new bandwidth %u\n", priv->bandwidth,
Michel Ludwig701672e2007-07-18 10:29:10 -0300216 bandwidth);
217
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300218 if (priv->need_load_generic) {
219 if (priv->bandwidth==8)
Michel Ludwig701672e2007-07-18 10:29:10 -0300220 name = firmware_8MHZ_INIT0;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300221 else
222 name = firmware_INIT0;
223
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300224 /* Reset is needed before loading firmware */
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300225 rc = priv->tuner_callback(priv->video_dev,
226 XC2028_TUNER_RESET, 0);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300227 if (rc<0)
228 return rc;
229
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300230 rc = load_firmware(fe,name);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300231 if (rc<0)
232 return rc;
233
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300234 priv->need_load_generic=0;
235 priv->firm_type=0;
236 if(priv->mode == T_DIGITAL_TV) {
Michel Ludwig701672e2007-07-18 10:29:10 -0300237 change_digital_bandwidth=1;
238 }
239 }
240
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300241 tuner_info("I should change bandwidth %u\n",
Michel Ludwig701672e2007-07-18 10:29:10 -0300242 change_digital_bandwidth);
243
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300244 /* FIXME: t->std makes no sense here */
Michel Ludwig701672e2007-07-18 10:29:10 -0300245 if (change_digital_bandwidth) {
246 switch(bandwidth) {
247 case BANDWIDTH_8_MHZ:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300248 std = V4L2_STD_DTV_8MHZ;
Michel Ludwig701672e2007-07-18 10:29:10 -0300249 break;
250
251 case BANDWIDTH_7_MHZ:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300252 std = V4L2_STD_DTV_7MHZ;
Michel Ludwig701672e2007-07-18 10:29:10 -0300253 break;
254
255 case BANDWIDTH_6_MHZ:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300256 std = V4L2_STD_DTV_6MHZ;
Michel Ludwig701672e2007-07-18 10:29:10 -0300257 break;
258
259 default:
260 tuner_info("error: bandwidth not supported.\n");
261 };
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300262 priv->bandwidth = bandwidth;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300263 }
264
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300265 if (priv->firm_type & std) {
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300266 tuner_info("xc3028: no need to load a std-specific firmware.\n");
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300267 return 0;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300268 }
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300269
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300270 rc = load_firmware(fe,firmware_INIT1);
Michel Ludwigc2622e52007-07-18 10:26:38 -0300271
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300272 if (std & V4L2_STD_MN)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300273 name=firmware_MN;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300274 else if (std & V4L2_STD_DTV_6MHZ)
Michel Ludwig701672e2007-07-18 10:29:10 -0300275 name=firmware_6M;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300276 else if (std & V4L2_STD_DTV_7MHZ)
Michel Ludwig701672e2007-07-18 10:29:10 -0300277 name=firmware_7M;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300278 else if (std & V4L2_STD_DTV_8MHZ)
Michel Ludwig701672e2007-07-18 10:29:10 -0300279 name=firmware_8M;
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300280 else if (std & V4L2_STD_PAL_B)
Michel Ludwig701672e2007-07-18 10:29:10 -0300281 name=firmware_B;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300282 else
283 name=firmware_DK;
284
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300285 tuner_info("loading firmware named %s.\n", name);
286 rc = load_firmware(fe, name);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300287 if (rc<0)
288 return rc;
289
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300290 version = xc2028_get_reg(priv, 0x4);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300291 tuner_info("Firmware version is %d.%d\n",
292 (version>>4)&0x0f,(version)&0x0f);
293
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300294 priv->firm_type=std;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300295
296 return 0;
297}
298
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300299static int xc2028_signal(struct dvb_frontend *fe, u16 *strength)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300300{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300301 struct xc2028_data *priv = fe->tuner_priv;
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300302 int frq_lock, signal=0;
303
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300304 tuner_info("%s called\n", __FUNCTION__);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300305
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300306 mutex_lock(&priv->lock);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300307
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300308 *strength = 0;
309
310 frq_lock = xc2028_get_reg(priv, 0x2);
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300311 if (frq_lock<=0)
312 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300313
314 /* Frequency is locked. Return signal quality */
315
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300316 signal = xc2028_get_reg(priv, 0x40);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300317
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300318 if(signal<=0) {
319 signal=frq_lock;
320 }
321
322ret:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300323 mutex_unlock(&priv->lock);
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300324
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300325 *strength = signal;
326
327 return 0;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300328}
329
330#define DIV 15625
331
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300332static int generic_set_tv_freq(struct dvb_frontend *fe, u32 freq /* in Hz */,
333 enum tuner_mode new_mode,
334 v4l2_std_id std,
335 fe_bandwidth_t bandwidth)
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300336{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300337 struct xc2028_data *priv = fe->tuner_priv;
338 int rc=-EINVAL;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300339 unsigned char buf[5];
Michel Ludwig701672e2007-07-18 10:29:10 -0300340 u32 div, offset = 0;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300341
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300342 tuner_info("%s called\n", __FUNCTION__);
343
Mauro Carvalho Chehabd4e76682007-07-18 23:14:25 -0300344 /* HACK: It seems that specific firmware need to be reloaded
345 when freq is changed */
Michel Ludwig701672e2007-07-18 10:29:10 -0300346
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300347 mutex_lock(&priv->lock);
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300348
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300349 priv->firm_type=0;
Michel Ludwig701672e2007-07-18 10:29:10 -0300350
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300351 /* Reset GPIO 1 */
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300352 rc = priv->tuner_callback(priv->video_dev, XC2028_TUNER_RESET, 0);
353 if (rc<0)
354 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300355
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300356 msleep(10);
357 tuner_info("should set frequency %d kHz)\n", freq / 1000);
358
359 if (check_firmware(fe, new_mode, std, bandwidth)<0)
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300360 goto ret;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300361
Mauro Carvalho Chehabd4e76682007-07-18 23:14:25 -0300362 if(new_mode == T_DIGITAL_TV)
363 offset = 2750000;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300364
Mauro Carvalho Chehabd4e76682007-07-18 23:14:25 -0300365 div = (freq - offset + DIV/2)/DIV;
Mauro Carvalho Chehab2e4160c2007-07-18 13:33:23 -0300366
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300367 /* CMD= Set frequency */
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300368 send_seq(priv, {0x00, 0x02, 0x00, 0x00});
369 rc = priv->tuner_callback(priv->video_dev, XC2028_RESET_CLK, 1);
370 if (rc<0)
371 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300372
373 msleep(10);
Michel Ludwig701672e2007-07-18 10:29:10 -0300374
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300375 buf[0]= 0xff & (div>>24);
376 buf[1]= 0xff & (div>>16);
377 buf[2]= 0xff & (div>>8);
378 buf[3]= 0xff & (div);
379 buf[4]= 0;
380
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300381 i2c_send(rc, priv, buf, sizeof(buf));
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300382 if (rc<0)
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300383 goto ret;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300384 msleep(100);
385
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300386 priv->frequency=freq;
387
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300388 printk("divider= %02x %02x %02x %02x (freq=%d.%02d)\n",
389 buf[1],buf[2],buf[3],buf[4],
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300390 freq / 1000000, (freq%1000000)/10000);
391
392 rc=0;
Mauro Carvalho Chehab3b205322007-09-27 18:27:03 -0300393
394ret:
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300395 mutex_unlock(&priv->lock);
396
397 return rc;
Mauro Carvalho Chehab6cb45872007-10-02 11:57:03 -0300398}
399
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300400static int xc2028_set_tv_freq(struct dvb_frontend *fe,
401 struct analog_parameters *p)
Michel Ludwig701672e2007-07-18 10:29:10 -0300402{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300403 struct xc2028_data *priv = fe->tuner_priv;
Michel Ludwig701672e2007-07-18 10:29:10 -0300404
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300405 tuner_info("%s called\n", __FUNCTION__);
406
407 return generic_set_tv_freq(fe, 62500l*p->frequency, T_ANALOG_TV,
408 p->std,
409 BANDWIDTH_8_MHZ /* NOT USED */);
Michel Ludwig701672e2007-07-18 10:29:10 -0300410}
411
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300412static int xc2028_set_params(struct dvb_frontend *fe,
Michel Ludwig701672e2007-07-18 10:29:10 -0300413 struct dvb_frontend_parameters *p)
414{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300415 struct xc2028_data *priv = fe->tuner_priv;
Michel Ludwig701672e2007-07-18 10:29:10 -0300416
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300417 tuner_info("%s called\n", __FUNCTION__);
Michel Ludwig701672e2007-07-18 10:29:10 -0300418
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300419 /* FIXME: Only OFDM implemented */
420 if (fe->ops.info.type != FE_OFDM) {
421 tuner_info ("DTV type not implemented.\n");
422 return -EINVAL;
423 }
Michel Ludwig701672e2007-07-18 10:29:10 -0300424
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300425 return generic_set_tv_freq(fe, p->frequency, T_DIGITAL_TV,
426 0, /* NOT USED */
427 p->u.ofdm.bandwidth);
428
Michel Ludwig701672e2007-07-18 10:29:10 -0300429}
430
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300431static int xc2028_dvb_release(struct dvb_frontend *fe)
Michel Ludwig701672e2007-07-18 10:29:10 -0300432{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300433 struct xc2028_data *priv = fe->tuner_priv;
Michel Ludwig701672e2007-07-18 10:29:10 -0300434
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300435 tuner_info("%s called\n", __FUNCTION__);
436
437 priv->count--;
438
439 if (!priv->count)
440 kfree (priv);
Michel Ludwig701672e2007-07-18 10:29:10 -0300441
442 return 0;
443}
444
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300445static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency)
Michel Ludwig701672e2007-07-18 10:29:10 -0300446{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300447 struct xc2028_data *priv = fe->tuner_priv;
448
449 tuner_info("%s called\n", __FUNCTION__);
450
451 *frequency = priv->frequency;
Michel Ludwig701672e2007-07-18 10:29:10 -0300452
453 return 0;
454}
455
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300456static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
Michel Ludwig701672e2007-07-18 10:29:10 -0300457 .info = {
458 .name = "Xceive XC3028",
459 .frequency_min = 42000000,
460 .frequency_max = 864000000,
461 .frequency_step = 50000,
462 },
463
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300464 .set_analog_params = xc2028_set_tv_freq,
465 .release = xc2028_dvb_release,
466 .get_frequency = xc2028_get_frequency,
467 .get_rf_strength = xc2028_signal,
468 .set_params = xc2028_set_params,
Michel Ludwig701672e2007-07-18 10:29:10 -0300469
470// int (*sleep)(struct dvb_frontend *fe);
Michel Ludwig701672e2007-07-18 10:29:10 -0300471// int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth);
Michel Ludwig701672e2007-07-18 10:29:10 -0300472// int (*get_status)(struct dvb_frontend *fe, u32 *status);
473};
474
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300475int xc2028_attach(struct dvb_frontend *fe, struct i2c_adapter* i2c_adap,
476 u8 i2c_addr, struct device *dev, void *video_dev,
477 int (*tuner_callback) (void *dev, int command,int arg))
Michel Ludwig701672e2007-07-18 10:29:10 -0300478{
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300479 struct xc2028_data *priv;
Michel Ludwig701672e2007-07-18 10:29:10 -0300480
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300481 printk( KERN_INFO PREFIX "Xcv2028/3028 init called!\n");
482
483 if (NULL == dev)
484 return -ENODEV;
485
486 if (NULL == video_dev)
487 return -ENODEV;
488
489 if (!tuner_callback) {
490 printk( KERN_ERR PREFIX "No tuner callback!\n");
491 return -EINVAL;
492 }
493
494 list_for_each_entry(priv, &xc2028_list, xc2028_list) {
495 if (priv->dev == dev) {
496 dev = NULL;
497 priv->count++;
498 }
499 }
500
501 if (dev) {
502 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
503 if (priv == NULL)
504 return -ENOMEM;
505
506 fe->tuner_priv = priv;
507
508 priv->bandwidth=BANDWIDTH_6_MHZ;
509 priv->need_load_generic=1;
510 priv->mode = T_UNINITIALIZED;
511 priv->i2c_props.addr = i2c_addr;
512 priv->i2c_props.adap = i2c_adap;
513 priv->dev = dev;
514 priv->video_dev = video_dev;
515 priv->tuner_callback = tuner_callback;
516
517 mutex_init(&priv->lock);
518
519 list_add_tail(&priv->xc2028_list,&xc2028_list);
520 }
521
522 memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops,
523 sizeof(xc2028_dvb_tuner_ops));
524
525 tuner_info("type set to %s\n", "XCeive xc2028/xc3028 tuner");
Michel Ludwig701672e2007-07-18 10:29:10 -0300526
527 return 0;
528}
529
530EXPORT_SYMBOL(xc2028_attach);
531
Mauro Carvalho Chehab215b95b2007-10-23 15:24:06 -0300532MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver");
533MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
534MODULE_LICENSE("GPL");