si2157.c revision 6cc8a35dcf6bceb4c6db38ae7862d826b3afb6a2
1ba92ae0f83d415cb764f50cf4e47970011fbe2afAntti Palosaari/* 21b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen * Silicon Labs Si2157/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 58e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari dev_dbg(&s->client->dev, "%s: cmd execution took %d ms\n", 59e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari __func__, 60e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari jiffies_to_msecs(jiffies) - 61e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari (jiffies_to_msecs(timeout) - TIMEOUT)); 62930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 63e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari if (!((cmd->args[0] >> 7) & 0x01)) { 64e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari ret = -ETIMEDOUT; 65e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari goto err_mutex_unlock; 66e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari } 67930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari } 68930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 69e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari ret = 0; 70e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari 71930a873081986393f6e7e0fb9275753c1485277bAntti Palosaarierr_mutex_unlock: 72930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari mutex_unlock(&s->i2c_mutex); 73930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari if (ret) 74930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari goto err; 75930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 76930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return 0; 77930a873081986393f6e7e0fb9275753c1485277bAntti Palosaarierr: 78930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); 79930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return ret; 80930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari} 81930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 82930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic int si2157_init(struct dvb_frontend *fe) 83930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari{ 84930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157 *s = fe->tuner_priv; 857d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari int ret, len, remaining; 86b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen struct si2157_cmd cmd; 871b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen const struct firmware *fw = NULL; 881b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen u8 *fw_file; 897d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari unsigned int chip_id; 90930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 91930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari dev_dbg(&s->client->dev, "%s:\n", __func__); 92930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 93b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen /* configure? */ 94b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15); 95b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen cmd.wlen = 15; 96b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen cmd.rlen = 1; 97b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen ret = si2157_cmd_execute(s, &cmd); 98b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen if (ret) 99b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen goto err; 100b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen 101b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen /* query chip revision */ 102b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen memcpy(cmd.args, "\x02", 1); 103b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen cmd.wlen = 1; 104b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen cmd.rlen = 13; 105b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen ret = si2157_cmd_execute(s, &cmd); 106b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen if (ret) 107b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen goto err; 108b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen 1097d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 | 1107d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari cmd.args[4] << 0; 1117d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari 1127d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari #define SI2158_A20 ('A' << 24 | 58 << 16 | '2' << 8 | '0' << 0) 1137d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari #define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0) 1147d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari 1157d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari switch (chip_id) { 1167d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari case SI2158_A20: 1177d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari fw_file = SI2158_A20_FIRMWARE; 1187d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari break; 1197d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari case SI2157_A30: 1207d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari goto skip_fw_download; 1217d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari break; 1227d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari default: 1237d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari dev_err(&s->client->dev, 1247d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari "%s: unkown chip version Si21%d-%c%c%c\n", 1257d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari KBUILD_MODNAME, cmd.args[2], cmd.args[1], 1267d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari cmd.args[3], cmd.args[4]); 1277d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari ret = -EINVAL; 1287d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari goto err; 1297d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari } 1301b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1317d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari /* cold state - try to download firmware */ 1327d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari dev_info(&s->client->dev, "%s: found a '%s' in cold state\n", 1337d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari KBUILD_MODNAME, si2157_ops.info.name); 1341b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1357d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari /* request the firmware, this will block and timeout */ 1367d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari ret = request_firmware(&fw, fw_file, &s->client->dev); 1377d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari if (ret) { 1387d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari dev_err(&s->client->dev, "%s: firmware file '%s' not found\n", 1397d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari KBUILD_MODNAME, fw_file); 1407d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari goto err; 1417d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari } 1421b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1437d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari /* firmware should be n chunks of 17 bytes */ 1447d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari if (fw->size % 17 != 0) { 1457d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari dev_err(&s->client->dev, "%s: firmware file '%s' is invalid\n", 1461b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen KBUILD_MODNAME, fw_file); 1477d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari ret = -EINVAL; 1487d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari goto err; 1497d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari } 1501b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1517d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari dev_info(&s->client->dev, "%s: downloading firmware from file '%s'\n", 1527d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari KBUILD_MODNAME, fw_file); 1531b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1547d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari for (remaining = fw->size; remaining > 0; remaining -= 17) { 1557d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari len = fw->data[fw->size - remaining]; 1567d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); 1577d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari cmd.wlen = len; 1587d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari cmd.rlen = 1; 1597d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari ret = si2157_cmd_execute(s, &cmd); 1607d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari if (ret) { 1617d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari dev_err(&s->client->dev, 1627d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari "%s: firmware download failed=%d\n", 1637d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari KBUILD_MODNAME, ret); 1647d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari goto err; 1651b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen } 1661b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen } 1671b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1687d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari release_firmware(fw); 1697d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari fw = NULL; 1707d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari 1717d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaariskip_fw_download: 172b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen /* reboot the tuner with new firmware? */ 173b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen memcpy(cmd.args, "\x01\x01", 2); 174b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen cmd.wlen = 2; 175b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen cmd.rlen = 1; 176b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen ret = si2157_cmd_execute(s, &cmd); 177b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen if (ret) 178b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen goto err; 179b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen 180930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari s->active = true; 181930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 182930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return 0; 183b154121c6470b927794ff7ed17355aedfaa073caOlli Salonenerr: 1847d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari if (fw) 1857d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari release_firmware(fw); 1867d6bc608e062f6b6667c9eeeb17055f017ecadb1Antti Palosaari 187b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); 188b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen return ret; 189930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari} 190930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 191930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic int si2157_sleep(struct dvb_frontend *fe) 192930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari{ 193930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157 *s = fe->tuner_priv; 194a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari int ret; 195a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari struct si2157_cmd cmd; 196930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 197930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari dev_dbg(&s->client->dev, "%s:\n", __func__); 198930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 199930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari s->active = false; 200930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 201a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari memcpy(cmd.args, "\x13", 1); 202e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari cmd.wlen = 1; 203e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari cmd.rlen = 0; 204a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari ret = si2157_cmd_execute(s, &cmd); 205a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari if (ret) 206a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari goto err; 207a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari 208930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return 0; 209a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaarierr: 210a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); 211a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari return ret; 212930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari} 213930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 214930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic int si2157_set_params(struct dvb_frontend *fe) 215930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari{ 216930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157 *s = fe->tuner_priv; 217930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct dtv_frontend_properties *c = &fe->dtv_property_cache; 218930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari int ret; 219930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157_cmd cmd; 220a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen u8 bandwidth, delivery_system; 221930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 222930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari dev_dbg(&s->client->dev, 223930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari "%s: delivery_system=%d frequency=%u bandwidth_hz=%u\n", 224930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari __func__, c->delivery_system, c->frequency, 225930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari c->bandwidth_hz); 226930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 227930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari if (!s->active) { 228930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari ret = -EAGAIN; 229930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari goto err; 230930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari } 231930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 232a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen if (c->bandwidth_hz <= 6000000) 233a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen bandwidth = 0x06; 234a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen else if (c->bandwidth_hz <= 7000000) 235a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen bandwidth = 0x07; 236a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen else if (c->bandwidth_hz <= 8000000) 237a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen bandwidth = 0x08; 238a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen else 239a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen bandwidth = 0x0f; 240a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen 241a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen switch (c->delivery_system) { 242a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen case SYS_DVBT: 243a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen case SYS_DVBT2: /* it seems DVB-T and DVB-T2 both are 0x20 here */ 244a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen delivery_system = 0x20; 245a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen break; 246a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen case SYS_DVBC_ANNEX_A: 247a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen delivery_system = 0x30; 248a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen break; 249a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen default: 250a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen ret = -EINVAL; 251a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen goto err; 252a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen } 253a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen 254a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen memcpy(cmd.args, "\x14\x00\x03\x07\x00\x00", 6); 255a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen cmd.args[4] = delivery_system | bandwidth; 256a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen cmd.wlen = 6; 257a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen cmd.rlen = 1; 258a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen ret = si2157_cmd_execute(s, &cmd); 259a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen if (ret) 260a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen goto err; 261a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen 262930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari /* set frequency */ 263b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen memcpy(cmd.args, "\x41\x00\x00\x00\x00\x00\x00\x00", 8); 264930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari cmd.args[4] = (c->frequency >> 0) & 0xff; 265930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari cmd.args[5] = (c->frequency >> 8) & 0xff; 266930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari cmd.args[6] = (c->frequency >> 16) & 0xff; 267930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari cmd.args[7] = (c->frequency >> 24) & 0xff; 268e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari cmd.wlen = 8; 269e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari cmd.rlen = 1; 270930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari ret = si2157_cmd_execute(s, &cmd); 271930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari if (ret) 272930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari goto err; 273930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 274930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return 0; 275930a873081986393f6e7e0fb9275753c1485277bAntti Palosaarierr: 276930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); 277930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return ret; 278930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari} 279930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 2806cc8a35dcf6bceb4c6db38ae7862d826b3afb6a2Olli Salonenstatic const struct dvb_tuner_ops si2157_ops = { 281930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .info = { 2821b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen .name = "Silicon Labs Si2157/Si2158", 283ae4c8919bb761c7f209fb260a82304a54616da0dAntti Palosaari .frequency_min = 110000000, 284930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .frequency_max = 862000000, 285930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari }, 286930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 287930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .init = si2157_init, 288930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .sleep = si2157_sleep, 289930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .set_params = si2157_set_params, 290930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari}; 291930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 292930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic int si2157_probe(struct i2c_client *client, 293930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari const struct i2c_device_id *id) 294930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari{ 295930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157_config *cfg = client->dev.platform_data; 296930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct dvb_frontend *fe = cfg->fe; 297930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157 *s; 298930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157_cmd cmd; 299930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari int ret; 300930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 301930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari s = kzalloc(sizeof(struct si2157), GFP_KERNEL); 302930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari if (!s) { 303930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari ret = -ENOMEM; 304930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); 305930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari goto err; 306930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari } 307930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 308930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari s->client = client; 309930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari s->fe = cfg->fe; 310930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari mutex_init(&s->i2c_mutex); 311930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 312930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari /* check if the tuner is there */ 313e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari cmd.wlen = 0; 314e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari cmd.rlen = 1; 315930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari ret = si2157_cmd_execute(s, &cmd); 316930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari if (ret) 317930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari goto err; 318930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 319930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari fe->tuner_priv = s; 3206cc8a35dcf6bceb4c6db38ae7862d826b3afb6a2Olli Salonen memcpy(&fe->ops.tuner_ops, &si2157_ops, 321930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari sizeof(struct dvb_tuner_ops)); 322930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 323930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari i2c_set_clientdata(client, s); 324930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 325930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari dev_info(&s->client->dev, 3261b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen "%s: Silicon Labs Si2157/Si2158 successfully attached\n", 327930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari KBUILD_MODNAME); 328930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return 0; 329930a873081986393f6e7e0fb9275753c1485277bAntti Palosaarierr: 330930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret); 331930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari kfree(s); 332930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 333930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return ret; 334930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari} 335930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 336930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic int si2157_remove(struct i2c_client *client) 337930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari{ 338930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157 *s = i2c_get_clientdata(client); 339930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct dvb_frontend *fe = s->fe; 340930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 341930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari dev_dbg(&client->dev, "%s:\n", __func__); 342930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 343930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); 344930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari fe->tuner_priv = NULL; 345930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari kfree(s); 346930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 347930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return 0; 348930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari} 349930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 350930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic const struct i2c_device_id si2157_id[] = { 351930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari {"si2157", 0}, 352930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari {} 353930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari}; 354930a873081986393f6e7e0fb9275753c1485277bAntti PalosaariMODULE_DEVICE_TABLE(i2c, si2157_id); 355930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 356930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic struct i2c_driver si2157_driver = { 357930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .driver = { 358930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .owner = THIS_MODULE, 359930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .name = "si2157", 360930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari }, 361930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .probe = si2157_probe, 362930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .remove = si2157_remove, 363930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .id_table = si2157_id, 364930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari}; 365930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 366930a873081986393f6e7e0fb9275753c1485277bAntti Palosaarimodule_i2c_driver(si2157_driver); 367930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 3681b92373f4b845019064d6a7c47b2ba72ebac191cOlli SalonenMODULE_DESCRIPTION("Silicon Labs Si2157/Si2158 silicon tuner driver"); 369930a873081986393f6e7e0fb9275753c1485277bAntti PalosaariMODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); 370930a873081986393f6e7e0fb9275753c1485277bAntti PalosaariMODULE_LICENSE("GPL"); 371bac53a2c604779297ac8ee54ce7eda4cc07b65f5Antti PalosaariMODULE_FIRMWARE(SI2158_A20_FIRMWARE); 372