si2157.c revision 67d0113a224f0fb1be784f7553fdeafd82cadc6c
1/* 2 * Silicon Labs Si2157/2158 silicon tuner driver 3 * 4 * Copyright (C) 2014 Antti Palosaari <crope@iki.fi> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 */ 16 17#include "si2157_priv.h" 18 19static const struct dvb_tuner_ops si2157_ops; 20 21/* execute firmware command */ 22static int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd) 23{ 24 int ret; 25 unsigned long timeout; 26 27 mutex_lock(&s->i2c_mutex); 28 29 if (cmd->wlen) { 30 /* write cmd and args for firmware */ 31 ret = i2c_master_send(s->client, cmd->args, cmd->wlen); 32 if (ret < 0) { 33 goto err_mutex_unlock; 34 } else if (ret != cmd->wlen) { 35 ret = -EREMOTEIO; 36 goto err_mutex_unlock; 37 } 38 } 39 40 if (cmd->rlen) { 41 /* wait cmd execution terminate */ 42 #define TIMEOUT 80 43 timeout = jiffies + msecs_to_jiffies(TIMEOUT); 44 while (!time_after(jiffies, timeout)) { 45 ret = i2c_master_recv(s->client, cmd->args, cmd->rlen); 46 if (ret < 0) { 47 goto err_mutex_unlock; 48 } else if (ret != cmd->rlen) { 49 ret = -EREMOTEIO; 50 goto err_mutex_unlock; 51 } 52 53 /* firmware ready? */ 54 if ((cmd->args[0] >> 7) & 0x01) 55 break; 56 } 57 58 dev_dbg(&s->client->dev, "cmd execution took %d ms\n", 59 jiffies_to_msecs(jiffies) - 60 (jiffies_to_msecs(timeout) - TIMEOUT)); 61 62 if (!((cmd->args[0] >> 7) & 0x01)) { 63 ret = -ETIMEDOUT; 64 goto err_mutex_unlock; 65 } 66 } 67 68 ret = 0; 69 70err_mutex_unlock: 71 mutex_unlock(&s->i2c_mutex); 72 if (ret) 73 goto err; 74 75 return 0; 76err: 77 dev_dbg(&s->client->dev, "failed=%d\n", ret); 78 return ret; 79} 80 81static int si2157_init(struct dvb_frontend *fe) 82{ 83 struct si2157 *s = fe->tuner_priv; 84 int ret, len, remaining; 85 struct si2157_cmd cmd; 86 const struct firmware *fw = NULL; 87 u8 *fw_file; 88 unsigned int chip_id; 89 90 dev_dbg(&s->client->dev, "\n"); 91 92 /* configure? */ 93 memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15); 94 cmd.wlen = 15; 95 cmd.rlen = 1; 96 ret = si2157_cmd_execute(s, &cmd); 97 if (ret) 98 goto err; 99 100 /* query chip revision */ 101 memcpy(cmd.args, "\x02", 1); 102 cmd.wlen = 1; 103 cmd.rlen = 13; 104 ret = si2157_cmd_execute(s, &cmd); 105 if (ret) 106 goto err; 107 108 chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 | 109 cmd.args[4] << 0; 110 111 #define SI2158_A20 ('A' << 24 | 58 << 16 | '2' << 8 | '0' << 0) 112 #define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0) 113 114 switch (chip_id) { 115 case SI2158_A20: 116 fw_file = SI2158_A20_FIRMWARE; 117 break; 118 case SI2157_A30: 119 goto skip_fw_download; 120 break; 121 default: 122 dev_err(&s->client->dev, 123 "unknown chip version Si21%d-%c%c%c\n", 124 cmd.args[2], cmd.args[1], 125 cmd.args[3], cmd.args[4]); 126 ret = -EINVAL; 127 goto err; 128 } 129 130 /* cold state - try to download firmware */ 131 dev_info(&s->client->dev, "found a '%s' in cold state\n", 132 si2157_ops.info.name); 133 134 /* request the firmware, this will block and timeout */ 135 ret = request_firmware(&fw, fw_file, &s->client->dev); 136 if (ret) { 137 dev_err(&s->client->dev, "firmware file '%s' not found\n", 138 fw_file); 139 goto err; 140 } 141 142 /* firmware should be n chunks of 17 bytes */ 143 if (fw->size % 17 != 0) { 144 dev_err(&s->client->dev, "firmware file '%s' is invalid\n", 145 fw_file); 146 ret = -EINVAL; 147 goto err; 148 } 149 150 dev_info(&s->client->dev, "downloading firmware from file '%s'\n", 151 fw_file); 152 153 for (remaining = fw->size; remaining > 0; remaining -= 17) { 154 len = fw->data[fw->size - remaining]; 155 memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); 156 cmd.wlen = len; 157 cmd.rlen = 1; 158 ret = si2157_cmd_execute(s, &cmd); 159 if (ret) { 160 dev_err(&s->client->dev, 161 "firmware download failed=%d\n", 162 ret); 163 goto err; 164 } 165 } 166 167 release_firmware(fw); 168 fw = NULL; 169 170skip_fw_download: 171 /* reboot the tuner with new firmware? */ 172 memcpy(cmd.args, "\x01\x01", 2); 173 cmd.wlen = 2; 174 cmd.rlen = 1; 175 ret = si2157_cmd_execute(s, &cmd); 176 if (ret) 177 goto err; 178 179 s->active = true; 180 181 return 0; 182err: 183 if (fw) 184 release_firmware(fw); 185 186 dev_dbg(&s->client->dev, "failed=%d\n", ret); 187 return ret; 188} 189 190static int si2157_sleep(struct dvb_frontend *fe) 191{ 192 struct si2157 *s = fe->tuner_priv; 193 int ret; 194 struct si2157_cmd cmd; 195 196 dev_dbg(&s->client->dev, "\n"); 197 198 s->active = false; 199 200 memcpy(cmd.args, "\x13", 1); 201 cmd.wlen = 1; 202 cmd.rlen = 0; 203 ret = si2157_cmd_execute(s, &cmd); 204 if (ret) 205 goto err; 206 207 return 0; 208err: 209 dev_dbg(&s->client->dev, "failed=%d\n", ret); 210 return ret; 211} 212 213static int si2157_set_params(struct dvb_frontend *fe) 214{ 215 struct si2157 *s = fe->tuner_priv; 216 struct dtv_frontend_properties *c = &fe->dtv_property_cache; 217 int ret; 218 struct si2157_cmd cmd; 219 u8 bandwidth, delivery_system; 220 221 dev_dbg(&s->client->dev, 222 "delivery_system=%d frequency=%u bandwidth_hz=%u\n", 223 c->delivery_system, c->frequency, 224 c->bandwidth_hz); 225 226 if (!s->active) { 227 ret = -EAGAIN; 228 goto err; 229 } 230 231 if (c->bandwidth_hz <= 6000000) 232 bandwidth = 0x06; 233 else if (c->bandwidth_hz <= 7000000) 234 bandwidth = 0x07; 235 else if (c->bandwidth_hz <= 8000000) 236 bandwidth = 0x08; 237 else 238 bandwidth = 0x0f; 239 240 switch (c->delivery_system) { 241 case SYS_DVBT: 242 case SYS_DVBT2: /* it seems DVB-T and DVB-T2 both are 0x20 here */ 243 delivery_system = 0x20; 244 break; 245 case SYS_DVBC_ANNEX_A: 246 delivery_system = 0x30; 247 break; 248 default: 249 ret = -EINVAL; 250 goto err; 251 } 252 253 memcpy(cmd.args, "\x14\x00\x03\x07\x00\x00", 6); 254 cmd.args[4] = delivery_system | bandwidth; 255 if (s->inversion) 256 cmd.args[5] = 0x01; 257 cmd.wlen = 6; 258 cmd.rlen = 1; 259 ret = si2157_cmd_execute(s, &cmd); 260 if (ret) 261 goto err; 262 263 /* set frequency */ 264 memcpy(cmd.args, "\x41\x00\x00\x00\x00\x00\x00\x00", 8); 265 cmd.args[4] = (c->frequency >> 0) & 0xff; 266 cmd.args[5] = (c->frequency >> 8) & 0xff; 267 cmd.args[6] = (c->frequency >> 16) & 0xff; 268 cmd.args[7] = (c->frequency >> 24) & 0xff; 269 cmd.wlen = 8; 270 cmd.rlen = 1; 271 ret = si2157_cmd_execute(s, &cmd); 272 if (ret) 273 goto err; 274 275 return 0; 276err: 277 dev_dbg(&s->client->dev, "failed=%d\n", ret); 278 return ret; 279} 280 281static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 282{ 283 *frequency = 5000000; /* default value of property 0x0706 */ 284 return 0; 285} 286 287static const struct dvb_tuner_ops si2157_ops = { 288 .info = { 289 .name = "Silicon Labs Si2157/Si2158", 290 .frequency_min = 110000000, 291 .frequency_max = 862000000, 292 }, 293 294 .init = si2157_init, 295 .sleep = si2157_sleep, 296 .set_params = si2157_set_params, 297 .get_if_frequency = si2157_get_if_frequency, 298}; 299 300static int si2157_probe(struct i2c_client *client, 301 const struct i2c_device_id *id) 302{ 303 struct si2157_config *cfg = client->dev.platform_data; 304 struct dvb_frontend *fe = cfg->fe; 305 struct si2157 *s; 306 struct si2157_cmd cmd; 307 int ret; 308 309 s = kzalloc(sizeof(struct si2157), GFP_KERNEL); 310 if (!s) { 311 ret = -ENOMEM; 312 dev_err(&client->dev, "kzalloc() failed\n"); 313 goto err; 314 } 315 316 s->client = client; 317 s->fe = cfg->fe; 318 s->inversion = cfg->inversion; 319 mutex_init(&s->i2c_mutex); 320 321 /* check if the tuner is there */ 322 cmd.wlen = 0; 323 cmd.rlen = 1; 324 ret = si2157_cmd_execute(s, &cmd); 325 if (ret) 326 goto err; 327 328 fe->tuner_priv = s; 329 memcpy(&fe->ops.tuner_ops, &si2157_ops, 330 sizeof(struct dvb_tuner_ops)); 331 332 i2c_set_clientdata(client, s); 333 334 dev_info(&s->client->dev, 335 "Silicon Labs Si2157/Si2158 successfully attached\n"); 336 return 0; 337err: 338 dev_dbg(&client->dev, "failed=%d\n", ret); 339 kfree(s); 340 341 return ret; 342} 343 344static int si2157_remove(struct i2c_client *client) 345{ 346 struct si2157 *s = i2c_get_clientdata(client); 347 struct dvb_frontend *fe = s->fe; 348 349 dev_dbg(&client->dev, "\n"); 350 351 memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); 352 fe->tuner_priv = NULL; 353 kfree(s); 354 355 return 0; 356} 357 358static const struct i2c_device_id si2157_id[] = { 359 {"si2157", 0}, 360 {} 361}; 362MODULE_DEVICE_TABLE(i2c, si2157_id); 363 364static struct i2c_driver si2157_driver = { 365 .driver = { 366 .owner = THIS_MODULE, 367 .name = "si2157", 368 }, 369 .probe = si2157_probe, 370 .remove = si2157_remove, 371 .id_table = si2157_id, 372}; 373 374module_i2c_driver(si2157_driver); 375 376MODULE_DESCRIPTION("Silicon Labs Si2157/Si2158 silicon tuner driver"); 377MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); 378MODULE_LICENSE("GPL"); 379MODULE_FIRMWARE(SI2158_A20_FIRMWARE); 380