18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Mantis VP-2040 driver 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci Copyright (C) Manu Abraham (abraham.manu@gmail.com) 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci*/ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/signal.h> 108c2ecf20Sopenharmony_ci#include <linux/sched.h> 118c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <media/dmxdev.h> 148c2ecf20Sopenharmony_ci#include <media/dvbdev.h> 158c2ecf20Sopenharmony_ci#include <media/dvb_demux.h> 168c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 178c2ecf20Sopenharmony_ci#include <media/dvb_net.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "tda1002x.h" 208c2ecf20Sopenharmony_ci#include "mantis_common.h" 218c2ecf20Sopenharmony_ci#include "mantis_ioc.h" 228c2ecf20Sopenharmony_ci#include "mantis_dvb.h" 238c2ecf20Sopenharmony_ci#include "mantis_vp2040.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define MANTIS_MODEL_NAME "VP-2040" 268c2ecf20Sopenharmony_ci#define MANTIS_DEV_TYPE "DVB-C" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic struct tda1002x_config vp2040_tda1002x_cu1216_config = { 298c2ecf20Sopenharmony_ci .demod_address = 0x18 >> 1, 308c2ecf20Sopenharmony_ci .invert = 1, 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic struct tda10023_config vp2040_tda10023_cu1216_config = { 348c2ecf20Sopenharmony_ci .demod_address = 0x18 >> 1, 358c2ecf20Sopenharmony_ci .invert = 1, 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic int tda1002x_cu1216_tuner_set(struct dvb_frontend *fe) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p = &fe->dtv_property_cache; 418c2ecf20Sopenharmony_ci struct mantis_pci *mantis = fe->dvb->priv; 428c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = &mantis->adapter; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci u8 buf[6]; 458c2ecf20Sopenharmony_ci struct i2c_msg msg = {.addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf)}; 468c2ecf20Sopenharmony_ci int i; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define CU1216_IF 36125000 498c2ecf20Sopenharmony_ci#define TUNER_MUL 62500 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci u32 div = (p->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci buf[0] = (div >> 8) & 0x7f; 548c2ecf20Sopenharmony_ci buf[1] = div & 0xff; 558c2ecf20Sopenharmony_ci buf[2] = 0xce; 568c2ecf20Sopenharmony_ci buf[3] = (p->frequency < 150000000 ? 0x01 : 578c2ecf20Sopenharmony_ci p->frequency < 445000000 ? 0x02 : 0x04); 588c2ecf20Sopenharmony_ci buf[4] = 0xde; 598c2ecf20Sopenharmony_ci buf[5] = 0x20; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 628c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (i2c_transfer(adapter, &msg, 1) != 1) 658c2ecf20Sopenharmony_ci return -EIO; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* wait for the pll lock */ 688c2ecf20Sopenharmony_ci msg.flags = I2C_M_RD; 698c2ecf20Sopenharmony_ci msg.len = 1; 708c2ecf20Sopenharmony_ci for (i = 0; i < 20; i++) { 718c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 728c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (i2c_transfer(adapter, &msg, 1) == 1 && (buf[0] & 0x40)) 758c2ecf20Sopenharmony_ci break; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci msleep(10); 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* switch the charge pump to the lower current */ 818c2ecf20Sopenharmony_ci msg.flags = 0; 828c2ecf20Sopenharmony_ci msg.len = 2; 838c2ecf20Sopenharmony_ci msg.buf = &buf[2]; 848c2ecf20Sopenharmony_ci buf[2] &= ~0x40; 858c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 868c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (i2c_transfer(adapter, &msg, 1) != 1) 898c2ecf20Sopenharmony_ci return -EIO; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic u8 read_pwm(struct mantis_pci *mantis) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = &mantis->adapter; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci u8 b = 0xff; 998c2ecf20Sopenharmony_ci u8 pwm; 1008c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { 1018c2ecf20Sopenharmony_ci {.addr = 0x50, .flags = 0, .buf = &b, .len = 1}, 1028c2ecf20Sopenharmony_ci {.addr = 0x50, .flags = I2C_M_RD, .buf = &pwm, .len = 1} 1038c2ecf20Sopenharmony_ci }; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if ((i2c_transfer(adapter, msg, 2) != 2) 1068c2ecf20Sopenharmony_ci || (pwm == 0xff)) 1078c2ecf20Sopenharmony_ci pwm = 0x48; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return pwm; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int vp2040_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = &mantis->adapter; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci int err = 0; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci err = mantis_frontend_power(mantis, POWER_ON); 1198c2ecf20Sopenharmony_ci if (err == 0) { 1208c2ecf20Sopenharmony_ci mantis_frontend_soft_reset(mantis); 1218c2ecf20Sopenharmony_ci msleep(250); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci dprintk(MANTIS_ERROR, 1, "Probing for CU1216 (DVB-C)"); 1248c2ecf20Sopenharmony_ci fe = dvb_attach(tda10021_attach, &vp2040_tda1002x_cu1216_config, 1258c2ecf20Sopenharmony_ci adapter, 1268c2ecf20Sopenharmony_ci read_pwm(mantis)); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (fe) { 1298c2ecf20Sopenharmony_ci dprintk(MANTIS_ERROR, 1, 1308c2ecf20Sopenharmony_ci "found Philips CU1216 DVB-C frontend (TDA10021) @ 0x%02x", 1318c2ecf20Sopenharmony_ci vp2040_tda1002x_cu1216_config.demod_address); 1328c2ecf20Sopenharmony_ci } else { 1338c2ecf20Sopenharmony_ci fe = dvb_attach(tda10023_attach, &vp2040_tda10023_cu1216_config, 1348c2ecf20Sopenharmony_ci adapter, 1358c2ecf20Sopenharmony_ci read_pwm(mantis)); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (fe) { 1388c2ecf20Sopenharmony_ci dprintk(MANTIS_ERROR, 1, 1398c2ecf20Sopenharmony_ci "found Philips CU1216 DVB-C frontend (TDA10023) @ 0x%02x", 1408c2ecf20Sopenharmony_ci vp2040_tda1002x_cu1216_config.demod_address); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (fe) { 1458c2ecf20Sopenharmony_ci fe->ops.tuner_ops.set_params = tda1002x_cu1216_tuner_set; 1468c2ecf20Sopenharmony_ci dprintk(MANTIS_ERROR, 1, "Mantis DVB-C Philips CU1216 frontend attach success"); 1478c2ecf20Sopenharmony_ci } else { 1488c2ecf20Sopenharmony_ci return -1; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci } else { 1518c2ecf20Sopenharmony_ci dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", 1528c2ecf20Sopenharmony_ci adapter->name, 1538c2ecf20Sopenharmony_ci err); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return -EIO; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci mantis->fe = fe; 1588c2ecf20Sopenharmony_ci dprintk(MANTIS_DEBUG, 1, "Done!"); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistruct mantis_hwconfig vp2040_config = { 1648c2ecf20Sopenharmony_ci .model_name = MANTIS_MODEL_NAME, 1658c2ecf20Sopenharmony_ci .dev_type = MANTIS_DEV_TYPE, 1668c2ecf20Sopenharmony_ci .ts_size = MANTIS_TS_204, 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci .baud_rate = MANTIS_BAUD_9600, 1698c2ecf20Sopenharmony_ci .parity = MANTIS_PARITY_NONE, 1708c2ecf20Sopenharmony_ci .bytes = 0, 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci .frontend_init = vp2040_frontend_init, 1738c2ecf20Sopenharmony_ci .power = GPIF_A12, 1748c2ecf20Sopenharmony_ci .reset = GPIF_A13, 1758c2ecf20Sopenharmony_ci}; 176