Commit 4c66c920 authored by Michael Krufky's avatar Michael Krufky Committed by Mauro Carvalho Chehab

[media] dvb-usb: add ATSC support for the Hauppauge WinTV-Aero-M

Adds new driver, mxl111sf, to support the WinTV-Aero-M
Signed-off-by: default avatarMichael Krufky <mkrufky@kernellabs.com>
Reviewed-by: default avatarSteven Toth <stoth@kernellabs.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent f8a26f05
......@@ -381,3 +381,11 @@ config DVB_USB_IT913X
select DVB_IT913X_FE
help
Say Y here to support the it913x device
config DVB_USB_MXL111SF
tristate "MxL111SF DTV USB2.0 support"
depends on DVB_USB
select DVB_LGDT3305 if !DVB_FE_CUSTOMISE
select VIDEO_TVEEPROM
help
Say Y here to support the MxL111SF USB2.0 DTV receiver.
......@@ -97,6 +97,10 @@ obj-$(CONFIG_DVB_USB_TECHNISAT_USB2) += dvb-usb-technisat-usb2.o
dvb-usb-it913x-objs := it913x.o
obj-$(CONFIG_DVB_USB_IT913X) += dvb-usb-it913x.o
dvb-usb-mxl111sf-objs = mxl111sf.o mxl111sf-phy.o mxl111sf-i2c.o mxl111sf-gpio.o
obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-usb-mxl111sf.o
obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o
ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
# due to tuner-xc3028
ccflags-y += -Idrivers/media/common/tuners
......
/*
* mxl111sf-gpio.c - driver for the MaxLinear MXL111SF
*
* Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "mxl111sf-gpio.h"
#include "mxl111sf-i2c.h"
#include "mxl111sf.h"
/* ------------------------------------------------------------------------- */
#define MXL_GPIO_MUX_REG_0 0x84
#define MXL_GPIO_MUX_REG_1 0x89
#define MXL_GPIO_MUX_REG_2 0x82
#define MXL_GPIO_DIR_INPUT 0
#define MXL_GPIO_DIR_OUTPUT 1
static int mxl111sf_set_gpo_state(struct mxl111sf_state *state, u8 pin, u8 val)
{
int ret;
u8 tmp;
mxl_debug_adv("(%d, %d)", pin, val);
if ((pin > 0) && (pin < 8)) {
ret = mxl111sf_read_reg(state, 0x19, &tmp);
if (mxl_fail(ret))
goto fail;
tmp &= ~(1 << (pin - 1));
tmp |= (val << (pin - 1));
ret = mxl111sf_write_reg(state, 0x19, tmp);
if (mxl_fail(ret))
goto fail;
} else if (pin <= 10) {
if (pin == 0)
pin += 7;
ret = mxl111sf_read_reg(state, 0x30, &tmp);
if (mxl_fail(ret))
goto fail;
tmp &= ~(1 << (pin - 3));
tmp |= (val << (pin - 3));
ret = mxl111sf_write_reg(state, 0x30, tmp);
if (mxl_fail(ret))
goto fail;
} else
ret = -EINVAL;
fail:
return ret;
}
static int mxl111sf_get_gpi_state(struct mxl111sf_state *state, u8 pin, u8 *val)
{
int ret;
u8 tmp;
mxl_debug("(0x%02x)", pin);
*val = 0;
switch (pin) {
case 0:
case 1:
case 2:
case 3:
ret = mxl111sf_read_reg(state, 0x23, &tmp);
if (mxl_fail(ret))
goto fail;
*val = (tmp >> (pin + 4)) & 0x01;
break;
case 4:
case 5:
case 6:
case 7:
ret = mxl111sf_read_reg(state, 0x2f, &tmp);
if (mxl_fail(ret))
goto fail;
*val = (tmp >> pin) & 0x01;
break;
case 8:
case 9:
case 10:
ret = mxl111sf_read_reg(state, 0x22, &tmp);
if (mxl_fail(ret))
goto fail;
*val = (tmp >> (pin - 3)) & 0x01;
break;
default:
return -EINVAL; /* invalid pin */
}
fail:
return ret;
}
struct mxl_gpio_cfg {
u8 pin;
u8 dir;
u8 val;
};
static int mxl111sf_config_gpio_pins(struct mxl111sf_state *state,
struct mxl_gpio_cfg *gpio_cfg)
{
int ret;
u8 tmp;
mxl_debug_adv("(%d, %d)", gpio_cfg->pin, gpio_cfg->dir);
switch (gpio_cfg->pin) {
case 0:
case 1:
case 2:
case 3:
ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_0, &tmp);
if (mxl_fail(ret))
goto fail;
tmp &= ~(1 << (gpio_cfg->pin + 4));
tmp |= (gpio_cfg->dir << (gpio_cfg->pin + 4));
ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_0, tmp);
if (mxl_fail(ret))
goto fail;
break;
case 4:
case 5:
case 6:
case 7:
ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_1, &tmp);
if (mxl_fail(ret))
goto fail;
tmp &= ~(1 << gpio_cfg->pin);
tmp |= (gpio_cfg->dir << gpio_cfg->pin);
ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_1, tmp);
if (mxl_fail(ret))
goto fail;
break;
case 8:
case 9:
case 10:
ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_2, &tmp);
if (mxl_fail(ret))
goto fail;
tmp &= ~(1 << (gpio_cfg->pin - 3));
tmp |= (gpio_cfg->dir << (gpio_cfg->pin - 3));
ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_2, tmp);
if (mxl_fail(ret))
goto fail;
break;
default:
return -EINVAL; /* invalid pin */
}
ret = (MXL_GPIO_DIR_OUTPUT == gpio_cfg->dir) ?
mxl111sf_set_gpo_state(state,
gpio_cfg->pin, gpio_cfg->val) :
mxl111sf_get_gpi_state(state,
gpio_cfg->pin, &gpio_cfg->val);
mxl_fail(ret);
fail:
return ret;
}
static int mxl111sf_hw_do_set_gpio(struct mxl111sf_state *state,
int gpio, int direction, int val)
{
struct mxl_gpio_cfg gpio_config = {
.pin = gpio,
.dir = direction,
.val = val,
};
mxl_debug("(%d, %d, %d)", gpio, direction, val);
return mxl111sf_config_gpio_pins(state, &gpio_config);
}
/* ------------------------------------------------------------------------- */
#define PIN_MUX_MPEG_MODE_MASK 0x40 /* 0x17 <6> */
#define PIN_MUX_MPEG_PAR_EN_MASK 0x01 /* 0x18 <0> */
#define PIN_MUX_MPEG_SER_EN_MASK 0x02 /* 0x18 <1> */
#define PIN_MUX_MPG_IN_MUX_MASK 0x80 /* 0x3D <7> */
#define PIN_MUX_BT656_ENABLE_MASK 0x04 /* 0x12 <2> */
#define PIN_MUX_I2S_ENABLE_MASK 0x40 /* 0x15 <6> */
#define PIN_MUX_SPI_MODE_MASK 0x10 /* 0x3D <4> */
#define PIN_MUX_MCLK_EN_CTRL_MASK 0x10 /* 0x82 <4> */
#define PIN_MUX_MPSYN_EN_CTRL_MASK 0x20 /* 0x82 <5> */
#define PIN_MUX_MDVAL_EN_CTRL_MASK 0x40 /* 0x82 <6> */
#define PIN_MUX_MPERR_EN_CTRL_MASK 0x80 /* 0x82 <7> */
#define PIN_MUX_MDAT_EN_0_MASK 0x10 /* 0x84 <4> */
#define PIN_MUX_MDAT_EN_1_MASK 0x20 /* 0x84 <5> */
#define PIN_MUX_MDAT_EN_2_MASK 0x40 /* 0x84 <6> */
#define PIN_MUX_MDAT_EN_3_MASK 0x80 /* 0x84 <7> */
#define PIN_MUX_MDAT_EN_4_MASK 0x10 /* 0x89 <4> */
#define PIN_MUX_MDAT_EN_5_MASK 0x20 /* 0x89 <5> */
#define PIN_MUX_MDAT_EN_6_MASK 0x40 /* 0x89 <6> */
#define PIN_MUX_MDAT_EN_7_MASK 0x80 /* 0x89 <7> */
int mxl111sf_config_pin_mux_modes(struct mxl111sf_state *state,
enum mxl111sf_mux_config pin_mux_config)
{
u8 r12, r15, r17, r18, r3D, r82, r84, r89;
int ret;
mxl_debug("(%d)", pin_mux_config);
ret = mxl111sf_read_reg(state, 0x17, &r17);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, 0x18, &r18);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, 0x12, &r12);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, 0x15, &r15);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, 0x82, &r82);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, 0x84, &r84);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, 0x89, &r89);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, 0x3D, &r3D);
if (mxl_fail(ret))
goto fail;
switch (pin_mux_config) {
case PIN_MUX_TS_OUT_PARALLEL:
/* mpeg_mode = 1 */
r17 |= PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 1 */
r18 |= PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 0 */
r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 0 */
r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 0 */
r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 0 */
r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 0 */
r3D &= ~PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 1 */
r82 |= PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 1 */
r82 |= PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 1 */
r82 |= PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 1 */
r82 |= PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0xF */
r84 |= 0xF0;
/* mdat_en_ctrl[7:4] = 0xF */
r89 |= 0xF0;
break;
case PIN_MUX_TS_OUT_SERIAL:
/* mpeg_mode = 1 */
r17 |= PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 0 */
r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 1 */
r18 |= PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 0 */
r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 0 */
r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 0 */
r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 0 */
r3D &= ~PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 1 */
r82 |= PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 1 */
r82 |= PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 1 */
r82 |= PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 1 */
r82 |= PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0xF */
r84 |= 0xF0;
/* mdat_en_ctrl[7:4] = 0xF */
r89 |= 0xF0;
break;
case PIN_MUX_GPIO_MODE:
/* mpeg_mode = 0 */
r17 &= ~PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 0 */
r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 0 */
r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 0 */
r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 0 */
r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 0 */
r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 0 */
r3D &= ~PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 0 */
r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 0 */
r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0x0 */
r84 &= 0x0F;
/* mdat_en_ctrl[7:4] = 0x0 */
r89 &= 0x0F;
break;
case PIN_MUX_TS_SERIAL_IN_MODE_0:
/* mpeg_mode = 0 */
r17 &= ~PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 0 */
r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 1 */
r18 |= PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 0 */
r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 0 */
r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 0 */
r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 0 */
r3D &= ~PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 0 */
r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 0 */
r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0x0 */
r84 &= 0x0F;
/* mdat_en_ctrl[7:4] = 0x0 */
r89 &= 0x0F;
break;
case PIN_MUX_TS_SERIAL_IN_MODE_1:
/* mpeg_mode = 0 */
r17 &= ~PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 0 */
r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 1 */
r18 |= PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 1 */
r3D |= PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 0 */
r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 0 */
r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 0 */
r3D &= ~PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 0 */
r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 0 */
r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0x0 */
r84 &= 0x0F;
/* mdat_en_ctrl[7:4] = 0x0 */
r89 &= 0x0F;
break;
case PIN_MUX_TS_SPI_IN_MODE_1:
/* mpeg_mode = 0 */
r17 &= ~PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 0 */
r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 1 */
r18 |= PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 1 */
r3D |= PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 0 */
r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 1 */
r15 |= PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 1 */
r3D |= PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 0 */
r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 0 */
r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0x0 */
r84 &= 0x0F;
/* mdat_en_ctrl[7:4] = 0x0 */
r89 &= 0x0F;
break;
case PIN_MUX_TS_SPI_IN_MODE_0:
/* mpeg_mode = 0 */
r17 &= ~PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 0 */
r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 1 */
r18 |= PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 0 */
r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 0 */
r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 1 */
r15 |= PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 1 */
r3D |= PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 0 */
r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 0 */
r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0x0 */
r84 &= 0x0F;
/* mdat_en_ctrl[7:4] = 0x0 */
r89 &= 0x0F;
break;
case PIN_MUX_TS_PARALLEL_IN:
/* mpeg_mode = 0 */
r17 &= ~PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 1 */
r18 |= PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 0 */
r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 0 */
r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 0 */
r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 0 */
r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 0 */
r3D &= ~PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 0 */
r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 0 */
r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0x0 */
r84 &= 0x0F;
/* mdat_en_ctrl[7:4] = 0x0 */
r89 &= 0x0F;
break;
case PIN_MUX_BT656_I2S_MODE:
/* mpeg_mode = 0 */
r17 &= ~PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 0 */
r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 0 */
r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 0 */
r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 1 */
r12 |= PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 1 */
r15 |= PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 0 */
r3D &= ~PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 0 */
r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 0 */
r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0x0 */
r84 &= 0x0F;
/* mdat_en_ctrl[7:4] = 0x0 */
r89 &= 0x0F;
break;
case PIN_MUX_DEFAULT:
default:
/* mpeg_mode = 1 */
r17 |= PIN_MUX_MPEG_MODE_MASK;
/* mpeg_par_en = 0 */
r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK;
/* mpeg_ser_en = 0 */
r18 &= ~PIN_MUX_MPEG_SER_EN_MASK;
/* mpg_in_mux = 0 */
r3D &= ~PIN_MUX_MPG_IN_MUX_MASK;
/* bt656_enable = 0 */
r12 &= ~PIN_MUX_BT656_ENABLE_MASK;
/* i2s_enable = 0 */
r15 &= ~PIN_MUX_I2S_ENABLE_MASK;
/* spi_mode = 0 */
r3D &= ~PIN_MUX_SPI_MODE_MASK;
/* mclk_en_ctrl = 0 */
r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK;
/* mperr_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK;
/* mdval_en_ctrl = 0 */
r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK;
/* mpsyn_en_ctrl = 0 */
r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK;
/* mdat_en_ctrl[3:0] = 0x0 */
r84 &= 0x0F;
/* mdat_en_ctrl[7:4] = 0x0 */
r89 &= 0x0F;
break;
}
ret = mxl111sf_write_reg(state, 0x17, r17);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, 0x18, r18);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, 0x12, r12);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, 0x15, r15);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, 0x82, r82);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, 0x84, r84);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, 0x89, r89);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, 0x3D, r3D);
if (mxl_fail(ret))
goto fail;
fail:
return ret;
}
/* ------------------------------------------------------------------------- */
static int mxl111sf_hw_set_gpio(struct mxl111sf_state *state, int gpio, int val)
{
return mxl111sf_hw_do_set_gpio(state, gpio, MXL_GPIO_DIR_OUTPUT, val);
}
static int mxl111sf_hw_gpio_initialize(struct mxl111sf_state *state)
{
u8 gpioval = 0x07; /* write protect enabled, signal LEDs off */
int i, ret;
mxl_debug("()");
for (i = 3; i < 8; i++) {
ret = mxl111sf_hw_set_gpio(state, i, (gpioval >> i) & 0x01);
if (mxl_fail(ret))
break;
}
return ret;
}
#define PCA9534_I2C_ADDR (0x40 >> 1)
static int pca9534_set_gpio(struct mxl111sf_state *state, int gpio, int val)
{
u8 w[2] = { 1, 0 };
u8 r = 0;
struct i2c_msg msg[] = {
{ .addr = PCA9534_I2C_ADDR,
.flags = 0, .buf = w, .len = 1 },
{ .addr = PCA9534_I2C_ADDR,
.flags = I2C_M_RD, .buf = &r, .len = 1 },
};
mxl_debug("(%d, %d)", gpio, val);
/* read current GPIO levels from flip-flop */
i2c_transfer(&state->d->i2c_adap, msg, 2);
/* prepare write buffer with current GPIO levels */
msg[0].len = 2;
#if 0
w[0] = 1;
#endif
w[1] = r;
/* clear the desired GPIO */
w[1] &= ~(1 << gpio);
/* set the desired GPIO value */
w[1] |= ((val ? 1 : 0) << gpio);
/* write new GPIO levels to flip-flop */
i2c_transfer(&state->d->i2c_adap, &msg[0], 1);
return 0;
}
static int pca9534_init_port_expander(struct mxl111sf_state *state)
{
u8 w[2] = { 1, 0x07 }; /* write protect enabled, signal LEDs off */
struct i2c_msg msg = {
.addr = PCA9534_I2C_ADDR,
.flags = 0, .buf = w, .len = 2
};
mxl_debug("()");
i2c_transfer(&state->d->i2c_adap, &msg, 1);
/* configure all pins as outputs */
w[0] = 3;
w[1] = 0;
i2c_transfer(&state->d->i2c_adap, &msg, 1);
return 0;
}
int mxl111sf_set_gpio(struct mxl111sf_state *state, int gpio, int val)
{
mxl_debug("(%d, %d)", gpio, val);
switch (state->gpio_port_expander) {
default:
mxl_printk(KERN_ERR,
"gpio_port_expander undefined, assuming PCA9534");
/* fall-thru */
case mxl111sf_PCA9534:
return pca9534_set_gpio(state, gpio, val);
case mxl111sf_gpio_hw:
return mxl111sf_hw_set_gpio(state, gpio, val);
}
}
static int mxl111sf_probe_port_expander(struct mxl111sf_state *state)
{
int ret;
u8 w = 1;
u8 r = 0;
struct i2c_msg msg[] = {
{ .flags = 0, .buf = &w, .len = 1 },
{ .flags = I2C_M_RD, .buf = &r, .len = 1 },
};
mxl_debug("()");
msg[0].addr = 0x70 >> 1;
msg[1].addr = 0x70 >> 1;
/* read current GPIO levels from flip-flop */
ret = i2c_transfer(&state->d->i2c_adap, msg, 2);
if (ret == 2) {
state->port_expander_addr = msg[0].addr;
state->gpio_port_expander = mxl111sf_PCA9534;
mxl_debug("found port expander at 0x%02x",
state->port_expander_addr);
return 0;
}
msg[0].addr = 0x40 >> 1;
msg[1].addr = 0x40 >> 1;
ret = i2c_transfer(&state->d->i2c_adap, msg, 2);
if (ret == 2) {
state->port_expander_addr = msg[0].addr;
state->gpio_port_expander = mxl111sf_PCA9534;
mxl_debug("found port expander at 0x%02x",
state->port_expander_addr);
return 0;
}
state->port_expander_addr = 0xff;
state->gpio_port_expander = mxl111sf_gpio_hw;
mxl_debug("using hardware gpio");
return 0;
}
int mxl111sf_init_port_expander(struct mxl111sf_state *state)
{
mxl_debug("()");
if (0x00 == state->port_expander_addr)
mxl111sf_probe_port_expander(state);
switch (state->gpio_port_expander) {
default:
mxl_printk(KERN_ERR,
"gpio_port_expander undefined, assuming PCA9534");
/* fall-thru */
case mxl111sf_PCA9534:
return pca9534_init_port_expander(state);
case mxl111sf_gpio_hw:
return mxl111sf_hw_gpio_initialize(state);
}
}
/* ------------------------------------------------------------------------ */
int mxl111sf_gpio_mode_switch(struct mxl111sf_state *state, unsigned int mode)
{
/* GPO:
* 3 - ATSC/MH# | 1 = ATSC transport, 0 = MH transport | default 0
* 4 - ATSC_RST## | 1 = ATSC enable, 0 = ATSC Reset | default 0
* 5 - ATSC_EN | 1 = ATSC power enable, 0 = ATSC power off | default 0
* 6 - MH_RESET# | 1 = MH enable, 0 = MH Reset | default 0
* 7 - MH_EN | 1 = MH power enable, 0 = MH power off | default 0
*/
mxl_debug("(%d)", mode);
switch (mode) {
case MXL111SF_GPIO_MOD_MH:
mxl111sf_set_gpio(state, 4, 0);
mxl111sf_set_gpio(state, 5, 0);
msleep(50);
mxl111sf_set_gpio(state, 7, 1);
msleep(50);
mxl111sf_set_gpio(state, 6, 1);
msleep(50);
mxl111sf_set_gpio(state, 3, 0);
break;
case MXL111SF_GPIO_MOD_ATSC:
mxl111sf_set_gpio(state, 6, 0);
mxl111sf_set_gpio(state, 7, 0);
msleep(50);
mxl111sf_set_gpio(state, 5, 1);
msleep(50);
mxl111sf_set_gpio(state, 4, 1);
msleep(50);
mxl111sf_set_gpio(state, 3, 1);
break;
default: /* DVBT / STANDBY */
mxl111sf_init_port_expander(state);
break;
}
return 0;
}
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
/*
* mxl111sf-gpio.h - driver for the MaxLinear MXL111SF
*
* Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _DVB_USB_MXL111SF_GPIO_H_
#define _DVB_USB_MXL111SF_GPIO_H_
#include "mxl111sf.h"
int mxl111sf_set_gpio(struct mxl111sf_state *state, int gpio, int val);
int mxl111sf_init_port_expander(struct mxl111sf_state *state);
#define MXL111SF_GPIO_MOD_DVBT 0
#define MXL111SF_GPIO_MOD_MH 1
#define MXL111SF_GPIO_MOD_ATSC 2
int mxl111sf_gpio_mode_switch(struct mxl111sf_state *state, unsigned int mode);
enum mxl111sf_mux_config {
PIN_MUX_DEFAULT = 0,
PIN_MUX_TS_OUT_PARALLEL,
PIN_MUX_TS_OUT_SERIAL,
PIN_MUX_GPIO_MODE,
PIN_MUX_TS_SERIAL_IN_MODE_0,
PIN_MUX_TS_SERIAL_IN_MODE_1,
PIN_MUX_TS_SPI_IN_MODE_0,
PIN_MUX_TS_SPI_IN_MODE_1,
PIN_MUX_TS_PARALLEL_IN,
PIN_MUX_BT656_I2S_MODE,
};
int mxl111sf_config_pin_mux_modes(struct mxl111sf_state *state,
enum mxl111sf_mux_config pin_mux_config);
#endif /* _DVB_USB_MXL111SF_GPIO_H_ */
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
/*
* mxl111sf-i2c.c - driver for the MaxLinear MXL111SF
*
* Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "mxl111sf-i2c.h"
#include "mxl111sf.h"
/* SW-I2C ----------------------------------------------------------------- */
#define SW_I2C_ADDR 0x1a
#define SW_I2C_EN 0x02
#define SW_SCL_OUT 0x04
#define SW_SDA_OUT 0x08
#define SW_SDA_IN 0x04
#define SW_I2C_BUSY_ADDR 0x2f
#define SW_I2C_BUSY 0x02
static int mxl111sf_i2c_bitbang_sendbyte(struct mxl111sf_state *state,
u8 byte)
{
int i, ret;
u8 data = 0;
mxl_i2c("(0x%02x)", byte);
ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data);
if (mxl_fail(ret))
goto fail;
for (i = 0; i < 8; i++) {
data = (byte & (0x80 >> i)) ? SW_SDA_OUT : 0;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | data);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | data | SW_SCL_OUT);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | data);
if (mxl_fail(ret))
goto fail;
}
/* last bit was 0 so we need to release SDA */
if (!(byte & 1)) {
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SDA_OUT);
if (mxl_fail(ret))
goto fail;
}
/* CLK high for ACK readback */
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data);
if (mxl_fail(ret))
goto fail;
/* drop the CLK after getting ACK, SDA will go high right away */
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SDA_OUT);
if (mxl_fail(ret))
goto fail;
if (data & SW_SDA_IN)
ret = -EIO;
fail:
return ret;
}
static int mxl111sf_i2c_bitbang_recvbyte(struct mxl111sf_state *state,
u8 *pbyte)
{
int i, ret;
u8 byte = 0;
u8 data = 0;
mxl_i2c("()");
*pbyte = 0;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SDA_OUT);
if (mxl_fail(ret))
goto fail;
for (i = 0; i < 8; i++) {
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN |
SW_SCL_OUT | SW_SDA_OUT);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data);
if (mxl_fail(ret))
goto fail;
if (data & SW_SDA_IN)
byte |= (0x80 >> i);
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SDA_OUT);
if (mxl_fail(ret))
goto fail;
}
*pbyte = byte;
fail:
return ret;
}
static int mxl111sf_i2c_start(struct mxl111sf_state *state)
{
int ret;
mxl_i2c("()");
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SCL_OUT);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN); /* start */
mxl_fail(ret);
fail:
return ret;
}
static int mxl111sf_i2c_stop(struct mxl111sf_state *state)
{
int ret;
mxl_i2c("()");
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN); /* stop */
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SCL_OUT);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_SCL_OUT | SW_SDA_OUT);
mxl_fail(ret);
fail:
return ret;
}
static int mxl111sf_i2c_ack(struct mxl111sf_state *state)
{
int ret;
u8 b = 0;
mxl_i2c("()");
ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &b);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN);
if (mxl_fail(ret))
goto fail;
/* pull SDA low */
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SCL_OUT);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SDA_OUT);
mxl_fail(ret);
fail:
return ret;
}
static int mxl111sf_i2c_nack(struct mxl111sf_state *state)
{
int ret;
mxl_i2c("()");
/* SDA high to signal last byte read from slave */
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, SW_I2C_ADDR,
0x10 | SW_I2C_EN | SW_SDA_OUT);
mxl_fail(ret);
fail:
return ret;
}
/* ------------------------------------------------------------------------ */
static int mxl111sf_i2c_sw_xfer_msg(struct mxl111sf_state *state,
struct i2c_msg *msg)
{
int i, ret;
mxl_i2c("()");
if (msg->flags & I2C_M_RD) {
ret = mxl111sf_i2c_start(state);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_i2c_bitbang_sendbyte(state,
(msg->addr << 1) | 0x01);
if (mxl_fail(ret)) {
mxl111sf_i2c_stop(state);
goto fail;
}
for (i = 0; i < msg->len; i++) {
ret = mxl111sf_i2c_bitbang_recvbyte(state,
&msg->buf[i]);
if (mxl_fail(ret)) {
mxl111sf_i2c_stop(state);
goto fail;
}
if (i < msg->len - 1)
mxl111sf_i2c_ack(state);
}
mxl111sf_i2c_nack(state);
ret = mxl111sf_i2c_stop(state);
if (mxl_fail(ret))
goto fail;
} else {
ret = mxl111sf_i2c_start(state);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_i2c_bitbang_sendbyte(state,
(msg->addr << 1) & 0xfe);
if (mxl_fail(ret)) {
mxl111sf_i2c_stop(state);
goto fail;
}
for (i = 0; i < msg->len; i++) {
ret = mxl111sf_i2c_bitbang_sendbyte(state,
msg->buf[i]);
if (mxl_fail(ret)) {
mxl111sf_i2c_stop(state);
goto fail;
}
}
/* FIXME: we only want to do this on the last transaction */
mxl111sf_i2c_stop(state);
}
fail:
return ret;
}
/* HW-I2C ----------------------------------------------------------------- */
#define USB_WRITE_I2C_CMD 0x99
#define USB_READ_I2C_CMD 0xdd
#define USB_END_I2C_CMD 0xfe
#define USB_WRITE_I2C_CMD_LEN 26
#define USB_READ_I2C_CMD_LEN 24
#define I2C_MUX_REG 0x30
#define I2C_CONTROL_REG 0x00
#define I2C_SLAVE_ADDR_REG 0x08
#define I2C_DATA_REG 0x0c
#define I2C_INT_STATUS_REG 0x10
static int mxl111sf_i2c_send_data(struct mxl111sf_state *state,
u8 index, u8 *wdata)
{
int ret = mxl111sf_ctrl_msg(state->d, wdata[0],
&wdata[1], 25, NULL, 0);
mxl_fail(ret);
return ret;
}
static int mxl111sf_i2c_get_data(struct mxl111sf_state *state,
u8 index, u8 *wdata, u8 *rdata)
{
int ret = mxl111sf_ctrl_msg(state->d, wdata[0],
&wdata[1], 25, rdata, 24);
mxl_fail(ret);
return ret;
}
static u8 mxl111sf_i2c_check_status(struct mxl111sf_state *state)
{
u8 status = 0;
u8 buf[26];
mxl_i2c_adv("()");
buf[0] = USB_READ_I2C_CMD;
buf[1] = 0x00;
buf[2] = I2C_INT_STATUS_REG;
buf[3] = 0x00;
buf[4] = 0x00;
buf[5] = USB_END_I2C_CMD;
mxl111sf_i2c_get_data(state, 0, buf, buf);
if (buf[1] & 0x04)
status = 1;
return status;
}
static u8 mxl111sf_i2c_check_fifo(struct mxl111sf_state *state)
{
u8 status = 0;
u8 buf[26];
mxl_i2c("()");
buf[0] = USB_READ_I2C_CMD;
buf[1] = 0x00;
buf[2] = I2C_MUX_REG;
buf[3] = 0x00;
buf[4] = 0x00;
buf[5] = I2C_INT_STATUS_REG;
buf[6] = 0x00;
buf[7] = 0x00;
buf[8] = USB_END_I2C_CMD;
mxl111sf_i2c_get_data(state, 0, buf, buf);
if (0x08 == (buf[1] & 0x08))
status = 1;
if ((buf[5] & 0x02) == 0x02)
mxl_i2c("(buf[5] & 0x02) == 0x02"); /* FIXME */
return status;
}
static int mxl111sf_i2c_readagain(struct mxl111sf_state *state,
u8 count, u8 *rbuf)
{
u8 i2c_w_data[26];
u8 i2c_r_data[24];
u8 i = 0;
u8 fifo_status = 0;
int ret;
int status = 0;
mxl_i2c("read %d bytes", count);
while ((fifo_status == 0) && (i++ < 5))
fifo_status = mxl111sf_i2c_check_fifo(state);
i2c_w_data[0] = 0xDD;
i2c_w_data[1] = 0x00;
for (i = 2; i < 26; i++)
i2c_w_data[i] = 0xFE;
for (i = 0; i < count; i++) {
i2c_w_data[2+(i*3)] = 0x0C;
i2c_w_data[3+(i*3)] = 0x00;
i2c_w_data[4+(i*3)] = 0x00;
}
ret = mxl111sf_i2c_get_data(state, 0, i2c_w_data, i2c_r_data);
/* Check for I2C NACK status */
if (mxl111sf_i2c_check_status(state) == 1) {
mxl_i2c("error!");
} else {
for (i = 0; i < count; i++) {
rbuf[i] = i2c_r_data[(i*3)+1];
mxl_i2c("%02x\t %02x",
i2c_r_data[(i*3)+1],
i2c_r_data[(i*3)+2]);
}
status = 1;
}
return status;
}
#define HWI2C400 1
static int mxl111sf_i2c_hw_xfer_msg(struct mxl111sf_state *state,
struct i2c_msg *msg)
{
int i, k, ret = 0;
u16 index = 0;
u8 buf[26];
u8 i2c_r_data[24];
u16 block_len;
u16 left_over_len;
u8 rd_status[8];
u8 ret_status;
u8 readbuff[26];
mxl_i2c("addr: 0x%02x, read buff len: %d, write buff len: %d",
msg->addr, (msg->flags & I2C_M_RD) ? msg->len : 0,
(!msg->flags & I2C_M_RD) ? msg->len : 0);
for (index = 0; index < 26; index++)
buf[index] = USB_END_I2C_CMD;
/* command to indicate data payload is destined for I2C interface */
buf[0] = USB_WRITE_I2C_CMD;
buf[1] = 0x00;
/* enable I2C interface */
buf[2] = I2C_MUX_REG;
buf[3] = 0x80;
buf[4] = 0x00;
/* enable I2C interface */
buf[5] = I2C_MUX_REG;
buf[6] = 0x81;
buf[7] = 0x00;
/* set Timeout register on I2C interface */
buf[8] = 0x14;
buf[9] = 0xff;
buf[10] = 0x00;
#if 0
/* enable Interrupts on I2C interface */
buf[8] = 0x24;
buf[9] = 0xF7;
buf[10] = 0x00;
#endif
buf[11] = 0x24;
buf[12] = 0xF7;
buf[13] = 0x00;
ret = mxl111sf_i2c_send_data(state, 0, buf);
/* write data on I2C bus */
if ((!msg->flags & I2C_M_RD) && (msg->len > 0)) {
mxl_i2c("%d\t%02x", msg->len, msg->buf[0]);
/* control register on I2C interface to initialize I2C bus */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0x5E;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
/* I2C Slave device Address */
buf[5] = I2C_SLAVE_ADDR_REG;
buf[6] = (msg->addr);
buf[7] = 0x00;
buf[8] = USB_END_I2C_CMD;
ret = mxl111sf_i2c_send_data(state, 0, buf);
/* check for slave device status */
if (mxl111sf_i2c_check_status(state) == 1) {
mxl_i2c("NACK writing slave address %02x",
msg->addr);
/* if NACK, stop I2C bus and exit */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0x4E;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
ret = -EIO;
goto exit;
}
/* I2C interface can do I2C operations in block of 8 bytes of
I2C data. calculation to figure out number of blocks of i2c
data required to program */
block_len = (msg->len / 8);
left_over_len = (msg->len % 8);
index = 0;
mxl_i2c("block_len %d, left_over_len %d",
block_len, left_over_len);
for (index = 0; index < block_len; index++) {
for (i = 0; i < 8; i++) {
/* write data on I2C interface */
buf[2+(i*3)] = I2C_DATA_REG;
buf[3+(i*3)] = msg->buf[(index*8)+i];
buf[4+(i*3)] = 0x00;
}
ret = mxl111sf_i2c_send_data(state, 0, buf);
/* check for I2C NACK status */
if (mxl111sf_i2c_check_status(state) == 1) {
mxl_i2c("NACK writing slave address %02x",
msg->addr);
/* if NACK, stop I2C bus and exit */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0x4E;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
ret = -EIO;
goto exit;
}
}
if (left_over_len) {
for (k = 0; k < 26; k++)
buf[k] = USB_END_I2C_CMD;
buf[0] = 0x99;
buf[1] = 0x00;
for (i = 0; i < left_over_len; i++) {
buf[2+(i*3)] = I2C_DATA_REG;
buf[3+(i*3)] = msg->buf[(index*8)+i];
mxl_i2c("index = %d %d data %d",
index, i, msg->buf[(index*8)+i]);
buf[4+(i*3)] = 0x00;
}
ret = mxl111sf_i2c_send_data(state, 0, buf);
/* check for I2C NACK status */
if (mxl111sf_i2c_check_status(state) == 1) {
mxl_i2c("NACK writing slave address %02x",
msg->addr);
/* if NACK, stop I2C bus and exit */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0x4E;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
ret = -EIO;
goto exit;
}
}
/* issue I2C STOP after write */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0x4E;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
}
/* read data from I2C bus */
if ((msg->flags & I2C_M_RD) && (msg->len > 0)) {
mxl_i2c("read buf len %d", msg->len);
/* command to indicate data payload is
destined for I2C interface */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0xDF;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
/* I2C xfer length */
buf[5] = 0x14;
buf[6] = (msg->len & 0xFF);
buf[7] = 0;
/* I2C slave device Address */
buf[8] = I2C_SLAVE_ADDR_REG;
buf[9] = msg->addr;
buf[10] = 0x00;
buf[11] = USB_END_I2C_CMD;
ret = mxl111sf_i2c_send_data(state, 0, buf);
/* check for I2C NACK status */
if (mxl111sf_i2c_check_status(state) == 1) {
mxl_i2c("NACK reading slave address %02x",
msg->addr);
/* if NACK, stop I2C bus and exit */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0xC7;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
ret = -EIO;
goto exit;
}
/* I2C interface can do I2C operations in block of 8 bytes of
I2C data. calculation to figure out number of blocks of
i2c data required to program */
block_len = ((msg->len) / 8);
left_over_len = ((msg->len) % 8);
index = 0;
mxl_i2c("block_len %d, left_over_len %d",
block_len, left_over_len);
/* command to read data from I2C interface */
buf[0] = USB_READ_I2C_CMD;
buf[1] = 0x00;
for (index = 0; index < block_len; index++) {
/* setup I2C read request packet on I2C interface */
for (i = 0; i < 8; i++) {
buf[2+(i*3)] = I2C_DATA_REG;
buf[3+(i*3)] = 0x00;
buf[4+(i*3)] = 0x00;
}
ret = mxl111sf_i2c_get_data(state, 0, buf, i2c_r_data);
/* check for I2C NACK status */
if (mxl111sf_i2c_check_status(state) == 1) {
mxl_i2c("NACK reading slave address %02x",
msg->addr);
/* if NACK, stop I2C bus and exit */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0xC7;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
ret = -EIO;
goto exit;
}
/* copy data from i2c data payload to read buffer */
for (i = 0; i < 8; i++) {
rd_status[i] = i2c_r_data[(i*3)+2];
if (rd_status[i] == 0x04) {
if (i < 7) {
mxl_i2c("i2c fifo empty!"
" @ %d", i);
msg->buf[(index*8)+i] =
i2c_r_data[(i*3)+1];
/* read again */
ret_status =
mxl111sf_i2c_readagain(
state, 8-(i+1),
readbuff);
if (ret_status == 1) {
for (k = 0;
k < 8-(i+1);
k++) {
msg->buf[(index*8)+(k+i+1)] =
readbuff[k];
mxl_i2c("read data: %02x\t %02x",
msg->buf[(index*8)+(k+i)],
(index*8)+(k+i));
mxl_i2c("read data: %02x\t %02x",
msg->buf[(index*8)+(k+i+1)],
readbuff[k]);
}
goto stop_copy;
} else {
mxl_i2c("readagain "
"ERROR!");
}
} else {
msg->buf[(index*8)+i] =
i2c_r_data[(i*3)+1];
}
} else {
msg->buf[(index*8)+i] =
i2c_r_data[(i*3)+1];
}
}
stop_copy:
;
}
if (left_over_len) {
for (k = 0; k < 26; k++)
buf[k] = USB_END_I2C_CMD;
buf[0] = 0xDD;
buf[1] = 0x00;
for (i = 0; i < left_over_len; i++) {
buf[2+(i*3)] = I2C_DATA_REG;
buf[3+(i*3)] = 0x00;
buf[4+(i*3)] = 0x00;
}
ret = mxl111sf_i2c_get_data(state, 0, buf,
i2c_r_data);
/* check for I2C NACK status */
if (mxl111sf_i2c_check_status(state) == 1) {
mxl_i2c("NACK reading slave address %02x",
msg->addr);
/* if NACK, stop I2C bus and exit */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0xC7;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
ret = -EIO;
goto exit;
}
for (i = 0; i < left_over_len; i++) {
msg->buf[(block_len*8)+i] =
i2c_r_data[(i*3)+1];
mxl_i2c("read data: %02x\t %02x",
i2c_r_data[(i*3)+1],
i2c_r_data[(i*3)+2]);
}
}
/* indicate I2C interface to issue NACK
after next I2C read op */
buf[0] = USB_WRITE_I2C_CMD;
buf[1] = 0x00;
/* control register */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0x17;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
buf[5] = USB_END_I2C_CMD;
ret = mxl111sf_i2c_send_data(state, 0, buf);
/* control register */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0xC7;
buf[4] = (HWI2C400) ? 0x03 : 0x0D;
}
exit:
/* STOP and disable I2C MUX */
buf[0] = USB_WRITE_I2C_CMD;
buf[1] = 0x00;
/* de-initilize I2C BUS */
buf[5] = USB_END_I2C_CMD;
mxl111sf_i2c_send_data(state, 0, buf);
/* Control Register */
buf[2] = I2C_CONTROL_REG;
buf[3] = 0xDF;
buf[4] = 0x03;
/* disable I2C interface */
buf[5] = I2C_MUX_REG;
buf[6] = 0x00;
buf[7] = 0x00;
/* de-initilize I2C BUS */
buf[8] = USB_END_I2C_CMD;
mxl111sf_i2c_send_data(state, 0, buf);
/* disable I2C interface */
buf[2] = I2C_MUX_REG;
buf[3] = 0x81;
buf[4] = 0x00;
/* disable I2C interface */
buf[5] = I2C_MUX_REG;
buf[6] = 0x00;
buf[7] = 0x00;
/* disable I2C interface */
buf[8] = I2C_MUX_REG;
buf[9] = 0x00;
buf[10] = 0x00;
buf[11] = USB_END_I2C_CMD;
mxl111sf_i2c_send_data(state, 0, buf);
return ret;
}
/* ------------------------------------------------------------------------ */
int mxl111sf_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg msg[], int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
struct mxl111sf_state *state = d->priv;
int hwi2c = (state->chip_rev > MXL111SF_V6);
int i, ret;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
for (i = 0; i < num; i++) {
ret = (hwi2c) ?
mxl111sf_i2c_hw_xfer_msg(state, &msg[i]) :
mxl111sf_i2c_sw_xfer_msg(state, &msg[i]);
if (mxl_fail(ret)) {
mxl_debug_adv("failed with error %d on i2c "
"transaction %d of %d, %sing %d bytes "
"to/from 0x%02x", ret, i+1, num,
(msg[i].flags & I2C_M_RD) ?
"read" : "writ",
msg[i].len, msg[i].addr);
break;
}
}
mutex_unlock(&d->i2c_mutex);
return i == num ? num : -EREMOTEIO;
}
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
/*
* mxl111sf-i2c.h - driver for the MaxLinear MXL111SF
*
* Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _DVB_USB_MXL111SF_I2C_H_
#define _DVB_USB_MXL111SF_I2C_H_
#include <linux/i2c.h>
int mxl111sf_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg msg[], int num);
#endif /* _DVB_USB_MXL111SF_I2C_H_ */
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
/*
* mxl111sf-phy.c - driver for the MaxLinear MXL111SF
*
* Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "mxl111sf-phy.h"
#include "mxl111sf-reg.h"
int mxl111sf_init_tuner_demod(struct mxl111sf_state *state)
{
struct mxl111sf_reg_ctrl_info mxl_111_overwrite_default[] = {
{0x07, 0xff, 0x0c},
{0x58, 0xff, 0x9d},
{0x09, 0xff, 0x00},
{0x06, 0xff, 0x06},
{0xc8, 0xff, 0x40}, /* ED_LE_WIN_OLD = 0 */
{0x8d, 0x01, 0x01}, /* NEGATE_Q */
{0x32, 0xff, 0xac}, /* DIG_RFREFSELECT = 12 */
{0x42, 0xff, 0x43}, /* DIG_REG_AMP = 4 */
{0x74, 0xff, 0xc4}, /* SSPUR_FS_PRIO = 4 */
{0x71, 0xff, 0xe6}, /* SPUR_ROT_PRIO_VAL = 1 */
{0x83, 0xff, 0x64}, /* INF_FILT1_THD_SC = 100 */
{0x85, 0xff, 0x64}, /* INF_FILT2_THD_SC = 100 */
{0x88, 0xff, 0xf0}, /* INF_THD = 240 */
{0x6f, 0xf0, 0xb0}, /* DFE_DLY = 11 */
{0x00, 0xff, 0x01}, /* Change to page 1 */
{0x81, 0xff, 0x11}, /* DSM_FERR_BYPASS = 1 */
{0xf4, 0xff, 0x07}, /* DIG_FREQ_CORR = 1 */
{0xd4, 0x1f, 0x0f}, /* SPUR_TEST_NOISE_TH = 15 */
{0xd6, 0xff, 0x0c}, /* SPUR_TEST_NOISE_PAPR = 12 */
{0x00, 0xff, 0x00}, /* Change to page 0 */
{0, 0, 0}
};
mxl_debug("()");
return mxl111sf_ctrl_program_regs(state, mxl_111_overwrite_default);
}
int mxl1x1sf_soft_reset(struct mxl111sf_state *state)
{
int ret;
mxl_debug("()");
ret = mxl111sf_write_reg(state, 0xff, 0x00); /* AIC */
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, 0x02, 0x01); /* get out of reset */
mxl_fail(ret);
fail:
return ret;
}
int mxl1x1sf_set_device_mode(struct mxl111sf_state *state, int mode)
{
int ret;
mxl_debug("(%s)", MXL_SOC_MODE == mode ?
"MXL_SOC_MODE" : "MXL_TUNER_MODE");
/* set device mode */
ret = mxl111sf_write_reg(state, 0x03,
MXL_SOC_MODE == mode ? 0x01 : 0x00);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg_mask(state,
0x7d, 0x40, MXL_SOC_MODE == mode ?
0x00 : /* enable impulse noise filter,
INF_BYP = 0 */
0x40); /* disable impulse noise filter,
INF_BYP = 1 */
if (mxl_fail(ret))
goto fail;
state->device_mode = mode;
fail:
return ret;
}
/* power up tuner */
int mxl1x1sf_top_master_ctrl(struct mxl111sf_state *state, int onoff)
{
mxl_debug("(%d)", onoff);
return mxl111sf_write_reg(state, 0x01, onoff ? 0x01 : 0x00);
}
int mxl111sf_disable_656_port(struct mxl111sf_state *state)
{
mxl_debug("()");
return mxl111sf_write_reg_mask(state, 0x12, 0x04, 0x00);
}
int mxl111sf_enable_usb_output(struct mxl111sf_state *state)
{
mxl_debug("()");
return mxl111sf_write_reg_mask(state, 0x17, 0x40, 0x00);
}
/* initialize TSIF as input port of MxL1X1SF for MPEG2 data transfer */
int mxl111sf_config_mpeg_in(struct mxl111sf_state *state,
unsigned int parallel_serial,
unsigned int msb_lsb_1st,
unsigned int clock_phase,
unsigned int mpeg_valid_pol,
unsigned int mpeg_sync_pol)
{
int ret;
u8 mode, tmp;
mxl_debug("(%u,%u,%u,%u,%u)", parallel_serial, msb_lsb_1st,
clock_phase, mpeg_valid_pol, mpeg_sync_pol);
/* Enable PIN MUX */
ret = mxl111sf_write_reg(state, V6_PIN_MUX_MODE_REG, V6_ENABLE_PIN_MUX);
mxl_fail(ret);
/* Configure MPEG Clock phase */
mxl111sf_read_reg(state, V6_MPEG_IN_CLK_INV_REG, &mode);
if (clock_phase == TSIF_NORMAL)
mode &= ~V6_INVERTED_CLK_PHASE;
else
mode |= V6_INVERTED_CLK_PHASE;
ret = mxl111sf_write_reg(state, V6_MPEG_IN_CLK_INV_REG, mode);
mxl_fail(ret);
/* Configure data input mode, MPEG Valid polarity, MPEG Sync polarity
* Get current configuration */
ret = mxl111sf_read_reg(state, V6_MPEG_IN_CTRL_REG, &mode);
mxl_fail(ret);
/* Data Input mode */
if (parallel_serial == TSIF_INPUT_PARALLEL) {
/* Disable serial mode */
mode &= ~V6_MPEG_IN_DATA_SERIAL;
/* Enable Parallel mode */
mode |= V6_MPEG_IN_DATA_PARALLEL;
} else {
/* Disable Parallel mode */
mode &= ~V6_MPEG_IN_DATA_PARALLEL;
/* Enable Serial Mode */
mode |= V6_MPEG_IN_DATA_SERIAL;
/* If serial interface is chosen, configure
MSB or LSB order in transmission */
ret = mxl111sf_read_reg(state,
V6_MPEG_INOUT_BIT_ORDER_CTRL_REG,
&tmp);
mxl_fail(ret);
if (msb_lsb_1st == MPEG_SER_MSB_FIRST_ENABLED)
tmp |= V6_MPEG_SER_MSB_FIRST;
else
tmp &= ~V6_MPEG_SER_MSB_FIRST;
ret = mxl111sf_write_reg(state,
V6_MPEG_INOUT_BIT_ORDER_CTRL_REG,
tmp);
mxl_fail(ret);
}
/* MPEG Sync polarity */
if (mpeg_sync_pol == TSIF_NORMAL)
mode &= ~V6_INVERTED_MPEG_SYNC;
else
mode |= V6_INVERTED_MPEG_SYNC;
/* MPEG Valid polarity */
if (mpeg_valid_pol == 0)
mode &= ~V6_INVERTED_MPEG_VALID;
else
mode |= V6_INVERTED_MPEG_VALID;
ret = mxl111sf_write_reg(state, V6_MPEG_IN_CTRL_REG, mode);
mxl_fail(ret);
return ret;
}
int mxl111sf_init_i2s_port(struct mxl111sf_state *state, u8 sample_size)
{
static struct mxl111sf_reg_ctrl_info init_i2s[] = {
{0x1b, 0xff, 0x1e}, /* pin mux mode, Choose 656/I2S input */
{0x15, 0x60, 0x60}, /* Enable I2S */
{0x17, 0xe0, 0x20}, /* Input, MPEG MODE USB,
Inverted 656 Clock, I2S_SOFT_RESET,
0 : Normal operation, 1 : Reset State */
#if 0
{0x12, 0x01, 0x00}, /* AUDIO_IRQ_CLR (Overflow Indicator) */
#endif
{0x00, 0xff, 0x02}, /* Change to Control Page */
{0x26, 0x0d, 0x0d}, /* I2S_MODE & BT656_SRC_SEL for FPGA only */
{0x00, 0xff, 0x00},
{0, 0, 0}
};
int ret;
mxl_debug("(0x%02x)", sample_size);
ret = mxl111sf_ctrl_program_regs(state, init_i2s);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, V6_I2S_NUM_SAMPLES_REG, sample_size);
mxl_fail(ret);
fail:
return ret;
}
int mxl111sf_disable_i2s_port(struct mxl111sf_state *state)
{
static struct mxl111sf_reg_ctrl_info disable_i2s[] = {
{0x15, 0x40, 0x00},
{0, 0, 0}
};
mxl_debug("()");
return mxl111sf_ctrl_program_regs(state, disable_i2s);
}
int mxl111sf_config_i2s(struct mxl111sf_state *state,
u8 msb_start_pos, u8 data_width)
{
int ret;
u8 tmp;
mxl_debug("(0x%02x, 0x%02x)", msb_start_pos, data_width);
ret = mxl111sf_read_reg(state, V6_I2S_STREAM_START_BIT_REG, &tmp);
if (mxl_fail(ret))
goto fail;
tmp &= 0xe0;
tmp |= msb_start_pos;
ret = mxl111sf_write_reg(state, V6_I2S_STREAM_START_BIT_REG, tmp);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, V6_I2S_STREAM_END_BIT_REG, &tmp);
if (mxl_fail(ret))
goto fail;
tmp &= 0xe0;
tmp |= data_width;
ret = mxl111sf_write_reg(state, V6_I2S_STREAM_END_BIT_REG, tmp);
mxl_fail(ret);
fail:
return ret;
}
int mxl111sf_config_spi(struct mxl111sf_state *state, int onoff)
{
u8 val;
int ret;
mxl_debug("(%d)", onoff);
ret = mxl111sf_write_reg(state, 0x00, 0x02);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_read_reg(state, V8_SPI_MODE_REG, &val);
if (mxl_fail(ret))
goto fail;
if (onoff)
val |= 0x04;
else
val &= ~0x04;
ret = mxl111sf_write_reg(state, V8_SPI_MODE_REG, val);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_write_reg(state, 0x00, 0x00);
if (mxl_fail(ret))
goto fail;
fail:
return ret;
}
int mxl111sf_idac_config(struct mxl111sf_state *state,
u8 control_mode, u8 current_setting,
u8 current_value, u8 hysteresis_value)
{
int ret;
u8 val;
/* current value will be set for both automatic & manual IDAC control */
val = current_value;
if (control_mode == IDAC_MANUAL_CONTROL) {
/* enable manual control of IDAC */
val |= IDAC_MANUAL_CONTROL_BIT_MASK;
if (current_setting == IDAC_CURRENT_SINKING_ENABLE)
/* enable current sinking in manual mode */
val |= IDAC_CURRENT_SINKING_BIT_MASK;
else
/* disable current sinking in manual mode */
val &= ~IDAC_CURRENT_SINKING_BIT_MASK;
} else {
/* disable manual control of IDAC */
val &= ~IDAC_MANUAL_CONTROL_BIT_MASK;
/* set hysteresis value reg: 0x0B<5:0> */
ret = mxl111sf_write_reg(state, V6_IDAC_HYSTERESIS_REG,
(hysteresis_value & 0x3F));
}
ret = mxl111sf_write_reg(state, V6_IDAC_SETTINGS_REG, val);
return val;
}
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
/*
* mxl111sf-phy.h - driver for the MaxLinear MXL111SF
*
* Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _DVB_USB_MXL111SF_PHY_H_
#define _DVB_USB_MXL111SF_PHY_H_
#include "mxl111sf.h"
int mxl1x1sf_soft_reset(struct mxl111sf_state *state);
int mxl1x1sf_set_device_mode(struct mxl111sf_state *state, int mode);
int mxl1x1sf_top_master_ctrl(struct mxl111sf_state *state, int onoff);
int mxl111sf_disable_656_port(struct mxl111sf_state *state);
int mxl111sf_init_tuner_demod(struct mxl111sf_state *state);
int mxl111sf_enable_usb_output(struct mxl111sf_state *state);
int mxl111sf_config_mpeg_in(struct mxl111sf_state *state,
unsigned int parallel_serial,
unsigned int msb_lsb_1st,
unsigned int clock_phase,
unsigned int mpeg_valid_pol,
unsigned int mpeg_sync_pol);
int mxl111sf_config_i2s(struct mxl111sf_state *state,
u8 msb_start_pos, u8 data_width);
int mxl111sf_init_i2s_port(struct mxl111sf_state *state, u8 sample_size);
int mxl111sf_disable_i2s_port(struct mxl111sf_state *state);
int mxl111sf_config_spi(struct mxl111sf_state *state, int onoff);
int mxl111sf_idac_config(struct mxl111sf_state *state,
u8 control_mode, u8 current_setting,
u8 current_value, u8 hysteresis_value);
#endif /* _DVB_USB_MXL111SF_PHY_H_ */
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
/*
* mxl111sf-reg.h - driver for the MaxLinear MXL111SF
*
* Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _DVB_USB_MXL111SF_REG_H_
#define _DVB_USB_MXL111SF_REG_H_
#define CHIP_ID_REG 0xFC
#define TOP_CHIP_REV_ID_REG 0xFA
#define V6_SNR_RB_LSB_REG 0x27
#define V6_SNR_RB_MSB_REG 0x28
#define V6_N_ACCUMULATE_REG 0x11
#define V6_RS_AVG_ERRORS_LSB_REG 0x2C
#define V6_RS_AVG_ERRORS_MSB_REG 0x2D
#define V6_IRQ_STATUS_REG 0x24
#define IRQ_MASK_FEC_LOCK 0x10
#define V6_SYNC_LOCK_REG 0x28
#define SYNC_LOCK_MASK 0x10
#define V6_RS_LOCK_DET_REG 0x28
#define RS_LOCK_DET_MASK 0x08
#define V6_INITACQ_NODETECT_REG 0x20
#define V6_FORCE_NFFT_CPSIZE_REG 0x20
#define V6_CODE_RATE_TPS_REG 0x29
#define V6_CODE_RATE_TPS_MASK 0x07
#define V6_CP_LOCK_DET_REG 0x28
#define V6_CP_LOCK_DET_MASK 0x04
#define V6_TPS_HIERACHY_REG 0x29
#define V6_TPS_HIERARCHY_INFO_MASK 0x40
#define V6_MODORDER_TPS_REG 0x2A
#define V6_PARAM_CONSTELLATION_MASK 0x30
#define V6_MODE_TPS_REG 0x2A
#define V6_PARAM_FFT_MODE_MASK 0x0C
#define V6_CP_TPS_REG 0x29
#define V6_PARAM_GI_MASK 0x30
#define V6_TPS_LOCK_REG 0x2A
#define V6_PARAM_TPS_LOCK_MASK 0x40
#define V6_FEC_PER_COUNT_REG 0x2E
#define V6_FEC_PER_SCALE_REG 0x2B
#define V6_FEC_PER_SCALE_MASK 0x03
#define V6_FEC_PER_CLR_REG 0x20
#define V6_FEC_PER_CLR_MASK 0x01
#define V6_PIN_MUX_MODE_REG 0x1B
#define V6_ENABLE_PIN_MUX 0x1E
#define V6_I2S_NUM_SAMPLES_REG 0x16
#define V6_MPEG_IN_CLK_INV_REG 0x17
#define V6_MPEG_IN_CTRL_REG 0x18
#define V6_INVERTED_CLK_PHASE 0x20
#define V6_MPEG_IN_DATA_PARALLEL 0x01
#define V6_MPEG_IN_DATA_SERIAL 0x02
#define V6_INVERTED_MPEG_SYNC 0x04
#define V6_INVERTED_MPEG_VALID 0x08
#define TSIF_INPUT_PARALLEL 0
#define TSIF_INPUT_SERIAL 1
#define TSIF_NORMAL 0
#define V6_MPEG_INOUT_BIT_ORDER_CTRL_REG 0x19
#define V6_MPEG_SER_MSB_FIRST 0x80
#define MPEG_SER_MSB_FIRST_ENABLED 0x01
#define V6_656_I2S_BUFF_STATUS_REG 0x2F
#define V6_656_OVERFLOW_MASK_BIT 0x08
#define V6_I2S_OVERFLOW_MASK_BIT 0x01
#define V6_I2S_STREAM_START_BIT_REG 0x14
#define V6_I2S_STREAM_END_BIT_REG 0x15
#define I2S_RIGHT_JUSTIFIED 0
#define I2S_LEFT_JUSTIFIED 1
#define I2S_DATA_FORMAT 2
#define V6_TUNER_LOOP_THRU_CONTROL_REG 0x09
#define V6_ENABLE_LOOP_THRU 0x01
#define TOTAL_NUM_IF_OUTPUT_FREQ 16
#define TUNER_NORMAL_IF_SPECTRUM 0x0
#define TUNER_INVERT_IF_SPECTRUM 0x10
#define V6_TUNER_IF_SEL_REG 0x06
#define V6_TUNER_IF_FCW_REG 0x3C
#define V6_TUNER_IF_FCW_BYP_REG 0x3D
#define V6_RF_LOCK_STATUS_REG 0x23
#define NUM_DIG_TV_CHANNEL 1000
#define V6_DIG_CLK_FREQ_SEL_REG 0x07
#define V6_REF_SYNTH_INT_REG 0x5C
#define V6_REF_SYNTH_REMAIN_REG 0x58
#define V6_DIG_RFREFSELECT_REG 0x32
#define V6_XTAL_CLK_OUT_GAIN_REG 0x31
#define V6_TUNER_LOOP_THRU_CTRL_REG 0x09
#define V6_DIG_XTAL_ENABLE_REG 0x06
#define V6_DIG_XTAL_BIAS_REG 0x66
#define V6_XTAL_CAP_REG 0x08
#define V6_GPO_CTRL_REG 0x18
#define MXL_GPO_0 0x00
#define MXL_GPO_1 0x01
#define V6_GPO_0_MASK 0x10
#define V6_GPO_1_MASK 0x20
#define V6_111SF_GPO_CTRL_REG 0x19
#define MXL_111SF_GPO_1 0x00
#define MXL_111SF_GPO_2 0x01
#define MXL_111SF_GPO_3 0x02
#define MXL_111SF_GPO_4 0x03
#define MXL_111SF_GPO_5 0x04
#define MXL_111SF_GPO_6 0x05
#define MXL_111SF_GPO_7 0x06
#define MXL_111SF_GPO_0_MASK 0x01
#define MXL_111SF_GPO_1_MASK 0x02
#define MXL_111SF_GPO_2_MASK 0x04
#define MXL_111SF_GPO_3_MASK 0x08
#define MXL_111SF_GPO_4_MASK 0x10
#define MXL_111SF_GPO_5_MASK 0x20
#define MXL_111SF_GPO_6_MASK 0x40
#define V6_ATSC_CONFIG_REG 0x0A
#define MXL_MODE_REG 0x03
#define START_TUNE_REG 0x1C
#define V6_IDAC_HYSTERESIS_REG 0x0B
#define V6_IDAC_SETTINGS_REG 0x0C
#define IDAC_MANUAL_CONTROL 1
#define IDAC_CURRENT_SINKING_ENABLE 1
#define IDAC_MANUAL_CONTROL_BIT_MASK 0x80
#define IDAC_CURRENT_SINKING_BIT_MASK 0x40
#define V8_SPI_MODE_REG 0xE9
#define V6_DIG_RF_PWR_LSB_REG 0x46
#define V6_DIG_RF_PWR_MSB_REG 0x47
#endif /* _DVB_USB_MXL111SF_REG_H_ */
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
/*
* mxl111sf-tuner.c - driver for the MaxLinear MXL111SF CMOS tuner
*
* Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "mxl111sf-tuner.h"
#include "mxl111sf-phy.h"
#include "mxl111sf-reg.h"
/* debug */
static int mxl111sf_tuner_debug;
module_param_named(debug, mxl111sf_tuner_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
#define mxl_dbg(fmt, arg...) \
if (mxl111sf_tuner_debug) \
mxl_printk(KERN_DEBUG, fmt, ##arg)
/* ------------------------------------------------------------------------ */
struct mxl111sf_tuner_state {
struct mxl111sf_state *mxl_state;
struct mxl111sf_tuner_config *cfg;
u32 frequency;
u32 bandwidth;
};
static int mxl111sf_tuner_read_reg(struct mxl111sf_tuner_state *state,
u8 addr, u8 *data)
{
return (state->cfg->read_reg) ?
state->cfg->read_reg(state->mxl_state, addr, data) :
-EINVAL;
}
static int mxl111sf_tuner_write_reg(struct mxl111sf_tuner_state *state,
u8 addr, u8 data)
{
return (state->cfg->write_reg) ?
state->cfg->write_reg(state->mxl_state, addr, data) :
-EINVAL;
}
static int mxl111sf_tuner_program_regs(struct mxl111sf_tuner_state *state,
struct mxl111sf_reg_ctrl_info *ctrl_reg_info)
{
return (state->cfg->program_regs) ?
state->cfg->program_regs(state->mxl_state, ctrl_reg_info) :
-EINVAL;
}
static int mxl1x1sf_tuner_top_master_ctrl(struct mxl111sf_tuner_state *state,
int onoff)
{
return (state->cfg->top_master_ctrl) ?
state->cfg->top_master_ctrl(state->mxl_state, onoff) :
-EINVAL;
}
/* ------------------------------------------------------------------------ */
static struct mxl111sf_reg_ctrl_info mxl_phy_tune_rf[] = {
{0x1d, 0x7f, 0x00}, /* channel bandwidth section 1/2/3,
DIG_MODEINDEX, _A, _CSF, */
{0x1e, 0xff, 0x00}, /* channel frequency (lo and fractional) */
{0x1f, 0xff, 0x00}, /* channel frequency (hi for integer portion) */
{0, 0, 0}
};
/* ------------------------------------------------------------------------ */
static struct mxl111sf_reg_ctrl_info *mxl111sf_calc_phy_tune_regs(u32 freq,
u8 bw)
{
u8 filt_bw;
/* set channel bandwidth */
switch (bw) {
case 0: /* ATSC */
filt_bw = 25;
break;
case 1: /* QAM */
filt_bw = 69;
break;
case 6:
filt_bw = 21;
break;
case 7:
filt_bw = 42;
break;
case 8:
filt_bw = 63;
break;
default:
err("%s: invalid bandwidth setting!", __func__);
return NULL;
}
/* calculate RF channel */
freq /= 1000000;
freq *= 64;
#if 0
/* do round */
freq += 0.5;
#endif
/* set bandwidth */
mxl_phy_tune_rf[0].data = filt_bw;
/* set RF */
mxl_phy_tune_rf[1].data = (freq & 0xff);
mxl_phy_tune_rf[2].data = (freq >> 8) & 0xff;
/* start tune */
return mxl_phy_tune_rf;
}
static int mxl1x1sf_tuner_set_if_output_freq(struct mxl111sf_tuner_state *state)
{
int ret;
u8 ctrl;
#if 0
u16 iffcw;
u32 if_freq;
#endif
mxl_dbg("(IF polarity = %d, IF freq = 0x%02x)",
state->cfg->invert_spectrum, state->cfg->if_freq);
/* set IF polarity */
ctrl = state->cfg->invert_spectrum;
ctrl |= state->cfg->if_freq;
ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_SEL_REG, ctrl);
if (mxl_fail(ret))
goto fail;
#if 0
if_freq /= 1000000;
/* do round */
if_freq += 0.5;
if (MXL_IF_LO == state->cfg->if_freq) {
ctrl = 0x08;
iffcw = (u16)(if_freq / (108 * 4096));
} else if (MXL_IF_HI == state->cfg->if_freq) {
ctrl = 0x08;
iffcw = (u16)(if_freq / (216 * 4096));
} else {
ctrl = 0;
iffcw = 0;
}
ctrl |= (iffcw >> 8);
#endif
ret = mxl111sf_tuner_read_reg(state, V6_TUNER_IF_FCW_BYP_REG, &ctrl);
if (mxl_fail(ret))
goto fail;
ctrl &= 0xf0;
ctrl |= 0x90;
ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_BYP_REG, ctrl);
if (mxl_fail(ret))
goto fail;
#if 0
ctrl = iffcw & 0x00ff;
#endif
ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_REG, ctrl);
mxl_fail(ret);
fail:
return ret;
}
static int mxl1x1sf_tune_rf(struct dvb_frontend *fe, u32 freq, u8 bw)
{
struct mxl111sf_tuner_state *state = fe->tuner_priv;
static struct mxl111sf_reg_ctrl_info *reg_ctrl_array;
int ret;
u8 mxl_mode;
mxl_dbg("(freq = %d, bw = 0x%x)", freq, bw);
/* stop tune */
ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 0);
if (mxl_fail(ret))
goto fail;
/* check device mode */
ret = mxl111sf_tuner_read_reg(state, MXL_MODE_REG, &mxl_mode);
if (mxl_fail(ret))
goto fail;
/* Fill out registers for channel tune */
reg_ctrl_array = mxl111sf_calc_phy_tune_regs(freq, bw);
if (!reg_ctrl_array)
return -EINVAL;
ret = mxl111sf_tuner_program_regs(state, reg_ctrl_array);
if (mxl_fail(ret))
goto fail;
if ((mxl_mode & MXL_DEV_MODE_MASK) == MXL_TUNER_MODE) {
/* IF tuner mode only */
mxl1x1sf_tuner_top_master_ctrl(state, 0);
mxl1x1sf_tuner_top_master_ctrl(state, 1);
mxl1x1sf_tuner_set_if_output_freq(state);
}
ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 1);
if (mxl_fail(ret))
goto fail;
if (state->cfg->ant_hunt)
state->cfg->ant_hunt(fe);
fail:
return ret;
}
static int mxl1x1sf_tuner_get_lock_status(struct mxl111sf_tuner_state *state,
int *rf_synth_lock,
int *ref_synth_lock)
{
int ret;
u8 data;
*rf_synth_lock = 0;
*ref_synth_lock = 0;
ret = mxl111sf_tuner_read_reg(state, V6_RF_LOCK_STATUS_REG, &data);
if (mxl_fail(ret))
goto fail;
*ref_synth_lock = ((data & 0x03) == 0x03) ? 1 : 0;
*rf_synth_lock = ((data & 0x0c) == 0x0c) ? 1 : 0;
fail:
return ret;
}
#if 0
static int mxl1x1sf_tuner_loop_thru_ctrl(struct mxl111sf_tuner_state *state,
int onoff)
{
return mxl111sf_tuner_write_reg(state, V6_TUNER_LOOP_THRU_CTRL_REG,
onoff ? 1 : 0);
}
#endif
/* ------------------------------------------------------------------------ */
static int mxl111sf_tuner_set_params(struct dvb_frontend *fe,
struct dvb_frontend_parameters *params)
{
struct mxl111sf_tuner_state *state = fe->tuner_priv;
int ret;
u8 bw;
mxl_dbg("()");
if (fe->ops.info.type == FE_ATSC) {
switch (params->u.vsb.modulation) {
case VSB_8:
case VSB_16:
bw = 0; /* ATSC */
break;
case QAM_64:
case QAM_256:
bw = 1; /* US CABLE */
break;
default:
err("%s: modulation not set!", __func__);
return -EINVAL;
}
} else if (fe->ops.info.type == FE_OFDM) {
switch (params->u.ofdm.bandwidth) {
case BANDWIDTH_6_MHZ:
bw = 6;
break;
case BANDWIDTH_7_MHZ:
bw = 7;
break;
case BANDWIDTH_8_MHZ:
bw = 8;
break;
default:
err("%s: bandwidth not set!", __func__);
return -EINVAL;
}
} else {
err("%s: modulation type not supported!", __func__);
return -EINVAL;
}
ret = mxl1x1sf_tune_rf(fe, params->frequency, bw);
if (mxl_fail(ret))
goto fail;
state->frequency = params->frequency;
state->bandwidth = (fe->ops.info.type == FE_OFDM) ?
params->u.ofdm.bandwidth : 0;
fail:
return ret;
}
/* ------------------------------------------------------------------------ */
#if 0
static int mxl111sf_tuner_init(struct dvb_frontend *fe)
{
struct mxl111sf_tuner_state *state = fe->tuner_priv;
int ret;
/* wake from standby handled by usb driver */
return ret;
}
static int mxl111sf_tuner_sleep(struct dvb_frontend *fe)
{
struct mxl111sf_tuner_state *state = fe->tuner_priv;
int ret;
/* enter standby mode handled by usb driver */
return ret;
}
#endif
/* ------------------------------------------------------------------------ */
static int mxl111sf_tuner_get_status(struct dvb_frontend *fe, u32 *status)
{
struct mxl111sf_tuner_state *state = fe->tuner_priv;
int rf_locked, ref_locked, ret;
*status = 0;
ret = mxl1x1sf_tuner_get_lock_status(state, &rf_locked, &ref_locked);
if (mxl_fail(ret))
goto fail;
mxl_info("%s%s", rf_locked ? "rf locked " : "",
ref_locked ? "ref locked" : "");
if ((rf_locked) || (ref_locked))
*status |= TUNER_STATUS_LOCKED;
fail:
return ret;
}
static int mxl111sf_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
{
struct mxl111sf_tuner_state *state = fe->tuner_priv;
u8 val1, val2;
int ret;
*strength = 0;
ret = mxl111sf_tuner_write_reg(state, 0x00, 0x02);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_LSB_REG, &val1);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_MSB_REG, &val2);
if (mxl_fail(ret))
goto fail;
*strength = val1 | ((val2 & 0x07) << 8);
fail:
ret = mxl111sf_tuner_write_reg(state, 0x00, 0x00);
mxl_fail(ret);
return ret;
}
/* ------------------------------------------------------------------------ */
static int mxl111sf_tuner_get_frequency(struct dvb_frontend *fe, u32 *frequency)
{
struct mxl111sf_tuner_state *state = fe->tuner_priv;
*frequency = state->frequency;
return 0;
}
static int mxl111sf_tuner_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
{
struct mxl111sf_tuner_state *state = fe->tuner_priv;
*bandwidth = state->bandwidth;
return 0;
}
static int mxl111sf_tuner_release(struct dvb_frontend *fe)
{
struct mxl111sf_tuner_state *state = fe->tuner_priv;
mxl_dbg("()");
kfree(state);
fe->tuner_priv = NULL;
return 0;
}
/* ------------------------------------------------------------------------- */
static struct dvb_tuner_ops mxl111sf_tuner_tuner_ops = {
.info = {
.name = "MaxLinear MxL111SF",
#if 0
.frequency_min = ,
.frequency_max = ,
.frequency_step = ,
#endif
},
#if 0
.init = mxl111sf_tuner_init,
.sleep = mxl111sf_tuner_sleep,
#endif
.set_params = mxl111sf_tuner_set_params,
.get_status = mxl111sf_tuner_get_status,
.get_rf_strength = mxl111sf_get_rf_strength,
.get_frequency = mxl111sf_tuner_get_frequency,
.get_bandwidth = mxl111sf_tuner_get_bandwidth,
.release = mxl111sf_tuner_release,
};
struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe,
struct mxl111sf_state *mxl_state,
struct mxl111sf_tuner_config *cfg)
{
struct mxl111sf_tuner_state *state = NULL;
mxl_dbg("()");
state = kzalloc(sizeof(struct mxl111sf_tuner_state), GFP_KERNEL);
if (state == NULL)
return NULL;
state->mxl_state = mxl_state;
state->cfg = cfg;
memcpy(&fe->ops.tuner_ops, &mxl111sf_tuner_tuner_ops,
sizeof(struct dvb_tuner_ops));
fe->tuner_priv = state;
return fe;
}
EXPORT_SYMBOL_GPL(mxl111sf_tuner_attach);
MODULE_DESCRIPTION("MaxLinear MxL111SF CMOS tuner driver");
MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1");
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* ---------------------------------------------------------------------------
* Local variables:
* c-basic-offset: 8
* End:
*/
/*
* mxl111sf-tuner.h - driver for the MaxLinear MXL111SF CMOS tuner
*
* Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __MXL111SF_TUNER_H__
#define __MXL111SF_TUNER_H__
#include "dvb_frontend.h"
#include "mxl111sf.h"
enum mxl_if_freq {
#if 0
MXL_IF_LO = 0x00, /* other IF < 9MHz */
#endif
MXL_IF_4_0 = 0x01, /* 4.0 MHz */
MXL_IF_4_5 = 0x02, /* 4.5 MHz */
MXL_IF_4_57 = 0x03, /* 4.57 MHz */
MXL_IF_5_0 = 0x04, /* 5.0 MHz */
MXL_IF_5_38 = 0x05, /* 5.38 MHz */
MXL_IF_6_0 = 0x06, /* 6.0 MHz */
MXL_IF_6_28 = 0x07, /* 6.28 MHz */
MXL_IF_7_2 = 0x08, /* 7.2 MHz */
MXL_IF_35_25 = 0x09, /* 35.25 MHz */
MXL_IF_36 = 0x0a, /* 36 MHz */
MXL_IF_36_15 = 0x0b, /* 36.15 MHz */
MXL_IF_44 = 0x0c, /* 44 MHz */
#if 0
MXL_IF_HI = 0x0f, /* other IF > 35 MHz and < 45 MHz */
#endif
};
struct mxl111sf_tuner_config {
enum mxl_if_freq if_freq;
unsigned int invert_spectrum:1;
int (*read_reg)(struct mxl111sf_state *state, u8 addr, u8 *data);
int (*write_reg)(struct mxl111sf_state *state, u8 addr, u8 data);
int (*program_regs)(struct mxl111sf_state *state,
struct mxl111sf_reg_ctrl_info *ctrl_reg_info);
int (*top_master_ctrl)(struct mxl111sf_state *state, int onoff);
int (*ant_hunt)(struct dvb_frontend *fe);
};
/* ------------------------------------------------------------------------ */
#if defined(CONFIG_DVB_USB_MXL111SF) || \
(defined(CONFIG_DVB_USB_MXL111SF_MODULE) && defined(MODULE))
extern
struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe,
struct mxl111sf_state *mxl_state,
struct mxl111sf_tuner_config *cfg);
#else
static inline
struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe,
struct mxl111sf_state *mxl_state
struct mxl111sf_tuner_config *cfg)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
#endif
#endif /* __MXL111SF_TUNER_H__ */
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* ---------------------------------------------------------------------------
* Local variables:
* c-basic-offset: 8
* End:
*/
/*
* Copyright (C) 2010 Michael Krufky (mkrufky@kernellabs.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#include <linux/vmalloc.h>
#include <linux/i2c.h>
#include "mxl111sf.h"
#include "mxl111sf-reg.h"
#include "mxl111sf-phy.h"
#include "mxl111sf-i2c.h"
#include "mxl111sf-gpio.h"
#include "mxl111sf-tuner.h"
#include "lgdt3305.h"
int dvb_usb_mxl111sf_debug;
module_param_named(debug, dvb_usb_mxl111sf_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level "
"(1=info, 2=xfer, 4=i2c, 8=reg, 16=adv (or-able)).");
int dvb_usb_mxl111sf_isoc;
module_param_named(isoc, dvb_usb_mxl111sf_isoc, int, 0644);
MODULE_PARM_DESC(isoc, "enable usb isoc xfer (0=bulk, 1=isoc).");
#define ANT_PATH_AUTO 0
#define ANT_PATH_EXTERNAL 1
#define ANT_PATH_INTERNAL 2
int dvb_usb_mxl111sf_rfswitch =
#if 0
ANT_PATH_AUTO;
#else
ANT_PATH_EXTERNAL;
#endif
module_param_named(rfswitch, dvb_usb_mxl111sf_rfswitch, int, 0644);
MODULE_PARM_DESC(rfswitch, "force rf switch position (0=auto, 1=ext, 2=int).");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
#define deb_info(args...) dprintk(dvb_usb_mxl111sf_debug, 0x13, args)
#define deb_reg(args...) dprintk(dvb_usb_mxl111sf_debug, 0x08, args)
#define deb_adv(args...) dprintk(dvb_usb_mxl111sf_debug, MXL_ADV_DBG, args)
int mxl111sf_ctrl_msg(struct dvb_usb_device *d,
u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
{
int wo = (rbuf == NULL || rlen == 0); /* write-only */
int ret;
u8 sndbuf[1+wlen];
deb_adv("%s(wlen = %d, rlen = %d)\n", __func__, wlen, rlen);
memset(sndbuf, 0, 1+wlen);
sndbuf[0] = cmd;
memcpy(&sndbuf[1], wbuf, wlen);
ret = (wo) ? dvb_usb_generic_write(d, sndbuf, 1+wlen) :
dvb_usb_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen, 0);
mxl_fail(ret);
return ret;
}
/* ------------------------------------------------------------------------ */
#define MXL_CMD_REG_READ 0xaa
#define MXL_CMD_REG_WRITE 0x55
int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data)
{
u8 buf[2];
int ret;
ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_READ, &addr, 1, buf, 2);
if (mxl_fail(ret)) {
mxl_debug("error reading reg: 0x%02x", addr);
goto fail;
}
if (buf[0] == addr)
*data = buf[1];
else {
err("invalid response reading reg: 0x%02x != 0x%02x, 0x%02x",
addr, buf[0], buf[1]);
ret = -EINVAL;
}
deb_reg("R: (0x%02x, 0x%02x)\n", addr, *data);
fail:
return ret;
}
int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data)
{
u8 buf[] = { addr, data };
int ret;
deb_reg("W: (0x%02x, 0x%02x)\n", addr, data);
ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_WRITE, buf, 2, NULL, 0);
if (mxl_fail(ret))
err("error writing reg: 0x%02x, val: 0x%02x", addr, data);
return ret;
}
/* ------------------------------------------------------------------------ */
int mxl111sf_write_reg_mask(struct mxl111sf_state *state,
u8 addr, u8 mask, u8 data)
{
int ret;
u8 val;
if (mask != 0xff) {
ret = mxl111sf_read_reg(state, addr, &val);
#if 1
/* dont know why this usually errors out on the first try */
if (mxl_fail(ret))
err("error writing addr: 0x%02x, mask: 0x%02x, "
"data: 0x%02x, retrying...", addr, mask, data);
ret = mxl111sf_read_reg(state, addr, &val);
#endif
if (mxl_fail(ret))
goto fail;
}
val &= ~mask;
val |= data;
ret = mxl111sf_write_reg(state, addr, val);
mxl_fail(ret);
fail:
return ret;
}
/* ------------------------------------------------------------------------ */
int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state,
struct mxl111sf_reg_ctrl_info *ctrl_reg_info)
{
int i, ret = 0;
for (i = 0; ctrl_reg_info[i].addr |
ctrl_reg_info[i].mask |
ctrl_reg_info[i].data; i++) {
ret = mxl111sf_write_reg_mask(state,
ctrl_reg_info[i].addr,
ctrl_reg_info[i].mask,
ctrl_reg_info[i].data);
if (mxl_fail(ret)) {
err("failed on reg #%d (0x%02x)", i,
ctrl_reg_info[i].addr);
break;
}
}
return ret;
}
/* ------------------------------------------------------------------------ */
static int mxl1x1sf_get_chip_info(struct mxl111sf_state *state)
{
int ret;
u8 id, ver;
char *mxl_chip, *mxl_rev;
if ((state->chip_id) && (state->chip_ver))
return 0;
ret = mxl111sf_read_reg(state, CHIP_ID_REG, &id);
if (mxl_fail(ret))
goto fail;
state->chip_id = id;
ret = mxl111sf_read_reg(state, TOP_CHIP_REV_ID_REG, &ver);
if (mxl_fail(ret))
goto fail;
state->chip_ver = ver;
switch (id) {
case 0x61:
mxl_chip = "MxL101SF";
break;
case 0x63:
mxl_chip = "MxL111SF";
break;
default:
mxl_chip = "UNKNOWN MxL1X1";
break;
}
switch (ver) {
case 0x36:
state->chip_rev = MXL111SF_V6;
mxl_rev = "v6";
break;
case 0x08:
state->chip_rev = MXL111SF_V8_100;
mxl_rev = "v8_100";
break;
case 0x18:
state->chip_rev = MXL111SF_V8_200;
mxl_rev = "v8_200";
break;
default:
state->chip_rev = 0;
mxl_rev = "UNKNOWN REVISION";
break;
}
info("%s detected, %s (0x%x)", mxl_chip, mxl_rev, ver);
fail:
return ret;
}
#define get_chip_info(state) \
({ \
int ___ret; \
___ret = mxl1x1sf_get_chip_info(state); \
if (mxl_fail(___ret)) { \
mxl_debug("failed to get chip info" \
" on first probe attempt"); \
___ret = mxl1x1sf_get_chip_info(state); \
if (mxl_fail(___ret)) \
err("failed to get chip info during probe"); \
else \
mxl_debug("probe needed a retry " \
"in order to succeed."); \
} \
___ret; \
})
/* ------------------------------------------------------------------------ */
static int mxl111sf_power_ctrl(struct dvb_usb_device *d, int onoff)
{
/* power control depends on which adapter is being woken:
* save this for init, instead, via mxl111sf_adap_fe_init */
return 0;
}
static int mxl111sf_adap_fe_init(struct dvb_frontend *fe)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
struct dvb_usb_device *d = adap->dev;
struct mxl111sf_state *state = d->priv;
struct mxl111sf_adap_state *adap_state = adap->priv;
int err;
/* exit if we didnt initialize the driver yet */
if (!state->chip_id) {
mxl_debug("driver not yet initialized, exit.");
goto fail;
}
deb_info("%s()\n", __func__);
mutex_lock(&state->fe_lock);
state->alt_mode = adap_state->alt_mode;
if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
err("set interface failed");
err = mxl1x1sf_soft_reset(state);
mxl_fail(err);
err = mxl111sf_init_tuner_demod(state);
mxl_fail(err);
err = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
mxl_fail(err);
mxl111sf_enable_usb_output(state);
mxl_fail(err);
mxl1x1sf_top_master_ctrl(state, 1);
mxl_fail(err);
if ((MXL111SF_GPIO_MOD_DVBT != adap_state->gpio_mode) &&
(state->chip_rev > MXL111SF_V6)) {
mxl111sf_config_pin_mux_modes(state,
PIN_MUX_TS_SPI_IN_MODE_1);
mxl_fail(err);
}
err = mxl111sf_init_port_expander(state);
if (!mxl_fail(err)) {
state->gpio_mode = adap_state->gpio_mode;
err = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
mxl_fail(err);
#if 0
err = fe->ops.init(fe);
#endif
msleep(100); /* add short delay after enabling
* the demod before touching it */
}
return (adap_state->fe_init) ? adap_state->fe_init(fe) : 0;
fail:
return -ENODEV;
}
static int mxl111sf_adap_fe_sleep(struct dvb_frontend *fe)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
struct dvb_usb_device *d = adap->dev;
struct mxl111sf_state *state = d->priv;
struct mxl111sf_adap_state *adap_state = adap->priv;
int err;
/* exit if we didnt initialize the driver yet */
if (!state->chip_id) {
mxl_debug("driver not yet initialized, exit.");
goto fail;
}
deb_info("%s()\n", __func__);
err = (adap_state->fe_sleep) ? adap_state->fe_sleep(fe) : 0;
mutex_unlock(&state->fe_lock);
return err;
fail:
return -ENODEV;
}
static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
struct dvb_usb_device *d = adap->dev;
struct mxl111sf_state *state = d->priv;
struct mxl111sf_adap_state *adap_state = adap->priv;
int ret = 0;
u8 tmp;
deb_info("%s(%d)\n", __func__, onoff);
if (onoff) {
ret = mxl111sf_enable_usb_output(state);
mxl_fail(ret);
ret = mxl111sf_config_mpeg_in(state, 1, 1,
adap_state->ep6_clockphase,
0, 0);
mxl_fail(ret);
} else {
ret = mxl111sf_disable_656_port(state);
mxl_fail(ret);
}
mxl111sf_read_reg(state, 0x12, &tmp);
tmp &= ~0x04;
mxl111sf_write_reg(state, 0x12, tmp);
return ret;
}
/* ------------------------------------------------------------------------ */
static struct lgdt3305_config hauppauge_lgdt3305_config = {
.i2c_addr = 0xb2 >> 1,
.mpeg_mode = LGDT3305_MPEG_SERIAL,
.tpclk_edge = LGDT3305_TPCLK_RISING_EDGE,
.tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
.deny_i2c_rptr = 1,
.spectral_inversion = 0,
.qam_if_khz = 6000,
.vsb_if_khz = 6000,
};
static int mxl111sf_lgdt3305_frontend_attach(struct dvb_usb_adapter *adap)
{
struct dvb_usb_device *d = adap->dev;
struct mxl111sf_state *state = d->priv;
struct mxl111sf_adap_state *adap_state = adap->priv;
int ret;
deb_adv("%s()\n", __func__);
/* save a pointer to the dvb_usb_device in device state */
state->d = d;
adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1;
state->alt_mode = adap_state->alt_mode;
if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
err("set interface failed");
state->gpio_mode = MXL111SF_GPIO_MOD_ATSC;
adap_state->gpio_mode = state->gpio_mode;
adap_state->device_mode = MXL_TUNER_MODE;
adap_state->ep6_clockphase = 1;
ret = mxl1x1sf_soft_reset(state);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_init_tuner_demod(state);
if (mxl_fail(ret))
goto fail;
ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_enable_usb_output(state);
if (mxl_fail(ret))
goto fail;
ret = mxl1x1sf_top_master_ctrl(state, 1);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_init_port_expander(state);
if (mxl_fail(ret))
goto fail;
ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
if (mxl_fail(ret))
goto fail;
adap->fe[0] = dvb_attach(lgdt3305_attach,
&hauppauge_lgdt3305_config,
&adap->dev->i2c_adap);
if (adap->fe[0]) {
adap_state->fe_init = adap->fe[0]->ops.init;
adap->fe[0]->ops.init = mxl111sf_adap_fe_init;
adap_state->fe_sleep = adap->fe[0]->ops.sleep;
adap->fe[0]->ops.sleep = mxl111sf_adap_fe_sleep;
return 0;
}
ret = -EIO;
fail:
return ret;
}
static inline int mxl111sf_set_ant_path(struct mxl111sf_state *state,
int antpath)
{
return mxl111sf_idac_config(state, 1, 1,
(antpath == ANT_PATH_INTERNAL) ?
0x3f : 0x00, 0);
}
#define DbgAntHunt(x, pwr0, pwr1, pwr2, pwr3) \
err("%s(%d) FINAL input set to %s rxPwr:%d|%d|%d|%d\n", \
__func__, __LINE__, \
(ANT_PATH_EXTERNAL == x) ? "EXTERNAL" : "INTERNAL", \
pwr0, pwr1, pwr2, pwr3)
#define ANT_HUNT_SLEEP 90
#define ANT_EXT_TWEAK 0
static int mxl111sf_ant_hunt(struct dvb_frontend *fe)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
struct dvb_usb_device *d = adap->dev;
struct mxl111sf_state *state = d->priv;
int antctrl = dvb_usb_mxl111sf_rfswitch;
u16 rxPwrA, rxPwr0, rxPwr1, rxPwr2;
/* FIXME: must force EXTERNAL for QAM - done elsewhere */
mxl111sf_set_ant_path(state, antctrl == ANT_PATH_AUTO ?
ANT_PATH_EXTERNAL : antctrl);
if (antctrl == ANT_PATH_AUTO) {
#if 0
msleep(ANT_HUNT_SLEEP);
#endif
fe->ops.tuner_ops.get_rf_strength(fe, &rxPwrA);
mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL);
msleep(ANT_HUNT_SLEEP);
fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr0);
mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL);
msleep(ANT_HUNT_SLEEP);
fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr1);
mxl111sf_set_ant_path(state, ANT_PATH_INTERNAL);
msleep(ANT_HUNT_SLEEP);
fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr2);
if (rxPwr1+ANT_EXT_TWEAK >= rxPwr2) {
/* return with EXTERNAL enabled */
mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL);
DbgAntHunt(ANT_PATH_EXTERNAL, rxPwrA,
rxPwr0, rxPwr1, rxPwr2);
} else {
/* return with INTERNAL enabled */
DbgAntHunt(ANT_PATH_INTERNAL, rxPwrA,
rxPwr0, rxPwr1, rxPwr2);
}
}
return 0;
}
static struct mxl111sf_tuner_config mxl_tuner_config = {
.if_freq = MXL_IF_6_0, /* applies to external IF output, only */
.invert_spectrum = 0,
.read_reg = mxl111sf_read_reg,
.write_reg = mxl111sf_write_reg,
.program_regs = mxl111sf_ctrl_program_regs,
.top_master_ctrl = mxl1x1sf_top_master_ctrl,
.ant_hunt = mxl111sf_ant_hunt,
};
static int mxl111sf_attach_tuner(struct dvb_usb_adapter *adap)
{
struct dvb_usb_device *d = adap->dev;
struct mxl111sf_state *state = d->priv;
deb_adv("%s()\n", __func__);
if (NULL != dvb_attach(mxl111sf_tuner_attach, adap->fe[0], state,
&mxl_tuner_config))
return 0;
return -EIO;
}
static int mxl111sf_fe_ioctl_override(struct dvb_frontend *fe,
unsigned int cmd, void *parg,
unsigned int stage)
{
int err = 0;
switch (stage) {
case DVB_FE_IOCTL_PRE:
switch (cmd) {
case FE_READ_SIGNAL_STRENGTH:
err = fe->ops.tuner_ops.get_rf_strength(fe, parg);
/* If no error occurs, prevent dvb-core from handling
* this IOCTL, otherwise return the error */
if (0 == err)
err = 1;
break;
}
break;
case DVB_FE_IOCTL_POST:
/* no post-ioctl handling required */
break;
}
return err;
};
static u32 mxl111sf_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
struct i2c_algorithm mxl111sf_i2c_algo = {
.master_xfer = mxl111sf_i2c_xfer,
.functionality = mxl111sf_i2c_func,
#ifdef NEED_ALGO_CONTROL
.algo_control = dummy_algo_control,
#endif
};
/* DVB USB Driver stuff */
static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties;
static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties;
static int mxl111sf_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct dvb_usb_device *d = NULL;
deb_adv("%s()\n", __func__);
if (((dvb_usb_mxl111sf_isoc) &&
(0 == dvb_usb_device_init(intf,
&mxl111sf_atsc_isoc_properties,
THIS_MODULE, &d, adapter_nr))) ||
0 == dvb_usb_device_init(intf,
&mxl111sf_atsc_bulk_properties,
THIS_MODULE, &d, adapter_nr) || 0) {
struct mxl111sf_state *state = d->priv;
static u8 eeprom[256];
struct i2c_client c;
int ret;
ret = get_chip_info(state);
if (mxl_fail(ret))
err("failed to get chip info during probe");
mutex_init(&state->fe_lock);
if (state->chip_rev > MXL111SF_V6)
mxl111sf_config_pin_mux_modes(state,
PIN_MUX_TS_SPI_IN_MODE_1);
c.adapter = &d->i2c_adap;
c.addr = 0xa0 >> 1;
ret = tveeprom_read(&c, eeprom, sizeof(eeprom));
if (mxl_fail(ret))
return 0;
tveeprom_hauppauge_analog(&c, &state->tv,
(0x84 == eeprom[0xa0]) ?
eeprom + 0xa0 : eeprom + 0x80);
#if 0
switch (state->tv.model) {
case 117001:
case 126001:
case 138001:
break;
default:
printk(KERN_WARNING "%s: warning: "
"unknown hauppauge model #%d\n",
__func__, state->tv.model);
}
#endif
return 0;
}
err("Your device is not yet supported by this driver. "
"See kernellabs.com for more info");
return -EINVAL;
}
static struct usb_device_id mxl111sf_table[] = {
/* 0 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc600) }, /* ATSC+ IR */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xc601) }, /* ATSC */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xc602) }, /* + */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xc603) }, /* ATSC+ */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xc604) }, /* DVBT */
/* 5 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc609) }, /* ATSC IR */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60a) }, /* + IR */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60b) }, /* ATSC+ IR */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60c) }, /* DVBT IR */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xc653) }, /* ATSC+ */
/*10 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc65b) }, /* ATSC+ IR */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xb700) }, /* ATSC+ sw */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xb701) }, /* ATSC sw */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xb702) }, /* + sw */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xb703) }, /* ATSC+ sw */
/*15 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb704) }, /* DVBT sw */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xb753) }, /* ATSC+ sw */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xb763) }, /* ATSC+ no */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xb764) }, /* DVBT no */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xd853) }, /* ATSC+ sw */
/*20 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd854) }, /* DVBT sw */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xd863) }, /* ATSC+ no */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xd864) }, /* DVBT no */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8d3) }, /* ATSC+ sw */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8d4) }, /* DVBT sw */
/*25 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8e3) }, /* ATSC+ no */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8e4) }, /* DVBT no */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8ff) }, /* ATSC+ */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xc612) }, /* + */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xc613) }, /* ATSC+ */
/*30 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc61a) }, /* + IR */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xc61b) }, /* ATSC+ IR */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xb757) }, /* ATSC+DVBT sw */
{ USB_DEVICE(USB_VID_HAUPPAUGE, 0xb767) }, /* ATSC+DVBT no */
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, mxl111sf_table);
#define MXL111SF_EP6_BULK_STREAMING_CONFIG \
.streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \
.stream = { \
.type = USB_BULK, \
.count = 5, \
.endpoint = 0x06, \
.u = { \
.bulk = { \
.buffersize = 8192, \
} \
} \
}
/* FIXME */
#define MXL111SF_EP6_ISOC_STREAMING_CONFIG \
.streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \
.stream = { \
.type = USB_ISOC, \
.count = 5, \
.endpoint = 0x06, \
.u = { \
.isoc = { \
.framesperurb = 24, \
.framesize = 3072, \
.interval = 1, \
} \
} \
}
#define MXL111SF_DEFAULT_DEVICE_PROPERTIES \
.caps = DVB_USB_IS_AN_I2C_ADAPTER, \
.usb_ctrl = DEVICE_SPECIFIC, \
/* use usb alt setting 1 for EP4 ISOC transfer (dvb-t), \
EP6 BULK transfer (atsc/qam), \
use usb alt setting 2 for EP4 BULK transfer (dvb-t), \
EP6 ISOC transfer (atsc/qam), \
*/ \
.power_ctrl = mxl111sf_power_ctrl, \
.i2c_algo = &mxl111sf_i2c_algo, \
.generic_bulk_ctrl_endpoint = MXL_EP2_REG_WRITE, \
.generic_bulk_ctrl_endpoint_response = MXL_EP1_REG_READ, \
.size_of_priv = sizeof(struct mxl111sf_state)
static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = {
MXL111SF_DEFAULT_DEVICE_PROPERTIES,
.num_adapters = 1,
.adapter = {
{
.size_of_priv = sizeof(struct mxl111sf_adap_state),
.fe_ioctl_override = mxl111sf_fe_ioctl_override,
.frontend_attach = mxl111sf_lgdt3305_frontend_attach,
.tuner_attach = mxl111sf_attach_tuner,
MXL111SF_EP6_BULK_STREAMING_CONFIG,
},
},
.num_device_descs = 6,
.devices = {
{ "Hauppauge 126xxx ATSC (bulk)",
{ NULL },
{ &mxl111sf_table[1], &mxl111sf_table[5],
NULL },
},
{ "Hauppauge 117xxx ATSC (bulk)",
{ NULL },
{ &mxl111sf_table[12],
NULL },
},
{ "Hauppauge 126xxx ATSC+ (bulk)",
{ NULL },
{ &mxl111sf_table[0], &mxl111sf_table[3],
&mxl111sf_table[7], &mxl111sf_table[9],
&mxl111sf_table[10], NULL },
},
{ "Hauppauge 117xxx ATSC+ (bulk)",
{ NULL },
{ &mxl111sf_table[11], &mxl111sf_table[14],
&mxl111sf_table[16], &mxl111sf_table[17],
&mxl111sf_table[32], &mxl111sf_table[33],
NULL },
},
{ "Hauppauge Mercury (tp-bulk)",
{ NULL },
{ &mxl111sf_table[19], &mxl111sf_table[21],
&mxl111sf_table[23], &mxl111sf_table[25],
&mxl111sf_table[27], NULL },
},
{ "Hauppauge WinTV-Aero-M",
{ NULL },
{ &mxl111sf_table[29], &mxl111sf_table[31],
NULL },
},
}
};
static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = {
MXL111SF_DEFAULT_DEVICE_PROPERTIES,
.num_adapters = 1,
.adapter = {
{
.size_of_priv = sizeof(struct mxl111sf_adap_state),
.fe_ioctl_override = mxl111sf_fe_ioctl_override,
.frontend_attach = mxl111sf_lgdt3305_frontend_attach,
.tuner_attach = mxl111sf_attach_tuner,
MXL111SF_EP6_ISOC_STREAMING_CONFIG,
},
},
.num_device_descs = 6,
.devices = {
{ "Hauppauge 126xxx ATSC (isoc)",
{ NULL },
{ &mxl111sf_table[1], &mxl111sf_table[5],
NULL },
},
{ "Hauppauge 117xxx ATSC (isoc)",
{ NULL },
{ &mxl111sf_table[12],
NULL },
},
{ "Hauppauge 126xxx ATSC+ (isoc)",
{ NULL },
{ &mxl111sf_table[0], &mxl111sf_table[3],
&mxl111sf_table[7], &mxl111sf_table[9],
&mxl111sf_table[10], NULL },
},
{ "Hauppauge 117xxx ATSC+ (isoc)",
{ NULL },
{ &mxl111sf_table[11], &mxl111sf_table[14],
&mxl111sf_table[16], &mxl111sf_table[17],
&mxl111sf_table[32], &mxl111sf_table[33],
NULL },
},
{ "Hauppauge Mercury (tp-isoc)",
{ NULL },
{ &mxl111sf_table[19], &mxl111sf_table[21],
&mxl111sf_table[23], &mxl111sf_table[25],
&mxl111sf_table[27], NULL },
},
{ "Hauppauge WinTV-Aero-M (tp-isoc)",
{ NULL },
{ &mxl111sf_table[29], &mxl111sf_table[31],
NULL },
},
}
};
static struct usb_driver mxl111sf_driver = {
.name = "dvb_usb_mxl111sf",
.probe = mxl111sf_probe,
.disconnect = dvb_usb_device_exit,
.id_table = mxl111sf_table,
};
static int __init mxl111sf_module_init(void)
{
int result = usb_register(&mxl111sf_driver);
if (result) {
err("usb_register failed. Error number %d", result);
return result;
}
return 0;
}
static void __exit mxl111sf_module_exit(void)
{
usb_deregister(&mxl111sf_driver);
}
module_init(mxl111sf_module_init);
module_exit(mxl111sf_module_exit);
MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>");
MODULE_DESCRIPTION("Driver for MaxLinear MxL111SF");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
/*
* Copyright (C) 2010 Michael Krufky (mkrufky@kernellabs.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
#ifndef _DVB_USB_MXL111SF_H_
#define _DVB_USB_MXL111SF_H_
#ifdef DVB_USB_LOG_PREFIX
#undef DVB_USB_LOG_PREFIX
#endif
#define DVB_USB_LOG_PREFIX "mxl111sf"
#include "dvb-usb.h"
#include <media/tveeprom.h>
#define MXL_EP1_REG_READ 1
#define MXL_EP2_REG_WRITE 2
#define MXL_EP3_INTERRUPT 3
#define MXL_EP4_MPEG2 4
#define MXL_EP5_I2S 5
#define MXL_EP6_656 6
#define MXL_EP6_MPEG2 6
#ifdef USING_ENUM_mxl111sf_current_mode
enum mxl111sf_current_mode {
mxl_mode_dvbt = MXL_EP4_MPEG2,
mxl_mode_mh = MXL_EP5_I2S,
mxl_mode_atsc = MXL_EP6_MPEG2,
};
#endif
enum mxl111sf_gpio_port_expander {
mxl111sf_gpio_hw,
mxl111sf_PCA9534,
};
struct mxl111sf_state {
struct dvb_usb_device *d;
enum mxl111sf_gpio_port_expander gpio_port_expander;
u8 port_expander_addr;
u8 chip_id;
u8 chip_ver;
#define MXL111SF_V6 1
#define MXL111SF_V8_100 2
#define MXL111SF_V8_200 3
u8 chip_rev;
#ifdef USING_ENUM_mxl111sf_current_mode
enum mxl111sf_current_mode current_mode;
#endif
#define MXL_TUNER_MODE 0
#define MXL_SOC_MODE 1
#define MXL_DEV_MODE_MASK 0x01
#if 1
int device_mode;
#endif
/* use usb alt setting 1 for EP4 ISOC transfer (dvb-t),
EP5 BULK transfer (atsc-mh),
EP6 BULK transfer (atsc/qam),
use usb alt setting 2 for EP4 BULK transfer (dvb-t),
EP5 ISOC transfer (atsc-mh),
EP6 ISOC transfer (atsc/qam),
*/
int alt_mode;
int gpio_mode;
struct tveeprom tv;
struct mutex fe_lock;
};
struct mxl111sf_adap_state {
int alt_mode;
int gpio_mode;
int device_mode;
int ep6_clockphase;
int (*fe_init)(struct dvb_frontend *);
int (*fe_sleep)(struct dvb_frontend *);
};
int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data);
int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data);
struct mxl111sf_reg_ctrl_info {
u8 addr;
u8 mask;
u8 data;
};
int mxl111sf_write_reg_mask(struct mxl111sf_state *state,
u8 addr, u8 mask, u8 data);
int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state,
struct mxl111sf_reg_ctrl_info *ctrl_reg_info);
/* needed for hardware i2c functions in mxl111sf-i2c.c:
* mxl111sf_i2c_send_data / mxl111sf_i2c_get_data */
int mxl111sf_ctrl_msg(struct dvb_usb_device *d,
u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen);
#define mxl_printk(kern, fmt, arg...) \
printk(kern "%s: " fmt "\n", __func__, ##arg)
#define mxl_info(fmt, arg...) \
mxl_printk(KERN_INFO, fmt, ##arg)
extern int dvb_usb_mxl111sf_debug;
#define mxl_debug(fmt, arg...) \
if (dvb_usb_mxl111sf_debug) \
mxl_printk(KERN_DEBUG, fmt, ##arg)
#define MXL_I2C_DBG 0x04
#define MXL_ADV_DBG 0x10
#define mxl_debug_adv(fmt, arg...) \
if (dvb_usb_mxl111sf_debug & MXL_ADV_DBG) \
mxl_printk(KERN_DEBUG, fmt, ##arg)
#define mxl_i2c(fmt, arg...) \
if (dvb_usb_mxl111sf_debug & MXL_I2C_DBG) \
mxl_printk(KERN_DEBUG, fmt, ##arg)
#define mxl_i2c_adv(fmt, arg...) \
if ((dvb_usb_mxl111sf_debug & (MXL_I2C_DBG | MXL_ADV_DBG)) == \
(MXL_I2C_DBG | MXL_ADV_DBG)) \
mxl_printk(KERN_DEBUG, fmt, ##arg)
/* The following allows the mxl_fail() macro defined below to work
* in externel modules, such as mxl111sf-tuner.ko, even though
* dvb_usb_mxl111sf_debug is not defined within those modules */
#ifdef __MXL111SF_TUNER_H__
#define MXL_ADV_DEBUG_ENABLED MXL_ADV_DBG
#else
#define MXL_ADV_DEBUG_ENABLED dvb_usb_mxl111sf_debug
#endif
#define mxl_fail(ret) \
({ \
int __ret; \
__ret = (ret < 0); \
if ((__ret) && (MXL_ADV_DEBUG_ENABLED & MXL_ADV_DBG)) \
mxl_printk(KERN_ERR, "error %d on line %d", \
ret, __LINE__); \
__ret; \
})
#endif /* _DVB_USB_MXL111SF_H_ */
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment