// SPDX-License-Identifier: GPL-2.0
/*
* Earthsoft PT3 driver
*
* Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
*/
#include <linux/freezer.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/string.h>
#include <linux/sched/signal.h>
#include <media/dmxdev.h>
#include <media/dvbdev.h>
#include <media/dvb_demux.h>
#include <media/dvb_frontend.h>
#include "pt3.h"
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static bool one_adapter;
module_param(one_adapter, bool, 0444);
MODULE_PARM_DESC(one_adapter, "Place FE's together under one adapter.");
static int num_bufs = 4;
module_param(num_bufs, int, 0444);
MODULE_PARM_DESC(num_bufs, "Number of DMA buffer (188KiB) per FE.");
static const struct i2c_algorithm pt3_i2c_algo = {
.master_xfer = &pt3_i2c_master_xfer,
.functionality = &pt3_i2c_functionality,
};
static const struct pt3_adap_config adap_conf[PT3_NUM_FE] = {
{
.demod_info = {
I2C_BOARD_INFO(TC90522_I2C_DEV_SAT, 0x11),
},
.tuner_info = {
I2C_BOARD_INFO("qm1d1c0042", 0x63),
},
.tuner_cfg.qm1d1c0042 = {
.lpf = 1,
},
.init_freq = 1049480 - 300,
},
{
.demod_info = {
I2C_BOARD_INFO(TC90522_I2C_DEV_TER, 0x10),
},
.tuner_info = {
I2C_BOARD_INFO("mxl301rf", 0x62),
},
.init_freq = 515142857,
},
{
.demod_info = {
I2C_BOARD_INFO(TC90522_I2C_DEV_SAT, 0x13),
},
.tuner_info = {
I2C_BOARD_INFO("qm1d1c0042", 0x60),
},
.tuner_cfg.qm1d1c0042 = {
.lpf = 1,
},
.init_freq = 1049480 + 300,
},
{
.demod_info = {
I2C_BOARD_INFO(TC90522_I2C_DEV_TER, 0x12),
},
.tuner_info = {
I2C_BOARD_INFO("mxl301rf", 0x61),
},
.init_freq = 521142857,
},
};
struct reg_val {
u8 reg;
u8 val;
};
static int
pt3_demod_write(struct pt3_adapter *adap, const struct reg_val *data, int num)
{
struct i2c_msg msg;
int i, ret;
ret = 0;
msg.addr = adap->i2c_demod->addr;
msg.flags = 0;
msg.len = 2;
for (i = 0; i < num; i++) {
msg.buf = (u8 *)&data[i];
ret = i2c_transfer(adap->i2c_demod->adapter, &msg, 1);
if (ret == 0)
ret = -EREMOTE;
if (ret < 0)
return ret;
}
return 0;
}
static inline void pt3_lnb_ctrl(struct pt3_board *pt3, bool on)
{
iowrite32((on ? 0x0f : 0x0c), pt3->regs[0] + REG_SYSTEM_W);
}
static inline struct pt3_adapter *pt3_find_adapter(struct dvb_frontend *fe)
{
struct pt3_board *pt3;
int i;
if (one_adapter) {
pt3 = fe->dvb->priv;
for (i = 0; i < PT3_NUM_FE; i++)
if (pt3->adaps[i]->fe == fe)
return pt3->adaps[i];
}
return container_of(fe->dvb, struct pt3_adapter, dvb_adap);
}
/*
* all 4 tuners in PT3 are packaged in a can module (Sharp VA4M6JC2103).
* it seems that they share the power lines and Amp power line and
* adaps[3] controls those powers.
*/
static int
pt3_set_tuner_power(struct pt3_board *pt3, bool tuner_on, bool amp_on)
{
struct reg_val rv = { 0x1e, 0x99 };
if (tuner_on)
rv.val |= 0x40;
if (amp_on)
rv.val |= 0x04;
return pt3_demod_write(pt3->adaps[PT3_NUM_FE - 1], &rv, 1);
}
static int pt3_set_lna(struct dvb_frontend *fe)
{
struct pt3_adapter *adap;
struct pt3_board *pt3;
u32 val;
int ret;
/* LNA is shared btw. 2 TERR-tuners */
adap = pt3_find_adapter(fe);
val = fe->dtv_property_cache.lna;
if (val == LNA_AUTO || val == adap->cur_lna)
return 0;
pt3 = adap->dvb_adap.priv;
if (mutex_lock_interruptible(&pt3->lock))
return -ERESTARTSYS;
if (val)
pt3->lna_on_cnt++;
else
pt3->lna_on_cnt--;
if (val && pt3->lna_on_cnt <= 1) {
pt3->lna_on_cnt = 1;
ret = pt3_set_tuner_power(pt3, true, true);
} else if (!val && pt3->lna_on_cnt <= 0) {
pt3->lna_on_cnt = 0;
ret =