162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Antti Palosaari <crope@iki.fi> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "si2157_priv.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cistatic const struct dvb_tuner_ops si2157_ops; 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic int tuner_lock_debug; 1362306a36Sopenharmony_cimodule_param(tuner_lock_debug, int, 0644); 1462306a36Sopenharmony_ciMODULE_PARM_DESC(tuner_lock_debug, "if set, signal lock is briefly waited on after setting params"); 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* execute firmware command */ 1762306a36Sopenharmony_cistatic int si2157_cmd_execute(struct i2c_client *client, struct si2157_cmd *cmd) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci struct si2157_dev *dev = i2c_get_clientdata(client); 2062306a36Sopenharmony_ci int ret; 2162306a36Sopenharmony_ci unsigned long timeout; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci mutex_lock(&dev->i2c_mutex); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci if (cmd->wlen) { 2662306a36Sopenharmony_ci /* write cmd and args for firmware */ 2762306a36Sopenharmony_ci ret = i2c_master_send(client, cmd->args, cmd->wlen); 2862306a36Sopenharmony_ci if (ret < 0) { 2962306a36Sopenharmony_ci goto err_mutex_unlock; 3062306a36Sopenharmony_ci } else if (ret != cmd->wlen) { 3162306a36Sopenharmony_ci ret = -EREMOTEIO; 3262306a36Sopenharmony_ci goto err_mutex_unlock; 3362306a36Sopenharmony_ci } 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (cmd->rlen) { 3762306a36Sopenharmony_ci /* wait cmd execution terminate */ 3862306a36Sopenharmony_ci #define TIMEOUT 80 3962306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(TIMEOUT); 4062306a36Sopenharmony_ci while (!time_after(jiffies, timeout)) { 4162306a36Sopenharmony_ci ret = i2c_master_recv(client, cmd->args, cmd->rlen); 4262306a36Sopenharmony_ci if (ret < 0) { 4362306a36Sopenharmony_ci goto err_mutex_unlock; 4462306a36Sopenharmony_ci } else if (ret != cmd->rlen) { 4562306a36Sopenharmony_ci ret = -EREMOTEIO; 4662306a36Sopenharmony_ci goto err_mutex_unlock; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* firmware ready? */ 5062306a36Sopenharmony_ci if ((cmd->args[0] >> 7) & 0x01) 5162306a36Sopenharmony_ci break; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci dev_dbg(&client->dev, "cmd execution took %d ms, status=%x\n", 5562306a36Sopenharmony_ci jiffies_to_msecs(jiffies) - 5662306a36Sopenharmony_ci (jiffies_to_msecs(timeout) - TIMEOUT), 5762306a36Sopenharmony_ci cmd->args[0]); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (!((cmd->args[0] >> 7) & 0x01)) { 6062306a36Sopenharmony_ci ret = -ETIMEDOUT; 6162306a36Sopenharmony_ci goto err_mutex_unlock; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci /* check error status bit */ 6462306a36Sopenharmony_ci if (cmd->args[0] & 0x40) { 6562306a36Sopenharmony_ci ret = -EAGAIN; 6662306a36Sopenharmony_ci goto err_mutex_unlock; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci mutex_unlock(&dev->i2c_mutex); 7162306a36Sopenharmony_ci return 0; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cierr_mutex_unlock: 7462306a36Sopenharmony_ci mutex_unlock(&dev->i2c_mutex); 7562306a36Sopenharmony_ci dev_dbg(&client->dev, "failed=%d\n", ret); 7662306a36Sopenharmony_ci return ret; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic const struct si2157_tuner_info si2157_tuners[] = { 8062306a36Sopenharmony_ci { SI2141, 0x60, false, SI2141_60_FIRMWARE, SI2141_A10_FIRMWARE }, 8162306a36Sopenharmony_ci { SI2141, 0x61, false, SI2141_61_FIRMWARE, SI2141_A10_FIRMWARE }, 8262306a36Sopenharmony_ci { SI2146, 0x11, false, SI2146_11_FIRMWARE, NULL }, 8362306a36Sopenharmony_ci { SI2147, 0x50, false, SI2147_50_FIRMWARE, NULL }, 8462306a36Sopenharmony_ci { SI2148, 0x32, true, SI2148_32_FIRMWARE, SI2158_A20_FIRMWARE }, 8562306a36Sopenharmony_ci { SI2148, 0x33, true, SI2148_33_FIRMWARE, SI2158_A20_FIRMWARE }, 8662306a36Sopenharmony_ci { SI2157, 0x50, false, SI2157_50_FIRMWARE, SI2157_A30_FIRMWARE }, 8762306a36Sopenharmony_ci { SI2158, 0x50, false, SI2158_50_FIRMWARE, SI2158_A20_FIRMWARE }, 8862306a36Sopenharmony_ci { SI2158, 0x51, false, SI2158_51_FIRMWARE, SI2158_A20_FIRMWARE }, 8962306a36Sopenharmony_ci { SI2177, 0x50, false, SI2177_50_FIRMWARE, SI2157_A30_FIRMWARE }, 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int si2157_load_firmware(struct dvb_frontend *fe, 9362306a36Sopenharmony_ci const char *fw_name) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct i2c_client *client = fe->tuner_priv; 9662306a36Sopenharmony_ci const struct firmware *fw; 9762306a36Sopenharmony_ci int ret, len, remaining; 9862306a36Sopenharmony_ci struct si2157_cmd cmd; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* request the firmware, this will block and timeout */ 10162306a36Sopenharmony_ci ret = firmware_request_nowarn(&fw, fw_name, &client->dev); 10262306a36Sopenharmony_ci if (ret) 10362306a36Sopenharmony_ci return ret; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* firmware should be n chunks of 17 bytes */ 10662306a36Sopenharmony_ci if (fw->size % 17 != 0) { 10762306a36Sopenharmony_ci dev_err(&client->dev, "firmware file '%s' is invalid\n", 10862306a36Sopenharmony_ci fw_name); 10962306a36Sopenharmony_ci ret = -EINVAL; 11062306a36Sopenharmony_ci goto err_release_firmware; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci dev_info(&client->dev, "downloading firmware from file '%s'\n", 11462306a36Sopenharmony_ci fw_name); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci for (remaining = fw->size; remaining > 0; remaining -= 17) { 11762306a36Sopenharmony_ci len = fw->data[fw->size - remaining]; 11862306a36Sopenharmony_ci if (len > SI2157_ARGLEN) { 11962306a36Sopenharmony_ci dev_err(&client->dev, "Bad firmware length\n"); 12062306a36Sopenharmony_ci ret = -EINVAL; 12162306a36Sopenharmony_ci goto err_release_firmware; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); 12462306a36Sopenharmony_ci cmd.wlen = len; 12562306a36Sopenharmony_ci cmd.rlen = 1; 12662306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 12762306a36Sopenharmony_ci if (ret) { 12862306a36Sopenharmony_ci dev_err(&client->dev, "firmware download failed %d\n", 12962306a36Sopenharmony_ci ret); 13062306a36Sopenharmony_ci goto err_release_firmware; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cierr_release_firmware: 13562306a36Sopenharmony_ci release_firmware(fw); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return ret; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int si2157_find_and_load_firmware(struct dvb_frontend *fe) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct i2c_client *client = fe->tuner_priv; 14362306a36Sopenharmony_ci struct si2157_dev *dev = i2c_get_clientdata(client); 14462306a36Sopenharmony_ci const char *fw_alt_name = NULL; 14562306a36Sopenharmony_ci unsigned char part_id, rom_id; 14662306a36Sopenharmony_ci const char *fw_name = NULL; 14762306a36Sopenharmony_ci struct si2157_cmd cmd; 14862306a36Sopenharmony_ci bool required = true; 14962306a36Sopenharmony_ci int ret, i; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (dev->dont_load_firmware) { 15262306a36Sopenharmony_ci dev_info(&client->dev, 15362306a36Sopenharmony_ci "device is buggy, skipping firmware download\n"); 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* query chip revision */ 15862306a36Sopenharmony_ci memcpy(cmd.args, "\x02", 1); 15962306a36Sopenharmony_ci cmd.wlen = 1; 16062306a36Sopenharmony_ci cmd.rlen = 13; 16162306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 16262306a36Sopenharmony_ci if (ret) 16362306a36Sopenharmony_ci return ret; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci part_id = cmd.args[2]; 16662306a36Sopenharmony_ci rom_id = cmd.args[12]; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(si2157_tuners); i++) { 16962306a36Sopenharmony_ci if (si2157_tuners[i].part_id != part_id) 17062306a36Sopenharmony_ci continue; 17162306a36Sopenharmony_ci required = si2157_tuners[i].required; 17262306a36Sopenharmony_ci fw_alt_name = si2157_tuners[i].fw_alt_name; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Both part and rom ID match */ 17562306a36Sopenharmony_ci if (si2157_tuners[i].rom_id == rom_id) { 17662306a36Sopenharmony_ci fw_name = si2157_tuners[i].fw_name; 17762306a36Sopenharmony_ci break; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (required && !fw_name && !fw_alt_name) { 18262306a36Sopenharmony_ci dev_err(&client->dev, 18362306a36Sopenharmony_ci "unknown chip version Si21%d-%c%c%c ROM 0x%02x\n", 18462306a36Sopenharmony_ci part_id, cmd.args[1], cmd.args[3], cmd.args[4], rom_id); 18562306a36Sopenharmony_ci return -EINVAL; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* Update the part id based on device's report */ 18962306a36Sopenharmony_ci dev->part_id = part_id; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci dev_info(&client->dev, 19262306a36Sopenharmony_ci "found a 'Silicon Labs Si21%d-%c%c%c ROM 0x%02x'\n", 19362306a36Sopenharmony_ci part_id, cmd.args[1], cmd.args[3], cmd.args[4], rom_id); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (fw_name) 19662306a36Sopenharmony_ci ret = si2157_load_firmware(fe, fw_name); 19762306a36Sopenharmony_ci else 19862306a36Sopenharmony_ci ret = -ENOENT; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* Try alternate name, if any */ 20162306a36Sopenharmony_ci if (ret == -ENOENT && fw_alt_name) 20262306a36Sopenharmony_ci ret = si2157_load_firmware(fe, fw_alt_name); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (ret == -ENOENT) { 20562306a36Sopenharmony_ci if (!required) { 20662306a36Sopenharmony_ci dev_info(&client->dev, "Using ROM firmware.\n"); 20762306a36Sopenharmony_ci return 0; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci dev_err(&client->dev, "Can't continue without a firmware.\n"); 21062306a36Sopenharmony_ci } else if (ret < 0) { 21162306a36Sopenharmony_ci dev_err(&client->dev, "error %d when loading firmware\n", ret); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci return ret; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic int si2157_init(struct dvb_frontend *fe) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 21962306a36Sopenharmony_ci struct i2c_client *client = fe->tuner_priv; 22062306a36Sopenharmony_ci struct si2157_dev *dev = i2c_get_clientdata(client); 22162306a36Sopenharmony_ci unsigned int xtal_trim; 22262306a36Sopenharmony_ci struct si2157_cmd cmd; 22362306a36Sopenharmony_ci int ret; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci dev_dbg(&client->dev, "\n"); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Try to get Xtal trim property, to verify tuner still running */ 22862306a36Sopenharmony_ci memcpy(cmd.args, "\x15\x00\x02\x04", 4); 22962306a36Sopenharmony_ci cmd.wlen = 4; 23062306a36Sopenharmony_ci cmd.rlen = 4; 23162306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci xtal_trim = cmd.args[2] | (cmd.args[3] << 8); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (ret == 0 && xtal_trim < 16) 23662306a36Sopenharmony_ci goto warm; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci dev->if_frequency = 0; /* we no longer know current tuner state */ 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* power up */ 24162306a36Sopenharmony_ci if (dev->part_id == SI2146) { 24262306a36Sopenharmony_ci /* clock_mode = XTAL, clock_freq = 24MHz */ 24362306a36Sopenharmony_ci memcpy(cmd.args, "\xc0\x05\x01\x00\x00\x0b\x00\x00\x01", 9); 24462306a36Sopenharmony_ci cmd.wlen = 9; 24562306a36Sopenharmony_ci } else if (dev->part_id == SI2141) { 24662306a36Sopenharmony_ci /* clock_mode: XTAL, xout enabled */ 24762306a36Sopenharmony_ci memcpy(cmd.args, "\xc0\x00\x0d\x0e\x00\x01\x01\x01\x01\x03", 10); 24862306a36Sopenharmony_ci cmd.wlen = 10; 24962306a36Sopenharmony_ci } else { 25062306a36Sopenharmony_ci memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15); 25162306a36Sopenharmony_ci cmd.wlen = 15; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci cmd.rlen = 1; 25462306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 25562306a36Sopenharmony_ci if (ret && (dev->part_id != SI2141 || ret != -EAGAIN)) 25662306a36Sopenharmony_ci goto err; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Si2141 needs a wake up command */ 25962306a36Sopenharmony_ci if (dev->part_id == SI2141) { 26062306a36Sopenharmony_ci memcpy(cmd.args, "\xc0\x08\x01\x02\x00\x00\x01", 7); 26162306a36Sopenharmony_ci cmd.wlen = 7; 26262306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 26362306a36Sopenharmony_ci if (ret) 26462306a36Sopenharmony_ci goto err; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* Try to load the firmware */ 26862306a36Sopenharmony_ci ret = si2157_find_and_load_firmware(fe); 26962306a36Sopenharmony_ci if (ret < 0) 27062306a36Sopenharmony_ci goto err; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* reboot the tuner with new firmware? */ 27362306a36Sopenharmony_ci memcpy(cmd.args, "\x01\x01", 2); 27462306a36Sopenharmony_ci cmd.wlen = 2; 27562306a36Sopenharmony_ci cmd.rlen = 1; 27662306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 27762306a36Sopenharmony_ci if (ret) 27862306a36Sopenharmony_ci goto err; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* query firmware version */ 28162306a36Sopenharmony_ci memcpy(cmd.args, "\x11", 1); 28262306a36Sopenharmony_ci cmd.wlen = 1; 28362306a36Sopenharmony_ci cmd.rlen = 10; 28462306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 28562306a36Sopenharmony_ci if (ret) 28662306a36Sopenharmony_ci goto err; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci dev_info(&client->dev, "firmware version: %c.%c.%d\n", 28962306a36Sopenharmony_ci cmd.args[6], cmd.args[7], cmd.args[8]); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* enable tuner status flags */ 29262306a36Sopenharmony_ci memcpy(cmd.args, "\x14\x00\x01\x05\x01\x00", 6); 29362306a36Sopenharmony_ci cmd.wlen = 6; 29462306a36Sopenharmony_ci cmd.rlen = 1; 29562306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 29662306a36Sopenharmony_ci if (ret) 29762306a36Sopenharmony_ci goto err; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci memcpy(cmd.args, "\x14\x00\x01\x06\x01\x00", 6); 30062306a36Sopenharmony_ci cmd.wlen = 6; 30162306a36Sopenharmony_ci cmd.rlen = 1; 30262306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 30362306a36Sopenharmony_ci if (ret) 30462306a36Sopenharmony_ci goto err; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci memcpy(cmd.args, "\x14\x00\x01\x07\x01\x00", 6); 30762306a36Sopenharmony_ci cmd.wlen = 6; 30862306a36Sopenharmony_ci cmd.rlen = 1; 30962306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 31062306a36Sopenharmony_ci if (ret) 31162306a36Sopenharmony_ci goto err; 31262306a36Sopenharmony_ciwarm: 31362306a36Sopenharmony_ci /* init statistics in order signal app which are supported */ 31462306a36Sopenharmony_ci c->strength.len = 1; 31562306a36Sopenharmony_ci c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 31662306a36Sopenharmony_ci /* start statistics polling */ 31762306a36Sopenharmony_ci schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(1000)); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci dev->active = true; 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cierr: 32362306a36Sopenharmony_ci dev_dbg(&client->dev, "failed=%d\n", ret); 32462306a36Sopenharmony_ci return ret; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic int si2157_sleep(struct dvb_frontend *fe) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci struct i2c_client *client = fe->tuner_priv; 33062306a36Sopenharmony_ci struct si2157_dev *dev = i2c_get_clientdata(client); 33162306a36Sopenharmony_ci int ret; 33262306a36Sopenharmony_ci struct si2157_cmd cmd; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci dev_dbg(&client->dev, "\n"); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci dev->active = false; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* stop statistics polling */ 33962306a36Sopenharmony_ci cancel_delayed_work_sync(&dev->stat_work); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* standby */ 34262306a36Sopenharmony_ci memcpy(cmd.args, "\x16\x00", 2); 34362306a36Sopenharmony_ci cmd.wlen = 2; 34462306a36Sopenharmony_ci cmd.rlen = 1; 34562306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 34662306a36Sopenharmony_ci if (ret) 34762306a36Sopenharmony_ci goto err; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_cierr: 35162306a36Sopenharmony_ci dev_dbg(&client->dev, "failed=%d\n", ret); 35262306a36Sopenharmony_ci return ret; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic int si2157_tune_wait(struct i2c_client *client, u8 is_digital) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci#define TUN_TIMEOUT 40 35862306a36Sopenharmony_ci#define DIG_TIMEOUT 30 35962306a36Sopenharmony_ci#define ANALOG_TIMEOUT 150 36062306a36Sopenharmony_ci struct si2157_dev *dev = i2c_get_clientdata(client); 36162306a36Sopenharmony_ci int ret; 36262306a36Sopenharmony_ci unsigned long timeout; 36362306a36Sopenharmony_ci unsigned long start_time; 36462306a36Sopenharmony_ci u8 wait_status; 36562306a36Sopenharmony_ci u8 tune_lock_mask; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (is_digital) 36862306a36Sopenharmony_ci tune_lock_mask = 0x04; 36962306a36Sopenharmony_ci else 37062306a36Sopenharmony_ci tune_lock_mask = 0x02; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci mutex_lock(&dev->i2c_mutex); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* wait tuner command complete */ 37562306a36Sopenharmony_ci start_time = jiffies; 37662306a36Sopenharmony_ci timeout = start_time + msecs_to_jiffies(TUN_TIMEOUT); 37762306a36Sopenharmony_ci while (1) { 37862306a36Sopenharmony_ci ret = i2c_master_recv(client, &wait_status, 37962306a36Sopenharmony_ci sizeof(wait_status)); 38062306a36Sopenharmony_ci if (ret < 0) { 38162306a36Sopenharmony_ci goto err_mutex_unlock; 38262306a36Sopenharmony_ci } else if (ret != sizeof(wait_status)) { 38362306a36Sopenharmony_ci ret = -EREMOTEIO; 38462306a36Sopenharmony_ci goto err_mutex_unlock; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (time_after(jiffies, timeout)) 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* tuner done? */ 39162306a36Sopenharmony_ci if ((wait_status & 0x81) == 0x81) 39262306a36Sopenharmony_ci break; 39362306a36Sopenharmony_ci usleep_range(5000, 10000); 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci dev_dbg(&client->dev, "tuning took %d ms, status=0x%x\n", 39762306a36Sopenharmony_ci jiffies_to_msecs(jiffies) - jiffies_to_msecs(start_time), 39862306a36Sopenharmony_ci wait_status); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* if we tuned ok, wait a bit for tuner lock */ 40162306a36Sopenharmony_ci if (tuner_lock_debug && (wait_status & 0x81) == 0x81) { 40262306a36Sopenharmony_ci if (is_digital) 40362306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(DIG_TIMEOUT); 40462306a36Sopenharmony_ci else 40562306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(ANALOG_TIMEOUT); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci while (!time_after(jiffies, timeout)) { 40862306a36Sopenharmony_ci ret = i2c_master_recv(client, &wait_status, 40962306a36Sopenharmony_ci sizeof(wait_status)); 41062306a36Sopenharmony_ci if (ret < 0) { 41162306a36Sopenharmony_ci goto err_mutex_unlock; 41262306a36Sopenharmony_ci } else if (ret != sizeof(wait_status)) { 41362306a36Sopenharmony_ci ret = -EREMOTEIO; 41462306a36Sopenharmony_ci goto err_mutex_unlock; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* tuner locked? */ 41862306a36Sopenharmony_ci if (wait_status & tune_lock_mask) 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci usleep_range(5000, 10000); 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci dev_dbg(&client->dev, "tuning+lock took %d ms, status=0x%x\n", 42462306a36Sopenharmony_ci jiffies_to_msecs(jiffies) - jiffies_to_msecs(start_time), 42562306a36Sopenharmony_ci wait_status); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if ((wait_status & 0xc0) != 0x80) { 42962306a36Sopenharmony_ci ret = -ETIMEDOUT; 43062306a36Sopenharmony_ci goto err_mutex_unlock; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci mutex_unlock(&dev->i2c_mutex); 43462306a36Sopenharmony_ci return 0; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cierr_mutex_unlock: 43762306a36Sopenharmony_ci mutex_unlock(&dev->i2c_mutex); 43862306a36Sopenharmony_ci dev_err(&client->dev, "failed=%d\n", ret); 43962306a36Sopenharmony_ci return ret; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic int si2157_set_params(struct dvb_frontend *fe) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci struct i2c_client *client = fe->tuner_priv; 44562306a36Sopenharmony_ci struct si2157_dev *dev = i2c_get_clientdata(client); 44662306a36Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 44762306a36Sopenharmony_ci int ret; 44862306a36Sopenharmony_ci struct si2157_cmd cmd; 44962306a36Sopenharmony_ci u8 bw, delivery_system; 45062306a36Sopenharmony_ci u32 bandwidth; 45162306a36Sopenharmony_ci u32 if_frequency = 5000000; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci dev_dbg(&client->dev, 45462306a36Sopenharmony_ci "delivery_system=%d frequency=%u bandwidth_hz=%u\n", 45562306a36Sopenharmony_ci c->delivery_system, c->frequency, c->bandwidth_hz); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (!dev->active) { 45862306a36Sopenharmony_ci ret = -EAGAIN; 45962306a36Sopenharmony_ci goto err; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (SUPPORTS_1700KHz(dev) && c->bandwidth_hz <= 1700000) { 46362306a36Sopenharmony_ci bandwidth = 1700000; 46462306a36Sopenharmony_ci bw = 9; 46562306a36Sopenharmony_ci } else if (c->bandwidth_hz <= 6000000) { 46662306a36Sopenharmony_ci bandwidth = 6000000; 46762306a36Sopenharmony_ci bw = 6; 46862306a36Sopenharmony_ci } else if (SUPPORTS_1700KHz(dev) && c->bandwidth_hz <= 6100000) { 46962306a36Sopenharmony_ci bandwidth = 6100000; 47062306a36Sopenharmony_ci bw = 10; 47162306a36Sopenharmony_ci } else if (c->bandwidth_hz <= 7000000) { 47262306a36Sopenharmony_ci bandwidth = 7000000; 47362306a36Sopenharmony_ci bw = 7; 47462306a36Sopenharmony_ci } else { 47562306a36Sopenharmony_ci bandwidth = 8000000; 47662306a36Sopenharmony_ci bw = 8; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci switch (c->delivery_system) { 48062306a36Sopenharmony_ci case SYS_ATSC: 48162306a36Sopenharmony_ci delivery_system = 0x00; 48262306a36Sopenharmony_ci if_frequency = 3250000; 48362306a36Sopenharmony_ci break; 48462306a36Sopenharmony_ci case SYS_DVBC_ANNEX_B: 48562306a36Sopenharmony_ci delivery_system = 0x10; 48662306a36Sopenharmony_ci if_frequency = 4000000; 48762306a36Sopenharmony_ci break; 48862306a36Sopenharmony_ci case SYS_DVBT: 48962306a36Sopenharmony_ci case SYS_DVBT2: /* it seems DVB-T and DVB-T2 both are 0x20 here */ 49062306a36Sopenharmony_ci delivery_system = 0x20; 49162306a36Sopenharmony_ci break; 49262306a36Sopenharmony_ci case SYS_DVBC_ANNEX_A: 49362306a36Sopenharmony_ci case SYS_DVBC_ANNEX_C: 49462306a36Sopenharmony_ci delivery_system = 0x30; 49562306a36Sopenharmony_ci break; 49662306a36Sopenharmony_ci case SYS_ISDBT: 49762306a36Sopenharmony_ci delivery_system = 0x40; 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci case SYS_DTMB: 50062306a36Sopenharmony_ci delivery_system = 0x60; 50162306a36Sopenharmony_ci break; 50262306a36Sopenharmony_ci default: 50362306a36Sopenharmony_ci ret = -EINVAL; 50462306a36Sopenharmony_ci goto err; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci memcpy(cmd.args, "\x14\x00\x03\x07\x00\x00", 6); 50862306a36Sopenharmony_ci cmd.args[4] = delivery_system | bw; 50962306a36Sopenharmony_ci if (dev->inversion) 51062306a36Sopenharmony_ci cmd.args[5] = 0x01; 51162306a36Sopenharmony_ci cmd.wlen = 6; 51262306a36Sopenharmony_ci cmd.rlen = 4; 51362306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 51462306a36Sopenharmony_ci if (ret) 51562306a36Sopenharmony_ci goto err; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* On SI2146, set DTV AGC source to DLIF_AGC_3DB */ 51862306a36Sopenharmony_ci if (dev->part_id == SI2146) 51962306a36Sopenharmony_ci memcpy(cmd.args, "\x14\x00\x02\x07\x00\x01", 6); 52062306a36Sopenharmony_ci else 52162306a36Sopenharmony_ci memcpy(cmd.args, "\x14\x00\x02\x07\x00\x00", 6); 52262306a36Sopenharmony_ci cmd.args[4] = dev->if_port; 52362306a36Sopenharmony_ci cmd.wlen = 6; 52462306a36Sopenharmony_ci cmd.rlen = 4; 52562306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 52662306a36Sopenharmony_ci if (ret) 52762306a36Sopenharmony_ci goto err; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* set digital if frequency if needed */ 53062306a36Sopenharmony_ci if (if_frequency != dev->if_frequency) { 53162306a36Sopenharmony_ci memcpy(cmd.args, "\x14\x00\x06\x07", 4); 53262306a36Sopenharmony_ci cmd.args[4] = (if_frequency / 1000) & 0xff; 53362306a36Sopenharmony_ci cmd.args[5] = ((if_frequency / 1000) >> 8) & 0xff; 53462306a36Sopenharmony_ci cmd.wlen = 6; 53562306a36Sopenharmony_ci cmd.rlen = 4; 53662306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 53762306a36Sopenharmony_ci if (ret) 53862306a36Sopenharmony_ci goto err; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci dev->if_frequency = if_frequency; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* set digital frequency */ 54462306a36Sopenharmony_ci memcpy(cmd.args, "\x41\x00\x00\x00\x00\x00\x00\x00", 8); 54562306a36Sopenharmony_ci cmd.args[4] = (c->frequency >> 0) & 0xff; 54662306a36Sopenharmony_ci cmd.args[5] = (c->frequency >> 8) & 0xff; 54762306a36Sopenharmony_ci cmd.args[6] = (c->frequency >> 16) & 0xff; 54862306a36Sopenharmony_ci cmd.args[7] = (c->frequency >> 24) & 0xff; 54962306a36Sopenharmony_ci cmd.wlen = 8; 55062306a36Sopenharmony_ci cmd.rlen = 1; 55162306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 55262306a36Sopenharmony_ci if (ret) 55362306a36Sopenharmony_ci goto err; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci dev->bandwidth = bandwidth; 55662306a36Sopenharmony_ci dev->frequency = c->frequency; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci si2157_tune_wait(client, 1); /* wait to complete, ignore any errors */ 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci return 0; 56162306a36Sopenharmony_cierr: 56262306a36Sopenharmony_ci dev->bandwidth = 0; 56362306a36Sopenharmony_ci dev->frequency = 0; 56462306a36Sopenharmony_ci dev->if_frequency = 0; 56562306a36Sopenharmony_ci dev_dbg(&client->dev, "failed=%d\n", ret); 56662306a36Sopenharmony_ci return ret; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic int si2157_set_analog_params(struct dvb_frontend *fe, 57062306a36Sopenharmony_ci struct analog_parameters *params) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci struct i2c_client *client = fe->tuner_priv; 57362306a36Sopenharmony_ci struct si2157_dev *dev = i2c_get_clientdata(client); 57462306a36Sopenharmony_ci char *std; /* for debugging */ 57562306a36Sopenharmony_ci int ret; 57662306a36Sopenharmony_ci struct si2157_cmd cmd; 57762306a36Sopenharmony_ci u32 bandwidth = 0; 57862306a36Sopenharmony_ci u32 if_frequency = 0; 57962306a36Sopenharmony_ci u32 freq = 0; 58062306a36Sopenharmony_ci u64 tmp_lval = 0; 58162306a36Sopenharmony_ci u8 system = 0; 58262306a36Sopenharmony_ci u8 color = 0; /* 0=NTSC/PAL, 0x10=SECAM */ 58362306a36Sopenharmony_ci u8 invert_analog = 1; /* analog tuner spectrum; 0=normal, 1=inverted */ 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (!SUPPORTS_ATV_IF(dev)) { 58662306a36Sopenharmony_ci dev_info(&client->dev, "Analog tuning not supported yet for Si21%d\n", 58762306a36Sopenharmony_ci dev->part_id); 58862306a36Sopenharmony_ci ret = -EINVAL; 58962306a36Sopenharmony_ci goto err; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (!dev->active) 59362306a36Sopenharmony_ci si2157_init(fe); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (!dev->active) { 59662306a36Sopenharmony_ci ret = -EAGAIN; 59762306a36Sopenharmony_ci goto err; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci if (params->mode == V4L2_TUNER_RADIO) { 60062306a36Sopenharmony_ci /* 60162306a36Sopenharmony_ci * std = "fm"; 60262306a36Sopenharmony_ci * bandwidth = 1700000; //best can do for FM, AGC will be a mess though 60362306a36Sopenharmony_ci * if_frequency = 1250000; //HVR-225x(saa7164), HVR-12xx(cx23885) 60462306a36Sopenharmony_ci * if_frequency = 6600000; //HVR-9xx(cx231xx) 60562306a36Sopenharmony_ci * if_frequency = 5500000; //HVR-19xx(pvrusb2) 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_ci dev_err(&client->dev, "si2157 does not currently support FM radio\n"); 60862306a36Sopenharmony_ci ret = -EINVAL; 60962306a36Sopenharmony_ci goto err; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci tmp_lval = params->frequency * 625LL; 61262306a36Sopenharmony_ci do_div(tmp_lval, 10); /* convert to HZ */ 61362306a36Sopenharmony_ci freq = (u32)tmp_lval; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (freq < 1000000) /* is freq in KHz */ 61662306a36Sopenharmony_ci freq = freq * 1000; 61762306a36Sopenharmony_ci dev->frequency = freq; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci /* if_frequency values based on tda187271C2 */ 62062306a36Sopenharmony_ci if (params->std & (V4L2_STD_B | V4L2_STD_GH)) { 62162306a36Sopenharmony_ci if (freq >= 470000000) { 62262306a36Sopenharmony_ci std = "palGH"; 62362306a36Sopenharmony_ci bandwidth = 8000000; 62462306a36Sopenharmony_ci if_frequency = 6000000; 62562306a36Sopenharmony_ci system = 1; 62662306a36Sopenharmony_ci if (params->std & 62762306a36Sopenharmony_ci (V4L2_STD_SECAM_G | V4L2_STD_SECAM_H)) { 62862306a36Sopenharmony_ci std = "secamGH"; 62962306a36Sopenharmony_ci color = 0x10; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci } else { 63262306a36Sopenharmony_ci std = "palB"; 63362306a36Sopenharmony_ci bandwidth = 7000000; 63462306a36Sopenharmony_ci if_frequency = 6000000; 63562306a36Sopenharmony_ci system = 0; 63662306a36Sopenharmony_ci if (params->std & V4L2_STD_SECAM_B) { 63762306a36Sopenharmony_ci std = "secamB"; 63862306a36Sopenharmony_ci color = 0x10; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci } else if (params->std & V4L2_STD_MN) { 64262306a36Sopenharmony_ci std = "MN"; 64362306a36Sopenharmony_ci bandwidth = 6000000; 64462306a36Sopenharmony_ci if_frequency = 5400000; 64562306a36Sopenharmony_ci system = 2; 64662306a36Sopenharmony_ci } else if (params->std & V4L2_STD_PAL_I) { 64762306a36Sopenharmony_ci std = "palI"; 64862306a36Sopenharmony_ci bandwidth = 8000000; 64962306a36Sopenharmony_ci if_frequency = 7250000; /* TODO: does not work yet */ 65062306a36Sopenharmony_ci system = 4; 65162306a36Sopenharmony_ci } else if (params->std & V4L2_STD_DK) { 65262306a36Sopenharmony_ci std = "palDK"; 65362306a36Sopenharmony_ci bandwidth = 8000000; 65462306a36Sopenharmony_ci if_frequency = 6900000; /* TODO: does not work yet */ 65562306a36Sopenharmony_ci system = 5; 65662306a36Sopenharmony_ci if (params->std & V4L2_STD_SECAM_DK) { 65762306a36Sopenharmony_ci std = "secamDK"; 65862306a36Sopenharmony_ci color = 0x10; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci } else if (params->std & V4L2_STD_SECAM_L) { 66162306a36Sopenharmony_ci std = "secamL"; 66262306a36Sopenharmony_ci bandwidth = 8000000; 66362306a36Sopenharmony_ci if_frequency = 6750000; /* TODO: untested */ 66462306a36Sopenharmony_ci system = 6; 66562306a36Sopenharmony_ci color = 0x10; 66662306a36Sopenharmony_ci } else if (params->std & V4L2_STD_SECAM_LC) { 66762306a36Sopenharmony_ci std = "secamL'"; 66862306a36Sopenharmony_ci bandwidth = 7000000; 66962306a36Sopenharmony_ci if_frequency = 1250000; /* TODO: untested */ 67062306a36Sopenharmony_ci system = 7; 67162306a36Sopenharmony_ci color = 0x10; 67262306a36Sopenharmony_ci } else { 67362306a36Sopenharmony_ci std = "unknown"; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci /* calc channel center freq */ 67662306a36Sopenharmony_ci freq = freq - 1250000 + (bandwidth / 2); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci dev_dbg(&client->dev, 67962306a36Sopenharmony_ci "mode=%d system=%u std='%s' params->frequency=%u center freq=%u if=%u bandwidth=%u\n", 68062306a36Sopenharmony_ci params->mode, system, std, params->frequency, 68162306a36Sopenharmony_ci freq, if_frequency, bandwidth); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci /* set analog IF port */ 68462306a36Sopenharmony_ci memcpy(cmd.args, "\x14\x00\x03\x06\x08\x02", 6); 68562306a36Sopenharmony_ci /* in using dev->if_port, we assume analog and digital IF's */ 68662306a36Sopenharmony_ci /* are always on different ports */ 68762306a36Sopenharmony_ci /* assumes if_port definition is 0 or 1 for digital out */ 68862306a36Sopenharmony_ci cmd.args[4] = (dev->if_port == 1) ? 8 : 10; 68962306a36Sopenharmony_ci /* Analog AGC assumed external */ 69062306a36Sopenharmony_ci cmd.args[5] = (dev->if_port == 1) ? 2 : 1; 69162306a36Sopenharmony_ci cmd.wlen = 6; 69262306a36Sopenharmony_ci cmd.rlen = 4; 69362306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 69462306a36Sopenharmony_ci if (ret) 69562306a36Sopenharmony_ci goto err; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* set analog IF output config */ 69862306a36Sopenharmony_ci memcpy(cmd.args, "\x14\x00\x0d\x06\x94\x64", 6); 69962306a36Sopenharmony_ci cmd.wlen = 6; 70062306a36Sopenharmony_ci cmd.rlen = 4; 70162306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 70262306a36Sopenharmony_ci if (ret) 70362306a36Sopenharmony_ci goto err; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* make this distinct from a digital IF */ 70662306a36Sopenharmony_ci dev->if_frequency = if_frequency | 1; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* calc and set tuner analog if center frequency */ 70962306a36Sopenharmony_ci if_frequency = if_frequency + 1250000 - (bandwidth / 2); 71062306a36Sopenharmony_ci dev_dbg(&client->dev, "IF Ctr freq=%d\n", if_frequency); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci memcpy(cmd.args, "\x14\x00\x0C\x06", 4); 71362306a36Sopenharmony_ci cmd.args[4] = (if_frequency / 1000) & 0xff; 71462306a36Sopenharmony_ci cmd.args[5] = ((if_frequency / 1000) >> 8) & 0xff; 71562306a36Sopenharmony_ci cmd.wlen = 6; 71662306a36Sopenharmony_ci cmd.rlen = 4; 71762306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 71862306a36Sopenharmony_ci if (ret) 71962306a36Sopenharmony_ci goto err; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci /* set analog AGC config */ 72262306a36Sopenharmony_ci memcpy(cmd.args, "\x14\x00\x07\x06\x32\xc8", 6); 72362306a36Sopenharmony_ci cmd.wlen = 6; 72462306a36Sopenharmony_ci cmd.rlen = 4; 72562306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 72662306a36Sopenharmony_ci if (ret) 72762306a36Sopenharmony_ci goto err; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* set analog video mode */ 73062306a36Sopenharmony_ci memcpy(cmd.args, "\x14\x00\x04\x06\x00\x00", 6); 73162306a36Sopenharmony_ci cmd.args[4] = system | color; 73262306a36Sopenharmony_ci /* can use dev->inversion if assumed applies to both digital/analog */ 73362306a36Sopenharmony_ci if (invert_analog) 73462306a36Sopenharmony_ci cmd.args[5] |= 0x02; 73562306a36Sopenharmony_ci cmd.wlen = 6; 73662306a36Sopenharmony_ci cmd.rlen = 1; 73762306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 73862306a36Sopenharmony_ci if (ret) 73962306a36Sopenharmony_ci goto err; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* set analog frequency */ 74262306a36Sopenharmony_ci memcpy(cmd.args, "\x41\x01\x00\x00\x00\x00\x00\x00", 8); 74362306a36Sopenharmony_ci cmd.args[4] = (freq >> 0) & 0xff; 74462306a36Sopenharmony_ci cmd.args[5] = (freq >> 8) & 0xff; 74562306a36Sopenharmony_ci cmd.args[6] = (freq >> 16) & 0xff; 74662306a36Sopenharmony_ci cmd.args[7] = (freq >> 24) & 0xff; 74762306a36Sopenharmony_ci cmd.wlen = 8; 74862306a36Sopenharmony_ci cmd.rlen = 1; 74962306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 75062306a36Sopenharmony_ci if (ret) 75162306a36Sopenharmony_ci goto err; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci dev->bandwidth = bandwidth; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci si2157_tune_wait(client, 0); /* wait to complete, ignore any errors */ 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci return 0; 75862306a36Sopenharmony_cierr: 75962306a36Sopenharmony_ci dev->bandwidth = 0; 76062306a36Sopenharmony_ci dev->frequency = 0; 76162306a36Sopenharmony_ci dev->if_frequency = 0; 76262306a36Sopenharmony_ci dev_dbg(&client->dev, "failed=%d\n", ret); 76362306a36Sopenharmony_ci return ret; 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_cistatic int si2157_get_frequency(struct dvb_frontend *fe, u32 *frequency) 76762306a36Sopenharmony_ci{ 76862306a36Sopenharmony_ci struct i2c_client *client = fe->tuner_priv; 76962306a36Sopenharmony_ci struct si2157_dev *dev = i2c_get_clientdata(client); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci *frequency = dev->frequency; 77262306a36Sopenharmony_ci dev_dbg(&client->dev, "freq=%u\n", dev->frequency); 77362306a36Sopenharmony_ci return 0; 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic int si2157_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci struct i2c_client *client = fe->tuner_priv; 77962306a36Sopenharmony_ci struct si2157_dev *dev = i2c_get_clientdata(client); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci *bandwidth = dev->bandwidth; 78262306a36Sopenharmony_ci dev_dbg(&client->dev, "bandwidth=%u\n", dev->bandwidth); 78362306a36Sopenharmony_ci return 0; 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci struct i2c_client *client = fe->tuner_priv; 78962306a36Sopenharmony_ci struct si2157_dev *dev = i2c_get_clientdata(client); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci *frequency = dev->if_frequency & ~1; /* strip analog IF indicator bit */ 79262306a36Sopenharmony_ci dev_dbg(&client->dev, "if_frequency=%u\n", *frequency); 79362306a36Sopenharmony_ci return 0; 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cistatic int si2157_get_rf_strength(struct dvb_frontend *fe, u16 *rssi) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci struct i2c_client *client = fe->tuner_priv; 79962306a36Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 80062306a36Sopenharmony_ci struct si2157_cmd cmd; 80162306a36Sopenharmony_ci int ret; 80262306a36Sopenharmony_ci int strength; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci dev_dbg(&client->dev, "\n"); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci memcpy(cmd.args, "\x42\x00", 2); 80762306a36Sopenharmony_ci cmd.wlen = 2; 80862306a36Sopenharmony_ci cmd.rlen = 12; 80962306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 81062306a36Sopenharmony_ci if (ret) 81162306a36Sopenharmony_ci goto err; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci c->strength.stat[0].scale = FE_SCALE_DECIBEL; 81462306a36Sopenharmony_ci c->strength.stat[0].svalue = (s8)cmd.args[3] * 1000; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci /* normalize values based on Silicon Labs reference 81762306a36Sopenharmony_ci * add 100, then anything > 80 is 100% signal 81862306a36Sopenharmony_ci */ 81962306a36Sopenharmony_ci strength = (s8)cmd.args[3] + 100; 82062306a36Sopenharmony_ci strength = clamp_val(strength, 0, 80); 82162306a36Sopenharmony_ci *rssi = (u16)(strength * 0xffff / 80); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci dev_dbg(&client->dev, "strength=%d rssi=%u\n", 82462306a36Sopenharmony_ci (s8)cmd.args[3], *rssi); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci return 0; 82762306a36Sopenharmony_cierr: 82862306a36Sopenharmony_ci dev_dbg(&client->dev, "failed=%d\n", ret); 82962306a36Sopenharmony_ci return ret; 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_cistatic const struct dvb_tuner_ops si2157_ops = { 83362306a36Sopenharmony_ci .info = { 83462306a36Sopenharmony_ci .name = "Silicon Labs Si2141/Si2146/2147/2148/2157/2158", 83562306a36Sopenharmony_ci .frequency_min_hz = 42 * MHz, 83662306a36Sopenharmony_ci .frequency_max_hz = 870 * MHz, 83762306a36Sopenharmony_ci }, 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci .init = si2157_init, 84062306a36Sopenharmony_ci .sleep = si2157_sleep, 84162306a36Sopenharmony_ci .set_params = si2157_set_params, 84262306a36Sopenharmony_ci .set_analog_params = si2157_set_analog_params, 84362306a36Sopenharmony_ci .get_frequency = si2157_get_frequency, 84462306a36Sopenharmony_ci .get_bandwidth = si2157_get_bandwidth, 84562306a36Sopenharmony_ci .get_if_frequency = si2157_get_if_frequency, 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci .get_rf_strength = si2157_get_rf_strength, 84862306a36Sopenharmony_ci}; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic void si2157_stat_work(struct work_struct *work) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci struct si2157_dev *dev = container_of(work, struct si2157_dev, stat_work.work); 85362306a36Sopenharmony_ci struct dvb_frontend *fe = dev->fe; 85462306a36Sopenharmony_ci struct i2c_client *client = fe->tuner_priv; 85562306a36Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 85662306a36Sopenharmony_ci struct si2157_cmd cmd; 85762306a36Sopenharmony_ci int ret; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci dev_dbg(&client->dev, "\n"); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci memcpy(cmd.args, "\x42\x00", 2); 86262306a36Sopenharmony_ci cmd.wlen = 2; 86362306a36Sopenharmony_ci cmd.rlen = 12; 86462306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 86562306a36Sopenharmony_ci if (ret) 86662306a36Sopenharmony_ci goto err; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci c->strength.stat[0].scale = FE_SCALE_DECIBEL; 86962306a36Sopenharmony_ci c->strength.stat[0].svalue = (s8) cmd.args[3] * 1000; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000)); 87262306a36Sopenharmony_ci return; 87362306a36Sopenharmony_cierr: 87462306a36Sopenharmony_ci c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 87562306a36Sopenharmony_ci dev_dbg(&client->dev, "failed=%d\n", ret); 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic int si2157_probe(struct i2c_client *client) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci const struct i2c_device_id *id = i2c_client_get_device_id(client); 88162306a36Sopenharmony_ci struct si2157_config *cfg = client->dev.platform_data; 88262306a36Sopenharmony_ci struct dvb_frontend *fe = cfg->fe; 88362306a36Sopenharmony_ci struct si2157_dev *dev; 88462306a36Sopenharmony_ci struct si2157_cmd cmd; 88562306a36Sopenharmony_ci int ret; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 88862306a36Sopenharmony_ci if (!dev) { 88962306a36Sopenharmony_ci ret = -ENOMEM; 89062306a36Sopenharmony_ci dev_err(&client->dev, "kzalloc() failed\n"); 89162306a36Sopenharmony_ci goto err; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci i2c_set_clientdata(client, dev); 89562306a36Sopenharmony_ci dev->fe = cfg->fe; 89662306a36Sopenharmony_ci dev->inversion = cfg->inversion; 89762306a36Sopenharmony_ci dev->dont_load_firmware = cfg->dont_load_firmware; 89862306a36Sopenharmony_ci dev->if_port = cfg->if_port; 89962306a36Sopenharmony_ci dev->part_id = (u8)id->driver_data; 90062306a36Sopenharmony_ci dev->if_frequency = 5000000; /* default value of property 0x0706 */ 90162306a36Sopenharmony_ci mutex_init(&dev->i2c_mutex); 90262306a36Sopenharmony_ci INIT_DELAYED_WORK(&dev->stat_work, si2157_stat_work); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci /* check if the tuner is there */ 90562306a36Sopenharmony_ci cmd.wlen = 0; 90662306a36Sopenharmony_ci cmd.rlen = 1; 90762306a36Sopenharmony_ci ret = si2157_cmd_execute(client, &cmd); 90862306a36Sopenharmony_ci if (ret && ret != -EAGAIN) 90962306a36Sopenharmony_ci goto err_kfree; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &si2157_ops, sizeof(struct dvb_tuner_ops)); 91262306a36Sopenharmony_ci fe->tuner_priv = client; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER 91562306a36Sopenharmony_ci if (cfg->mdev) { 91662306a36Sopenharmony_ci dev->mdev = cfg->mdev; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci dev->ent.name = KBUILD_MODNAME; 91962306a36Sopenharmony_ci dev->ent.function = MEDIA_ENT_F_TUNER; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci dev->pad[SI2157_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK; 92262306a36Sopenharmony_ci dev->pad[SI2157_PAD_RF_INPUT].sig_type = PAD_SIGNAL_ANALOG; 92362306a36Sopenharmony_ci dev->pad[SI2157_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE; 92462306a36Sopenharmony_ci dev->pad[SI2157_PAD_VID_OUT].sig_type = PAD_SIGNAL_ANALOG; 92562306a36Sopenharmony_ci dev->pad[SI2157_PAD_AUD_OUT].flags = MEDIA_PAD_FL_SOURCE; 92662306a36Sopenharmony_ci dev->pad[SI2157_PAD_AUD_OUT].sig_type = PAD_SIGNAL_AUDIO; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci ret = media_entity_pads_init(&dev->ent, SI2157_NUM_PADS, 92962306a36Sopenharmony_ci &dev->pad[0]); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (ret) 93262306a36Sopenharmony_ci goto err_kfree; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci ret = media_device_register_entity(cfg->mdev, &dev->ent); 93562306a36Sopenharmony_ci if (ret) { 93662306a36Sopenharmony_ci media_entity_cleanup(&dev->ent); 93762306a36Sopenharmony_ci goto err_kfree; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci#endif 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci dev_info(&client->dev, "Silicon Labs Si21%d successfully attached\n", 94362306a36Sopenharmony_ci dev->part_id); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci return 0; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_cierr_kfree: 94862306a36Sopenharmony_ci kfree(dev); 94962306a36Sopenharmony_cierr: 95062306a36Sopenharmony_ci dev_dbg(&client->dev, "failed=%d\n", ret); 95162306a36Sopenharmony_ci return ret; 95262306a36Sopenharmony_ci} 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_cistatic void si2157_remove(struct i2c_client *client) 95562306a36Sopenharmony_ci{ 95662306a36Sopenharmony_ci struct si2157_dev *dev = i2c_get_clientdata(client); 95762306a36Sopenharmony_ci struct dvb_frontend *fe = dev->fe; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci dev_dbg(&client->dev, "\n"); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci /* stop statistics polling */ 96262306a36Sopenharmony_ci cancel_delayed_work_sync(&dev->stat_work); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER_DVB 96562306a36Sopenharmony_ci if (dev->mdev) 96662306a36Sopenharmony_ci media_device_unregister_entity(&dev->ent); 96762306a36Sopenharmony_ci#endif 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); 97062306a36Sopenharmony_ci fe->tuner_priv = NULL; 97162306a36Sopenharmony_ci kfree(dev); 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci/* 97562306a36Sopenharmony_ci * The part_id used here will only be used on buggy devices that don't 97662306a36Sopenharmony_ci * accept firmware uploads. Non-buggy devices should just use "si2157" for 97762306a36Sopenharmony_ci * all SiLabs TER tuners, as the driver should auto-detect it. 97862306a36Sopenharmony_ci */ 97962306a36Sopenharmony_cistatic const struct i2c_device_id si2157_id_table[] = { 98062306a36Sopenharmony_ci {"si2157", SI2157}, 98162306a36Sopenharmony_ci {"si2146", SI2146}, 98262306a36Sopenharmony_ci {"si2141", SI2141}, 98362306a36Sopenharmony_ci {"si2177", SI2177}, 98462306a36Sopenharmony_ci {} 98562306a36Sopenharmony_ci}; 98662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, si2157_id_table); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cistatic struct i2c_driver si2157_driver = { 98962306a36Sopenharmony_ci .driver = { 99062306a36Sopenharmony_ci .name = "si2157", 99162306a36Sopenharmony_ci .suppress_bind_attrs = true, 99262306a36Sopenharmony_ci }, 99362306a36Sopenharmony_ci .probe = si2157_probe, 99462306a36Sopenharmony_ci .remove = si2157_remove, 99562306a36Sopenharmony_ci .id_table = si2157_id_table, 99662306a36Sopenharmony_ci}; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cimodule_i2c_driver(si2157_driver); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ciMODULE_DESCRIPTION("Silicon Labs Si2141/Si2146/2147/2148/2157/2158 silicon tuner driver"); 100162306a36Sopenharmony_ciMODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); 100262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 100362306a36Sopenharmony_ciMODULE_FIRMWARE(SI2158_A20_FIRMWARE); 100462306a36Sopenharmony_ciMODULE_FIRMWARE(SI2141_A10_FIRMWARE); 100562306a36Sopenharmony_ciMODULE_FIRMWARE(SI2157_A30_FIRMWARE); 100662306a36Sopenharmony_ciMODULE_FIRMWARE(SI2141_60_FIRMWARE); 100762306a36Sopenharmony_ciMODULE_FIRMWARE(SI2141_61_FIRMWARE); 100862306a36Sopenharmony_ciMODULE_FIRMWARE(SI2146_11_FIRMWARE); 100962306a36Sopenharmony_ciMODULE_FIRMWARE(SI2147_50_FIRMWARE); 101062306a36Sopenharmony_ciMODULE_FIRMWARE(SI2148_32_FIRMWARE); 101162306a36Sopenharmony_ciMODULE_FIRMWARE(SI2148_33_FIRMWARE); 101262306a36Sopenharmony_ciMODULE_FIRMWARE(SI2157_50_FIRMWARE); 101362306a36Sopenharmony_ciMODULE_FIRMWARE(SI2158_50_FIRMWARE); 101462306a36Sopenharmony_ciMODULE_FIRMWARE(SI2158_51_FIRMWARE); 101562306a36Sopenharmony_ciMODULE_FIRMWARE(SI2177_50_FIRMWARE); 1016