1*a0c7056fSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 24e2c53fdSIgor M. Liplianin /* 34e2c53fdSIgor M. Liplianin * lnbp22.h - driver for lnb supply and control ic lnbp22 44e2c53fdSIgor M. Liplianin * 54e2c53fdSIgor M. Liplianin * Copyright (C) 2006 Dominik Kuhlen 64e2c53fdSIgor M. Liplianin * Based on lnbp21 driver 74e2c53fdSIgor M. Liplianin * 8991ce92fSMauro Carvalho Chehab * the project's page is at https://linuxtv.org 94e2c53fdSIgor M. Liplianin */ 104e2c53fdSIgor M. Liplianin #include <linux/delay.h> 114e2c53fdSIgor M. Liplianin #include <linux/errno.h> 124e2c53fdSIgor M. Liplianin #include <linux/init.h> 134e2c53fdSIgor M. Liplianin #include <linux/kernel.h> 144e2c53fdSIgor M. Liplianin #include <linux/module.h> 154e2c53fdSIgor M. Liplianin #include <linux/moduleparam.h> 164e2c53fdSIgor M. Liplianin #include <linux/string.h> 174e2c53fdSIgor M. Liplianin #include <linux/slab.h> 184e2c53fdSIgor M. Liplianin 19fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h> 204e2c53fdSIgor M. Liplianin #include "lnbp22.h" 214e2c53fdSIgor M. Liplianin 224e2c53fdSIgor M. Liplianin static int debug; 234e2c53fdSIgor M. Liplianin module_param(debug, int, 0644); 244e2c53fdSIgor M. Liplianin MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); 254e2c53fdSIgor M. Liplianin 264e2c53fdSIgor M. Liplianin 274e2c53fdSIgor M. Liplianin #define dprintk(lvl, arg...) if (debug >= (lvl)) printk(arg) 284e2c53fdSIgor M. Liplianin 294e2c53fdSIgor M. Liplianin struct lnbp22 { 304e2c53fdSIgor M. Liplianin u8 config[4]; 314e2c53fdSIgor M. Liplianin struct i2c_adapter *i2c; 324e2c53fdSIgor M. Liplianin }; 334e2c53fdSIgor M. Liplianin 340df289a2SMauro Carvalho Chehab static int lnbp22_set_voltage(struct dvb_frontend *fe, 350df289a2SMauro Carvalho Chehab enum fe_sec_voltage voltage) 364e2c53fdSIgor M. Liplianin { 374e2c53fdSIgor M. Liplianin struct lnbp22 *lnbp22 = (struct lnbp22 *)fe->sec_priv; 384e2c53fdSIgor M. Liplianin struct i2c_msg msg = { 394e2c53fdSIgor M. Liplianin .addr = 0x08, 404e2c53fdSIgor M. Liplianin .flags = 0, 414e2c53fdSIgor M. Liplianin .buf = (char *)&lnbp22->config, 424e2c53fdSIgor M. Liplianin .len = sizeof(lnbp22->config), 434e2c53fdSIgor M. Liplianin }; 444e2c53fdSIgor M. Liplianin 454e2c53fdSIgor M. Liplianin dprintk(1, "%s: %d (18V=%d 13V=%d)\n", __func__, voltage, 464e2c53fdSIgor M. Liplianin SEC_VOLTAGE_18, SEC_VOLTAGE_13); 474e2c53fdSIgor M. Liplianin 484e2c53fdSIgor M. Liplianin lnbp22->config[3] = 0x60; /* Power down */ 494e2c53fdSIgor M. Liplianin switch (voltage) { 504e2c53fdSIgor M. Liplianin case SEC_VOLTAGE_OFF: 514e2c53fdSIgor M. Liplianin break; 524e2c53fdSIgor M. Liplianin case SEC_VOLTAGE_13: 534e2c53fdSIgor M. Liplianin lnbp22->config[3] |= LNBP22_EN; 544e2c53fdSIgor M. Liplianin break; 554e2c53fdSIgor M. Liplianin case SEC_VOLTAGE_18: 564e2c53fdSIgor M. Liplianin lnbp22->config[3] |= (LNBP22_EN | LNBP22_VSEL); 574e2c53fdSIgor M. Liplianin break; 584e2c53fdSIgor M. Liplianin default: 594e2c53fdSIgor M. Liplianin return -EINVAL; 60c2c1b415SPeter Senna Tschudin } 614e2c53fdSIgor M. Liplianin 624e2c53fdSIgor M. Liplianin dprintk(1, "%s: 0x%02x)\n", __func__, lnbp22->config[3]); 634e2c53fdSIgor M. Liplianin return (i2c_transfer(lnbp22->i2c, &msg, 1) == 1) ? 0 : -EIO; 644e2c53fdSIgor M. Liplianin } 654e2c53fdSIgor M. Liplianin 664e2c53fdSIgor M. Liplianin static int lnbp22_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) 674e2c53fdSIgor M. Liplianin { 684e2c53fdSIgor M. Liplianin struct lnbp22 *lnbp22 = (struct lnbp22 *) fe->sec_priv; 694e2c53fdSIgor M. Liplianin struct i2c_msg msg = { 704e2c53fdSIgor M. Liplianin .addr = 0x08, 714e2c53fdSIgor M. Liplianin .flags = 0, 724e2c53fdSIgor M. Liplianin .buf = (char *)&lnbp22->config, 734e2c53fdSIgor M. Liplianin .len = sizeof(lnbp22->config), 744e2c53fdSIgor M. Liplianin }; 754e2c53fdSIgor M. Liplianin 764e2c53fdSIgor M. Liplianin dprintk(1, "%s: %d\n", __func__, (int)arg); 774e2c53fdSIgor M. Liplianin if (arg) 784e2c53fdSIgor M. Liplianin lnbp22->config[3] |= LNBP22_LLC; 794e2c53fdSIgor M. Liplianin else 804e2c53fdSIgor M. Liplianin lnbp22->config[3] &= ~LNBP22_LLC; 814e2c53fdSIgor M. Liplianin 824e2c53fdSIgor M. Liplianin return (i2c_transfer(lnbp22->i2c, &msg, 1) == 1) ? 0 : -EIO; 834e2c53fdSIgor M. Liplianin } 844e2c53fdSIgor M. Liplianin 854e2c53fdSIgor M. Liplianin static void lnbp22_release(struct dvb_frontend *fe) 864e2c53fdSIgor M. Liplianin { 874e2c53fdSIgor M. Liplianin dprintk(1, "%s\n", __func__); 884e2c53fdSIgor M. Liplianin /* LNBP power off */ 894e2c53fdSIgor M. Liplianin lnbp22_set_voltage(fe, SEC_VOLTAGE_OFF); 904e2c53fdSIgor M. Liplianin 914e2c53fdSIgor M. Liplianin /* free data */ 924e2c53fdSIgor M. Liplianin kfree(fe->sec_priv); 934e2c53fdSIgor M. Liplianin fe->sec_priv = NULL; 944e2c53fdSIgor M. Liplianin } 954e2c53fdSIgor M. Liplianin 964e2c53fdSIgor M. Liplianin struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe, 974e2c53fdSIgor M. Liplianin struct i2c_adapter *i2c) 984e2c53fdSIgor M. Liplianin { 994e2c53fdSIgor M. Liplianin struct lnbp22 *lnbp22 = kmalloc(sizeof(struct lnbp22), GFP_KERNEL); 1004e2c53fdSIgor M. Liplianin if (!lnbp22) 1014e2c53fdSIgor M. Liplianin return NULL; 1024e2c53fdSIgor M. Liplianin 1034e2c53fdSIgor M. Liplianin /* default configuration */ 1044e2c53fdSIgor M. Liplianin lnbp22->config[0] = 0x00; /* ? */ 1054e2c53fdSIgor M. Liplianin lnbp22->config[1] = 0x28; /* ? */ 1064e2c53fdSIgor M. Liplianin lnbp22->config[2] = 0x48; /* ? */ 1074e2c53fdSIgor M. Liplianin lnbp22->config[3] = 0x60; /* Power down */ 1084e2c53fdSIgor M. Liplianin lnbp22->i2c = i2c; 1094e2c53fdSIgor M. Liplianin fe->sec_priv = lnbp22; 1104e2c53fdSIgor M. Liplianin 1114e2c53fdSIgor M. Liplianin /* detect if it is present or not */ 1124e2c53fdSIgor M. Liplianin if (lnbp22_set_voltage(fe, SEC_VOLTAGE_OFF)) { 1134e2c53fdSIgor M. Liplianin dprintk(0, "%s LNBP22 not found\n", __func__); 1144e2c53fdSIgor M. Liplianin kfree(lnbp22); 1154e2c53fdSIgor M. Liplianin fe->sec_priv = NULL; 1164e2c53fdSIgor M. Liplianin return NULL; 1174e2c53fdSIgor M. Liplianin } 1184e2c53fdSIgor M. Liplianin 1194e2c53fdSIgor M. Liplianin /* install release callback */ 1204e2c53fdSIgor M. Liplianin fe->ops.release_sec = lnbp22_release; 1214e2c53fdSIgor M. Liplianin 1224e2c53fdSIgor M. Liplianin /* override frontend ops */ 1234e2c53fdSIgor M. Liplianin fe->ops.set_voltage = lnbp22_set_voltage; 1244e2c53fdSIgor M. Liplianin fe->ops.enable_high_lnb_voltage = lnbp22_enable_high_lnb_voltage; 1254e2c53fdSIgor M. Liplianin 1264e2c53fdSIgor M. Liplianin return fe; 1274e2c53fdSIgor M. Liplianin } 1284e2c53fdSIgor M. Liplianin EXPORT_SYMBOL(lnbp22_attach); 1294e2c53fdSIgor M. Liplianin 1304e2c53fdSIgor M. Liplianin MODULE_DESCRIPTION("Driver for lnb supply and control ic lnbp22"); 1314e2c53fdSIgor M. Liplianin MODULE_AUTHOR("Dominik Kuhlen"); 1324e2c53fdSIgor M. Liplianin MODULE_LICENSE("GPL"); 133