mirror of
https://github.com/torvalds/linux.git
synced 2024-11-07 04:32:03 +00:00
[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: Michael Krufky <mkrufky@kernellabs.com> Reviewed-by: Steven Toth <stoth@kernellabs.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
f8a26f052a
commit
4c66c9205c
@ -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
|
||||
|
763
drivers/media/dvb/dvb-usb/mxl111sf-gpio.c
Normal file
763
drivers/media/dvb/dvb-usb/mxl111sf-gpio.c
Normal file
@ -0,0 +1,763 @@
|
||||
/*
|
||||
* 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:
|
||||
*/
|
56
drivers/media/dvb/dvb-usb/mxl111sf-gpio.h
Normal file
56
drivers/media/dvb/dvb-usb/mxl111sf-gpio.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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:
|
||||
*/
|
851
drivers/media/dvb/dvb-usb/mxl111sf-i2c.c
Normal file
851
drivers/media/dvb/dvb-usb/mxl111sf-i2c.c
Normal file
@ -0,0 +1,851 @@
|
||||
/*
|
||||
* 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:
|
||||
*/
|
35
drivers/media/dvb/dvb-usb/mxl111sf-i2c.h
Normal file
35
drivers/media/dvb/dvb-usb/mxl111sf-i2c.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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:
|
||||
*/
|
342
drivers/media/dvb/dvb-usb/mxl111sf-phy.c
Normal file
342
drivers/media/dvb/dvb-usb/mxl111sf-phy.c
Normal file
@ -0,0 +1,342 @@
|
||||
/*
|
||||
* 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:
|
||||
*/
|
53
drivers/media/dvb/dvb-usb/mxl111sf-phy.h
Normal file
53
drivers/media/dvb/dvb-usb/mxl111sf-phy.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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:
|
||||
*/
|
179
drivers/media/dvb/dvb-usb/mxl111sf-reg.h
Normal file
179
drivers/media/dvb/dvb-usb/mxl111sf-reg.h
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* 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:
|
||||
*/
|
476
drivers/media/dvb/dvb-usb/mxl111sf-tuner.c
Normal file
476
drivers/media/dvb/dvb-usb/mxl111sf-tuner.c
Normal file
@ -0,0 +1,476 @@
|
||||
/*
|
||||
* 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:
|
||||
*/
|
89
drivers/media/dvb/dvb-usb/mxl111sf-tuner.h
Normal file
89
drivers/media/dvb/dvb-usb/mxl111sf-tuner.h
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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:
|
||||
*/
|
||||
|
854
drivers/media/dvb/dvb-usb/mxl111sf.c
Normal file
854
drivers/media/dvb/dvb-usb/mxl111sf.c
Normal file
@ -0,0 +1,854 @@
|
||||
/*
|
||||
* 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:
|
||||
*/
|
158
drivers/media/dvb/dvb-usb/mxl111sf.h
Normal file
158
drivers/media/dvb/dvb-usb/mxl111sf.h
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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:
|
||||
*/
|
Loading…
Reference in New Issue
Block a user