266 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Sharp QM1D1B0004 satellite tuner4 *5 * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>6 *7 * based on (former) drivers/media/pci/pt1/va1j5jf8007s.c.8 */9 10/*11 * Note:12 * Since the data-sheet of this tuner chip is not available,13 * this driver lacks some tuner_ops and config options.14 * In addition, the implementation might be dependent on the specific use15 * in the FE module: VA1J5JF8007S and/or in the product: Earthsoft PT1/PT2.16 */17 18#include <linux/kernel.h>19#include <linux/module.h>20#include <media/dvb_frontend.h>21#include "qm1d1b0004.h"22 23/*24 * Tuner I/F (copied from the former va1j5jf8007s.c)25 * b[0] I2C addr26 * b[1] "0":1, BG:2, divider_quotient[7:3]:527 * b[2] divider_quotient[2:0]:3, divider_remainder:528 * b[3] "111":3, LPF[3:2]:2, TM:1, "0":1, REF:129 * b[4] BANDX, PSC:1, LPF[1:0]:2, DIV:1, "0":130 *31 * PLL frequency step :=32 * REF == 0 -> PLL XTL frequency(4MHz) / 833 * REF == 1 -> PLL XTL frequency(4MHz) / 434 *35 * PreScaler :=36 * PSC == 0 -> x3237 * PSC == 1 -> x1638 *39 * divider_quotient := (frequency / PLL frequency step) / PreScaler40 * divider_remainder := (frequency / PLL frequency step) % PreScaler41 *42 * LPF := LPF Frequency / 1000 / 2 - 243 * LPF Frequency @ baudrate=28.86Mbps = 3000044 *45 * band (1..9)46 * band 1 (freq < 986000) -> DIV:1, BANDX:5, PSC:147 * band 2 (freq < 1072000) -> DIV:1, BANDX:6, PSC:148 * band 3 (freq < 1154000) -> DIV:1, BANDX:7, PSC:049 * band 4 (freq < 1291000) -> DIV:0, BANDX:1, PSC:050 * band 5 (freq < 1447000) -> DIV:0, BANDX:2, PSC:051 * band 6 (freq < 1615000) -> DIV:0, BANDX:3, PSC:052 * band 7 (freq < 1791000) -> DIV:0, BANDX:4, PSC:053 * band 8 (freq < 1972000) -> DIV:0, BANDX:5, PSC:054 * band 9 (freq < 2150000) -> DIV:0, BANDX:6, PSC:055 */56 57#define QM1D1B0004_PSC_MASK (1 << 4)58 59#define QM1D1B0004_XTL_FREQ 400060#define QM1D1B0004_LPF_FALLBACK 3000061 62#if 0 /* Currently unused */63static const struct qm1d1b0004_config default_cfg = {64 .lpf_freq = QM1D1B0004_CFG_LPF_DFLT,65 .half_step = false,66};67#endif68 69struct qm1d1b0004_state {70 struct qm1d1b0004_config cfg;71 struct i2c_client *i2c;72};73 74 75struct qm1d1b0004_cb_map {76 u32 frequency;77 u8 cb;78};79 80static const struct qm1d1b0004_cb_map cb_maps[] = {81 { 986000, 0xb2 },82 { 1072000, 0xd2 },83 { 1154000, 0xe2 },84 { 1291000, 0x20 },85 { 1447000, 0x40 },86 { 1615000, 0x60 },87 { 1791000, 0x80 },88 { 1972000, 0xa0 },89};90 91static u8 lookup_cb(u32 frequency)92{93 int i;94 const struct qm1d1b0004_cb_map *map;95 96 for (i = 0; i < ARRAY_SIZE(cb_maps); i++) {97 map = &cb_maps[i];98 if (frequency < map->frequency)99 return map->cb;100 }101 return 0xc0;102}103 104static int qm1d1b0004_set_params(struct dvb_frontend *fe)105{106 struct qm1d1b0004_state *state;107 u32 frequency, pll, lpf_freq;108 u16 word;109 u8 buf[4], cb, lpf;110 int ret;111 112 state = fe->tuner_priv;113 frequency = fe->dtv_property_cache.frequency;114 115 pll = QM1D1B0004_XTL_FREQ / 4;116 if (state->cfg.half_step)117 pll /= 2;118 word = DIV_ROUND_CLOSEST(frequency, pll);119 cb = lookup_cb(frequency);120 if (cb & QM1D1B0004_PSC_MASK)121 word = (word << 1 & ~0x1f) | (word & 0x0f);122 123 /* step.1: set frequency with BG:2, TM:0(4MHZ), LPF:4MHz */124 buf[0] = 0x40 | word >> 8;125 buf[1] = word;126 /* inconsisnten with the above I/F doc. maybe the doc is wrong */127 buf[2] = 0xe0 | state->cfg.half_step;128 buf[3] = cb;129 ret = i2c_master_send(state->i2c, buf, 4);130 if (ret < 0)131 return ret;132 133 /* step.2: set TM:1 */134 buf[0] = 0xe4 | state->cfg.half_step;135 ret = i2c_master_send(state->i2c, buf, 1);136 if (ret < 0)137 return ret;138 msleep(20);139 140 /* step.3: set LPF */141 lpf_freq = state->cfg.lpf_freq;142 if (lpf_freq == QM1D1B0004_CFG_LPF_DFLT)143 lpf_freq = fe->dtv_property_cache.symbol_rate / 1000;144 if (lpf_freq == 0)145 lpf_freq = QM1D1B0004_LPF_FALLBACK;146 lpf = DIV_ROUND_UP(lpf_freq, 2000) - 2;147 buf[0] = 0xe4 | ((lpf & 0x0c) << 1) | state->cfg.half_step;148 buf[1] = cb | ((lpf & 0x03) << 2);149 ret = i2c_master_send(state->i2c, buf, 2);150 if (ret < 0)151 return ret;152 153 /* step.4: read PLL lock? */154 buf[0] = 0;155 ret = i2c_master_recv(state->i2c, buf, 1);156 if (ret < 0)157 return ret;158 return 0;159}160 161 162static int qm1d1b0004_set_config(struct dvb_frontend *fe, void *priv_cfg)163{164 struct qm1d1b0004_state *state;165 166 state = fe->tuner_priv;167 memcpy(&state->cfg, priv_cfg, sizeof(state->cfg));168 return 0;169}170 171 172static int qm1d1b0004_init(struct dvb_frontend *fe)173{174 struct qm1d1b0004_state *state;175 u8 buf[2] = {0xf8, 0x04};176 177 state = fe->tuner_priv;178 if (state->cfg.half_step)179 buf[0] |= 0x01;180 181 return i2c_master_send(state->i2c, buf, 2);182}183 184 185static const struct dvb_tuner_ops qm1d1b0004_ops = {186 .info = {187 .name = "Sharp qm1d1b0004",188 189 .frequency_min_hz = 950 * MHz,190 .frequency_max_hz = 2150 * MHz,191 },192 193 .init = qm1d1b0004_init,194 195 .set_params = qm1d1b0004_set_params,196 .set_config = qm1d1b0004_set_config,197};198 199static int200qm1d1b0004_probe(struct i2c_client *client)201{202 struct dvb_frontend *fe;203 struct qm1d1b0004_config *cfg;204 struct qm1d1b0004_state *state;205 int ret;206 207 cfg = client->dev.platform_data;208 fe = cfg->fe;209 i2c_set_clientdata(client, fe);210 211 fe->tuner_priv = kzalloc(sizeof(struct qm1d1b0004_state), GFP_KERNEL);212 if (!fe->tuner_priv) {213 ret = -ENOMEM;214 goto err_mem;215 }216 217 memcpy(&fe->ops.tuner_ops, &qm1d1b0004_ops, sizeof(fe->ops.tuner_ops));218 219 state = fe->tuner_priv;220 state->i2c = client;221 ret = qm1d1b0004_set_config(fe, cfg);222 if (ret != 0)223 goto err_priv;224 225 dev_info(&client->dev, "Sharp QM1D1B0004 attached.\n");226 return 0;227 228err_priv:229 kfree(fe->tuner_priv);230err_mem:231 fe->tuner_priv = NULL;232 return ret;233}234 235static void qm1d1b0004_remove(struct i2c_client *client)236{237 struct dvb_frontend *fe;238 239 fe = i2c_get_clientdata(client);240 kfree(fe->tuner_priv);241 fe->tuner_priv = NULL;242}243 244 245static const struct i2c_device_id qm1d1b0004_id[] = {246 { "qm1d1b0004" },247 {}248};249 250MODULE_DEVICE_TABLE(i2c, qm1d1b0004_id);251 252static struct i2c_driver qm1d1b0004_driver = {253 .driver = {254 .name = "qm1d1b0004",255 },256 .probe = qm1d1b0004_probe,257 .remove = qm1d1b0004_remove,258 .id_table = qm1d1b0004_id,259};260 261module_i2c_driver(qm1d1b0004_driver);262 263MODULE_DESCRIPTION("Sharp QM1D1B0004");264MODULE_AUTHOR("Akihiro Tsukada");265MODULE_LICENSE("GPL");266