return cxd2820r_wr_reg(priv, reg, val);
}
-int cxd2820r_gpio(struct dvb_frontend *fe)
+int cxd2820r_gpio(struct dvb_frontend *fe, u8 *gpio)
{
struct cxd2820r_priv *priv = fe->demodulator_priv;
int ret, i;
- u8 *gpio, tmp0, tmp1;
+ u8 tmp0, tmp1;
dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__,
fe->dtv_property_cache.delivery_system);
- switch (fe->dtv_property_cache.delivery_system) {
- case SYS_DVBT:
- gpio = priv->cfg.gpio_dvbt;
- break;
- case SYS_DVBT2:
- gpio = priv->cfg.gpio_dvbt2;
- break;
- case SYS_DVBC_ANNEX_AC:
- gpio = priv->cfg.gpio_dvbc;
- break;
- default:
- ret = -EINVAL;
- goto error;
- }
-
/* update GPIOs only when needed */
if (!memcmp(gpio, priv->gpio, sizeof(priv->gpio)))
return 0;
static void cxd2820r_release(struct dvb_frontend *fe)
{
struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
dev_dbg(&priv->i2c->dev, "%s\n", __func__);
+#ifdef CONFIG_GPIOLIB
+ /* remove GPIOs */
+ if (priv->gpio_chip.label) {
+ ret = gpiochip_remove(&priv->gpio_chip);
+ if (ret)
+ dev_err(&priv->i2c->dev, "%s: gpiochip_remove() " \
+ "failed=%d\n", KBUILD_MODNAME, ret);
+ }
+#endif
kfree(priv);
return;
}
return cxd2820r_wr_reg_mask(priv, 0xdb, enable ? 1 : 0, 0x1);
}
+#ifdef CONFIG_GPIOLIB
+static int cxd2820r_gpio_direction_output(struct gpio_chip *chip, unsigned nr,
+ int val)
+{
+ struct cxd2820r_priv *priv =
+ container_of(chip, struct cxd2820r_priv, gpio_chip);
+ u8 gpio[GPIO_COUNT];
+
+ dev_dbg(&priv->i2c->dev, "%s: nr=%d val=%d\n", __func__, nr, val);
+
+ memcpy(gpio, priv->gpio, sizeof(gpio));
+ gpio[nr] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | (val << 2);
+
+ return cxd2820r_gpio(&priv->fe, gpio);
+}
+
+static void cxd2820r_gpio_set(struct gpio_chip *chip, unsigned nr, int val)
+{
+ struct cxd2820r_priv *priv =
+ container_of(chip, struct cxd2820r_priv, gpio_chip);
+ u8 gpio[GPIO_COUNT];
+
+ dev_dbg(&priv->i2c->dev, "%s: nr=%d val=%d\n", __func__, nr, val);
+
+ memcpy(gpio, priv->gpio, sizeof(gpio));
+ gpio[nr] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | (val << 2);
+
+ (void) cxd2820r_gpio(&priv->fe, gpio);
+
+ return;
+}
+
+static int cxd2820r_gpio_get(struct gpio_chip *chip, unsigned nr)
+{
+ struct cxd2820r_priv *priv =
+ container_of(chip, struct cxd2820r_priv, gpio_chip);
+
+ dev_dbg(&priv->i2c->dev, "%s: nr=%d\n", __func__, nr);
+
+ return (priv->gpio[nr] >> 2) & 0x01;
+}
+#endif
+
static const struct dvb_frontend_ops cxd2820r_ops = {
.delsys = { SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A },
/* default: DVB-T/T2 */
};
struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg,
- struct i2c_adapter *i2c)
+ struct i2c_adapter *i2c, int *gpio_chip_base
+)
{
- struct cxd2820r_priv *priv = NULL;
+ struct cxd2820r_priv *priv;
int ret;
u8 tmp;
priv = kzalloc(sizeof (struct cxd2820r_priv), GFP_KERNEL);
- if (!priv)
+ if (!priv) {
+ ret = -ENOMEM;
+ dev_err(&i2c->dev, "%s: kzalloc() failed\n",
+ KBUILD_MODNAME);
goto error;
+ }
priv->i2c = i2c;
memcpy(&priv->cfg, cfg, sizeof (struct cxd2820r_config));
if (ret || tmp != 0xe1)
goto error;
+#ifdef CONFIG_GPIOLIB
+ /* add GPIOs */
+ if (gpio_chip_base) {
+ priv->gpio_chip.label = KBUILD_MODNAME;
+ priv->gpio_chip.dev = &priv->i2c->dev;
+ priv->gpio_chip.owner = THIS_MODULE;
+ priv->gpio_chip.direction_output =
+ cxd2820r_gpio_direction_output;
+ priv->gpio_chip.set = cxd2820r_gpio_set;
+ priv->gpio_chip.get = cxd2820r_gpio_get;
+ priv->gpio_chip.base = -1; /* dynamic allocation */
+ priv->gpio_chip.ngpio = GPIO_COUNT;
+ priv->gpio_chip.can_sleep = 1;
+ ret = gpiochip_add(&priv->gpio_chip);
+ if (ret)
+ goto error;
+
+ dev_dbg(&priv->i2c->dev, "%s: gpio_chip.base=%d\n", __func__,
+ priv->gpio_chip.base);
+
+ *gpio_chip_base = priv->gpio_chip.base;
+ }
+#endif
+
memcpy(&priv->fe.ops, &cxd2820r_ops, sizeof (struct dvb_frontend_ops));
priv->fe.demodulator_priv = priv;
return &priv->fe;
error:
+ dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
kfree(priv);
return NULL;
}
#include <media/videobuf-vmalloc.h>
#include <media/tuner.h>
#include "tuner-simple.h"
+#include <linux/gpio.h>
#include "lgdt330x.h"
#include "lgdt3305.h"
static struct cxd2820r_config em28xx_cxd2820r_config = {
.i2c_address = (0xd8 >> 1),
.ts_mode = CXD2820R_TS_SERIAL,
-
- /* enable LNA for DVB-T, DVB-T2 and DVB-C */
- .gpio_dvbt[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L,
- .gpio_dvbt2[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L,
- .gpio_dvbc[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L,
};
static struct tda18271_config em28xx_cxd2820r_tda18271_config = {
static int em28xx_dvb_init(struct em28xx *dev)
{
- int result = 0, mfe_shared = 0;
+ int result = 0, mfe_shared = 0, gpio_chip_base;
struct em28xx_dvb *dvb;
if (!dev->board.has_dvb) {
case EM28174_BOARD_PCTV_290E:
dvb->fe[0] = dvb_attach(cxd2820r_attach,
&em28xx_cxd2820r_config,
- &dev->i2c_adap);
+ &dev->i2c_adap,
+ &gpio_chip_base);
if (dvb->fe[0]) {
/* FE 0 attach tuner */
if (!dvb_attach(tda18271_attach,
goto out_free;
}
}
+
+ /* enable LNA for DVB-T, DVB-T2 and DVB-C */
+ result = gpio_request_one(gpio_chip_base, GPIOF_INIT_LOW,
+ "LNA");
+ if (result)
+ em28xx_errdev("gpio request failed %d\n", result);
+ else
+ gpio_free(gpio_chip_base);
+
+ result = 0; /* continue even set LNA fails */
break;
case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C:
{