162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Sharp QM1D1B0004 satellite tuner 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * based on (former) drivers/media/pci/pt1/va1j5jf8007s.c. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* 1162306a36Sopenharmony_ci * Note: 1262306a36Sopenharmony_ci * Since the data-sheet of this tuner chip is not available, 1362306a36Sopenharmony_ci * this driver lacks some tuner_ops and config options. 1462306a36Sopenharmony_ci * In addition, the implementation might be dependent on the specific use 1562306a36Sopenharmony_ci * in the FE module: VA1J5JF8007S and/or in the product: Earthsoft PT1/PT2. 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/kernel.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <media/dvb_frontend.h> 2162306a36Sopenharmony_ci#include "qm1d1b0004.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * Tuner I/F (copied from the former va1j5jf8007s.c) 2562306a36Sopenharmony_ci * b[0] I2C addr 2662306a36Sopenharmony_ci * b[1] "0":1, BG:2, divider_quotient[7:3]:5 2762306a36Sopenharmony_ci * b[2] divider_quotient[2:0]:3, divider_remainder:5 2862306a36Sopenharmony_ci * b[3] "111":3, LPF[3:2]:2, TM:1, "0":1, REF:1 2962306a36Sopenharmony_ci * b[4] BANDX, PSC:1, LPF[1:0]:2, DIV:1, "0":1 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * PLL frequency step := 3262306a36Sopenharmony_ci * REF == 0 -> PLL XTL frequency(4MHz) / 8 3362306a36Sopenharmony_ci * REF == 1 -> PLL XTL frequency(4MHz) / 4 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * PreScaler := 3662306a36Sopenharmony_ci * PSC == 0 -> x32 3762306a36Sopenharmony_ci * PSC == 1 -> x16 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * divider_quotient := (frequency / PLL frequency step) / PreScaler 4062306a36Sopenharmony_ci * divider_remainder := (frequency / PLL frequency step) % PreScaler 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * LPF := LPF Frequency / 1000 / 2 - 2 4362306a36Sopenharmony_ci * LPF Frequency @ baudrate=28.86Mbps = 30000 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * band (1..9) 4662306a36Sopenharmony_ci * band 1 (freq < 986000) -> DIV:1, BANDX:5, PSC:1 4762306a36Sopenharmony_ci * band 2 (freq < 1072000) -> DIV:1, BANDX:6, PSC:1 4862306a36Sopenharmony_ci * band 3 (freq < 1154000) -> DIV:1, BANDX:7, PSC:0 4962306a36Sopenharmony_ci * band 4 (freq < 1291000) -> DIV:0, BANDX:1, PSC:0 5062306a36Sopenharmony_ci * band 5 (freq < 1447000) -> DIV:0, BANDX:2, PSC:0 5162306a36Sopenharmony_ci * band 6 (freq < 1615000) -> DIV:0, BANDX:3, PSC:0 5262306a36Sopenharmony_ci * band 7 (freq < 1791000) -> DIV:0, BANDX:4, PSC:0 5362306a36Sopenharmony_ci * band 8 (freq < 1972000) -> DIV:0, BANDX:5, PSC:0 5462306a36Sopenharmony_ci * band 9 (freq < 2150000) -> DIV:0, BANDX:6, PSC:0 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define QM1D1B0004_PSC_MASK (1 << 4) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define QM1D1B0004_XTL_FREQ 4000 6062306a36Sopenharmony_ci#define QM1D1B0004_LPF_FALLBACK 30000 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#if 0 /* Currently unused */ 6362306a36Sopenharmony_cistatic const struct qm1d1b0004_config default_cfg = { 6462306a36Sopenharmony_ci .lpf_freq = QM1D1B0004_CFG_LPF_DFLT, 6562306a36Sopenharmony_ci .half_step = false, 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci#endif 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistruct qm1d1b0004_state { 7062306a36Sopenharmony_ci struct qm1d1b0004_config cfg; 7162306a36Sopenharmony_ci struct i2c_client *i2c; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistruct qm1d1b0004_cb_map { 7662306a36Sopenharmony_ci u32 frequency; 7762306a36Sopenharmony_ci u8 cb; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic const struct qm1d1b0004_cb_map cb_maps[] = { 8162306a36Sopenharmony_ci { 986000, 0xb2 }, 8262306a36Sopenharmony_ci { 1072000, 0xd2 }, 8362306a36Sopenharmony_ci { 1154000, 0xe2 }, 8462306a36Sopenharmony_ci { 1291000, 0x20 }, 8562306a36Sopenharmony_ci { 1447000, 0x40 }, 8662306a36Sopenharmony_ci { 1615000, 0x60 }, 8762306a36Sopenharmony_ci { 1791000, 0x80 }, 8862306a36Sopenharmony_ci { 1972000, 0xa0 }, 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic u8 lookup_cb(u32 frequency) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci int i; 9462306a36Sopenharmony_ci const struct qm1d1b0004_cb_map *map; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cb_maps); i++) { 9762306a36Sopenharmony_ci map = &cb_maps[i]; 9862306a36Sopenharmony_ci if (frequency < map->frequency) 9962306a36Sopenharmony_ci return map->cb; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci return 0xc0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int qm1d1b0004_set_params(struct dvb_frontend *fe) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct qm1d1b0004_state *state; 10762306a36Sopenharmony_ci u32 frequency, pll, lpf_freq; 10862306a36Sopenharmony_ci u16 word; 10962306a36Sopenharmony_ci u8 buf[4], cb, lpf; 11062306a36Sopenharmony_ci int ret; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci state = fe->tuner_priv; 11362306a36Sopenharmony_ci frequency = fe->dtv_property_cache.frequency; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci pll = QM1D1B0004_XTL_FREQ / 4; 11662306a36Sopenharmony_ci if (state->cfg.half_step) 11762306a36Sopenharmony_ci pll /= 2; 11862306a36Sopenharmony_ci word = DIV_ROUND_CLOSEST(frequency, pll); 11962306a36Sopenharmony_ci cb = lookup_cb(frequency); 12062306a36Sopenharmony_ci if (cb & QM1D1B0004_PSC_MASK) 12162306a36Sopenharmony_ci word = (word << 1 & ~0x1f) | (word & 0x0f); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* step.1: set frequency with BG:2, TM:0(4MHZ), LPF:4MHz */ 12462306a36Sopenharmony_ci buf[0] = 0x40 | word >> 8; 12562306a36Sopenharmony_ci buf[1] = word; 12662306a36Sopenharmony_ci /* inconsisnten with the above I/F doc. maybe the doc is wrong */ 12762306a36Sopenharmony_ci buf[2] = 0xe0 | state->cfg.half_step; 12862306a36Sopenharmony_ci buf[3] = cb; 12962306a36Sopenharmony_ci ret = i2c_master_send(state->i2c, buf, 4); 13062306a36Sopenharmony_ci if (ret < 0) 13162306a36Sopenharmony_ci return ret; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* step.2: set TM:1 */ 13462306a36Sopenharmony_ci buf[0] = 0xe4 | state->cfg.half_step; 13562306a36Sopenharmony_ci ret = i2c_master_send(state->i2c, buf, 1); 13662306a36Sopenharmony_ci if (ret < 0) 13762306a36Sopenharmony_ci return ret; 13862306a36Sopenharmony_ci msleep(20); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* step.3: set LPF */ 14162306a36Sopenharmony_ci lpf_freq = state->cfg.lpf_freq; 14262306a36Sopenharmony_ci if (lpf_freq == QM1D1B0004_CFG_LPF_DFLT) 14362306a36Sopenharmony_ci lpf_freq = fe->dtv_property_cache.symbol_rate / 1000; 14462306a36Sopenharmony_ci if (lpf_freq == 0) 14562306a36Sopenharmony_ci lpf_freq = QM1D1B0004_LPF_FALLBACK; 14662306a36Sopenharmony_ci lpf = DIV_ROUND_UP(lpf_freq, 2000) - 2; 14762306a36Sopenharmony_ci buf[0] = 0xe4 | ((lpf & 0x0c) << 1) | state->cfg.half_step; 14862306a36Sopenharmony_ci buf[1] = cb | ((lpf & 0x03) << 2); 14962306a36Sopenharmony_ci ret = i2c_master_send(state->i2c, buf, 2); 15062306a36Sopenharmony_ci if (ret < 0) 15162306a36Sopenharmony_ci return ret; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* step.4: read PLL lock? */ 15462306a36Sopenharmony_ci buf[0] = 0; 15562306a36Sopenharmony_ci ret = i2c_master_recv(state->i2c, buf, 1); 15662306a36Sopenharmony_ci if (ret < 0) 15762306a36Sopenharmony_ci return ret; 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic int qm1d1b0004_set_config(struct dvb_frontend *fe, void *priv_cfg) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct qm1d1b0004_state *state; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci state = fe->tuner_priv; 16762306a36Sopenharmony_ci memcpy(&state->cfg, priv_cfg, sizeof(state->cfg)); 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int qm1d1b0004_init(struct dvb_frontend *fe) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct qm1d1b0004_state *state; 17562306a36Sopenharmony_ci u8 buf[2] = {0xf8, 0x04}; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci state = fe->tuner_priv; 17862306a36Sopenharmony_ci if (state->cfg.half_step) 17962306a36Sopenharmony_ci buf[0] |= 0x01; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return i2c_master_send(state->i2c, buf, 2); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic const struct dvb_tuner_ops qm1d1b0004_ops = { 18662306a36Sopenharmony_ci .info = { 18762306a36Sopenharmony_ci .name = "Sharp qm1d1b0004", 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci .frequency_min_hz = 950 * MHz, 19062306a36Sopenharmony_ci .frequency_max_hz = 2150 * MHz, 19162306a36Sopenharmony_ci }, 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci .init = qm1d1b0004_init, 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci .set_params = qm1d1b0004_set_params, 19662306a36Sopenharmony_ci .set_config = qm1d1b0004_set_config, 19762306a36Sopenharmony_ci}; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic int 20062306a36Sopenharmony_ciqm1d1b0004_probe(struct i2c_client *client) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct dvb_frontend *fe; 20362306a36Sopenharmony_ci struct qm1d1b0004_config *cfg; 20462306a36Sopenharmony_ci struct qm1d1b0004_state *state; 20562306a36Sopenharmony_ci int ret; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci cfg = client->dev.platform_data; 20862306a36Sopenharmony_ci fe = cfg->fe; 20962306a36Sopenharmony_ci i2c_set_clientdata(client, fe); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci fe->tuner_priv = kzalloc(sizeof(struct qm1d1b0004_state), GFP_KERNEL); 21262306a36Sopenharmony_ci if (!fe->tuner_priv) { 21362306a36Sopenharmony_ci ret = -ENOMEM; 21462306a36Sopenharmony_ci goto err_mem; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &qm1d1b0004_ops, sizeof(fe->ops.tuner_ops)); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci state = fe->tuner_priv; 22062306a36Sopenharmony_ci state->i2c = client; 22162306a36Sopenharmony_ci ret = qm1d1b0004_set_config(fe, cfg); 22262306a36Sopenharmony_ci if (ret != 0) 22362306a36Sopenharmony_ci goto err_priv; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci dev_info(&client->dev, "Sharp QM1D1B0004 attached.\n"); 22662306a36Sopenharmony_ci return 0; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cierr_priv: 22962306a36Sopenharmony_ci kfree(fe->tuner_priv); 23062306a36Sopenharmony_cierr_mem: 23162306a36Sopenharmony_ci fe->tuner_priv = NULL; 23262306a36Sopenharmony_ci return ret; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic void qm1d1b0004_remove(struct i2c_client *client) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct dvb_frontend *fe; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci fe = i2c_get_clientdata(client); 24062306a36Sopenharmony_ci kfree(fe->tuner_priv); 24162306a36Sopenharmony_ci fe->tuner_priv = NULL; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic const struct i2c_device_id qm1d1b0004_id[] = { 24662306a36Sopenharmony_ci {"qm1d1b0004", 0}, 24762306a36Sopenharmony_ci {} 24862306a36Sopenharmony_ci}; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, qm1d1b0004_id); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic struct i2c_driver qm1d1b0004_driver = { 25362306a36Sopenharmony_ci .driver = { 25462306a36Sopenharmony_ci .name = "qm1d1b0004", 25562306a36Sopenharmony_ci }, 25662306a36Sopenharmony_ci .probe = qm1d1b0004_probe, 25762306a36Sopenharmony_ci .remove = qm1d1b0004_remove, 25862306a36Sopenharmony_ci .id_table = qm1d1b0004_id, 25962306a36Sopenharmony_ci}; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cimodule_i2c_driver(qm1d1b0004_driver); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ciMODULE_DESCRIPTION("Sharp QM1D1B0004"); 26462306a36Sopenharmony_ciMODULE_AUTHOR("Akihiro Tsukada"); 26562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 266