// SPDX-License-Identifier: GPL-2.0
/*
* Sharp QM1D1B0004 satellite tuner
*
* Copyright ( C ) 2014 Akihiro Tsukada < tskd08 @ gmail . com >
*
* based on ( former ) drivers / media / pci / pt1 / va1j5jf8007s . c .
*/
/*
* Note :
* Since the data - sheet of this tuner chip is not available ,
* this driver lacks some tuner_ops and config options .
* In addition , the implementation might be dependent on the specific use
* in the FE module : VA1J5JF8007S and / or in the product : Earthsoft PT1 / PT2 .
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <media/dvb_frontend.h>
#include "qm1d1b0004.h"
/*
* Tuner I / F ( copied from the former va1j5jf8007s . c )
* b [ 0 ] I2C addr
* b [ 1 ] " 0 " : 1 , BG : 2 , divider_quotient [ 7 : 3 ] : 5
* b [ 2 ] divider_quotient [ 2 : 0 ] : 3 , divider_remainder : 5
* b [ 3 ] " 111 " : 3 , LPF [ 3 : 2 ] : 2 , TM : 1 , " 0 " : 1 , REF : 1
* b [ 4 ] BANDX , PSC : 1 , LPF [ 1 : 0 ] : 2 , DIV : 1 , " 0 " : 1
*
* PLL frequency step : =
* REF = = 0 - > PLL XTL frequency ( 4 MHz ) / 8
* REF = = 1 - > PLL XTL frequency ( 4 MHz ) / 4
*
* PreScaler : =
* PSC = = 0 - > x32
* PSC = = 1 - > x16
*
* divider_quotient : = ( frequency / PLL frequency step ) / PreScaler
* divider_remainder : = ( frequency / PLL frequency step ) % PreScaler
*
* LPF : = LPF Frequency / 1000 / 2 - 2
* LPF Frequency @ baudrate = 28 . 86 Mbps = 30000
*
* band ( 1 . . 9 )
* band 1 ( freq < 986000 ) - > DIV : 1 , BANDX : 5 , PSC : 1
* band 2 ( freq < 1072000 ) - > DIV : 1 , BANDX : 6 , PSC : 1
* band 3 ( freq < 1154000 ) - > DIV : 1 , BANDX : 7 , PSC : 0
* band 4 ( freq < 1291000 ) - > DIV : 0 , BANDX : 1 , PSC : 0
* band 5 ( freq < 1447000 ) - > DIV : 0 , BANDX : 2 , PSC : 0
* band 6 ( freq < 1615000 ) - > DIV : 0 , BANDX : 3 , PSC : 0
* band 7 ( freq < 1791000 ) - > DIV : 0 , BANDX : 4 , PSC : 0
* band 8 ( freq < 1972000 ) - > DIV : 0 , BANDX : 5 , PSC : 0
* band 9 ( freq < 2150000 ) - > DIV : 0 , BANDX : 6 , PSC : 0
*/
#define QM1D1B0004_PSC_MASK (1 << 4 )
#define QM1D1B0004_XTL_FREQ 4000
#define QM1D1B0004_LPF_FALLBACK 30000
#if 0 /* Currently unused */
static const struct qm1d1b0004_config default_cfg = {
.lpf_freq = QM1D1B0004_CFG_LPF_DFLT,
.half_step = false ,
};
#endif
struct qm1d1b0004_state {
struct qm1d1b0004_config cfg;
struct i2c_client *i2c;
};
struct qm1d1b0004_cb_map {
u32 frequency;
u8 cb;
};
static const struct qm1d1b0004_cb_map cb_maps[] = {
{ 986000 , 0 xb2 },
{ 1072000 , 0 xd2 },
{ 1154000 , 0 xe2 },
{ 1291000 , 0 x20 },
{ 1447000 , 0 x40 },
{ 1615000 , 0 x60 },
{ 1791000 , 0 x80 },
{ 1972000 , 0 xa0 },
};
static u8 lookup_cb(u32 frequency)
{
int i;
const struct qm1d1b0004_cb_map *map;
for (i = 0 ; i < ARRAY_SIZE(cb_maps); i++) {
map = &cb_maps[i];
if (frequency < map->frequency)
return map->cb;
}
return 0 xc0;
}
static int qm1d1b0004_set_params(struct dvb_frontend *fe)
{
struct qm1d1b0004_state *state;
u32 frequency, pll, lpf_freq;
u16 word;
u8 buf[4 ], cb, lpf;
int ret;
state = fe->tuner_priv;
frequency = fe->dtv_property_cache.frequency;
pll = QM1D1B0004_XTL_FREQ / 4 ;
if (state->cfg.half_step)
pll /= 2 ;
word = DIV_ROUND_CLOSEST(frequency, pll);
cb = lookup_cb(frequency);
if (cb & QM1D1B0004_PSC_MASK)
word = (word << 1 & ~0 x1f) | (word & 0 x0f);
/* step.1: set frequency with BG:2, TM:0(4MHZ), LPF:4MHz */
buf[0 ] = 0 x40 | word >> 8 ;
buf[1 ] = word;
/* inconsisnten with the above I/F doc. maybe the doc is wrong */
buf[2 ] = 0 xe0 | state->cfg.half_step;
buf[3 ] = cb;
ret = i2c_master_send(state->i2c, buf, 4 );
if (ret < 0 )
return ret;
/* step.2: set TM:1 */
buf[0 ] = 0 xe4 | state->cfg.half_step;
ret = i2c_master_send(state->i2c, buf, 1 );
if (ret < 0 )
return ret;
msleep(20 );
/* step.3: set LPF */
lpf_freq = state->cfg.lpf_freq;
if (lpf_freq == QM1D1B0004_CFG_LPF_DFLT)
lpf_freq = fe->dtv_property_cache.symbol_rate / 1000 ;
if (lpf_freq == 0 )
lpf_freq = QM1D1B0004_LPF_FALLBACK;
lpf = DIV_ROUND_UP(lpf_freq, 2000 ) - 2 ;
buf[0 ] = 0 xe4 | ((lpf & 0 x0c) << 1 ) | state->cfg.half_step;
buf[1 ] = cb | ((lpf & 0 x03) << 2 );
ret = i2c_master_send(state->i2c, buf, 2 );
if (ret < 0 )
return ret;
/* step.4: read PLL lock? */
buf[0 ] = 0 ;
ret = i2c_master_recv(state->i2c, buf, 1 );
if (ret < 0 )
return ret;
return 0 ;
}
static int qm1d1b0004_set_config(struct dvb_frontend *fe, void *priv_cfg)
{
struct qm1d1b0004_state *state;
state = fe->tuner_priv;
memcpy(&state->cfg, priv_cfg, sizeof (state->cfg));
return 0 ;
}
static int qm1d1b0004_init(struct dvb_frontend *fe)
{
struct qm1d1b0004_state *state;
u8 buf[2 ] = {0 xf8, 0 x04};
state = fe->tuner_priv;
if (state->cfg.half_step)
buf[0 ] |= 0 x01;
return i2c_master_send(state->i2c, buf, 2 );
}
static const struct dvb_tuner_ops qm1d1b0004_ops = {
.info = {
.name = "Sharp qm1d1b0004" ,
.frequency_min_hz = 950 * MHz,
.frequency_max_hz = 2150 * MHz,
},
.init = qm1d1b0004_init,
.set_params = qm1d1b0004_set_params,
.set_config = qm1d1b0004_set_config,
};
static int
qm1d1b0004_probe(struct i2c_client *client)
{
struct dvb_frontend *fe;
struct qm1d1b0004_config *cfg;
struct qm1d1b0004_state *state;
int ret;
cfg = client->dev.platform_data;
fe = cfg->fe;
i2c_set_clientdata(client, fe);
fe->tuner_priv = kzalloc(sizeof (struct qm1d1b0004_state), GFP_KERNEL);
if (!fe->tuner_priv) {
ret = -ENOMEM;
goto err_mem;
}
memcpy(&fe->ops.tuner_ops, &qm1d1b0004_ops, sizeof (fe->ops.tuner_ops));
state = fe->tuner_priv;
state->i2c = client;
ret = qm1d1b0004_set_config(fe, cfg);
if (ret != 0 )
goto err_priv;
dev_info(&client->dev, "Sharp QM1D1B0004 attached.\n" );
return 0 ;
err_priv:
kfree(fe->tuner_priv);
err_mem:
fe->tuner_priv = NULL;
return ret;
}
static void qm1d1b0004_remove(struct i2c_client *client)
{
struct dvb_frontend *fe;
fe = i2c_get_clientdata(client);
kfree(fe->tuner_priv);
fe->tuner_priv = NULL;
}
static const struct i2c_device_id qm1d1b0004_id[] = {
{ "qm1d1b0004" },
{}
};
MODULE_DEVICE_TABLE(i2c, qm1d1b0004_id);
static struct i2c_driver qm1d1b0004_driver = {
.driver = {
.name = "qm1d1b0004" ,
},
.probe = qm1d1b0004_probe,
.remove = qm1d1b0004_remove,
.id_table = qm1d1b0004_id,
};
module_i2c_driver(qm1d1b0004_driver);
MODULE_DESCRIPTION("Sharp QM1D1B0004" );
MODULE_AUTHOR("Akihiro Tsukada" );
MODULE_LICENSE("GPL" );
Messung V0.5 in Prozent C=97 H=93 G=94
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-09-27)
¤
*© Formatika GbR, Deutschland