si2157.c 11.6 KB
Newer Older
1
/*
C
CrazyCat 已提交
2
 * Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver
3 4 5 6 7 8 9 10 11 12 13 14 15 16
 *
 * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 */

17 18
#include "si2157_priv.h"

19 20
static const struct dvb_tuner_ops si2157_ops;

21
/* execute firmware command */
22
static int si2157_cmd_execute(struct i2c_client *client, struct si2157_cmd *cmd)
23
{
24
	struct si2157_dev *dev = i2c_get_clientdata(client);
25 26 27
	int ret;
	unsigned long timeout;

28
	mutex_lock(&dev->i2c_mutex);
29

30
	if (cmd->wlen) {
31
		/* write cmd and args for firmware */
32
		ret = i2c_master_send(client, cmd->args, cmd->wlen);
33 34
		if (ret < 0) {
			goto err_mutex_unlock;
35
		} else if (ret != cmd->wlen) {
36 37 38 39 40
			ret = -EREMOTEIO;
			goto err_mutex_unlock;
		}
	}

41 42 43 44 45
	if (cmd->rlen) {
		/* wait cmd execution terminate */
		#define TIMEOUT 80
		timeout = jiffies + msecs_to_jiffies(TIMEOUT);
		while (!time_after(jiffies, timeout)) {
46
			ret = i2c_master_recv(client, cmd->args, cmd->rlen);
47 48 49 50 51 52 53 54 55 56
			if (ret < 0) {
				goto err_mutex_unlock;
			} else if (ret != cmd->rlen) {
				ret = -EREMOTEIO;
				goto err_mutex_unlock;
			}

			/* firmware ready? */
			if ((cmd->args[0] >> 7) & 0x01)
				break;
57 58
		}

59
		dev_dbg(&client->dev, "cmd execution took %d ms\n",
60 61
				jiffies_to_msecs(jiffies) -
				(jiffies_to_msecs(timeout) - TIMEOUT));
62

63 64 65 66
		if (!((cmd->args[0] >> 7) & 0x01)) {
			ret = -ETIMEDOUT;
			goto err_mutex_unlock;
		}
67 68
	}

69 70
	mutex_unlock(&dev->i2c_mutex);
	return 0;
71

72
err_mutex_unlock:
73
	mutex_unlock(&dev->i2c_mutex);
74
	dev_dbg(&client->dev, "failed=%d\n", ret);
75 76 77 78 79
	return ret;
}

static int si2157_init(struct dvb_frontend *fe)
{
80 81
	struct i2c_client *client = fe->tuner_priv;
	struct si2157_dev *dev = i2c_get_clientdata(client);
82
	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
83
	int ret, len, remaining;
84
	struct si2157_cmd cmd;
85
	const struct firmware *fw;
86
	const char *fw_name;
87
	unsigned int chip_id;
88

89
	dev_dbg(&client->dev, "\n");
90

91
	if (dev->fw_loaded)
92 93 94
		goto warm;

	/* power up */
95
	if (dev->chiptype == SI2157_CHIPTYPE_SI2146) {
96 97 98 99 100 101
		memcpy(cmd.args, "\xc0\x05\x01\x00\x00\x0b\x00\x00\x01", 9);
		cmd.wlen = 9;
	} else {
		memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15);
		cmd.wlen = 15;
	}
102
	cmd.rlen = 1;
103
	ret = si2157_cmd_execute(client, &cmd);
104 105 106 107 108 109 110
	if (ret)
		goto err;

	/* query chip revision */
	memcpy(cmd.args, "\x02", 1);
	cmd.wlen = 1;
	cmd.rlen = 13;
111
	ret = si2157_cmd_execute(client, &cmd);
112 113 114
	if (ret)
		goto err;

115 116 117 118
	chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 |
			cmd.args[4] << 0;

	#define SI2158_A20 ('A' << 24 | 58 << 16 | '2' << 8 | '0' << 0)
C
CrazyCat 已提交
119
	#define SI2148_A20 ('A' << 24 | 48 << 16 | '2' << 8 | '0' << 0)
120
	#define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0)
121
	#define SI2147_A30 ('A' << 24 | 47 << 16 | '3' << 8 | '0' << 0)
122
	#define SI2146_A10 ('A' << 24 | 46 << 16 | '1' << 8 | '0' << 0)
123 124 125

	switch (chip_id) {
	case SI2158_A20:
C
CrazyCat 已提交
126
	case SI2148_A20:
127
		fw_name = SI2158_A20_FIRMWARE;
128 129
		break;
	case SI2157_A30:
130
	case SI2147_A30:
131
	case SI2146_A10:
132
		fw_name = NULL;
133
		break;
134
	default:
135
		dev_err(&client->dev, "unknown chip version Si21%d-%c%c%c\n",
O
Olli Salonen 已提交
136
				cmd.args[2], cmd.args[1],
137 138 139 140
				cmd.args[3], cmd.args[4]);
		ret = -EINVAL;
		goto err;
	}
141

142 143 144
	dev_info(&client->dev, "found a 'Silicon Labs Si21%d-%c%c%c'\n",
			cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]);

145
	if (fw_name == NULL)
146
		goto skip_fw_download;
147

148
	/* request the firmware, this will block and timeout */
149
	ret = request_firmware(&fw, fw_name, &client->dev);
150
	if (ret) {
151
		dev_err(&client->dev, "firmware file '%s' not found\n",
152
				fw_name);
153 154
		goto err;
	}
155

156 157
	/* firmware should be n chunks of 17 bytes */
	if (fw->size % 17 != 0) {
158
		dev_err(&client->dev, "firmware file '%s' is invalid\n",
159
				fw_name);
160
		ret = -EINVAL;
161
		goto err_release_firmware;
162
	}
163

164
	dev_info(&client->dev, "downloading firmware from file '%s'\n",
165
			fw_name);
166

167 168 169 170 171
	for (remaining = fw->size; remaining > 0; remaining -= 17) {
		len = fw->data[fw->size - remaining];
		memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len);
		cmd.wlen = len;
		cmd.rlen = 1;
172
		ret = si2157_cmd_execute(client, &cmd);
173
		if (ret) {
174
			dev_err(&client->dev, "firmware download failed %d\n",
O
Olli Salonen 已提交
175
					ret);
176
			goto err_release_firmware;
177 178 179
		}
	}

180 181 182
	release_firmware(fw);

skip_fw_download:
183 184 185 186
	/* reboot the tuner with new firmware? */
	memcpy(cmd.args, "\x01\x01", 2);
	cmd.wlen = 2;
	cmd.rlen = 1;
187
	ret = si2157_cmd_execute(client, &cmd);
188 189 190
	if (ret)
		goto err;

191 192 193 194 195 196 197 198 199 200 201
	/* query firmware version */
	memcpy(cmd.args, "\x11", 1);
	cmd.wlen = 1;
	cmd.rlen = 10;
	ret = si2157_cmd_execute(client, &cmd);
	if (ret)
		goto err;

	dev_info(&client->dev, "firmware version: %c.%c.%d\n",
			cmd.args[6], cmd.args[7], cmd.args[8]);

202
	dev->fw_loaded = true;
203

204
warm:
205 206 207 208 209 210
	/* init statistics in order signal app which are supported */
	c->strength.len = 1;
	c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
	/* start statistics polling */
	schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(1000));

211
	dev->active = true;
212
	return 0;
213
err_release_firmware:
214
	release_firmware(fw);
215
err:
216
	dev_dbg(&client->dev, "failed=%d\n", ret);
217
	return ret;
218 219 220 221
}

static int si2157_sleep(struct dvb_frontend *fe)
{
222 223
	struct i2c_client *client = fe->tuner_priv;
	struct si2157_dev *dev = i2c_get_clientdata(client);
224 225
	int ret;
	struct si2157_cmd cmd;
226

227
	dev_dbg(&client->dev, "\n");
228

229
	dev->active = false;
230

231 232 233
	/* stop statistics polling */
	cancel_delayed_work_sync(&dev->stat_work);

234 235 236 237
	/* standby */
	memcpy(cmd.args, "\x16\x00", 2);
	cmd.wlen = 2;
	cmd.rlen = 1;
238
	ret = si2157_cmd_execute(client, &cmd);
239 240 241
	if (ret)
		goto err;

242
	return 0;
243
err:
244
	dev_dbg(&client->dev, "failed=%d\n", ret);
245
	return ret;
246 247 248 249
}

static int si2157_set_params(struct dvb_frontend *fe)
{
250 251
	struct i2c_client *client = fe->tuner_priv;
	struct si2157_dev *dev = i2c_get_clientdata(client);
252 253 254
	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
	int ret;
	struct si2157_cmd cmd;
255
	u8 bandwidth, delivery_system;
256
	u32 if_frequency = 5000000;
257

258
	dev_dbg(&client->dev,
O
Olli Salonen 已提交
259
			"delivery_system=%d frequency=%u bandwidth_hz=%u\n",
260
			c->delivery_system, c->frequency, c->bandwidth_hz);
261

262
	if (!dev->active) {
263 264 265 266
		ret = -EAGAIN;
		goto err;
	}

267 268 269 270 271 272 273 274 275 276
	if (c->bandwidth_hz <= 6000000)
		bandwidth = 0x06;
	else if (c->bandwidth_hz <= 7000000)
		bandwidth = 0x07;
	else if (c->bandwidth_hz <= 8000000)
		bandwidth = 0x08;
	else
		bandwidth = 0x0f;

	switch (c->delivery_system) {
277 278
	case SYS_ATSC:
			delivery_system = 0x00;
279
			if_frequency = 3250000;
280
			break;
281 282
	case SYS_DVBC_ANNEX_B:
			delivery_system = 0x10;
283
			if_frequency = 4000000;
284
			break;
285 286 287 288 289 290 291 292 293 294 295 296 297 298
	case SYS_DVBT:
	case SYS_DVBT2: /* it seems DVB-T and DVB-T2 both are 0x20 here */
			delivery_system = 0x20;
			break;
	case SYS_DVBC_ANNEX_A:
			delivery_system = 0x30;
			break;
	default:
			ret = -EINVAL;
			goto err;
	}

	memcpy(cmd.args, "\x14\x00\x03\x07\x00\x00", 6);
	cmd.args[4] = delivery_system | bandwidth;
299
	if (dev->inversion)
300
		cmd.args[5] = 0x01;
301
	cmd.wlen = 6;
302
	cmd.rlen = 4;
303
	ret = si2157_cmd_execute(client, &cmd);
304 305 306
	if (ret)
		goto err;

307
	if (dev->chiptype == SI2157_CHIPTYPE_SI2146)
308 309
		memcpy(cmd.args, "\x14\x00\x02\x07\x00\x01", 6);
	else
310 311
		memcpy(cmd.args, "\x14\x00\x02\x07\x00\x00", 6);
	cmd.args[4] = dev->if_port;
312 313
	cmd.wlen = 6;
	cmd.rlen = 4;
314
	ret = si2157_cmd_execute(client, &cmd);
315 316 317
	if (ret)
		goto err;

318 319 320 321 322 323 324 325 326 327 328 329 330 331
	/* set if frequency if needed */
	if (if_frequency != dev->if_frequency) {
		memcpy(cmd.args, "\x14\x00\x06\x07", 4);
		cmd.args[4] = (if_frequency / 1000) & 0xff;
		cmd.args[5] = ((if_frequency / 1000) >> 8) & 0xff;
		cmd.wlen = 6;
		cmd.rlen = 4;
		ret = si2157_cmd_execute(client, &cmd);
		if (ret)
			goto err;

		dev->if_frequency = if_frequency;
	}

332
	/* set frequency */
333
	memcpy(cmd.args, "\x41\x00\x00\x00\x00\x00\x00\x00", 8);
334 335 336 337
	cmd.args[4] = (c->frequency >>  0) & 0xff;
	cmd.args[5] = (c->frequency >>  8) & 0xff;
	cmd.args[6] = (c->frequency >> 16) & 0xff;
	cmd.args[7] = (c->frequency >> 24) & 0xff;
338 339
	cmd.wlen = 8;
	cmd.rlen = 1;
340
	ret = si2157_cmd_execute(client, &cmd);
341 342 343 344 345
	if (ret)
		goto err;

	return 0;
err:
346
	dev_dbg(&client->dev, "failed=%d\n", ret);
347 348 349
	return ret;
}

350 351
static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
{
352 353 354 355
	struct i2c_client *client = fe->tuner_priv;
	struct si2157_dev *dev = i2c_get_clientdata(client);

	*frequency = dev->if_frequency;
356 357 358
	return 0;
}

359
static const struct dvb_tuner_ops si2157_ops = {
360
	.info = {
C
CrazyCat 已提交
361
		.name           = "Silicon Labs Si2146/2147/2148/2157/2158",
362
		.frequency_min  = 55000000,
363 364 365 366 367 368
		.frequency_max  = 862000000,
	},

	.init = si2157_init,
	.sleep = si2157_sleep,
	.set_params = si2157_set_params,
369
	.get_if_frequency = si2157_get_if_frequency,
370 371
};

372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
static void si2157_stat_work(struct work_struct *work)
{
	struct si2157_dev *dev = container_of(work, struct si2157_dev, stat_work.work);
	struct dvb_frontend *fe = dev->fe;
	struct i2c_client *client = fe->tuner_priv;
	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
	struct si2157_cmd cmd;
	int ret;

	dev_dbg(&client->dev, "\n");

	memcpy(cmd.args, "\x42\x00", 2);
	cmd.wlen = 2;
	cmd.rlen = 12;
	ret = si2157_cmd_execute(client, &cmd);
	if (ret)
		goto err;

	c->strength.stat[0].scale = FE_SCALE_DECIBEL;
	c->strength.stat[0].svalue = (s8) cmd.args[3] * 1000;

	schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000));
	return;
err:
	c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
	dev_dbg(&client->dev, "failed=%d\n", ret);
}

400 401 402 403 404
static int si2157_probe(struct i2c_client *client,
		const struct i2c_device_id *id)
{
	struct si2157_config *cfg = client->dev.platform_data;
	struct dvb_frontend *fe = cfg->fe;
405
	struct si2157_dev *dev;
406 407 408
	struct si2157_cmd cmd;
	int ret;

409 410
	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev) {
411
		ret = -ENOMEM;
O
Olli Salonen 已提交
412
		dev_err(&client->dev, "kzalloc() failed\n");
413 414 415
		goto err;
	}

416
	i2c_set_clientdata(client, dev);
417 418
	dev->fe = cfg->fe;
	dev->inversion = cfg->inversion;
419
	dev->if_port = cfg->if_port;
420 421
	dev->fw_loaded = false;
	dev->chiptype = (u8)id->driver_data;
422
	dev->if_frequency = 5000000; /* default value of property 0x0706 */
423
	mutex_init(&dev->i2c_mutex);
424
	INIT_DELAYED_WORK(&dev->stat_work, si2157_stat_work);
425 426

	/* check if the tuner is there */
427 428
	cmd.wlen = 0;
	cmd.rlen = 1;
429
	ret = si2157_cmd_execute(client, &cmd);
430
	if (ret)
431
		goto err_kfree;
432

433
	memcpy(&fe->ops.tuner_ops, &si2157_ops, sizeof(struct dvb_tuner_ops));
434
	fe->tuner_priv = client;
435

436
	dev_info(&client->dev, "Silicon Labs %s successfully attached\n",
437
			dev->chiptype == SI2157_CHIPTYPE_SI2146 ?
C
CrazyCat 已提交
438
			"Si2146" : "Si2147/2148/2157/2158");
439

440
	return 0;
441 442 443

err_kfree:
	kfree(dev);
444
err:
O
Olli Salonen 已提交
445
	dev_dbg(&client->dev, "failed=%d\n", ret);
446 447 448 449 450
	return ret;
}

static int si2157_remove(struct i2c_client *client)
{
451 452
	struct si2157_dev *dev = i2c_get_clientdata(client);
	struct dvb_frontend *fe = dev->fe;
453

O
Olli Salonen 已提交
454
	dev_dbg(&client->dev, "\n");
455 456 457

	memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
	fe->tuner_priv = NULL;
458
	kfree(dev);
459 460 461 462

	return 0;
}

463 464 465
static const struct i2c_device_id si2157_id_table[] = {
	{"si2157", SI2157_CHIPTYPE_SI2157},
	{"si2146", SI2157_CHIPTYPE_SI2146},
466 467
	{}
};
468
MODULE_DEVICE_TABLE(i2c, si2157_id_table);
469 470 471 472 473 474 475

static struct i2c_driver si2157_driver = {
	.driver = {
		.name	= "si2157",
	},
	.probe		= si2157_probe,
	.remove		= si2157_remove,
476
	.id_table	= si2157_id_table,
477 478 479 480
};

module_i2c_driver(si2157_driver);

C
CrazyCat 已提交
481
MODULE_DESCRIPTION("Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver");
482 483
MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
MODULE_LICENSE("GPL");
484
MODULE_FIRMWARE(SI2158_A20_FIRMWARE);