Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

media: dvb-frontends: add LNBH29 LNB supply driver

Add support for STMicroelectronics LNBH29 LNB supply driver.

Signed-off-by: Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

authored by

Katsuhiro Suzuki and committed by
Mauro Carvalho Chehab
1b09dd9f 447d6685

+215
+10
drivers/media/dvb-frontends/Kconfig
··· 791 791 An SEC control chip. 792 792 Say Y when you want to support this chip. 793 793 794 + config DVB_LNBH29 795 + tristate "LNBH29 SEC controller" 796 + depends on DVB_CORE && I2C 797 + default m if !MEDIA_SUBDRV_AUTOSELECT 798 + help 799 + LNB power supply and control voltage 800 + regulator chip with step-up converter 801 + and I2C interface for STMicroelectronics LNBH29. 802 + Say Y when you want to support this chip. 803 + 794 804 config DVB_LNBP21 795 805 tristate "LNBP21/LNBH24 SEC controllers" 796 806 depends on DVB_CORE && I2C
+1
drivers/media/dvb-frontends/Makefile
··· 58 58 obj-$(CONFIG_DVB_LG2160) += lg2160.o 59 59 obj-$(CONFIG_DVB_CX24123) += cx24123.o 60 60 obj-$(CONFIG_DVB_LNBH25) += lnbh25.o 61 + obj-$(CONFIG_DVB_LNBH29) += lnbh29.o 61 62 obj-$(CONFIG_DVB_LNBP21) += lnbp21.o 62 63 obj-$(CONFIG_DVB_LNBP22) += lnbp22.o 63 64 obj-$(CONFIG_DVB_ISL6405) += isl6405.o
+168
drivers/media/dvb-frontends/lnbh29.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + // 3 + // Driver for LNB supply and control IC STMicroelectronics LNBH29 4 + // 5 + // Copyright (c) 2018 Socionext Inc. 6 + 7 + #include <linux/module.h> 8 + #include <linux/init.h> 9 + #include <linux/slab.h> 10 + 11 + #include <media/dvb_frontend.h> 12 + #include "lnbh29.h" 13 + 14 + /** 15 + * struct lnbh29_priv - LNBH29 driver private data 16 + * @i2c: Pointer to the I2C adapter structure 17 + * @i2c_address: I2C address of LNBH29 chip 18 + * @config: Registers configuration 19 + * offset 0: 1st register address, always 0x01 (DATA) 20 + * offset 1: DATA register value 21 + */ 22 + struct lnbh29_priv { 23 + struct i2c_adapter *i2c; 24 + u8 i2c_address; 25 + u8 config[2]; 26 + }; 27 + 28 + #define LNBH29_STATUS_OLF BIT(0) 29 + #define LNBH29_STATUS_OTF BIT(1) 30 + #define LNBH29_STATUS_VMON BIT(2) 31 + #define LNBH29_STATUS_PNG BIT(3) 32 + #define LNBH29_STATUS_PDO BIT(4) 33 + #define LNBH29_VSEL_MASK GENMASK(2, 0) 34 + #define LNBH29_VSEL_0 0x00 35 + /* Min: 13.188V, Typ: 13.667V, Max:14V */ 36 + #define LNBH29_VSEL_13 0x03 37 + /* Min: 18.158V, Typ: 18.817V, Max:19.475V */ 38 + #define LNBH29_VSEL_18 0x07 39 + 40 + static int lnbh29_read_vmon(struct lnbh29_priv *priv) 41 + { 42 + u8 addr = 0x00; 43 + u8 status[2]; 44 + int ret; 45 + struct i2c_msg msg[2] = { 46 + { 47 + .addr = priv->i2c_address, 48 + .flags = 0, 49 + .len = 1, 50 + .buf = &addr 51 + }, { 52 + .addr = priv->i2c_address, 53 + .flags = I2C_M_RD, 54 + .len = sizeof(status), 55 + .buf = status 56 + } 57 + }; 58 + 59 + ret = i2c_transfer(priv->i2c, msg, 2); 60 + if (ret >= 0 && ret != 2) 61 + ret = -EIO; 62 + if (ret < 0) { 63 + dev_dbg(&priv->i2c->dev, "LNBH29 I2C transfer failed (%d)\n", 64 + ret); 65 + return ret; 66 + } 67 + 68 + if (status[0] & (LNBH29_STATUS_OLF | LNBH29_STATUS_VMON)) { 69 + dev_err(&priv->i2c->dev, 70 + "LNBH29 voltage in failure state, status reg 0x%x\n", 71 + status[0]); 72 + return -EIO; 73 + } 74 + 75 + return 0; 76 + } 77 + 78 + static int lnbh29_set_voltage(struct dvb_frontend *fe, 79 + enum fe_sec_voltage voltage) 80 + { 81 + struct lnbh29_priv *priv = fe->sec_priv; 82 + u8 data_reg; 83 + int ret; 84 + struct i2c_msg msg = { 85 + .addr = priv->i2c_address, 86 + .flags = 0, 87 + .len = sizeof(priv->config), 88 + .buf = priv->config 89 + }; 90 + 91 + switch (voltage) { 92 + case SEC_VOLTAGE_OFF: 93 + data_reg = LNBH29_VSEL_0; 94 + break; 95 + case SEC_VOLTAGE_13: 96 + data_reg = LNBH29_VSEL_13; 97 + break; 98 + case SEC_VOLTAGE_18: 99 + data_reg = LNBH29_VSEL_18; 100 + break; 101 + default: 102 + return -EINVAL; 103 + } 104 + priv->config[1] &= ~LNBH29_VSEL_MASK; 105 + priv->config[1] |= data_reg; 106 + 107 + ret = i2c_transfer(priv->i2c, &msg, 1); 108 + if (ret >= 0 && ret != 1) 109 + ret = -EIO; 110 + if (ret < 0) { 111 + dev_err(&priv->i2c->dev, "LNBH29 I2C transfer error (%d)\n", 112 + ret); 113 + return ret; 114 + } 115 + 116 + /* Soft-start time (Vout 0V to 18V) is Typ. 6ms. */ 117 + usleep_range(6000, 20000); 118 + 119 + if (voltage == SEC_VOLTAGE_OFF) 120 + return 0; 121 + 122 + return lnbh29_read_vmon(priv); 123 + } 124 + 125 + static void lnbh29_release(struct dvb_frontend *fe) 126 + { 127 + lnbh29_set_voltage(fe, SEC_VOLTAGE_OFF); 128 + kfree(fe->sec_priv); 129 + fe->sec_priv = NULL; 130 + } 131 + 132 + struct dvb_frontend *lnbh29_attach(struct dvb_frontend *fe, 133 + struct lnbh29_config *cfg, 134 + struct i2c_adapter *i2c) 135 + { 136 + struct lnbh29_priv *priv; 137 + 138 + priv = kzalloc(sizeof(*priv), GFP_KERNEL); 139 + if (!priv) 140 + return NULL; 141 + 142 + priv->i2c_address = (cfg->i2c_address >> 1); 143 + priv->i2c = i2c; 144 + priv->config[0] = 0x01; 145 + priv->config[1] = cfg->data_config; 146 + fe->sec_priv = priv; 147 + 148 + if (lnbh29_set_voltage(fe, SEC_VOLTAGE_OFF)) { 149 + dev_err(&i2c->dev, "no LNBH29 found at I2C addr 0x%02x\n", 150 + priv->i2c_address); 151 + kfree(priv); 152 + fe->sec_priv = NULL; 153 + return NULL; 154 + } 155 + 156 + fe->ops.release_sec = lnbh29_release; 157 + fe->ops.set_voltage = lnbh29_set_voltage; 158 + 159 + dev_info(&i2c->dev, "LNBH29 attached at I2C addr 0x%02x\n", 160 + priv->i2c_address); 161 + 162 + return fe; 163 + } 164 + EXPORT_SYMBOL(lnbh29_attach); 165 + 166 + MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>"); 167 + MODULE_DESCRIPTION("STMicroelectronics LNBH29 driver"); 168 + MODULE_LICENSE("GPL v2");
+36
drivers/media/dvb-frontends/lnbh29.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Driver for LNB supply and control IC STMicroelectronics LNBH29 4 + * 5 + * Copyright (c) 2018 Socionext Inc. 6 + */ 7 + 8 + #ifndef LNBH29_H 9 + #define LNBH29_H 10 + 11 + #include <linux/i2c.h> 12 + #include <linux/dvb/frontend.h> 13 + 14 + /* Using very low E.S.R. capacitors or ceramic caps */ 15 + #define LNBH29_DATA_COMP BIT(3) 16 + 17 + struct lnbh29_config { 18 + u8 i2c_address; 19 + u8 data_config; 20 + }; 21 + 22 + #if IS_REACHABLE(CONFIG_DVB_LNBH29) 23 + struct dvb_frontend *lnbh29_attach(struct dvb_frontend *fe, 24 + struct lnbh29_config *cfg, 25 + struct i2c_adapter *i2c); 26 + #else 27 + static inline struct dvb_frontend *lnbh29_attach(struct dvb_frontend *fe, 28 + struct lnbh29_config *cfg, 29 + struct i2c_adapter *i2c) 30 + { 31 + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); 32 + return NULL; 33 + } 34 + #endif 35 + 36 + #endif