cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

qm1d1b0004.c (6174B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Sharp QM1D1B0004 satellite tuner
      4 *
      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 use
     15 * 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 addr
     26 * b[1] "0":1, BG:2, divider_quotient[7:3]:5
     27 * b[2] divider_quotient[2:0]:3, divider_remainder:5
     28 * b[3] "111":3, LPF[3:2]:2, TM:1, "0":1, REF:1
     29 * b[4] BANDX, PSC:1, LPF[1:0]:2, DIV:1, "0":1
     30 *
     31 * PLL frequency step :=
     32 *    REF == 0 -> PLL XTL frequency(4MHz) / 8
     33 *    REF == 1 -> PLL XTL frequency(4MHz) / 4
     34 *
     35 * PreScaler :=
     36 *    PSC == 0 -> x32
     37 *    PSC == 1 -> x16
     38 *
     39 * divider_quotient := (frequency / PLL frequency step) / PreScaler
     40 * divider_remainder := (frequency / PLL frequency step) % PreScaler
     41 *
     42 * LPF := LPF Frequency / 1000 / 2 - 2
     43 * LPF Frequency @ baudrate=28.86Mbps = 30000
     44 *
     45 * band (1..9)
     46 *   band 1 (freq <  986000) -> DIV:1, BANDX:5, PSC:1
     47 *   band 2 (freq < 1072000) -> DIV:1, BANDX:6, PSC:1
     48 *   band 3 (freq < 1154000) -> DIV:1, BANDX:7, PSC:0
     49 *   band 4 (freq < 1291000) -> DIV:0, BANDX:1, PSC:0
     50 *   band 5 (freq < 1447000) -> DIV:0, BANDX:2, PSC:0
     51 *   band 6 (freq < 1615000) -> DIV:0, BANDX:3, PSC:0
     52 *   band 7 (freq < 1791000) -> DIV:0, BANDX:4, PSC:0
     53 *   band 8 (freq < 1972000) -> DIV:0, BANDX:5, PSC:0
     54 *   band 9 (freq < 2150000) -> DIV:0, BANDX:6, PSC:0
     55 */
     56
     57#define QM1D1B0004_PSC_MASK (1 << 4)
     58
     59#define QM1D1B0004_XTL_FREQ 4000
     60#define QM1D1B0004_LPF_FALLBACK 30000
     61
     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#endif
     68
     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 int
    200qm1d1b0004_probe(struct i2c_client *client, const struct i2c_device_id *id)
    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 int 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	return 0;
    243}
    244
    245
    246static const struct i2c_device_id qm1d1b0004_id[] = {
    247	{"qm1d1b0004", 0},
    248	{}
    249};
    250
    251MODULE_DEVICE_TABLE(i2c, qm1d1b0004_id);
    252
    253static struct i2c_driver qm1d1b0004_driver = {
    254	.driver = {
    255		.name = "qm1d1b0004",
    256	},
    257	.probe    = qm1d1b0004_probe,
    258	.remove   = qm1d1b0004_remove,
    259	.id_table = qm1d1b0004_id,
    260};
    261
    262module_i2c_driver(qm1d1b0004_driver);
    263
    264MODULE_DESCRIPTION("Sharp QM1D1B0004");
    265MODULE_AUTHOR("Akihiro Tsukada");
    266MODULE_LICENSE("GPL");