1ba92ae0f83d415cb764f50cf4e47970011fbe2afAntti Palosaari/* 2d87a50586dbeb2b6019b9e695799838401f21db6Olli Salonen * Silicon Labs Si2147/2157/2158 silicon tuner driver 3ba92ae0f83d415cb764f50cf4e47970011fbe2afAntti Palosaari * 4ba92ae0f83d415cb764f50cf4e47970011fbe2afAntti Palosaari * Copyright (C) 2014 Antti Palosaari <crope@iki.fi> 5ba92ae0f83d415cb764f50cf4e47970011fbe2afAntti Palosaari * 6ba92ae0f83d415cb764f50cf4e47970011fbe2afAntti Palosaari * This program is free software; you can redistribute it and/or modify 7ba92ae0f83d415cb764f50cf4e47970011fbe2afAntti Palosaari * it under the terms of the GNU General Public License as published by 8ba92ae0f83d415cb764f50cf4e47970011fbe2afAntti Palosaari * the Free Software Foundation; either version 2 of the License, or 9ba92ae0f83d415cb764f50cf4e47970011fbe2afAntti Palosaari * (at your option) any later version. 10ba92ae0f83d415cb764f50cf4e47970011fbe2afAntti Palosaari * 11ba92ae0f83d415cb764f50cf4e47970011fbe2afAntti Palosaari * This program is distributed in the hope that it will be useful, 12ba92ae0f83d415cb764f50cf4e47970011fbe2afAntti Palosaari * but WITHOUT ANY WARRANTY; without even the implied warranty of 13ba92ae0f83d415cb764f50cf4e47970011fbe2afAntti Palosaari * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14ba92ae0f83d415cb764f50cf4e47970011fbe2afAntti Palosaari * GNU General Public License for more details. 15ba92ae0f83d415cb764f50cf4e47970011fbe2afAntti Palosaari */ 16ba92ae0f83d415cb764f50cf4e47970011fbe2afAntti Palosaari 17930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari#include "si2157_priv.h" 18930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 191b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonenstatic const struct dvb_tuner_ops si2157_ops; 201b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 21930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari/* execute firmware command */ 22930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd) 23930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari{ 24930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari int ret; 25930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari unsigned long timeout; 26930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 27930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari mutex_lock(&s->i2c_mutex); 28930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 29e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari if (cmd->wlen) { 30930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari /* write cmd and args for firmware */ 31e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari ret = i2c_master_send(s->client, cmd->args, cmd->wlen); 32930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari if (ret < 0) { 33930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari goto err_mutex_unlock; 34e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari } else if (ret != cmd->wlen) { 35930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari ret = -EREMOTEIO; 36930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari goto err_mutex_unlock; 37930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari } 38930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari } 39930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 40e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari if (cmd->rlen) { 41e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari /* wait cmd execution terminate */ 42e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari #define TIMEOUT 80 43e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari timeout = jiffies + msecs_to_jiffies(TIMEOUT); 44e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari while (!time_after(jiffies, timeout)) { 45e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari ret = i2c_master_recv(s->client, cmd->args, cmd->rlen); 46e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari if (ret < 0) { 47e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari goto err_mutex_unlock; 48e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari } else if (ret != cmd->rlen) { 49e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari ret = -EREMOTEIO; 50e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari goto err_mutex_unlock; 51e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari } 52e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari 53e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari /* firmware ready? */ 54e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari if ((cmd->args[0] >> 7) & 0x01) 55e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari break; 56930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari } 57930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 5867d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen dev_dbg(&s->client->dev, "cmd execution took %d ms\n", 59e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari jiffies_to_msecs(jiffies) - 60e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari (jiffies_to_msecs(timeout) - TIMEOUT)); 61930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 62e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari if (!((cmd->args[0] >> 7) & 0x01)) { 63e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari ret = -ETIMEDOUT; 64e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari goto err_mutex_unlock; 65e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari } 66930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari } 67930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 68e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari ret = 0; 69e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari 70930a873081986393f6e7e0fb9275753c1485277bAntti Palosaarierr_mutex_unlock: 71930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari mutex_unlock(&s->i2c_mutex); 72930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari if (ret) 73930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari goto err; 74930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 75930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return 0; 76930a873081986393f6e7e0fb9275753c1485277bAntti Palosaarierr: 7767d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen dev_dbg(&s->client->dev, "failed=%d\n", ret); 78930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return ret; 79930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari} 80930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 81930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic int si2157_init(struct dvb_frontend *fe) 82930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari{ 83930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157 *s = fe->tuner_priv; 847d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari int ret, len, remaining; 85b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen struct si2157_cmd cmd; 861b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen const struct firmware *fw = NULL; 871b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen u8 *fw_file; 887d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari unsigned int chip_id; 89930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 9067d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen dev_dbg(&s->client->dev, "\n"); 91930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 924cbf6ed910c88d7f6c15304f9a5a3ed86290dc06Olli Salonen if (s->fw_loaded) 934cbf6ed910c88d7f6c15304f9a5a3ed86290dc06Olli Salonen goto warm; 944cbf6ed910c88d7f6c15304f9a5a3ed86290dc06Olli Salonen 954cbf6ed910c88d7f6c15304f9a5a3ed86290dc06Olli Salonen /* power up */ 96b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15); 97b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen cmd.wlen = 15; 98b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen cmd.rlen = 1; 99b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen ret = si2157_cmd_execute(s, &cmd); 100b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen if (ret) 101b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen goto err; 102b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen 103b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen /* query chip revision */ 104b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen memcpy(cmd.args, "\x02", 1); 105b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen cmd.wlen = 1; 106b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen cmd.rlen = 13; 107b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen ret = si2157_cmd_execute(s, &cmd); 108b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen if (ret) 109b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen goto err; 110b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen 1117d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 | 1127d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari cmd.args[4] << 0; 1137d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari 1147d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari #define SI2158_A20 ('A' << 24 | 58 << 16 | '2' << 8 | '0' << 0) 1157d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari #define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0) 116d87a50586dbeb2b6019b9e695799838401f21db6Olli Salonen #define SI2147_A30 ('A' << 24 | 47 << 16 | '3' << 8 | '0' << 0) 1177d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari 1187d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari switch (chip_id) { 1197d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari case SI2158_A20: 1207d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari fw_file = SI2158_A20_FIRMWARE; 1217d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari break; 1227d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari case SI2157_A30: 123d87a50586dbeb2b6019b9e695799838401f21db6Olli Salonen case SI2147_A30: 1247d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari goto skip_fw_download; 1257d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari break; 1267d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari default: 1277d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari dev_err(&s->client->dev, 12867d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen "unknown chip version Si21%d-%c%c%c\n", 12967d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen cmd.args[2], cmd.args[1], 1307d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari cmd.args[3], cmd.args[4]); 1317d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari ret = -EINVAL; 1327d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari goto err; 1337d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari } 1341b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1357d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari /* cold state - try to download firmware */ 13667d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen dev_info(&s->client->dev, "found a '%s' in cold state\n", 13767d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen si2157_ops.info.name); 1381b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1397d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari /* request the firmware, this will block and timeout */ 1407d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari ret = request_firmware(&fw, fw_file, &s->client->dev); 1417d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari if (ret) { 14267d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen dev_err(&s->client->dev, "firmware file '%s' not found\n", 14367d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen fw_file); 1447d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari goto err; 1457d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari } 1461b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1477d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari /* firmware should be n chunks of 17 bytes */ 1487d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari if (fw->size % 17 != 0) { 14967d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen dev_err(&s->client->dev, "firmware file '%s' is invalid\n", 15067d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen fw_file); 1517d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari ret = -EINVAL; 1527d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari goto err; 1537d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari } 1541b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 15567d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen dev_info(&s->client->dev, "downloading firmware from file '%s'\n", 15667d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen fw_file); 1571b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1587d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari for (remaining = fw->size; remaining > 0; remaining -= 17) { 1597d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari len = fw->data[fw->size - remaining]; 1607d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); 1617d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari cmd.wlen = len; 1627d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari cmd.rlen = 1; 1637d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari ret = si2157_cmd_execute(s, &cmd); 1647d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari if (ret) { 1657d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari dev_err(&s->client->dev, 16667d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen "firmware download failed=%d\n", 16767d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen ret); 1687d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari goto err; 1691b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen } 1701b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen } 1711b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1727d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari release_firmware(fw); 1737d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari fw = NULL; 1747d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari 1757d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaariskip_fw_download: 176b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen /* reboot the tuner with new firmware? */ 177b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen memcpy(cmd.args, "\x01\x01", 2); 178b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen cmd.wlen = 2; 179b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen cmd.rlen = 1; 180b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen ret = si2157_cmd_execute(s, &cmd); 181b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen if (ret) 182b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen goto err; 183b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen 1844cbf6ed910c88d7f6c15304f9a5a3ed86290dc06Olli Salonen s->fw_loaded = true; 185930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 1864cbf6ed910c88d7f6c15304f9a5a3ed86290dc06Olli Salonenwarm: 1874cbf6ed910c88d7f6c15304f9a5a3ed86290dc06Olli Salonen s->active = true; 188930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return 0; 1894cbf6ed910c88d7f6c15304f9a5a3ed86290dc06Olli Salonen 190b154121c6470b927794ff7ed17355aedfaa073caOlli Salonenerr: 1917d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari if (fw) 1927d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari release_firmware(fw); 1937d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari 19467d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen dev_dbg(&s->client->dev, "failed=%d\n", ret); 195b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen return ret; 196930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari} 197930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 198930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic int si2157_sleep(struct dvb_frontend *fe) 199930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari{ 200930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157 *s = fe->tuner_priv; 201a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari int ret; 202a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari struct si2157_cmd cmd; 203930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 20467d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen dev_dbg(&s->client->dev, "\n"); 205930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 206930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari s->active = false; 207930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 2080e38233d329e463a64146080c008d8044651bd3fOlli Salonen /* standby */ 2090e38233d329e463a64146080c008d8044651bd3fOlli Salonen memcpy(cmd.args, "\x16\x00", 2); 2100e38233d329e463a64146080c008d8044651bd3fOlli Salonen cmd.wlen = 2; 2110e38233d329e463a64146080c008d8044651bd3fOlli Salonen cmd.rlen = 1; 212a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari ret = si2157_cmd_execute(s, &cmd); 213a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari if (ret) 214a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari goto err; 215a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari 216930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return 0; 217a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaarierr: 21867d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen dev_dbg(&s->client->dev, "failed=%d\n", ret); 219a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari return ret; 220930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari} 221930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 222930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic int si2157_set_params(struct dvb_frontend *fe) 223930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari{ 224930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157 *s = fe->tuner_priv; 225930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct dtv_frontend_properties *c = &fe->dtv_property_cache; 226930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari int ret; 227930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157_cmd cmd; 228a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen u8 bandwidth, delivery_system; 229930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 230930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari dev_dbg(&s->client->dev, 23167d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen "delivery_system=%d frequency=%u bandwidth_hz=%u\n", 23267d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen c->delivery_system, c->frequency, 233930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari c->bandwidth_hz); 234930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 235930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari if (!s->active) { 236930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari ret = -EAGAIN; 237930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari goto err; 238930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari } 239930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 240a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen if (c->bandwidth_hz <= 6000000) 241a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen bandwidth = 0x06; 242a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen else if (c->bandwidth_hz <= 7000000) 243a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen bandwidth = 0x07; 244a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen else if (c->bandwidth_hz <= 8000000) 245a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen bandwidth = 0x08; 246a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen else 247a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen bandwidth = 0x0f; 248a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen 249a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen switch (c->delivery_system) { 2505cd62db71dcf3a4722959d9f90ca4c5ca09502cbOlli Salonen case SYS_ATSC: 2515cd62db71dcf3a4722959d9f90ca4c5ca09502cbOlli Salonen delivery_system = 0x00; 2525cd62db71dcf3a4722959d9f90ca4c5ca09502cbOlli Salonen break; 253a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen case SYS_DVBT: 254a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen case SYS_DVBT2: /* it seems DVB-T and DVB-T2 both are 0x20 here */ 255a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen delivery_system = 0x20; 256a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen break; 257a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen case SYS_DVBC_ANNEX_A: 258a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen delivery_system = 0x30; 259a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen break; 260a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen default: 261a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen ret = -EINVAL; 262a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen goto err; 263a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen } 264a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen 265a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen memcpy(cmd.args, "\x14\x00\x03\x07\x00\x00", 6); 266a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen cmd.args[4] = delivery_system | bandwidth; 26705024efe1264c3379135d7223f2c84c8f7ef2172Matthias Schwarzott if (s->inversion) 26805024efe1264c3379135d7223f2c84c8f7ef2172Matthias Schwarzott cmd.args[5] = 0x01; 269a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen cmd.wlen = 6; 270d87a50586dbeb2b6019b9e695799838401f21db6Olli Salonen cmd.rlen = 4; 271d87a50586dbeb2b6019b9e695799838401f21db6Olli Salonen ret = si2157_cmd_execute(s, &cmd); 272d87a50586dbeb2b6019b9e695799838401f21db6Olli Salonen if (ret) 273d87a50586dbeb2b6019b9e695799838401f21db6Olli Salonen goto err; 274d87a50586dbeb2b6019b9e695799838401f21db6Olli Salonen 275d87a50586dbeb2b6019b9e695799838401f21db6Olli Salonen memcpy(cmd.args, "\x14\x00\x02\x07\x01\x00", 6); 276d87a50586dbeb2b6019b9e695799838401f21db6Olli Salonen cmd.wlen = 6; 277d87a50586dbeb2b6019b9e695799838401f21db6Olli Salonen cmd.rlen = 4; 278a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen ret = si2157_cmd_execute(s, &cmd); 279a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen if (ret) 280a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen goto err; 281a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen 282930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari /* set frequency */ 283b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen memcpy(cmd.args, "\x41\x00\x00\x00\x00\x00\x00\x00", 8); 284930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari cmd.args[4] = (c->frequency >> 0) & 0xff; 285930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari cmd.args[5] = (c->frequency >> 8) & 0xff; 286930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari cmd.args[6] = (c->frequency >> 16) & 0xff; 287930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari cmd.args[7] = (c->frequency >> 24) & 0xff; 288e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari cmd.wlen = 8; 289e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari cmd.rlen = 1; 290930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari ret = si2157_cmd_execute(s, &cmd); 291930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari if (ret) 292930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari goto err; 293930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 294930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return 0; 295930a873081986393f6e7e0fb9275753c1485277bAntti Palosaarierr: 29667d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen dev_dbg(&s->client->dev, "failed=%d\n", ret); 297930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return ret; 298930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari} 299930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 3001140540da0e4b00978607244b58f06a528b819c7Matthias Schwarzottstatic int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 3011140540da0e4b00978607244b58f06a528b819c7Matthias Schwarzott{ 3021140540da0e4b00978607244b58f06a528b819c7Matthias Schwarzott *frequency = 5000000; /* default value of property 0x0706 */ 3031140540da0e4b00978607244b58f06a528b819c7Matthias Schwarzott return 0; 3041140540da0e4b00978607244b58f06a528b819c7Matthias Schwarzott} 3051140540da0e4b00978607244b58f06a528b819c7Matthias Schwarzott 3066cc8a35dcf6bceb4c6db38ae7862d826b3afb6a2Olli Salonenstatic const struct dvb_tuner_ops si2157_ops = { 307930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .info = { 3081b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen .name = "Silicon Labs Si2157/Si2158", 309ae4c8919bb761c7f209fb260a82304a54616da0dAntti Palosaari .frequency_min = 110000000, 310930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .frequency_max = 862000000, 311930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari }, 312930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 313930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .init = si2157_init, 314930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .sleep = si2157_sleep, 315930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .set_params = si2157_set_params, 3161140540da0e4b00978607244b58f06a528b819c7Matthias Schwarzott .get_if_frequency = si2157_get_if_frequency, 317930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari}; 318930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 319930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic int si2157_probe(struct i2c_client *client, 320930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari const struct i2c_device_id *id) 321930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari{ 322930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157_config *cfg = client->dev.platform_data; 323930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct dvb_frontend *fe = cfg->fe; 324930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157 *s; 325930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157_cmd cmd; 326930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari int ret; 327930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 328930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari s = kzalloc(sizeof(struct si2157), GFP_KERNEL); 329930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari if (!s) { 330930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari ret = -ENOMEM; 33167d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen dev_err(&client->dev, "kzalloc() failed\n"); 332930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari goto err; 333930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari } 334930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 335930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari s->client = client; 336930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari s->fe = cfg->fe; 33705024efe1264c3379135d7223f2c84c8f7ef2172Matthias Schwarzott s->inversion = cfg->inversion; 3384cbf6ed910c88d7f6c15304f9a5a3ed86290dc06Olli Salonen s->fw_loaded = false; 339930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari mutex_init(&s->i2c_mutex); 340930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 341930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari /* check if the tuner is there */ 342e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari cmd.wlen = 0; 343e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari cmd.rlen = 1; 344930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari ret = si2157_cmd_execute(s, &cmd); 345930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari if (ret) 346930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari goto err; 347930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 348930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari fe->tuner_priv = s; 3496cc8a35dcf6bceb4c6db38ae7862d826b3afb6a2Olli Salonen memcpy(&fe->ops.tuner_ops, &si2157_ops, 350930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari sizeof(struct dvb_tuner_ops)); 351930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 352930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari i2c_set_clientdata(client, s); 353930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 354930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari dev_info(&s->client->dev, 35567d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen "Silicon Labs Si2157/Si2158 successfully attached\n"); 356930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return 0; 357930a873081986393f6e7e0fb9275753c1485277bAntti Palosaarierr: 35867d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen dev_dbg(&client->dev, "failed=%d\n", ret); 359930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari kfree(s); 360930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 361930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return ret; 362930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari} 363930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 364930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic int si2157_remove(struct i2c_client *client) 365930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari{ 366930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157 *s = i2c_get_clientdata(client); 367930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct dvb_frontend *fe = s->fe; 368930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 36967d0113a224f0fb1be784f7553fdeafd82cadc6cOlli Salonen dev_dbg(&client->dev, "\n"); 370930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 371930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); 372930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari fe->tuner_priv = NULL; 373930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari kfree(s); 374930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 375930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return 0; 376930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari} 377930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 378930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic const struct i2c_device_id si2157_id[] = { 379930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari {"si2157", 0}, 380930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari {} 381930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari}; 382930a873081986393f6e7e0fb9275753c1485277bAntti PalosaariMODULE_DEVICE_TABLE(i2c, si2157_id); 383930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 384930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic struct i2c_driver si2157_driver = { 385930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .driver = { 386930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .owner = THIS_MODULE, 387930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .name = "si2157", 388930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari }, 389930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .probe = si2157_probe, 390930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .remove = si2157_remove, 391930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .id_table = si2157_id, 392930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari}; 393930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 394930a873081986393f6e7e0fb9275753c1485277bAntti Palosaarimodule_i2c_driver(si2157_driver); 395930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 3961b92373f4b845019064d6a7c47b2ba72ebac191cOlli SalonenMODULE_DESCRIPTION("Silicon Labs Si2157/Si2158 silicon tuner driver"); 397930a873081986393f6e7e0fb9275753c1485277bAntti PalosaariMODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); 398930a873081986393f6e7e0fb9275753c1485277bAntti PalosaariMODULE_LICENSE("GPL"); 399bac53a2c604779297ac8ee54ce7eda4cc07b65f5Antti PalosaariMODULE_FIRMWARE(SI2158_A20_FIRMWARE); 400