si2157.c revision a1dad50d8cdce0be71489c7544ffcd36cf80e994
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; 851b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen int ret, remaining; 86b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen struct si2157_cmd cmd; 871b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen u8 chip, len = 0; 881b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen const struct firmware *fw = NULL; 891b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen u8 *fw_file; 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 1091b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen chip = cmd.args[2]; /* 57 for Si2157, 58 for Si2158 */ 1101b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1111b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen /* Si2158 requires firmware download */ 1121b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen if (chip == 58) { 1131b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen if (((cmd.args[1] & 0x0f) == 1) && (cmd.args[3] == '2') && 1141b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen (cmd.args[4] == '0')) 1151b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen fw_file = SI2158_A20_FIRMWARE; 1161b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen else { 1171b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen dev_err(&s->client->dev, 1181b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen "%s: no firmware file for Si%d-%c%c defined\n", 1191b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen KBUILD_MODNAME, chip, cmd.args[3], cmd.args[4]); 1201b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen ret = -EINVAL; 1211b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen goto err; 1221b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen } 1231b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1241b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen /* cold state - try to download firmware */ 1251b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen dev_info(&s->client->dev, "%s: found a '%s' in cold state\n", 1261b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen KBUILD_MODNAME, si2157_ops.info.name); 1271b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1281b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen /* request the firmware, this will block and timeout */ 1291b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen ret = request_firmware(&fw, fw_file, &s->client->dev); 1301b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen if (ret) { 1311b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen dev_err(&s->client->dev, "%s: firmware file '%s' not found\n", 1321b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen KBUILD_MODNAME, fw_file); 1331b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen goto err; 1341b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen } 1351b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1361b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen dev_info(&s->client->dev, "%s: downloading firmware from file '%s'\n", 1371b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen KBUILD_MODNAME, fw_file); 1381b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1391b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen /* firmware should be n chunks of 17 bytes */ 1401b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen if (fw->size % 17 != 0) { 1411b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen dev_err(&s->client->dev, "%s: firmware file '%s' is invalid\n", 1421b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen KBUILD_MODNAME, fw_file); 1431b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen ret = -EINVAL; 1441b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen goto err; 1451b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen } 1461b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1471b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen for (remaining = fw->size; remaining > 0; remaining -= 17) { 1481b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen memcpy(&len, &fw->data[fw->size - remaining], 1); 1491b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], 1501b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen len); 1511b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen cmd.wlen = len; 1521b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen cmd.rlen = 1; 1531b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen ret = si2157_cmd_execute(s, &cmd); 1541b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen if (ret) { 1551b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen dev_err(&s->client->dev, 1561b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen "%s: firmware download failed=%d\n", 1571b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen KBUILD_MODNAME, ret); 1581b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen goto err; 1591b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen } 1601b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen } 1611b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1621b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen release_firmware(fw); 1631b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen fw = NULL; 1641b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 1651b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen } 1661b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen 167b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen /* reboot the tuner with new firmware? */ 168b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen memcpy(cmd.args, "\x01\x01", 2); 169b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen cmd.wlen = 2; 170b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen cmd.rlen = 1; 171b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen ret = si2157_cmd_execute(s, &cmd); 172b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen if (ret) 173b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen goto err; 174b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen 175930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari s->active = true; 176930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 177930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return 0; 178b154121c6470b927794ff7ed17355aedfaa073caOlli Salonenerr: 179b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); 180b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen return ret; 181930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari} 182930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 183930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic int si2157_sleep(struct dvb_frontend *fe) 184930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari{ 185930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157 *s = fe->tuner_priv; 186a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari int ret; 187a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari struct si2157_cmd cmd; 188930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 189930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari dev_dbg(&s->client->dev, "%s:\n", __func__); 190930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 191930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari s->active = false; 192930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 193a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari memcpy(cmd.args, "\x13", 1); 194e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari cmd.wlen = 1; 195e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari cmd.rlen = 0; 196a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari ret = si2157_cmd_execute(s, &cmd); 197a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari if (ret) 198a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari goto err; 199a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari 200930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return 0; 201a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaarierr: 202a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); 203a83d7d17ad4c920b9b3f5330521fa346dddd3de3Antti Palosaari return ret; 204930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari} 205930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 206930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic int si2157_set_params(struct dvb_frontend *fe) 207930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari{ 208930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157 *s = fe->tuner_priv; 209930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct dtv_frontend_properties *c = &fe->dtv_property_cache; 210930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari int ret; 211930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157_cmd cmd; 212a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen u8 bandwidth, delivery_system; 213930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 214930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari dev_dbg(&s->client->dev, 215930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari "%s: delivery_system=%d frequency=%u bandwidth_hz=%u\n", 216930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari __func__, c->delivery_system, c->frequency, 217930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari c->bandwidth_hz); 218930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 219930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari if (!s->active) { 220930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari ret = -EAGAIN; 221930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari goto err; 222930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari } 223930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 224a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen if (c->bandwidth_hz <= 6000000) 225a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen bandwidth = 0x06; 226a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen else if (c->bandwidth_hz <= 7000000) 227a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen bandwidth = 0x07; 228a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen else if (c->bandwidth_hz <= 8000000) 229a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen bandwidth = 0x08; 230a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen else 231a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen bandwidth = 0x0f; 232a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen 233a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen switch (c->delivery_system) { 234a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen case SYS_DVBT: 235a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen case SYS_DVBT2: /* it seems DVB-T and DVB-T2 both are 0x20 here */ 236a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen delivery_system = 0x20; 237a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen break; 238a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen case SYS_DVBC_ANNEX_A: 239a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen delivery_system = 0x30; 240a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen break; 241a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen default: 242a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen ret = -EINVAL; 243a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen goto err; 244a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen } 245a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen 246a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen memcpy(cmd.args, "\x14\x00\x03\x07\x00\x00", 6); 247a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen cmd.args[4] = delivery_system | bandwidth; 248a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen cmd.wlen = 6; 249a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen cmd.rlen = 1; 250a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen ret = si2157_cmd_execute(s, &cmd); 251a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen if (ret) 252a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen goto err; 253a1dad50d8cdce0be71489c7544ffcd36cf80e994Olli Salonen 254930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari /* set frequency */ 255b154121c6470b927794ff7ed17355aedfaa073caOlli Salonen memcpy(cmd.args, "\x41\x00\x00\x00\x00\x00\x00\x00", 8); 256930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari cmd.args[4] = (c->frequency >> 0) & 0xff; 257930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari cmd.args[5] = (c->frequency >> 8) & 0xff; 258930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari cmd.args[6] = (c->frequency >> 16) & 0xff; 259930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari cmd.args[7] = (c->frequency >> 24) & 0xff; 260e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari cmd.wlen = 8; 261e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari cmd.rlen = 1; 262930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari ret = si2157_cmd_execute(s, &cmd); 263930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari if (ret) 264930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari goto err; 265930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 266930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return 0; 267930a873081986393f6e7e0fb9275753c1485277bAntti Palosaarierr: 268930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); 269930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return ret; 270930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari} 271930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 272930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic const struct dvb_tuner_ops si2157_tuner_ops = { 273930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .info = { 2741b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen .name = "Silicon Labs Si2157/Si2158", 275ae4c8919bb761c7f209fb260a82304a54616da0dAntti Palosaari .frequency_min = 110000000, 276930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .frequency_max = 862000000, 277930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari }, 278930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 279930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .init = si2157_init, 280930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .sleep = si2157_sleep, 281930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .set_params = si2157_set_params, 282930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari}; 283930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 284930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic int si2157_probe(struct i2c_client *client, 285930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari const struct i2c_device_id *id) 286930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari{ 287930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157_config *cfg = client->dev.platform_data; 288930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct dvb_frontend *fe = cfg->fe; 289930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157 *s; 290930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157_cmd cmd; 291930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari int ret; 292930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 293930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari s = kzalloc(sizeof(struct si2157), GFP_KERNEL); 294930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari if (!s) { 295930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari ret = -ENOMEM; 296930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); 297930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari goto err; 298930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari } 299930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 300930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari s->client = client; 301930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari s->fe = cfg->fe; 302930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari mutex_init(&s->i2c_mutex); 303930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 304930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari /* check if the tuner is there */ 305e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari cmd.wlen = 0; 306e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4Antti Palosaari cmd.rlen = 1; 307930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari ret = si2157_cmd_execute(s, &cmd); 308930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari if (ret) 309930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari goto err; 310930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 311930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari fe->tuner_priv = s; 312930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari memcpy(&fe->ops.tuner_ops, &si2157_tuner_ops, 313930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari sizeof(struct dvb_tuner_ops)); 314930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 315930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari i2c_set_clientdata(client, s); 316930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 317930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari dev_info(&s->client->dev, 3181b92373f4b845019064d6a7c47b2ba72ebac191cOlli Salonen "%s: Silicon Labs Si2157/Si2158 successfully attached\n", 319930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari KBUILD_MODNAME); 320930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return 0; 321930a873081986393f6e7e0fb9275753c1485277bAntti Palosaarierr: 322930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret); 323930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari kfree(s); 324930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 325930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return ret; 326930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari} 327930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 328930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic int si2157_remove(struct i2c_client *client) 329930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari{ 330930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct si2157 *s = i2c_get_clientdata(client); 331930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari struct dvb_frontend *fe = s->fe; 332930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 333930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari dev_dbg(&client->dev, "%s:\n", __func__); 334930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 335930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); 336930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari fe->tuner_priv = NULL; 337930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari kfree(s); 338930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 339930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari return 0; 340930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari} 341930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 342930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic const struct i2c_device_id si2157_id[] = { 343930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari {"si2157", 0}, 344930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari {} 345930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari}; 346930a873081986393f6e7e0fb9275753c1485277bAntti PalosaariMODULE_DEVICE_TABLE(i2c, si2157_id); 347930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 348930a873081986393f6e7e0fb9275753c1485277bAntti Palosaaristatic struct i2c_driver si2157_driver = { 349930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .driver = { 350930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .owner = THIS_MODULE, 351930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .name = "si2157", 352930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari }, 353930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .probe = si2157_probe, 354930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .remove = si2157_remove, 355930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari .id_table = si2157_id, 356930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari}; 357930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 358930a873081986393f6e7e0fb9275753c1485277bAntti Palosaarimodule_i2c_driver(si2157_driver); 359930a873081986393f6e7e0fb9275753c1485277bAntti Palosaari 3601b92373f4b845019064d6a7c47b2ba72ebac191cOlli SalonenMODULE_DESCRIPTION("Silicon Labs Si2157/Si2158 silicon tuner driver"); 361930a873081986393f6e7e0fb9275753c1485277bAntti PalosaariMODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); 362930a873081986393f6e7e0fb9275753c1485277bAntti PalosaariMODULE_LICENSE("GPL"); 363