Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6

* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6: (222 commits)
  V4L/DVB (13033): pt1: Don't use a deprecated DMA_BIT_MASK macro
  V4L/DVB (13029): radio-si4713: remove #include <linux/version.h>
  V4L/DVB (13027): go7007: convert printks to v4l2_info
  V4L/DVB (13026): s2250-board: Implement brightness and contrast controls
  V4L/DVB (13025): s2250-board: Fix memory leaks
  V4L/DVB (13024): go7007: Implement vidioc_g_std and vidioc_querystd
  V4L/DVB (13023): go7007: Merge struct gofh and go declarations
  V4L/DVB (13022): go7007: Fix mpeg controls
  V4L/DVB (13021): go7007: Fix whitespace and line lengths
  V4L/DVB (13020): go7007: Updates to Kconfig and Makefile
  V4L/DVB (13019): video: initial support for ADV7180
  V4L/DVB (13018): kzalloc failure ignored in au8522_probe()
  V4L/DVB (13017): gspca: kmalloc failure ignored in sd_start()
  V4L/DVB (13016): kmalloc failure ignored in lgdt3304_attach() and s921_attach()
  V4L/DVB (13015): kmalloc failure ignored in m920x_firmware_download()
  V4L/DVB (13014): Add support for Compro VideoMate E800 (DVB-T part only)
  V4L/DVB (13013): FM TX: si4713: Kconfig: Fixed two typos.
  V4L/DVB (13012): uvc: introduce missing kfree
  V4L/DVB (13011): Change tuner type of BeholdTV cards
  V4L/DVB (13009): gspca - stv06xx-hdcs: Reduce exposure range
  ...
This commit is contained in:
Linus Torvalds
2009-09-21 09:03:10 -07:00
361 changed files with 86897 additions and 4122 deletions

View File

@@ -210,7 +210,8 @@ int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len)
tda18271_i2c_gate_ctrl(fe, 0);
if (ret != 1)
tda_err("ERROR: i2c_transfer returned: %d\n", ret);
tda_err("ERROR: idx = 0x%x, len = %d, "
"i2c_transfer returned: %d\n", idx, len, ret);
return (ret == 1 ? 0 : ret);
}

View File

@@ -36,6 +36,27 @@ static LIST_HEAD(hybrid_tuner_instance_list);
/*---------------------------------------------------------------------*/
static int tda18271_toggle_output(struct dvb_frontend *fe, int standby)
{
struct tda18271_priv *priv = fe->tuner_priv;
int ret = tda18271_set_standby_mode(fe, standby ? 1 : 0,
priv->output_opt & TDA18271_OUTPUT_LT_OFF ? 1 : 0,
priv->output_opt & TDA18271_OUTPUT_XT_OFF ? 1 : 0);
if (tda_fail(ret))
goto fail;
tda_dbg("%s mode: xtal oscillator %s, slave tuner loop thru %s\n",
standby ? "standby" : "active",
priv->output_opt & TDA18271_OUTPUT_XT_OFF ? "off" : "on",
priv->output_opt & TDA18271_OUTPUT_LT_OFF ? "off" : "on");
fail:
return ret;
}
/*---------------------------------------------------------------------*/
static inline int charge_pump_source(struct dvb_frontend *fe, int force)
{
struct tda18271_priv *priv = fe->tuner_priv;
@@ -271,7 +292,7 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe,
tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt);
/* calculate temperature compensation */
rfcal_comp = dc_over_dt * (tm_current - priv->tm_rfcal);
rfcal_comp = dc_over_dt * (tm_current - priv->tm_rfcal) / 1000;
regs[R_EB14] = approx + rfcal_comp;
ret = tda18271_write_regs(fe, R_EB14, 1);
@@ -800,7 +821,7 @@ static int tda18271_init(struct dvb_frontend *fe)
mutex_lock(&priv->lock);
/* power up */
/* full power up */
ret = tda18271_set_standby_mode(fe, 0, 0, 0);
if (tda_fail(ret))
goto fail;
@@ -818,6 +839,21 @@ fail:
return ret;
}
static int tda18271_sleep(struct dvb_frontend *fe)
{
struct tda18271_priv *priv = fe->tuner_priv;
int ret;
mutex_lock(&priv->lock);
/* enter standby mode, with required output features enabled */
ret = tda18271_toggle_output(fe, 1);
mutex_unlock(&priv->lock);
return ret;
}
/* ------------------------------------------------------------------ */
static int tda18271_agc(struct dvb_frontend *fe)
@@ -827,8 +863,9 @@ static int tda18271_agc(struct dvb_frontend *fe)
switch (priv->config) {
case 0:
/* no LNA */
tda_dbg("no agc configuration provided\n");
/* no external agc configuration required */
if (tda18271_debug & DBG_ADV)
tda_dbg("no agc configuration provided\n");
break;
case 3:
/* switch with GPIO of saa713x */
@@ -1010,22 +1047,6 @@ fail:
return ret;
}
static int tda18271_sleep(struct dvb_frontend *fe)
{
struct tda18271_priv *priv = fe->tuner_priv;
int ret;
mutex_lock(&priv->lock);
/* standby mode w/ slave tuner output
* & loop thru & xtal oscillator on */
ret = tda18271_set_standby_mode(fe, 1, 0, 0);
mutex_unlock(&priv->lock);
return ret;
}
static int tda18271_release(struct dvb_frontend *fe)
{
struct tda18271_priv *priv = fe->tuner_priv;
@@ -1199,6 +1220,9 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO;
priv->role = (cfg) ? cfg->role : TDA18271_MASTER;
priv->config = (cfg) ? cfg->config : 0;
priv->small_i2c = (cfg) ? cfg->small_i2c : 0;
priv->output_opt = (cfg) ?
cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON;
/* tda18271_cal_on_startup == -1 when cal
* module option is unset */
@@ -1216,9 +1240,6 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
fe->tuner_priv = priv;
if (cfg)
priv->small_i2c = cfg->small_i2c;
if (tda_fail(tda18271_get_id(fe)))
goto fail;
@@ -1238,9 +1259,19 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
/* existing tuner instance */
fe->tuner_priv = priv;
/* allow dvb driver to override i2c gate setting */
if ((cfg) && (cfg->gate != TDA18271_GATE_ANALOG))
priv->gate = cfg->gate;
/* allow dvb driver to override configuration settings */
if (cfg) {
if (cfg->gate != TDA18271_GATE_ANALOG)
priv->gate = cfg->gate;
if (cfg->role)
priv->role = cfg->role;
if (cfg->config)
priv->config = cfg->config;
if (cfg->small_i2c)
priv->small_i2c = cfg->small_i2c;
if (cfg->output_opt)
priv->output_opt = cfg->output_opt;
}
break;
}

View File

@@ -962,10 +962,9 @@ struct tda18271_cid_target_map {
static struct tda18271_cid_target_map tda18271_cid_target[] = {
{ .rfmax = 46000, .target = 0x04, .limit = 1800 },
{ .rfmax = 52200, .target = 0x0a, .limit = 1500 },
{ .rfmax = 79100, .target = 0x01, .limit = 4000 },
{ .rfmax = 70100, .target = 0x01, .limit = 4000 },
{ .rfmax = 136800, .target = 0x18, .limit = 4000 },
{ .rfmax = 156700, .target = 0x18, .limit = 4000 },
{ .rfmax = 156700, .target = 0x18, .limit = 4000 },
{ .rfmax = 186250, .target = 0x0a, .limit = 4000 },
{ .rfmax = 230000, .target = 0x0a, .limit = 4000 },
{ .rfmax = 345000, .target = 0x18, .limit = 4000 },

View File

@@ -108,6 +108,7 @@ struct tda18271_priv {
enum tda18271_role role;
enum tda18271_i2c_gate gate;
enum tda18271_ver id;
enum tda18271_output_options output_opt;
unsigned int config; /* interface to saa713x / tda829x */
unsigned int tm_rfcal;

View File

@@ -67,6 +67,17 @@ enum tda18271_i2c_gate {
TDA18271_GATE_DIGITAL,
};
enum tda18271_output_options {
/* slave tuner output & loop thru & xtal oscillator always on */
TDA18271_OUTPUT_LT_XT_ON = 0,
/* slave tuner output loop thru off */
TDA18271_OUTPUT_LT_OFF = 1,
/* xtal oscillator off */
TDA18271_OUTPUT_XT_OFF = 2,
};
struct tda18271_config {
/* override default if freq / std settings (optional) */
struct tda18271_std_map *std_map;
@@ -77,6 +88,9 @@ struct tda18271_config {
/* use i2c gate provided by analog or digital demod */
enum tda18271_i2c_gate gate;
/* output options that can be disabled */
enum tda18271_output_options output_opt;
/* force rf tracking filter calibration on startup */
unsigned int rf_cal_on_startup:1;

View File

@@ -1320,6 +1320,23 @@ static struct tuner_params tuner_partsnic_pti_5nf05_params[] = {
},
};
/* --------- TUNER_PHILIPS_CU1216L - DVB-C NIM ------------------------- */
static struct tuner_range tuner_cu1216l_ranges[] = {
{ 16 * 160.25 /*MHz*/, 0xce, 0x01 },
{ 16 * 444.25 /*MHz*/, 0xce, 0x02 },
{ 16 * 999.99 , 0xce, 0x04 },
};
static struct tuner_params tuner_philips_cu1216l_params[] = {
{
.type = TUNER_PARAM_TYPE_DIGITAL,
.ranges = tuner_cu1216l_ranges,
.count = ARRAY_SIZE(tuner_cu1216l_ranges),
.iffreq = 16 * 36.125, /*MHz*/
},
};
/* --------------------------------------------------------------------- */
struct tunertype tuners[] = {
@@ -1778,6 +1795,16 @@ struct tunertype tuners[] = {
.params = tuner_partsnic_pti_5nf05_params,
.count = ARRAY_SIZE(tuner_partsnic_pti_5nf05_params),
},
[TUNER_PHILIPS_CU1216L] = {
.name = "Philips CU1216L",
.params = tuner_philips_cu1216l_params,
.count = ARRAY_SIZE(tuner_philips_cu1216l_params),
.stepsize = 62500,
},
[TUNER_NXP_TDA18271] = {
.name = "NXP TDA18271",
/* see tda18271-fe.c for details */
},
};
EXPORT_SYMBOL(tuners);

View File

@@ -68,6 +68,10 @@ comment "Supported FireWire (IEEE 1394) Adapters"
depends on DVB_CORE && IEEE1394
source "drivers/media/dvb/firewire/Kconfig"
comment "Supported Earthsoft PT1 Adapters"
depends on DVB_CORE && PCI && I2C
source "drivers/media/dvb/pt1/Kconfig"
comment "Supported DVB Frontends"
depends on DVB_CORE
source "drivers/media/dvb/frontends/Kconfig"

View File

@@ -2,6 +2,6 @@
# Makefile for the kernel multimedia device drivers.
#
obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dvb-usb/ pluto2/ siano/ dm1105/
obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dvb-usb/ pluto2/ siano/ dm1105/ pt1/
obj-$(CONFIG_DVB_FIREDTV) += firewire/

View File

@@ -850,6 +850,49 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe,
return 0;
}
static int dvb_frontend_clear_cache(struct dvb_frontend *fe)
{
int i;
memset(&(fe->dtv_property_cache), 0,
sizeof(struct dtv_frontend_properties));
fe->dtv_property_cache.state = DTV_CLEAR;
fe->dtv_property_cache.delivery_system = SYS_UNDEFINED;
fe->dtv_property_cache.inversion = INVERSION_AUTO;
fe->dtv_property_cache.fec_inner = FEC_AUTO;
fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_AUTO;
fe->dtv_property_cache.bandwidth_hz = BANDWIDTH_AUTO;
fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_AUTO;
fe->dtv_property_cache.hierarchy = HIERARCHY_AUTO;
fe->dtv_property_cache.symbol_rate = QAM_AUTO;
fe->dtv_property_cache.code_rate_HP = FEC_AUTO;
fe->dtv_property_cache.code_rate_LP = FEC_AUTO;
fe->dtv_property_cache.isdbt_partial_reception = -1;
fe->dtv_property_cache.isdbt_sb_mode = -1;
fe->dtv_property_cache.isdbt_sb_subchannel = -1;
fe->dtv_property_cache.isdbt_sb_segment_idx = -1;
fe->dtv_property_cache.isdbt_sb_segment_count = -1;
fe->dtv_property_cache.isdbt_layer_enabled = 0x7;
for (i = 0; i < 3; i++) {
fe->dtv_property_cache.layer[i].fec = FEC_AUTO;
fe->dtv_property_cache.layer[i].modulation = QAM_AUTO;
fe->dtv_property_cache.layer[i].interleaving = -1;
fe->dtv_property_cache.layer[i].segment_count = -1;
}
return 0;
}
#define _DTV_CMD(n, s, b) \
[n] = { \
.name = #n, \
.cmd = n, \
.set = s,\
.buffer = b \
}
static struct dtv_cmds_h dtv_cmds[] = {
[DTV_TUNE] = {
.name = "DTV_TUNE",
@@ -949,6 +992,47 @@ static struct dtv_cmds_h dtv_cmds[] = {
.cmd = DTV_TRANSMISSION_MODE,
.set = 1,
},
_DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 1, 0),
_DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 1, 0),
_DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID, 1, 0),
_DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX, 1, 0),
_DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT, 1, 0),
_DTV_CMD(DTV_ISDBT_LAYER_ENABLED, 1, 0),
_DTV_CMD(DTV_ISDBT_LAYERA_FEC, 1, 0),
_DTV_CMD(DTV_ISDBT_LAYERA_MODULATION, 1, 0),
_DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT, 1, 0),
_DTV_CMD(DTV_ISDBT_LAYERA_TIME_INTERLEAVING, 1, 0),
_DTV_CMD(DTV_ISDBT_LAYERB_FEC, 1, 0),
_DTV_CMD(DTV_ISDBT_LAYERB_MODULATION, 1, 0),
_DTV_CMD(DTV_ISDBT_LAYERB_SEGMENT_COUNT, 1, 0),
_DTV_CMD(DTV_ISDBT_LAYERB_TIME_INTERLEAVING, 1, 0),
_DTV_CMD(DTV_ISDBT_LAYERC_FEC, 1, 0),
_DTV_CMD(DTV_ISDBT_LAYERC_MODULATION, 1, 0),
_DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 1, 0),
_DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 1, 0),
_DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 0, 0),
_DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 0, 0),
_DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID, 0, 0),
_DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX, 0, 0),
_DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT, 0, 0),
_DTV_CMD(DTV_ISDBT_LAYER_ENABLED, 0, 0),
_DTV_CMD(DTV_ISDBT_LAYERA_FEC, 0, 0),
_DTV_CMD(DTV_ISDBT_LAYERA_MODULATION, 0, 0),
_DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT, 0, 0),
_DTV_CMD(DTV_ISDBT_LAYERA_TIME_INTERLEAVING, 0, 0),
_DTV_CMD(DTV_ISDBT_LAYERB_FEC, 0, 0),
_DTV_CMD(DTV_ISDBT_LAYERB_MODULATION, 0, 0),
_DTV_CMD(DTV_ISDBT_LAYERB_SEGMENT_COUNT, 0, 0),
_DTV_CMD(DTV_ISDBT_LAYERB_TIME_INTERLEAVING, 0, 0),
_DTV_CMD(DTV_ISDBT_LAYERC_FEC, 0, 0),
_DTV_CMD(DTV_ISDBT_LAYERC_MODULATION, 0, 0),
_DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 0, 0),
_DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 0, 0),
_DTV_CMD(DTV_ISDBS_TS_ID, 1, 0),
/* Get */
[DTV_DISEQC_SLAVE_REPLY] = {
.name = "DTV_DISEQC_SLAVE_REPLY",
@@ -956,6 +1040,7 @@ static struct dtv_cmds_h dtv_cmds[] = {
.set = 0,
.buffer = 1,
},
[DTV_API_VERSION] = {
.name = "DTV_API_VERSION",
.cmd = DTV_API_VERSION,
@@ -1165,14 +1250,21 @@ static void dtv_property_adv_params_sync(struct dvb_frontend *fe)
if(c->delivery_system == SYS_ISDBT) {
/* Fake out a generic DVB-T request so we pass validation in the ioctl */
p->frequency = c->frequency;
p->inversion = INVERSION_AUTO;
p->inversion = c->inversion;
p->u.ofdm.constellation = QAM_AUTO;
p->u.ofdm.code_rate_HP = FEC_AUTO;
p->u.ofdm.code_rate_LP = FEC_AUTO;
p->u.ofdm.bandwidth = BANDWIDTH_AUTO;
p->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO;
p->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO;
p->u.ofdm.hierarchy_information = HIERARCHY_AUTO;
if (c->bandwidth_hz == 8000000)
p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
else if (c->bandwidth_hz == 7000000)
p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
else if (c->bandwidth_hz == 6000000)
p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
else
p->u.ofdm.bandwidth = BANDWIDTH_AUTO;
}
}
@@ -1274,6 +1366,65 @@ static int dtv_property_process_get(struct dvb_frontend *fe,
case DTV_HIERARCHY:
tvp->u.data = fe->dtv_property_cache.hierarchy;
break;
/* ISDB-T Support here */
case DTV_ISDBT_PARTIAL_RECEPTION:
tvp->u.data = fe->dtv_property_cache.isdbt_partial_reception;
break;
case DTV_ISDBT_SOUND_BROADCASTING:
tvp->u.data = fe->dtv_property_cache.isdbt_sb_mode;
break;
case DTV_ISDBT_SB_SUBCHANNEL_ID:
tvp->u.data = fe->dtv_property_cache.isdbt_sb_subchannel;
break;
case DTV_ISDBT_SB_SEGMENT_IDX:
tvp->u.data = fe->dtv_property_cache.isdbt_sb_segment_idx;
break;
case DTV_ISDBT_SB_SEGMENT_COUNT:
tvp->u.data = fe->dtv_property_cache.isdbt_sb_segment_count;
break;
case DTV_ISDBT_LAYER_ENABLED:
tvp->u.data = fe->dtv_property_cache.isdbt_layer_enabled;
break;
case DTV_ISDBT_LAYERA_FEC:
tvp->u.data = fe->dtv_property_cache.layer[0].fec;
break;
case DTV_ISDBT_LAYERA_MODULATION:
tvp->u.data = fe->dtv_property_cache.layer[0].modulation;
break;
case DTV_ISDBT_LAYERA_SEGMENT_COUNT:
tvp->u.data = fe->dtv_property_cache.layer[0].segment_count;
break;
case DTV_ISDBT_LAYERA_TIME_INTERLEAVING:
tvp->u.data = fe->dtv_property_cache.layer[0].interleaving;
break;
case DTV_ISDBT_LAYERB_FEC:
tvp->u.data = fe->dtv_property_cache.layer[1].fec;
break;
case DTV_ISDBT_LAYERB_MODULATION:
tvp->u.data = fe->dtv_property_cache.layer[1].modulation;
break;
case DTV_ISDBT_LAYERB_SEGMENT_COUNT:
tvp->u.data = fe->dtv_property_cache.layer[1].segment_count;
break;
case DTV_ISDBT_LAYERB_TIME_INTERLEAVING:
tvp->u.data = fe->dtv_property_cache.layer[1].interleaving;
break;
case DTV_ISDBT_LAYERC_FEC:
tvp->u.data = fe->dtv_property_cache.layer[2].fec;
break;
case DTV_ISDBT_LAYERC_MODULATION:
tvp->u.data = fe->dtv_property_cache.layer[2].modulation;
break;
case DTV_ISDBT_LAYERC_SEGMENT_COUNT:
tvp->u.data = fe->dtv_property_cache.layer[2].segment_count;
break;
case DTV_ISDBT_LAYERC_TIME_INTERLEAVING:
tvp->u.data = fe->dtv_property_cache.layer[2].interleaving;
break;
case DTV_ISDBS_TS_ID:
tvp->u.data = fe->dtv_property_cache.isdbs_ts_id;
break;
default:
r = -1;
}
@@ -1302,10 +1453,8 @@ static int dtv_property_process_set(struct dvb_frontend *fe,
/* Reset a cache of data specific to the frontend here. This does
* not effect hardware.
*/
dvb_frontend_clear_cache(fe);
dprintk("%s() Flushing property cache\n", __func__);
memset(&fe->dtv_property_cache, 0, sizeof(struct dtv_frontend_properties));
fe->dtv_property_cache.state = tvp->cmd;
fe->dtv_property_cache.delivery_system = SYS_UNDEFINED;
break;
case DTV_TUNE:
/* interpret the cache of data, build either a traditional frontend
@@ -1371,6 +1520,65 @@ static int dtv_property_process_set(struct dvb_frontend *fe,
case DTV_HIERARCHY:
fe->dtv_property_cache.hierarchy = tvp->u.data;
break;
/* ISDB-T Support here */
case DTV_ISDBT_PARTIAL_RECEPTION:
fe->dtv_property_cache.isdbt_partial_reception = tvp->u.data;
break;
case DTV_ISDBT_SOUND_BROADCASTING:
fe->dtv_property_cache.isdbt_sb_mode = tvp->u.data;
break;
case DTV_ISDBT_SB_SUBCHANNEL_ID:
fe->dtv_property_cache.isdbt_sb_subchannel = tvp->u.data;
break;
case DTV_ISDBT_SB_SEGMENT_IDX:
fe->dtv_property_cache.isdbt_sb_segment_idx = tvp->u.data;
break;
case DTV_ISDBT_SB_SEGMENT_COUNT:
fe->dtv_property_cache.isdbt_sb_segment_count = tvp->u.data;
break;
case DTV_ISDBT_LAYER_ENABLED:
fe->dtv_property_cache.isdbt_layer_enabled = tvp->u.data;
break;
case DTV_ISDBT_LAYERA_FEC:
fe->dtv_property_cache.layer[0].fec = tvp->u.data;
break;
case DTV_ISDBT_LAYERA_MODULATION:
fe->dtv_property_cache.layer[0].modulation = tvp->u.data;
break;
case DTV_ISDBT_LAYERA_SEGMENT_COUNT:
fe->dtv_property_cache.layer[0].segment_count = tvp->u.data;
break;
case DTV_ISDBT_LAYERA_TIME_INTERLEAVING:
fe->dtv_property_cache.layer[0].interleaving = tvp->u.data;
break;
case DTV_ISDBT_LAYERB_FEC:
fe->dtv_property_cache.layer[1].fec = tvp->u.data;
break;
case DTV_ISDBT_LAYERB_MODULATION:
fe->dtv_property_cache.layer[1].modulation = tvp->u.data;
break;
case DTV_ISDBT_LAYERB_SEGMENT_COUNT:
fe->dtv_property_cache.layer[1].segment_count = tvp->u.data;
break;
case DTV_ISDBT_LAYERB_TIME_INTERLEAVING:
fe->dtv_property_cache.layer[1].interleaving = tvp->u.data;
break;
case DTV_ISDBT_LAYERC_FEC:
fe->dtv_property_cache.layer[2].fec = tvp->u.data;
break;
case DTV_ISDBT_LAYERC_MODULATION:
fe->dtv_property_cache.layer[2].modulation = tvp->u.data;
break;
case DTV_ISDBT_LAYERC_SEGMENT_COUNT:
fe->dtv_property_cache.layer[2].segment_count = tvp->u.data;
break;
case DTV_ISDBT_LAYERC_TIME_INTERLEAVING:
fe->dtv_property_cache.layer[2].interleaving = tvp->u.data;
break;
case DTV_ISDBS_TS_ID:
fe->dtv_property_cache.isdbs_ts_id = tvp->u.data;
break;
default:
r = -1;
}

View File

@@ -341,6 +341,23 @@ struct dtv_frontend_properties {
fe_rolloff_t rolloff;
fe_delivery_system_t delivery_system;
/* ISDB-T specifics */
u8 isdbt_partial_reception;
u8 isdbt_sb_mode;
u8 isdbt_sb_subchannel;
u32 isdbt_sb_segment_idx;
u32 isdbt_sb_segment_count;
u8 isdbt_layer_enabled;
struct {
u8 segment_count;
fe_code_rate_t fec;
fe_modulation_t modulation;
u8 interleaving;
} layer[3];
/* ISDB-T specifics */
u32 isdbs_ts_id;
};
struct dvb_frontend {

View File

@@ -71,6 +71,7 @@ config DVB_USB_DIB0700
depends on DVB_USB
select DVB_DIB7000P if !DVB_FE_CUSTOMISE
select DVB_DIB7000M if !DVB_FE_CUSTOMISE
select DVB_DIB8000 if !DVB_FE_CUSTOMISE
select DVB_DIB3000MC if !DVB_FE_CUSTOMISE
select DVB_S5H1411 if !DVB_FE_CUSTOMISE
select DVB_LGDT3305 if !DVB_FE_CUSTOMISE
@@ -87,7 +88,7 @@ config DVB_USB_DIB0700
Avermedia and other big and small companies.
For an up-to-date list of devices supported by this driver, have a look
on the Linux-DVB Wiki at www.linuxtv.org.
on the LinuxTV Wiki at www.linuxtv.org.
Say Y if you own such a device and want to use it. You should build it as
a module.
@@ -315,3 +316,9 @@ config DVB_USB_CE6230
select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
help
Say Y here to support the Intel CE6230 DVB-T USB2.0 receiver
config DVB_USB_FRIIO
tristate "Friio ISDB-T USB2.0 Receiver support"
depends on DVB_USB
help
Say Y here to support the Japanese DTV receiver Friio.

View File

@@ -79,6 +79,9 @@ obj-$(CONFIG_DVB_USB_CINERGY_T2) += dvb-usb-cinergyT2.o
dvb-usb-ce6230-objs = ce6230.o
obj-$(CONFIG_DVB_USB_CE6230) += dvb-usb-ce6230.o
dvb-usb-friio-objs = friio.o friio-fe.o
obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
# due to tuner-xc3028
EXTRA_CFLAGS += -Idrivers/media/common/tuners

View File

@@ -61,10 +61,13 @@ static struct af9013_config af9015_af9013_config[] = {
static int af9015_rw_udev(struct usb_device *udev, struct req_t *req)
{
#define BUF_LEN 63
#define REQ_HDR_LEN 8 /* send header size */
#define ACK_HDR_LEN 2 /* rece header size */
int act_len, ret;
u8 buf[64];
u8 buf[BUF_LEN];
u8 write = 1;
u8 msg_len = 8;
u8 msg_len = REQ_HDR_LEN;
static u8 seq; /* packet sequence number */
if (mutex_lock_interruptible(&af9015_usb_mutex) < 0)
@@ -94,7 +97,7 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req)
break;
case WRITE_MEMORY:
if (((req->addr & 0xff00) == 0xff00) ||
((req->addr & 0xae00) == 0xae00))
((req->addr & 0xff00) == 0xae00))
buf[0] = WRITE_VIRTUAL_MEMORY;
case WRITE_VIRTUAL_MEMORY:
case COPY_FIRMWARE:
@@ -107,17 +110,26 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req)
goto error_unlock;
}
/* buffer overflow check */
if ((write && (req->data_len > BUF_LEN - REQ_HDR_LEN)) ||
(!write && (req->data_len > BUF_LEN - ACK_HDR_LEN))) {
err("too much data; cmd:%d len:%d", req->cmd, req->data_len);
ret = -EINVAL;
goto error_unlock;
}
/* write requested */
if (write) {
memcpy(&buf[8], req->data, req->data_len);
memcpy(&buf[REQ_HDR_LEN], req->data, req->data_len);
msg_len += req->data_len;
}
deb_xfer(">>> ");
debug_dump(buf, msg_len, deb_xfer);
/* send req */
ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len,
&act_len, AF9015_USB_TIMEOUT);
&act_len, AF9015_USB_TIMEOUT);
if (ret)
err("bulk message failed:%d (%d/%d)", ret, msg_len, act_len);
else
@@ -130,10 +142,14 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req)
if (req->cmd == DOWNLOAD_FIRMWARE || req->cmd == RECONNECT_USB)
goto exit_unlock;
/* receive ack and data if read req */
msg_len = 1 + 1 + req->data_len; /* seq + status + data len */
/* write receives seq + status = 2 bytes
read receives seq + status + data = 2 + N bytes */
msg_len = ACK_HDR_LEN;
if (!write)
msg_len += req->data_len;
ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len,
&act_len, AF9015_USB_TIMEOUT);
&act_len, AF9015_USB_TIMEOUT);
if (ret) {
err("recv bulk message failed:%d", ret);
ret = -1;
@@ -159,7 +175,7 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req)
/* read request, copy returned data to return buf */
if (!write)
memcpy(req->data, &buf[2], req->data_len);
memcpy(req->data, &buf[ACK_HDR_LEN], req->data_len);
error_unlock:
exit_unlock:
@@ -369,12 +385,14 @@ static int af9015_init_endpoint(struct dvb_usb_device *d)
u8 packet_size;
deb_info("%s: USB speed:%d\n", __func__, d->udev->speed);
/* Windows driver uses packet count 21 for USB1.1 and 348 for USB2.0.
We use smaller - about 1/4 from the original, 5 and 87. */
#define TS_PACKET_SIZE 188
#define TS_USB20_PACKET_COUNT 348
#define TS_USB20_PACKET_COUNT 87
#define TS_USB20_FRAME_SIZE (TS_PACKET_SIZE*TS_USB20_PACKET_COUNT)
#define TS_USB11_PACKET_COUNT 21
#define TS_USB11_PACKET_COUNT 5
#define TS_USB11_FRAME_SIZE (TS_PACKET_SIZE*TS_USB11_PACKET_COUNT)
#define TS_USB20_MAX_PACKET_SIZE 512
@@ -868,13 +886,13 @@ static int af9015_read_config(struct usb_device *udev)
/* USB1.1 set smaller buffersize and disable 2nd adapter */
if (udev->speed == USB_SPEED_FULL) {
af9015_properties[i].adapter[0].stream.u.bulk.buffersize
= TS_USB11_MAX_PACKET_SIZE;
= TS_USB11_FRAME_SIZE;
/* disable 2nd adapter because we don't have
PID-filters */
af9015_config.dual_mode = 0;
} else {
af9015_properties[i].adapter[0].stream.u.bulk.buffersize
= TS_USB20_MAX_PACKET_SIZE;
= TS_USB20_FRAME_SIZE;
}
}
@@ -1310,7 +1328,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.u = {
.bulk = {
.buffersize =
TS_USB20_MAX_PACKET_SIZE,
TS_USB20_FRAME_SIZE,
}
}
},
@@ -1416,7 +1434,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.u = {
.bulk = {
.buffersize =
TS_USB20_MAX_PACKET_SIZE,
TS_USB20_FRAME_SIZE,
}
}
},
@@ -1522,7 +1540,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.u = {
.bulk = {
.buffersize =
TS_USB20_MAX_PACKET_SIZE,
TS_USB20_FRAME_SIZE,
}
}
},

View File

@@ -203,11 +203,11 @@ static struct i2c_algorithm anysee_i2c_algo = {
static int anysee_mt352_demod_init(struct dvb_frontend *fe)
{
static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x28 };
static u8 reset [] = { RESET, 0x80 };
static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0x20 };
static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 };
static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x28 };
static u8 reset[] = { RESET, 0x80 };
static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 };
static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0x20 };
static u8 gpp_ctl_cfg[] = { GPP_CTL, 0x33 };
static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
mt352_write(fe, clock_config, sizeof(clock_config));
@@ -485,7 +485,7 @@ static int anysee_probe(struct usb_interface *intf,
return ret;
}
static struct usb_device_id anysee_table [] = {
static struct usb_device_id anysee_table[] = {
{ USB_DEVICE(USB_VID_CYPRESS, USB_PID_ANYSEE) },
{ USB_DEVICE(USB_VID_AMT, USB_PID_ANYSEE) },
{ } /* Terminating entry */
@@ -511,7 +511,7 @@ static struct dvb_usb_device_properties anysee_properties = {
.endpoint = 0x82,
.u = {
.bulk = {
.buffersize = 512,
.buffersize = (16*512),
}
}
},

View File

@@ -274,7 +274,7 @@ static struct dvb_usb_device_properties ce6230_properties = {
.endpoint = 0x82,
.u = {
.bulk = {
.buffersize = 512,
.buffersize = (16*512),
}
}
},

View File

@@ -4,13 +4,14 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
* Copyright (C) 2005-7 DiBcom, SA
* Copyright (C) 2005-9 DiBcom, SA et al
*/
#include "dib0700.h"
#include "dib3000mc.h"
#include "dib7000m.h"
#include "dib7000p.h"
#include "dib8000.h"
#include "mt2060.h"
#include "mt2266.h"
#include "tuner-xc2028.h"
@@ -1098,11 +1099,13 @@ static struct dibx000_agc_config dib7070_agc_config = {
static int dib7070_tuner_reset(struct dvb_frontend *fe, int onoff)
{
deb_info("reset: %d", onoff);
return dib7000p_set_gpio(fe, 8, 0, !onoff);
}
static int dib7070_tuner_sleep(struct dvb_frontend *fe, int onoff)
{
deb_info("sleep: %d", onoff);
return dib7000p_set_gpio(fe, 9, 0, onoff);
}
@@ -1112,16 +1115,26 @@ static struct dib0070_config dib7070p_dib0070_config[2] = {
.reset = dib7070_tuner_reset,
.sleep = dib7070_tuner_sleep,
.clock_khz = 12000,
.clock_pad_drive = 4
.clock_pad_drive = 4,
.charge_pump = 2,
}, {
.i2c_address = DEFAULT_DIB0070_I2C_ADDRESS,
.reset = dib7070_tuner_reset,
.sleep = dib7070_tuner_sleep,
.clock_khz = 12000,
.charge_pump = 2,
}
};
static struct dib0070_config dib7770p_dib0070_config = {
.i2c_address = DEFAULT_DIB0070_I2C_ADDRESS,
.reset = dib7070_tuner_reset,
.sleep = dib7070_tuner_sleep,
.clock_khz = 12000,
.clock_pad_drive = 0,
.flip_chip = 1,
};
static int dib7070_set_param_override(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
@@ -1139,6 +1152,45 @@ static int dib7070_set_param_override(struct dvb_frontend *fe, struct dvb_fronte
return state->set_param_save(fe, fep);
}
static int dib7770_set_param_override(struct dvb_frontend *fe,
struct dvb_frontend_parameters *fep)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
struct dib0700_adapter_state *state = adap->priv;
u16 offset;
u8 band = BAND_OF_FREQUENCY(fep->frequency/1000);
switch (band) {
case BAND_VHF:
dib7000p_set_gpio(fe, 0, 0, 1);
offset = 850;
break;
case BAND_UHF:
default:
dib7000p_set_gpio(fe, 0, 0, 0);
offset = 250;
break;
}
deb_info("WBD for DiB7000P: %d\n", offset + dib0070_wbd_offset(fe));
dib7000p_set_wbd_ref(fe, offset + dib0070_wbd_offset(fe));
return state->set_param_save(fe, fep);
}
static int dib7770p_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe,
DIBX000_I2C_INTERFACE_TUNER, 1);
if (dvb_attach(dib0070_attach, adap->fe, tun_i2c,
&dib7770p_dib0070_config) == NULL)
return -ENODEV;
st->set_param_save = adap->fe->ops.tuner_ops.set_params;
adap->fe->ops.tuner_ops.set_params = dib7770_set_param_override;
return 0;
}
static int dib7070p_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
@@ -1217,6 +1269,306 @@ static int stk7070p_frontend_attach(struct dvb_usb_adapter *adap)
return adap->fe == NULL ? -ENODEV : 0;
}
/* DIB807x generic */
static struct dibx000_agc_config dib807x_agc_config[2] = {
{
BAND_VHF,
/* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0,
* P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0,
* P_agc_inv_pwm2=0,P_agc_inh_dc_rv_est=0,
* P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5,
* P_agc_write=0 */
(0 << 15) | (0 << 14) | (7 << 11) | (0 << 10) | (0 << 9) |
(0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) |
(0 << 0), /* setup*/
600, /* inv_gain*/
10, /* time_stabiliz*/
0, /* alpha_level*/
118, /* thlock*/
0, /* wbd_inv*/
3530, /* wbd_ref*/
1, /* wbd_sel*/
5, /* wbd_alpha*/
65535, /* agc1_max*/
0, /* agc1_min*/
65535, /* agc2_max*/
0, /* agc2_min*/
0, /* agc1_pt1*/
40, /* agc1_pt2*/
183, /* agc1_pt3*/
206, /* agc1_slope1*/
255, /* agc1_slope2*/
72, /* agc2_pt1*/
152, /* agc2_pt2*/
88, /* agc2_slope1*/
90, /* agc2_slope2*/
17, /* alpha_mant*/
27, /* alpha_exp*/
23, /* beta_mant*/
51, /* beta_exp*/
0, /* perform_agc_softsplit*/
}, {
BAND_UHF,
/* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0,
* P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0,
* P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0,
* P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5,
* P_agc_write=0 */
(0 << 15) | (0 << 14) | (1 << 11) | (0 << 10) | (0 << 9) |
(0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) |
(0 << 0), /* setup */
600, /* inv_gain*/
10, /* time_stabiliz*/
0, /* alpha_level*/
118, /* thlock*/
0, /* wbd_inv*/
3530, /* wbd_ref*/
1, /* wbd_sel*/
5, /* wbd_alpha*/
65535, /* agc1_max*/
0, /* agc1_min*/
65535, /* agc2_max*/
0, /* agc2_min*/
0, /* agc1_pt1*/
40, /* agc1_pt2*/
183, /* agc1_pt3*/
206, /* agc1_slope1*/
255, /* agc1_slope2*/
72, /* agc2_pt1*/
152, /* agc2_pt2*/
88, /* agc2_slope1*/
90, /* agc2_slope2*/
17, /* alpha_mant*/
27, /* alpha_exp*/
23, /* beta_mant*/
51, /* beta_exp*/
0, /* perform_agc_softsplit*/
}
};
static struct dibx000_bandwidth_config dib807x_bw_config_12_mhz = {
60000, 15000, /* internal, sampling*/
1, 20, 3, 1, 0, /* pll_cfg: prediv, ratio, range, reset, bypass*/
0, 0, 1, 1, 2, /* misc: refdiv, bypclk_div, IO_CLK_en_core,
ADClkSrc, modulo */
(3 << 14) | (1 << 12) | (599 << 0), /* sad_cfg: refsel, sel, freq_15k*/
(0 << 25) | 0, /* ifreq = 0.000000 MHz*/
18179755, /* timf*/
12000000, /* xtal_hz*/
};
static struct dib8000_config dib807x_dib8000_config[2] = {
{
.output_mpeg2_in_188_bytes = 1,
.agc_config_count = 2,
.agc = dib807x_agc_config,
.pll = &dib807x_bw_config_12_mhz,
.tuner_is_baseband = 1,
.gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS,
.gpio_val = DIB8000_GPIO_DEFAULT_VALUES,
.gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS,
.hostbus_diversity = 1,
.div_cfg = 1,
.agc_control = &dib0070_ctrl_agc_filter,
.output_mode = OUTMODE_MPEG2_FIFO,
.drives = 0x2d98,
}, {
.output_mpeg2_in_188_bytes = 1,
.agc_config_count = 2,
.agc = dib807x_agc_config,
.pll = &dib807x_bw_config_12_mhz,
.tuner_is_baseband = 1,
.gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS,
.gpio_val = DIB8000_GPIO_DEFAULT_VALUES,
.gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS,
.hostbus_diversity = 1,
.agc_control = &dib0070_ctrl_agc_filter,
.output_mode = OUTMODE_MPEG2_FIFO,
.drives = 0x2d98,
}
};
static int dib807x_tuner_reset(struct dvb_frontend *fe, int onoff)
{
return dib8000_set_gpio(fe, 5, 0, !onoff);
}
static int dib807x_tuner_sleep(struct dvb_frontend *fe, int onoff)
{
return dib8000_set_gpio(fe, 0, 0, onoff);
}
static const struct dib0070_wbd_gain_cfg dib8070_wbd_gain_cfg[] = {
{ 240, 7},
{ 0xffff, 6},
};
static struct dib0070_config dib807x_dib0070_config[2] = {
{
.i2c_address = DEFAULT_DIB0070_I2C_ADDRESS,
.reset = dib807x_tuner_reset,
.sleep = dib807x_tuner_sleep,
.clock_khz = 12000,
.clock_pad_drive = 4,
.vga_filter = 1,
.force_crystal_mode = 1,
.enable_third_order_filter = 1,
.charge_pump = 0,
.wbd_gain = dib8070_wbd_gain_cfg,
.osc_buffer_state = 0,
.freq_offset_khz_uhf = -100,
.freq_offset_khz_vhf = -100,
}, {
.i2c_address = DEFAULT_DIB0070_I2C_ADDRESS,
.reset = dib807x_tuner_reset,
.sleep = dib807x_tuner_sleep,
.clock_khz = 12000,
.clock_pad_drive = 2,
.vga_filter = 1,
.force_crystal_mode = 1,
.enable_third_order_filter = 1,
.charge_pump = 0,
.wbd_gain = dib8070_wbd_gain_cfg,
.osc_buffer_state = 0,
.freq_offset_khz_uhf = -25,
.freq_offset_khz_vhf = -25,
}
};
static int dib807x_set_param_override(struct dvb_frontend *fe,
struct dvb_frontend_parameters *fep)
{
struct dvb_usb_adapter *adap = fe->dvb->priv;
struct dib0700_adapter_state *state = adap->priv;
u16 offset = dib0070_wbd_offset(fe);
u8 band = BAND_OF_FREQUENCY(fep->frequency/1000);
switch (band) {
case BAND_VHF:
offset += 750;
break;
case BAND_UHF: /* fall-thru wanted */
default:
offset += 250; break;
}
deb_info("WBD for DiB8000: %d\n", offset);
dib8000_set_wbd_ref(fe, offset);
return state->set_param_save(fe, fep);
}
static int dib807x_tuner_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_adapter_state *st = adap->priv;
struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe,
DIBX000_I2C_INTERFACE_TUNER, 1);
if (adap->id == 0) {
if (dvb_attach(dib0070_attach, adap->fe, tun_i2c,
&dib807x_dib0070_config[0]) == NULL)
return -ENODEV;
} else {
if (dvb_attach(dib0070_attach, adap->fe, tun_i2c,
&dib807x_dib0070_config[1]) == NULL)
return -ENODEV;
}
st->set_param_save = adap->fe->ops.tuner_ops.set_params;
adap->fe->ops.tuner_ops.set_params = dib807x_set_param_override;
return 0;
}
/* STK807x */
static int stk807x_frontend_attach(struct dvb_usb_adapter *adap)
{
dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1);
msleep(10);
dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1);
dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1);
dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1);
dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0);
dib0700_ctrl_clock(adap->dev, 72, 1);
msleep(10);
dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
msleep(10);
dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18,
0x80);
adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80,
&dib807x_dib8000_config[0]);
return adap->fe == NULL ? -ENODEV : 0;
}
/* STK807xPVR */
static int stk807xpvr_frontend_attach0(struct dvb_usb_adapter *adap)
{
dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0);
msleep(30);
dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1);
msleep(500);
dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1);
dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1);
dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1);
dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0);
dib0700_ctrl_clock(adap->dev, 72, 1);
msleep(10);
dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
msleep(10);
dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
/* initialize IC 0 */
dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x12, 0x80);
adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80,
&dib807x_dib8000_config[0]);
return adap->fe == NULL ? -ENODEV : 0;
}
static int stk807xpvr_frontend_attach1(struct dvb_usb_adapter *adap)
{
/* initialize IC 1 */
dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x22, 0x82);
adap->fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82,
&dib807x_dib8000_config[1]);
return adap->fe == NULL ? -ENODEV : 0;
}
/* STK7070PD */
static struct dib7000p_config stk7070pd_dib7000p_config[2] = {
{
@@ -1500,7 +1852,15 @@ struct usb_device_id dib0700_usb_id_table[] = {
{ USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_T3) },
{ USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_T5) },
{ USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700D) },
{ USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700D_2) },
/* 55 */{ USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700D_2) },
{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73A) },
{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73ESE) },
{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV282E) },
{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7770P) },
/* 60 */{ USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_XXS_2) },
{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XPVR) },
{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XP) },
{ USB_DEVICE(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD) },
{ 0 } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -1565,7 +1925,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{ NULL },
},
{ "Leadtek Winfast DTV Dongle (STK7700P based)",
{ &dib0700_usb_id_table[8], &dib0700_usb_id_table[34] },
{ &dib0700_usb_id_table[8] },
{ NULL },
},
{ "AVerMedia AVerTV DVB-T Express",
@@ -1762,6 +2122,41 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys),
.rc_query = dib0700_rc_query
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
.num_adapters = 1,
.adapter = {
{
.frontend_attach = stk7070p_frontend_attach,
.tuner_attach = dib7070p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
.size_of_priv = sizeof(struct dib0700_adapter_state),
},
},
.num_device_descs = 3,
.devices = {
{ "Pinnacle PCTV 73A",
{ &dib0700_usb_id_table[56], NULL },
{ NULL },
},
{ "Pinnacle PCTV 73e SE",
{ &dib0700_usb_id_table[57], NULL },
{ NULL },
},
{ "Pinnacle PCTV 282e",
{ &dib0700_usb_id_table[58], NULL },
{ NULL },
},
},
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = dib0700_rc_keys,
.rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys),
.rc_query = dib0700_rc_query
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
.num_adapters = 2,
@@ -1927,6 +2322,102 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{ NULL },
},
},
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
.num_adapters = 1,
.adapter = {
{
.frontend_attach = stk7070p_frontend_attach,
.tuner_attach = dib7770p_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
},
.num_device_descs = 2,
.devices = {
{ "DiBcom STK7770P reference design",
{ &dib0700_usb_id_table[59], NULL },
{ NULL },
},
{ "Terratec Cinergy T USB XXS (HD)",
{ &dib0700_usb_id_table[34], &dib0700_usb_id_table[60] },
{ NULL },
},
},
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = dib0700_rc_keys,
.rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys),
.rc_query = dib0700_rc_query
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
.num_adapters = 1,
.adapter = {
{
.frontend_attach = stk807x_frontend_attach,
.tuner_attach = dib807x_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
},
.num_device_descs = 2,
.devices = {
{ "DiBcom STK807xP reference design",
{ &dib0700_usb_id_table[62], NULL },
{ NULL },
},
{ "Prolink Pixelview SBTVD",
{ &dib0700_usb_id_table[63], NULL },
{ NULL },
},
},
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = dib0700_rc_keys,
.rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys),
.rc_query = dib0700_rc_query
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
.num_adapters = 2,
.adapter = {
{
.frontend_attach = stk807xpvr_frontend_attach0,
.tuner_attach = dib807x_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
{
.frontend_attach = stk807xpvr_frontend_attach1,
.tuner_attach = dib807x_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x03),
.size_of_priv =
sizeof(struct dib0700_adapter_state),
},
},
.num_device_descs = 1,
.devices = {
{ "DiBcom STK807xPVR reference design",
{ &dib0700_usb_id_table[61], NULL },
{ NULL },
},
},
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = dib0700_rc_keys,
.rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys),
.rc_query = dib0700_rc_query
},
};

View File

@@ -46,6 +46,7 @@
#define USB_VID_MSI_2 0x1462
#define USB_VID_OPERA1 0x695c
#define USB_VID_PINNACLE 0x2304
#define USB_VID_PIXELVIEW 0x1554
#define USB_VID_TECHNOTREND 0x0b48
#define USB_VID_TERRATEC 0x0ccd
#define USB_VID_TELESTAR 0x10b9
@@ -59,6 +60,7 @@
#define USB_VID_YUAN 0x1164
#define USB_VID_XTENSIONS 0x1ae7
#define USB_VID_HUMAX_COEX 0x10b9
#define USB_VID_774 0x7a69
/* Product IDs */
#define USB_PID_ADSTECH_USB2_COLD 0xa333
@@ -95,7 +97,10 @@
#define USB_PID_DIBCOM_STK7700_U7000 0x7001
#define USB_PID_DIBCOM_STK7070P 0x1ebc
#define USB_PID_DIBCOM_STK7070PD 0x1ebe
#define USB_PID_DIBCOM_STK807XP 0x1f90
#define USB_PID_DIBCOM_STK807XPVR 0x1f98
#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131
#define USB_PID_DIBCOM_STK7770P 0x1e80
#define USB_PID_DPOSH_M9206_COLD 0x9206
#define USB_PID_DPOSH_M9206_WARM 0xa090
#define USB_PID_UNIWILL_STK7700P 0x6003
@@ -184,6 +189,7 @@
#define USB_PID_TERRATEC_CINERGY_HT_EXPRESS 0x0060
#define USB_PID_TERRATEC_CINERGY_T_EXPRESS 0x0062
#define USB_PID_TERRATEC_CINERGY_T_XXS 0x0078
#define USB_PID_TERRATEC_CINERGY_T_XXS_2 0x00ab
#define USB_PID_TERRATEC_T3 0x10a0
#define USB_PID_TERRATEC_T5 0x10a1
#define USB_PID_PINNACLE_EXPRESSCARD_320CX 0x022e
@@ -195,6 +201,10 @@
#define USB_PID_PINNACLE_PCTV73E 0x0237
#define USB_PID_PINNACLE_PCTV801E 0x023a
#define USB_PID_PINNACLE_PCTV801E_SE 0x023b
#define USB_PID_PINNACLE_PCTV73A 0x0243
#define USB_PID_PINNACLE_PCTV73ESE 0x0245
#define USB_PID_PINNACLE_PCTV282E 0x0248
#define USB_PID_PIXELVIEW_SBTVD 0x5010
#define USB_PID_PCTV_200E 0x020e
#define USB_PID_PCTV_400E 0x020f
#define USB_PID_PCTV_450E 0x0222
@@ -265,5 +275,6 @@
#define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020
#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000
#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM 0x5001
#define USB_PID_FRIIO_WHITE 0x0001
#endif

View File

@@ -0,0 +1,483 @@
/* DVB USB compliant Linux driver for the Friio USB2.0 ISDB-T receiver.
*
* Copyright (C) 2009 Akihiro Tsukada <tskd2@yahoo.co.jp>
*
* This module is based off the the gl861 and vp702x modules.
*
* 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/init.h>
#include <linux/string.h>
#include <linux/slab.h>
#include "friio.h"
struct jdvbt90502_state {
struct i2c_adapter *i2c;
struct dvb_frontend frontend;
struct jdvbt90502_config config;
};
/* NOTE: TC90502 has 16bit register-address? */
/* register 0x0100 is used for reading PLL status, so reg is u16 here */
static int jdvbt90502_reg_read(struct jdvbt90502_state *state,
const u16 reg, u8 *buf, const size_t count)
{
int ret;
u8 wbuf[3];
struct i2c_msg msg[2];
wbuf[0] = reg & 0xFF;
wbuf[1] = 0;
wbuf[2] = reg >> 8;
msg[0].addr = state->config.demod_address;
msg[0].flags = 0;
msg[0].buf = wbuf;
msg[0].len = sizeof(wbuf);
msg[1].addr = msg[0].addr;
msg[1].flags = I2C_M_RD;
msg[1].buf = buf;
msg[1].len = count;
ret = i2c_transfer(state->i2c, msg, 2);
if (ret != 2) {
deb_fe(" reg read failed.\n");
return -EREMOTEIO;
}
return 0;
}
/* currently 16bit register-address is not used, so reg is u8 here */
static int jdvbt90502_single_reg_write(struct jdvbt90502_state *state,
const u8 reg, const u8 val)
{
struct i2c_msg msg;
u8 wbuf[2];
wbuf[0] = reg;
wbuf[1] = val;
msg.addr = state->config.demod_address;
msg.flags = 0;
msg.buf = wbuf;
msg.len = sizeof(wbuf);
if (i2c_transfer(state->i2c, &msg, 1) != 1) {
deb_fe(" reg write failed.");
return -EREMOTEIO;
}
return 0;
}
static int _jdvbt90502_write(struct dvb_frontend *fe, u8 *buf, int len)
{
struct jdvbt90502_state *state = fe->demodulator_priv;
int err, i;
for (i = 0; i < len - 1; i++) {
err = jdvbt90502_single_reg_write(state,
buf[0] + i, buf[i + 1]);
if (err)
return err;
}
return 0;
}
/* read pll status byte via the demodulator's I2C register */
/* note: Win box reads it by 8B block at the I2C addr 0x30 from reg:0x80 */
static int jdvbt90502_pll_read(struct jdvbt90502_state *state, u8 *result)
{
int ret;
/* +1 for reading */
u8 pll_addr_byte = (state->config.pll_address << 1) + 1;
*result = 0;
ret = jdvbt90502_single_reg_write(state, JDVBT90502_2ND_I2C_REG,
pll_addr_byte);
if (ret)
goto error;
ret = jdvbt90502_reg_read(state, 0x0100, result, 1);
if (ret)
goto error;
deb_fe("PLL read val:%02x\n", *result);
return 0;
error:
deb_fe("%s:ret == %d\n", __func__, ret);
return -EREMOTEIO;
}
/* set pll frequency via the demodulator's I2C register */
static int jdvbt90502_pll_set_freq(struct jdvbt90502_state *state, u32 freq)
{
int ret;
int retry;
u8 res1;
u8 res2[9];
u8 pll_freq_cmd[PLL_CMD_LEN];
u8 pll_agc_cmd[PLL_CMD_LEN];
struct i2c_msg msg[2];
u32 f;
deb_fe("%s: freq=%d, step=%d\n", __func__, freq,
state->frontend.ops.info.frequency_stepsize);
/* freq -> oscilator frequency conversion. */
/* freq: 473,000,000 + n*6,000,000 (no 1/7MHz shift to center freq) */
/* add 400[1/7 MHZ] = 57.142857MHz. 57MHz for the IF, */
/* 1/7MHz for center freq shift */
f = freq / state->frontend.ops.info.frequency_stepsize;
f += 400;
pll_freq_cmd[DEMOD_REDIRECT_REG] = JDVBT90502_2ND_I2C_REG; /* 0xFE */
pll_freq_cmd[ADDRESS_BYTE] = state->config.pll_address << 1;
pll_freq_cmd[DIVIDER_BYTE1] = (f >> 8) & 0x7F;
pll_freq_cmd[DIVIDER_BYTE2] = f & 0xFF;
pll_freq_cmd[CONTROL_BYTE] = 0xB2; /* ref.divider:28, 4MHz/28=1/7MHz */
pll_freq_cmd[BANDSWITCH_BYTE] = 0x08; /* UHF band */
msg[0].addr = state->config.demod_address;
msg[0].flags = 0;
msg[0].buf = pll_freq_cmd;
msg[0].len = sizeof(pll_freq_cmd);
ret = i2c_transfer(state->i2c, &msg[0], 1);
if (ret != 1)
goto error;
udelay(50);
pll_agc_cmd[DEMOD_REDIRECT_REG] = pll_freq_cmd[DEMOD_REDIRECT_REG];
pll_agc_cmd[ADDRESS_BYTE] = pll_freq_cmd[ADDRESS_BYTE];
pll_agc_cmd[DIVIDER_BYTE1] = pll_freq_cmd[DIVIDER_BYTE1];
pll_agc_cmd[DIVIDER_BYTE2] = pll_freq_cmd[DIVIDER_BYTE2];
pll_agc_cmd[CONTROL_BYTE] = 0x9A; /* AGC_CTRL instead of BANDSWITCH */
pll_agc_cmd[AGC_CTRL_BYTE] = 0x50;
/* AGC Time Constant 2s, AGC take-over point:103dBuV(lowest) */
msg[1].addr = msg[0].addr;
msg[1].flags = 0;
msg[1].buf = pll_agc_cmd;
msg[1].len = sizeof(pll_agc_cmd);
ret = i2c_transfer(state->i2c, &msg[1], 1);
if (ret != 1)
goto error;
/* I don't know what these cmds are for, */
/* but the USB log on a windows box contains them */
ret = jdvbt90502_single_reg_write(state, 0x01, 0x40);
ret |= jdvbt90502_single_reg_write(state, 0x01, 0x00);
if (ret)
goto error;
udelay(100);
/* wait for the demod to be ready? */
#define RETRY_COUNT 5
for (retry = 0; retry < RETRY_COUNT; retry++) {
ret = jdvbt90502_reg_read(state, 0x0096, &res1, 1);
if (ret)
goto error;
/* if (res1 != 0x00) goto error; */
ret = jdvbt90502_reg_read(state, 0x00B0, res2, sizeof(res2));
if (ret)
goto error;
if (res2[0] >= 0xA7)
break;
msleep(100);
}
if (retry >= RETRY_COUNT) {
deb_fe("%s: FE does not get ready after freq setting.\n",
__func__);
return -EREMOTEIO;
}
return 0;
error:
deb_fe("%s:ret == %d\n", __func__, ret);
return -EREMOTEIO;
}
static int jdvbt90502_read_status(struct dvb_frontend *fe, fe_status_t *state)
{
u8 result;
int ret;
*state = FE_HAS_SIGNAL;
ret = jdvbt90502_pll_read(fe->demodulator_priv, &result);
if (ret) {
deb_fe("%s:ret == %d\n", __func__, ret);
return -EREMOTEIO;
}
*state = FE_HAS_SIGNAL
| FE_HAS_CARRIER
| FE_HAS_VITERBI
| FE_HAS_SYNC;
if (result & PLL_STATUS_LOCKED)
*state |= FE_HAS_LOCK;
return 0;
}
static int jdvbt90502_read_ber(struct dvb_frontend *fe, u32 *ber)
{
*ber = 0;
return 0;
}
static int jdvbt90502_read_signal_strength(struct dvb_frontend *fe,
u16 *strength)
{
int ret;
u8 rbuf[37];
*strength = 0;
/* status register (incl. signal strength) : 0x89 */
/* TODO: read just the necessary registers [0x8B..0x8D]? */
ret = jdvbt90502_reg_read(fe->demodulator_priv, 0x0089,
rbuf, sizeof(rbuf));
if (ret) {
deb_fe("%s:ret == %d\n", __func__, ret);
return -EREMOTEIO;
}
/* signal_strength: rbuf[2-4] (24bit BE), use lower 16bit for now. */
*strength = (rbuf[3] << 8) + rbuf[4];
if (rbuf[2])
*strength = 0xffff;
return 0;
}
static int jdvbt90502_read_snr(struct dvb_frontend *fe, u16 *snr)
{
*snr = 0x0101;
return 0;
}
static int jdvbt90502_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
{
*ucblocks = 0;
return 0;
}
static int jdvbt90502_get_tune_settings(struct dvb_frontend *fe,
struct dvb_frontend_tune_settings *fs)
{
fs->min_delay_ms = 500;
fs->step_size = 0;
fs->max_drift = 0;
return 0;
}
static int jdvbt90502_get_frontend(struct dvb_frontend *fe,
struct dvb_frontend_parameters *p)
{
p->inversion = INVERSION_AUTO;
p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
p->u.ofdm.code_rate_HP = FEC_AUTO;
p->u.ofdm.code_rate_LP = FEC_AUTO;
p->u.ofdm.constellation = QAM_64;
p->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO;
p->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO;
p->u.ofdm.hierarchy_information = HIERARCHY_AUTO;
return 0;
}
static int jdvbt90502_set_frontend(struct dvb_frontend *fe,
struct dvb_frontend_parameters *p)
{
/**
* NOTE: ignore all the paramters except frequency.
* others should be fixed to the proper value for ISDB-T,
* but don't check here.
*/
struct jdvbt90502_state *state = fe->demodulator_priv;
int ret;
deb_fe("%s: Freq:%d\n", __func__, p->frequency);
ret = jdvbt90502_pll_set_freq(state, p->frequency);
if (ret) {
deb_fe("%s:ret == %d\n", __func__, ret);
return -EREMOTEIO;
}
return 0;
}
static int jdvbt90502_sleep(struct dvb_frontend *fe)
{
deb_fe("%s called.\n", __func__);
return 0;
}
/**
* (reg, val) commad list to initialize this module.
* captured on a Windows box.
*/
static u8 init_code[][2] = {
{0x01, 0x40},
{0x04, 0x38},
{0x05, 0x40},
{0x07, 0x40},
{0x0F, 0x4F},
{0x11, 0x21},
{0x12, 0x0B},
{0x13, 0x2F},
{0x14, 0x31},
{0x16, 0x02},
{0x21, 0xC4},
{0x22, 0x20},
{0x2C, 0x79},
{0x2D, 0x34},
{0x2F, 0x00},
{0x30, 0x28},
{0x31, 0x31},
{0x32, 0xDF},
{0x38, 0x01},
{0x39, 0x78},
{0x3B, 0x33},
{0x3C, 0x33},
{0x48, 0x90},
{0x51, 0x68},
{0x5E, 0x38},
{0x71, 0x00},
{0x72, 0x08},
{0x77, 0x00},
{0xC0, 0x21},
{0xC1, 0x10},
{0xE4, 0x1A},
{0xEA, 0x1F},
{0x77, 0x00},
{0x71, 0x00},
{0x71, 0x00},
{0x76, 0x0C},
};
const static int init_code_len = sizeof(init_code) / sizeof(u8[2]);
static int jdvbt90502_init(struct dvb_frontend *fe)
{
int i = -1;
int ret;
struct i2c_msg msg;
struct jdvbt90502_state *state = fe->demodulator_priv;
deb_fe("%s called.\n", __func__);
msg.addr = state->config.demod_address;
msg.flags = 0;
msg.len = 2;
for (i = 0; i < init_code_len; i++) {
msg.buf = init_code[i];
ret = i2c_transfer(state->i2c, &msg, 1);
if (ret != 1)
goto error;
}
msleep(100);
return 0;
error:
deb_fe("%s: init_code[%d] failed. ret==%d\n", __func__, i, ret);
return -EREMOTEIO;
}
static void jdvbt90502_release(struct dvb_frontend *fe)
{
struct jdvbt90502_state *state = fe->demodulator_priv;
kfree(state);
}
static struct dvb_frontend_ops jdvbt90502_ops;
struct dvb_frontend *jdvbt90502_attach(struct dvb_usb_device *d)
{
struct jdvbt90502_state *state = NULL;
deb_info("%s called.\n", __func__);
/* allocate memory for the internal state */
state = kzalloc(sizeof(struct jdvbt90502_state), GFP_KERNEL);
if (state == NULL)
goto error;
/* setup the state */
state->i2c = &d->i2c_adap;
memcpy(&state->config, &friio_fe_config, sizeof(friio_fe_config));
/* create dvb_frontend */
memcpy(&state->frontend.ops, &jdvbt90502_ops,
sizeof(jdvbt90502_ops));
state->frontend.demodulator_priv = state;
if (jdvbt90502_init(&state->frontend) < 0)
goto error;
return &state->frontend;
error:
kfree(state);
return NULL;
}
static struct dvb_frontend_ops jdvbt90502_ops = {
.info = {
.name = "Comtech JDVBT90502 ISDB-T",
.type = FE_OFDM,
.frequency_min = 473000000, /* UHF 13ch, center */
.frequency_max = 767142857, /* UHF 62ch, center */
.frequency_stepsize = JDVBT90502_PLL_CLK /
JDVBT90502_PLL_DIVIDER,
.frequency_tolerance = 0,
/* NOTE: this driver ignores all parameters but frequency. */
.caps = FE_CAN_INVERSION_AUTO |
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
FE_CAN_TRANSMISSION_MODE_AUTO |
FE_CAN_GUARD_INTERVAL_AUTO |
FE_CAN_HIERARCHY_AUTO,
},
.release = jdvbt90502_release,
.init = jdvbt90502_init,
.sleep = jdvbt90502_sleep,
.write = _jdvbt90502_write,
.set_frontend = jdvbt90502_set_frontend,
.get_frontend = jdvbt90502_get_frontend,
.get_tune_settings = jdvbt90502_get_tune_settings,
.read_status = jdvbt90502_read_status,
.read_ber = jdvbt90502_read_ber,
.read_signal_strength = jdvbt90502_read_signal_strength,
.read_snr = jdvbt90502_read_snr,
.read_ucblocks = jdvbt90502_read_ucblocks,
};

View File

@@ -0,0 +1,525 @@
/* DVB USB compliant Linux driver for the Friio USB2.0 ISDB-T receiver.
*
* Copyright (C) 2009 Akihiro Tsukada <tskd2@yahoo.co.jp>
*
* This module is based off the the gl861 and vp702x modules.
*
* 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 "friio.h"
/* debug */
int dvb_usb_friio_debug;
module_param_named(debug, dvb_usb_friio_debug, int, 0644);
MODULE_PARM_DESC(debug,
"set debugging level (1=info,2=xfer,4=rc,8=fe (or-able))."
DVB_USB_DEBUG_STATUS);
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
/**
* Indirect I2C access to the PLL via FE.
* whole I2C protocol data to the PLL is sent via the FE's I2C register.
* This is done by a control msg to the FE with the I2C data accompanied, and
* a specific USB request number is assigned for that purpose.
*
* this func sends wbuf[1..] to the I2C register wbuf[0] at addr (= at FE).
* TODO: refoctored, smarter i2c functions.
*/
static int gl861_i2c_ctrlmsg_data(struct dvb_usb_device *d, u8 addr,
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
u16 index = wbuf[0]; /* must be JDVBT90502_2ND_I2C_REG(=0xFE) */
u16 value = addr << (8 + 1);
int wo = (rbuf == NULL || rlen == 0); /* write only */
u8 req, type;
deb_xfer("write to PLL:0x%02x via FE reg:0x%02x, len:%d\n",
wbuf[1], wbuf[0], wlen - 1);
if (wo && wlen >= 2) {
req = GL861_REQ_I2C_DATA_CTRL_WRITE;
type = GL861_WRITE;
udelay(20);
return usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0),
req, type, value, index,
&wbuf[1], wlen - 1, 2000);
}
deb_xfer("not supported ctrl-msg, aborting.");
return -EINVAL;
}
/* normal I2C access (without extra data arguments).
* write to the register wbuf[0] at I2C address addr with the value wbuf[1],
* or read from the register wbuf[0].
* register address can be 16bit (wbuf[2]<<8 | wbuf[0]) if wlen==3
*/
static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr,
u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
{
u16 index;
u16 value = addr << (8 + 1);
int wo = (rbuf == NULL || rlen == 0); /* write-only */
u8 req, type;
unsigned int pipe;
/* special case for the indirect I2C access to the PLL via FE, */
if (addr == friio_fe_config.demod_address &&
wbuf[0] == JDVBT90502_2ND_I2C_REG)
return gl861_i2c_ctrlmsg_data(d, addr, wbuf, wlen, rbuf, rlen);
if (wo) {
req = GL861_REQ_I2C_WRITE;
type = GL861_WRITE;
pipe = usb_sndctrlpipe(d->udev, 0);
} else { /* rw */
req = GL861_REQ_I2C_READ;
type = GL861_READ;
pipe = usb_rcvctrlpipe(d->udev, 0);
}
switch (wlen) {
case 1:
index = wbuf[0];
break;
case 2:
index = wbuf[0];
value = value + wbuf[1];
break;
case 3:
/* special case for 16bit register-address */
index = (wbuf[2] << 8) | wbuf[0];
value = value + wbuf[1];
break;
default:
deb_xfer("wlen = %x, aborting.", wlen);
return -EINVAL;
}
msleep(1);
return usb_control_msg(d->udev, pipe, req, type,
value, index, rbuf, rlen, 2000);
}
/* I2C */
static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i;
if (num > 2)
return -EINVAL;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
for (i = 0; i < num; i++) {
/* write/read request */
if (i + 1 < num && (msg[i + 1].flags & I2C_M_RD)) {
if (gl861_i2c_msg(d, msg[i].addr,
msg[i].buf, msg[i].len,
msg[i + 1].buf, msg[i + 1].len) < 0)
break;
i++;
} else
if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf,
msg[i].len, NULL, 0) < 0)
break;
}
mutex_unlock(&d->i2c_mutex);
return i;
}
static u32 gl861_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static int friio_ext_ctl(struct dvb_usb_adapter *adap,
u32 sat_color, int lnb_on)
{
int i;
int ret;
struct i2c_msg msg;
u8 buf[2];
u32 mask;
u8 lnb = (lnb_on) ? FRIIO_CTL_LNB : 0;
msg.addr = 0x00;
msg.flags = 0;
msg.len = 2;
msg.buf = buf;
buf[0] = 0x00;
/* send 2bit header (&B10) */
buf[1] = lnb | FRIIO_CTL_LED | FRIIO_CTL_STROBE;
ret = gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1);
buf[1] |= FRIIO_CTL_CLK;
ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1);
buf[1] = lnb | FRIIO_CTL_STROBE;
ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1);
buf[1] |= FRIIO_CTL_CLK;
ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1);
/* send 32bit(satur, R, G, B) data in serial */
mask = 1 << 31;
for (i = 0; i < 32; i++) {
buf[1] = lnb | FRIIO_CTL_STROBE;
if (sat_color & mask)
buf[1] |= FRIIO_CTL_LED;
ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1);
buf[1] |= FRIIO_CTL_CLK;
ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1);
mask >>= 1;
}
/* set the strobe off */
buf[1] = lnb;
ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1);
buf[1] |= FRIIO_CTL_CLK;
ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1);
return (ret == 70);
}
static int friio_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff);
/* TODO: move these init cmds to the FE's init routine? */
static u8 streaming_init_cmds[][2] = {
{0x33, 0x08},
{0x37, 0x40},
{0x3A, 0x1F},
{0x3B, 0xFF},
{0x3C, 0x1F},
{0x3D, 0xFF},
{0x38, 0x00},
{0x35, 0x00},
{0x39, 0x00},
{0x36, 0x00},
};
static int cmdlen = sizeof(streaming_init_cmds) / 2;
/*
* Command sequence in this init function is a replay
* of the captured USB commands from the Windows proprietary driver.
*/
static int friio_initialize(struct dvb_usb_device *d)
{
int ret;
int i;
int retry = 0;
u8 rbuf[2];
u8 wbuf[3];
deb_info("%s called.\n", __func__);
/* use gl861_i2c_msg instead of gl861_i2c_xfer(), */
/* because the i2c device is not set up yet. */
wbuf[0] = 0x11;
wbuf[1] = 0x02;
ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0);
if (ret < 0)
goto error;
msleep(2);
wbuf[0] = 0x11;
wbuf[1] = 0x00;
ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0);
if (ret < 0)
goto error;
msleep(1);
/* following msgs should be in the FE's init code? */
/* cmd sequence to identify the device type? (friio black/white) */
wbuf[0] = 0x03;
wbuf[1] = 0x80;
/* can't use gl861_i2c_cmd, as the register-addr is 16bit(0x0100) */
ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0),
GL861_REQ_I2C_DATA_CTRL_WRITE, GL861_WRITE,
0x1200, 0x0100, wbuf, 2, 2000);
if (ret < 0)
goto error;
msleep(2);
wbuf[0] = 0x00;
wbuf[2] = 0x01; /* reg.0x0100 */
wbuf[1] = 0x00;
ret = gl861_i2c_msg(d, 0x12 >> 1, wbuf, 3, rbuf, 2);
/* my Friio White returns 0xffff. */
if (ret < 0 || rbuf[0] != 0xff || rbuf[1] != 0xff)
goto error;
msleep(2);
wbuf[0] = 0x03;
wbuf[1] = 0x80;
ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0),
GL861_REQ_I2C_DATA_CTRL_WRITE, GL861_WRITE,
0x9000, 0x0100, wbuf, 2, 2000);
if (ret < 0)
goto error;
msleep(2);
wbuf[0] = 0x00;
wbuf[2] = 0x01; /* reg.0x0100 */
wbuf[1] = 0x00;
ret = gl861_i2c_msg(d, 0x90 >> 1, wbuf, 3, rbuf, 2);
/* my Friio White returns 0xffff again. */
if (ret < 0 || rbuf[0] != 0xff || rbuf[1] != 0xff)
goto error;
msleep(1);
restart:
/* ============ start DEMOD init cmds ================== */
/* read PLL status to clear the POR bit */
wbuf[0] = JDVBT90502_2ND_I2C_REG;
wbuf[1] = (FRIIO_PLL_ADDR << 1) + 1; /* +1 for reading */
ret = gl861_i2c_msg(d, FRIIO_DEMOD_ADDR, wbuf, 2, NULL, 0);
if (ret < 0)
goto error;
msleep(5);
/* note: DEMODULATOR has 16bit register-address. */
wbuf[0] = 0x00;
wbuf[2] = 0x01; /* reg addr: 0x0100 */
wbuf[1] = 0x00; /* val: not used */
ret = gl861_i2c_msg(d, FRIIO_DEMOD_ADDR, wbuf, 3, rbuf, 1);
if (ret < 0)
goto error;
/*
msleep(1);
wbuf[0] = 0x80;
wbuf[1] = 0x00;
ret = gl861_i2c_msg(d, FRIIO_DEMOD_ADDR, wbuf, 2, rbuf, 1);
if (ret < 0)
goto error;
*/
if (rbuf[0] & 0x80) { /* still in PowerOnReset state? */
if (++retry > 3) {
deb_info("failed to get the correct"
" FE demod status:0x%02x\n", rbuf[0]);
goto error;
}
msleep(100);
goto restart;
}
/* TODO: check return value in rbuf */
/* =========== end DEMOD init cmds ===================== */
msleep(1);
wbuf[0] = 0x30;
wbuf[1] = 0x04;
ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0);
if (ret < 0)
goto error;
msleep(2);
/* following 2 cmds unnecessary? */
wbuf[0] = 0x00;
wbuf[1] = 0x01;
ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0);
if (ret < 0)
goto error;
wbuf[0] = 0x06;
wbuf[1] = 0x0F;
ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0);
if (ret < 0)
goto error;
/* some streaming ctl cmds (maybe) */
msleep(10);
for (i = 0; i < cmdlen; i++) {
ret = gl861_i2c_msg(d, 0x00, streaming_init_cmds[i], 2,
NULL, 0);
if (ret < 0)
goto error;
msleep(1);
}
msleep(20);
/* change the LED color etc. */
ret = friio_streaming_ctrl(&d->adapter[0], 0);
if (ret < 0)
goto error;
return 0;
error:
deb_info("%s:ret == %d\n", __func__, ret);
return -EIO;
}
/* Callbacks for DVB USB */
static int friio_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
int ret;
deb_info("%s called.(%d)\n", __func__, onoff);
/* set the LED color and saturation (and LNB on) */
if (onoff)
ret = friio_ext_ctl(adap, 0x6400ff64, 1);
else
ret = friio_ext_ctl(adap, 0x96ff00ff, 1);
if (ret != 1) {
deb_info("%s failed to send cmdx. ret==%d\n", __func__, ret);
return -EREMOTEIO;
}
return 0;
}
static int friio_frontend_attach(struct dvb_usb_adapter *adap)
{
if (friio_initialize(adap->dev) < 0)
return -EIO;
adap->fe = jdvbt90502_attach(adap->dev);
if (adap->fe == NULL)
return -EIO;
return 0;
}
/* DVB USB Driver stuff */
static struct dvb_usb_device_properties friio_properties;
static int friio_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct dvb_usb_device *d;
struct usb_host_interface *alt;
int ret;
if (intf->num_altsetting < GL861_ALTSETTING_COUNT)
return -ENODEV;
alt = usb_altnum_to_altsetting(intf, FRIIO_BULK_ALTSETTING);
if (alt == NULL) {
deb_rc("not alt found!\n");
return -ENODEV;
}
ret = usb_set_interface(interface_to_usbdev(intf),
alt->desc.bInterfaceNumber,
alt->desc.bAlternateSetting);
if (ret != 0) {
deb_rc("failed to set alt-setting!\n");
return ret;
}
ret = dvb_usb_device_init(intf, &friio_properties,
THIS_MODULE, &d, adapter_nr);
if (ret == 0)
friio_streaming_ctrl(&d->adapter[0], 1);
return ret;
}
struct jdvbt90502_config friio_fe_config = {
.demod_address = FRIIO_DEMOD_ADDR,
.pll_address = FRIIO_PLL_ADDR,
};
static struct i2c_algorithm gl861_i2c_algo = {
.master_xfer = gl861_i2c_xfer,
.functionality = gl861_i2c_func,
};
static struct usb_device_id friio_table[] = {
{ USB_DEVICE(USB_VID_774, USB_PID_FRIIO_WHITE) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, friio_table);
static struct dvb_usb_device_properties friio_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = DEVICE_SPECIFIC,
.size_of_priv = 0,
.num_adapters = 1,
.adapter = {
/* caps:0 => no pid filter, 188B TS packet */
/* GL861 has a HW pid filter, but no info available. */
{
.caps = 0,
.frontend_attach = friio_frontend_attach,
.streaming_ctrl = friio_streaming_ctrl,
.stream = {
.type = USB_BULK,
/* count <= MAX_NO_URBS_FOR_DATA_STREAM(10) */
.count = 8,
.endpoint = 0x01,
.u = {
/* GL861 has 6KB buf inside */
.bulk = {
.buffersize = 16384,
}
}
},
}
},
.i2c_algo = &gl861_i2c_algo,
.num_device_descs = 1,
.devices = {
{
.name = "774 Friio ISDB-T USB2.0",
.cold_ids = { NULL },
.warm_ids = { &friio_table[0], NULL },
},
}
};
static struct usb_driver friio_driver = {
.name = "dvb_usb_friio",
.probe = friio_probe,
.disconnect = dvb_usb_device_exit,
.id_table = friio_table,
};
/* module stuff */
static int __init friio_module_init(void)
{
int ret;
ret = usb_register(&friio_driver);
if (ret)
err("usb_register failed. Error number %d", ret);
return ret;
}
static void __exit friio_module_exit(void)
{
/* deregister this driver from the USB subsystem */
usb_deregister(&friio_driver);
}
module_init(friio_module_init);
module_exit(friio_module_exit);
MODULE_AUTHOR("Akihiro Tsukada <tskd2@yahoo.co.jp>");
MODULE_DESCRIPTION("Driver for Friio ISDB-T USB2.0 Receiver");
MODULE_VERSION("0.2");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,99 @@
/* DVB USB compliant Linux driver for the Friio USB2.0 ISDB-T receiver.
*
* Copyright (C) 2009 Akihiro Tsukada <tskd2@yahoo.co.jp>
*
* This module is based off the the gl861 and vp702x modules.
*
* 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_FRIIO_H_
#define _DVB_USB_FRIIO_H_
/**
* Friio Components
* USB hub: AU4254
* USB controller(+ TS dmx & streaming): GL861
* Frontend: comtech JDVBT-90502
* (tuner PLL: tua6034, I2C addr:(0xC0 >> 1))
* (OFDM demodulator: TC90502, I2C addr:(0x30 >> 1))
* LED x3 (+LNB) controll: PIC 16F676
* EEPROM: 24C08
*
* (USB smart card reader: AU9522)
*
*/
#define DVB_USB_LOG_PREFIX "friio"
#include "dvb-usb.h"
extern int dvb_usb_friio_debug;
#define deb_info(args...) dprintk(dvb_usb_friio_debug, 0x01, args)
#define deb_xfer(args...) dprintk(dvb_usb_friio_debug, 0x02, args)
#define deb_rc(args...) dprintk(dvb_usb_friio_debug, 0x04, args)
#define deb_fe(args...) dprintk(dvb_usb_friio_debug, 0x08, args)
/* Vendor requests */
#define GL861_WRITE 0x40
#define GL861_READ 0xc0
/* command bytes */
#define GL861_REQ_I2C_WRITE 0x01
#define GL861_REQ_I2C_READ 0x02
/* For control msg with data argument */
/* Used for accessing the PLL on the secondary I2C bus of FE via GL861 */
#define GL861_REQ_I2C_DATA_CTRL_WRITE 0x03
#define GL861_ALTSETTING_COUNT 2
#define FRIIO_BULK_ALTSETTING 0
#define FRIIO_ISOC_ALTSETTING 1
/* LED & LNB control via PIC. */
/* basically, it's serial control with clock and strobe. */
/* write the below 4bit control data to the reg 0x00 at the I2C addr 0x00 */
/* when controlling the LEDs, 32bit(saturation, R, G, B) is sent on the bit3*/
#define FRIIO_CTL_LNB (1 << 0)
#define FRIIO_CTL_STROBE (1 << 1)
#define FRIIO_CTL_CLK (1 << 2)
#define FRIIO_CTL_LED (1 << 3)
/* Front End related */
#define FRIIO_DEMOD_ADDR (0x30 >> 1)
#define FRIIO_PLL_ADDR (0xC0 >> 1)
#define JDVBT90502_PLL_CLK 4000000
#define JDVBT90502_PLL_DIVIDER 28
#define JDVBT90502_2ND_I2C_REG 0xFE
/* byte index for pll i2c command data structure*/
/* see datasheet for tua6034 */
#define DEMOD_REDIRECT_REG 0
#define ADDRESS_BYTE 1
#define DIVIDER_BYTE1 2
#define DIVIDER_BYTE2 3
#define CONTROL_BYTE 4
#define BANDSWITCH_BYTE 5
#define AGC_CTRL_BYTE 5
#define PLL_CMD_LEN 6
/* bit masks for PLL STATUS response */
#define PLL_STATUS_POR_MODE 0x80 /* 1: Power on Reset (test) Mode */
#define PLL_STATUS_LOCKED 0x40 /* 1: locked */
#define PLL_STATUS_AGC_ACTIVE 0x08 /* 1:active */
#define PLL_STATUS_TESTMODE 0x07 /* digital output level (5 level) */
/* 0.15Vcc step 0x00: < 0.15Vcc, ..., 0x04: >= 0.6Vcc (<= 1Vcc) */
struct jdvbt90502_config {
u8 demod_address; /* i2c addr for demodulator IC */
u8 pll_address; /* PLL addr on the secondary i2c*/
};
extern struct jdvbt90502_config friio_fe_config;
extern struct dvb_frontend *jdvbt90502_attach(struct dvb_usb_device *d);
#endif

View File

@@ -337,6 +337,8 @@ static int m920x_firmware_download(struct usb_device *udev, const struct firmwar
int i, pass, ret = 0;
buff = kmalloc(65536, GFP_KERNEL);
if (buff == NULL)
return -ENOMEM;
if ((ret = m920x_read(udev, M9206_FILTER, 0x0, 0x8000, read, 4)) != 0)
goto done;

View File

@@ -484,6 +484,14 @@ config DVB_S921
AN ISDB-T DQPSK, QPSK, 16QAM and 64QAM 1seg tuner module.
Say Y when you want to support this frontend.
config DVB_DIB8000
tristate "DiBcom 8000MB/MC"
depends on DVB_CORE && I2C
default m if DVB_FE_CUSTOMISE
help
A driver for DiBcom's DiB8000 ISDB-T/ISDB-Tsb demodulator.
Say Y when you want to support this frontend.
comment "Digital terrestrial only tuners/PLL"
depends on DVB_CORE

View File

@@ -23,6 +23,7 @@ obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o
obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o
obj-$(CONFIG_DVB_DIB7000M) += dib7000m.o dibx000_common.o
obj-$(CONFIG_DVB_DIB7000P) += dib7000p.o dibx000_common.o
obj-$(CONFIG_DVB_DIB8000) += dib8000.o dibx000_common.o
obj-$(CONFIG_DVB_MT312) += mt312.o
obj-$(CONFIG_DVB_VES1820) += ves1820.o
obj-$(CONFIG_DVB_VES1X93) += ves1x93.o

View File

@@ -792,6 +792,11 @@ static int au8522_probe(struct i2c_client *client,
}
demod_config = kzalloc(sizeof(struct au8522_config), GFP_KERNEL);
if (demod_config == NULL) {
if (instance == 1)
kfree(state);
return -ENOMEM;
}
demod_config->demod_address = 0x8e >> 1;
state->config = demod_config;

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,11 @@ struct i2c_adapter;
#define DEFAULT_DIB0070_I2C_ADDRESS 0x60
struct dib0070_wbd_gain_cfg {
u16 freq;
u16 wbd_gain_val;
};
struct dib0070_config {
u8 i2c_address;
@@ -26,26 +31,28 @@ struct dib0070_config {
int freq_offset_khz_uhf;
int freq_offset_khz_vhf;
u8 osc_buffer_state; /* 0= normal, 1= tri-state */
u32 clock_khz;
u8 clock_pad_drive; /* (Drive + 1) * 2mA */
u8 osc_buffer_state; /* 0= normal, 1= tri-state */
u32 clock_khz;
u8 clock_pad_drive; /* (Drive + 1) * 2mA */
u8 invert_iq; /* invert Q - in case I or Q is inverted on the board */
u8 invert_iq; /* invert Q - in case I or Q is inverted on the board */
u8 force_crystal_mode; /* if == 0 -> decision is made in the driver default: <24 -> 2, >=24 -> 1 */
u8 force_crystal_mode; /* if == 0 -> decision is made in the driver default: <24 -> 2, >=24 -> 1 */
u8 flip_chip;
u8 enable_third_order_filter;
u8 charge_pump;
const struct dib0070_wbd_gain_cfg *wbd_gain;
u8 vga_filter;
};
#if defined(CONFIG_DVB_TUNER_DIB0070) || (defined(CONFIG_DVB_TUNER_DIB0070_MODULE) && defined(MODULE))
extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe,
struct i2c_adapter *i2c,
struct dib0070_config *cfg);
extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg);
extern u16 dib0070_wbd_offset(struct dvb_frontend *);
#else
static inline struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe,
struct i2c_adapter *i2c,
struct dib0070_config *cfg)
static inline struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
@@ -57,5 +64,6 @@ static inline u16 dib0070_wbd_offset(struct dvb_frontend *fe)
return -ENODEV;
}
#endif
extern void dib0070_ctrl_agc_filter(struct dvb_frontend *, u8 open);
#endif

View File

@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/i2c.h>
#include "dvb_math.h"
#include "dvb_frontend.h"
#include "dib7000p.h"
@@ -1217,7 +1218,37 @@ static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
static int dib7000p_read_snr(struct dvb_frontend* fe, u16 *snr)
{
*snr = 0x0000;
struct dib7000p_state *state = fe->demodulator_priv;
u16 val;
s32 signal_mant, signal_exp, noise_mant, noise_exp;
u32 result = 0;
val = dib7000p_read_word(state, 479);
noise_mant = (val >> 4) & 0xff;
noise_exp = ((val & 0xf) << 2);
val = dib7000p_read_word(state, 480);
noise_exp += ((val >> 14) & 0x3);
if ((noise_exp & 0x20) != 0)
noise_exp -= 0x40;
signal_mant = (val >> 6) & 0xFF;
signal_exp = (val & 0x3F);
if ((signal_exp & 0x20) != 0)
signal_exp -= 0x40;
if (signal_mant != 0)
result = intlog10(2) * 10 * signal_exp + 10 *
intlog10(signal_mant);
else
result = intlog10(2) * 10 * signal_exp - 100;
if (noise_mant != 0)
result -= intlog10(2) * 10 * noise_exp + 10 *
intlog10(noise_mant);
else
result -= intlog10(2) * 10 * noise_exp - 100;
*snr = result / ((1 << 24) / 10);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,79 @@
#ifndef DIB8000_H
#define DIB8000_H
#include "dibx000_common.h"
struct dib8000_config {
u8 output_mpeg2_in_188_bytes;
u8 hostbus_diversity;
u8 tuner_is_baseband;
int (*update_lna) (struct dvb_frontend *, u16 agc_global);
u8 agc_config_count;
struct dibx000_agc_config *agc;
struct dibx000_bandwidth_config *pll;
#define DIB8000_GPIO_DEFAULT_DIRECTIONS 0xffff
u16 gpio_dir;
#define DIB8000_GPIO_DEFAULT_VALUES 0x0000
u16 gpio_val;
#define DIB8000_GPIO_PWM_POS0(v) ((v & 0xf) << 12)
#define DIB8000_GPIO_PWM_POS1(v) ((v & 0xf) << 8 )
#define DIB8000_GPIO_PWM_POS2(v) ((v & 0xf) << 4 )
#define DIB8000_GPIO_PWM_POS3(v) (v & 0xf)
#define DIB8000_GPIO_DEFAULT_PWM_POS 0xffff
u16 gpio_pwm_pos;
u16 pwm_freq_div;
void (*agc_control) (struct dvb_frontend *, u8 before);
u16 drives;
u16 diversity_delay;
u8 div_cfg;
u8 output_mode;
u8 refclksel;
};
#define DEFAULT_DIB8000_I2C_ADDRESS 18
#if defined(CONFIG_DVB_DIB8000) || (defined(CONFIG_DVB_DIB8000_MODULE) && defined(MODULE))
extern struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg);
extern struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int);
extern int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr);
extern int dib8000_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val);
extern int dib8000_set_wbd_ref(struct dvb_frontend *, u16 value);
#else
static inline struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
static inline struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface i, int x)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
}
int dib8000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
}
int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
}
#endif
#endif

View File

@@ -15,29 +15,31 @@ static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val)
(val >> 8) & 0xff, val & 0xff,
};
struct i2c_msg msg = {
.addr = mst->i2c_addr, .flags = 0, .buf = b, .len = 4
.addr = mst->i2c_addr,.flags = 0,.buf = b,.len = 4
};
return i2c_transfer(mst->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
}
static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf)
static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst,
enum dibx000_i2c_interface intf)
{
if (mst->device_rev > DIB3000MC && mst->selected_interface != intf) {
dprintk("selecting interface: %d\n",intf);
dprintk("selecting interface: %d\n", intf);
mst->selected_interface = intf;
return dibx000_write_word(mst, mst->base_reg + 4, intf);
}
return 0;
}
static int dibx000_i2c_gate_ctrl(struct dibx000_i2c_master *mst, u8 tx[4], u8 addr, int onoff)
static int dibx000_i2c_gate_ctrl(struct dibx000_i2c_master *mst, u8 tx[4],
u8 addr, int onoff)
{
u16 val;
if (onoff)
val = addr << 8; // bit 7 = use master or not, if 0, the gate is open
val = addr << 8; // bit 7 = use master or not, if 0, the gate is open
else
val = 1 << 7;
@@ -45,7 +47,7 @@ static int dibx000_i2c_gate_ctrl(struct dibx000_i2c_master *mst, u8 tx[4], u8 ad
val <<= 1;
tx[0] = (((mst->base_reg + 1) >> 8) & 0xff);
tx[1] = ( (mst->base_reg + 1) & 0xff);
tx[1] = ((mst->base_reg + 1) & 0xff);
tx[2] = val >> 8;
tx[3] = val & 0xff;
@@ -57,59 +59,78 @@ static u32 dibx000_i2c_func(struct i2c_adapter *adapter)
return I2C_FUNC_I2C;
}
static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num)
static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg msg[], int num)
{
struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap);
struct i2c_msg m[2 + num];
u8 tx_open[4], tx_close[4];
memset(m,0, sizeof(struct i2c_msg) * (2 + num));
memset(m, 0, sizeof(struct i2c_msg) * (2 + num));
dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER);
dibx000_i2c_gate_ctrl(mst, tx_open, msg[0].addr, 1);
dibx000_i2c_gate_ctrl(mst, tx_open, msg[0].addr, 1);
m[0].addr = mst->i2c_addr;
m[0].buf = tx_open;
m[0].len = 4;
m[0].buf = tx_open;
m[0].len = 4;
memcpy(&m[1], msg, sizeof(struct i2c_msg) * num);
dibx000_i2c_gate_ctrl(mst, tx_close, 0, 0);
m[num+1].addr = mst->i2c_addr;
m[num+1].buf = tx_close;
m[num+1].len = 4;
m[num + 1].addr = mst->i2c_addr;
m[num + 1].buf = tx_close;
m[num + 1].len = 4;
return i2c_transfer(mst->i2c_adap, m, 2+num) == 2 + num ? num : -EIO;
return i2c_transfer(mst->i2c_adap, m, 2 + num) == 2 + num ? num : -EIO;
}
static struct i2c_algorithm dibx000_i2c_gated_tuner_algo = {
.master_xfer = dibx000_i2c_gated_tuner_xfer,
.master_xfer = dibx000_i2c_gated_tuner_xfer,
.functionality = dibx000_i2c_func,
};
struct i2c_adapter * dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf, int gating)
struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst,
enum dibx000_i2c_interface intf,
int gating)
{
struct i2c_adapter *i2c = NULL;
switch (intf) {
case DIBX000_I2C_INTERFACE_TUNER:
if (gating)
i2c = &mst->gated_tuner_i2c_adap;
break;
default:
printk(KERN_ERR "DiBX000: incorrect I2C interface selected\n");
break;
case DIBX000_I2C_INTERFACE_TUNER:
if (gating)
i2c = &mst->gated_tuner_i2c_adap;
break;
default:
printk(KERN_ERR "DiBX000: incorrect I2C interface selected\n");
break;
}
return i2c;
}
EXPORT_SYMBOL(dibx000_get_i2c_adapter);
static int i2c_adapter_init(struct i2c_adapter *i2c_adap, struct i2c_algorithm *algo, const char *name, struct dibx000_i2c_master *mst)
void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst)
{
/* initialize the i2c-master by closing the gate */
u8 tx[4];
struct i2c_msg m = {.addr = mst->i2c_addr,.buf = tx,.len = 4 };
dibx000_i2c_gate_ctrl(mst, tx, 0, 0);
i2c_transfer(mst->i2c_adap, &m, 1);
mst->selected_interface = 0xff; // the first time force a select of the I2C
dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER);
}
EXPORT_SYMBOL(dibx000_reset_i2c_master);
static int i2c_adapter_init(struct i2c_adapter *i2c_adap,
struct i2c_algorithm *algo, const char *name,
struct dibx000_i2c_master *mst)
{
strncpy(i2c_adap->name, name, sizeof(i2c_adap->name));
i2c_adap->class = I2C_CLASS_TV_DIGITAL,
i2c_adap->algo = algo;
i2c_adap->class = I2C_CLASS_TV_DIGITAL, i2c_adap->algo = algo;
i2c_adap->algo_data = NULL;
i2c_set_adapdata(i2c_adap, mst);
if (i2c_add_adapter(i2c_adap) < 0)
@@ -117,34 +138,40 @@ static int i2c_adapter_init(struct i2c_adapter *i2c_adap, struct i2c_algorithm *
return 0;
}
int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, struct i2c_adapter *i2c_adap, u8 i2c_addr)
int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev,
struct i2c_adapter *i2c_adap, u8 i2c_addr)
{
u8 tx[4];
struct i2c_msg m = { .addr = i2c_addr >> 1, .buf = tx, .len = 4 };
struct i2c_msg m = {.addr = i2c_addr >> 1,.buf = tx,.len = 4 };
mst->device_rev = device_rev;
mst->i2c_adap = i2c_adap;
mst->i2c_addr = i2c_addr >> 1;
mst->i2c_adap = i2c_adap;
mst->i2c_addr = i2c_addr >> 1;
if (device_rev == DIB7000P)
if (device_rev == DIB7000P || device_rev == DIB8000)
mst->base_reg = 1024;
else
mst->base_reg = 768;
if (i2c_adapter_init(&mst->gated_tuner_i2c_adap, &dibx000_i2c_gated_tuner_algo, "DiBX000 tuner I2C bus", mst) != 0)
printk(KERN_ERR "DiBX000: could not initialize the tuner i2c_adapter\n");
if (i2c_adapter_init
(&mst->gated_tuner_i2c_adap, &dibx000_i2c_gated_tuner_algo,
"DiBX000 tuner I2C bus", mst) != 0)
printk(KERN_ERR
"DiBX000: could not initialize the tuner i2c_adapter\n");
/* initialize the i2c-master by closing the gate */
dibx000_i2c_gate_ctrl(mst, tx, 0, 0);
return i2c_transfer(i2c_adap, &m, 1) == 1;
}
EXPORT_SYMBOL(dibx000_init_i2c_master);
void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst)
{
i2c_del_adapter(&mst->gated_tuner_i2c_adap);
}
EXPORT_SYMBOL(dibx000_exit_i2c_master);
MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");

View File

@@ -2,7 +2,7 @@
#define DIBX000_COMMON_H
enum dibx000_i2c_interface {
DIBX000_I2C_INTERFACE_TUNER = 0,
DIBX000_I2C_INTERFACE_TUNER = 0,
DIBX000_I2C_INTERFACE_GPIO_1_2 = 1,
DIBX000_I2C_INTERFACE_GPIO_3_4 = 2
};
@@ -12,22 +12,29 @@ struct dibx000_i2c_master {
#define DIB7000 2
#define DIB7000P 11
#define DIB7000MC 12
#define DIB8000 13
u16 device_rev;
enum dibx000_i2c_interface selected_interface;
// struct i2c_adapter tuner_i2c_adap;
struct i2c_adapter gated_tuner_i2c_adap;
// struct i2c_adapter tuner_i2c_adap;
struct i2c_adapter gated_tuner_i2c_adap;
struct i2c_adapter *i2c_adap;
u8 i2c_addr;
u8 i2c_addr;
u16 base_reg;
};
extern int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, struct i2c_adapter *i2c_adap, u8 i2c_addr);
extern struct i2c_adapter * dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf, int gating);
extern int dibx000_init_i2c_master(struct dibx000_i2c_master *mst,
u16 device_rev, struct i2c_adapter *i2c_adap,
u8 i2c_addr);
extern struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master
*mst,
enum dibx000_i2c_interface
intf, int gating);
extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst);
extern void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst);
#define BAND_LBAND 0x01
#define BAND_UHF 0x02
@@ -41,18 +48,18 @@ extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst);
(freq_kHz) <= 2000000 ? BAND_LBAND : BAND_SBAND )
struct dibx000_agc_config {
/* defines the capabilities of this AGC-setting - using the BAND_-defines*/
u8 band_caps;
/* defines the capabilities of this AGC-setting - using the BAND_-defines */
u8 band_caps;
u16 setup;
u16 inv_gain;
u16 time_stabiliz;
u8 alpha_level;
u8 alpha_level;
u16 thlock;
u8 wbd_inv;
u8 wbd_inv;
u16 wbd_ref;
u8 wbd_sel;
u8 wbd_alpha;
@@ -92,8 +99,8 @@ struct dibx000_agc_config {
};
struct dibx000_bandwidth_config {
u32 internal;
u32 sampling;
u32 internal;
u32 sampling;
u8 pll_prediv;
u8 pll_ratio;

View File

@@ -363,6 +363,8 @@ struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config,
struct lgdt3304_state *state;
state = kzalloc(sizeof(struct lgdt3304_state), GFP_KERNEL);
if (state == NULL)
return NULL;
state->addr = config->i2c_address;
state->i2c = i2c;

View File

@@ -169,6 +169,8 @@ struct dvb_frontend* s921_attach(const struct s921_config *config,
struct s921_state *state;
state = kzalloc(sizeof(struct s921_state), GFP_KERNEL);
if (state == NULL)
return NULL;
state->addr = config->i2c_address;
state->i2c = i2c;

View File

@@ -0,0 +1,12 @@
config DVB_PT1
tristate "PT1 cards"
depends on DVB_CORE && PCI && I2C
help
Support for Earthsoft PT1 PCI cards.
Since these cards have no MPEG decoder onboard, they transmit
only compressed MPEG data over the PCI bus, so you need
an external software decoder to watch TV on your computer.
Say Y or M if you own such a device and want to use it.

View File

@@ -0,0 +1,5 @@
earth-pt1-objs := pt1.o va1j5jf8007s.o va1j5jf8007t.o
obj-$(CONFIG_DVB_PT1) += earth-pt1.o
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -Idrivers/media/dvb/frontends

1056
drivers/media/dvb/pt1/pt1.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,658 @@
/*
* ISDB-S driver for VA1J5JF8007
*
* Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
*
* based on pt1dvr - http://pt1dvr.sourceforge.jp/
* by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
*
* 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 <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include "dvb_frontend.h"
#include "va1j5jf8007s.h"
enum va1j5jf8007s_tune_state {
VA1J5JF8007S_IDLE,
VA1J5JF8007S_SET_FREQUENCY_1,
VA1J5JF8007S_SET_FREQUENCY_2,
VA1J5JF8007S_SET_FREQUENCY_3,
VA1J5JF8007S_CHECK_FREQUENCY,
VA1J5JF8007S_SET_MODULATION,
VA1J5JF8007S_CHECK_MODULATION,
VA1J5JF8007S_SET_TS_ID,
VA1J5JF8007S_CHECK_TS_ID,
VA1J5JF8007S_TRACK,
};
struct va1j5jf8007s_state {
const struct va1j5jf8007s_config *config;
struct i2c_adapter *adap;
struct dvb_frontend fe;
enum va1j5jf8007s_tune_state tune_state;
};
static int va1j5jf8007s_get_frontend_algo(struct dvb_frontend *fe)
{
return DVBFE_ALGO_HW;
}
static int
va1j5jf8007s_read_status(struct dvb_frontend *fe, fe_status_t *status)
{
struct va1j5jf8007s_state *state;
state = fe->demodulator_priv;
switch (state->tune_state) {
case VA1J5JF8007S_IDLE:
case VA1J5JF8007S_SET_FREQUENCY_1:
case VA1J5JF8007S_SET_FREQUENCY_2:
case VA1J5JF8007S_SET_FREQUENCY_3:
case VA1J5JF8007S_CHECK_FREQUENCY:
*status = 0;
return 0;
case VA1J5JF8007S_SET_MODULATION:
case VA1J5JF8007S_CHECK_MODULATION:
*status |= FE_HAS_SIGNAL;
return 0;
case VA1J5JF8007S_SET_TS_ID:
case VA1J5JF8007S_CHECK_TS_ID:
*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
return 0;
case VA1J5JF8007S_TRACK:
*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
return 0;
}
BUG();
}
struct va1j5jf8007s_cb_map {
u32 frequency;
u8 cb;
};
static const struct va1j5jf8007s_cb_map va1j5jf8007s_cb_maps[] = {
{ 986000, 0xb2 },
{ 1072000, 0xd2 },
{ 1154000, 0xe2 },
{ 1291000, 0x20 },
{ 1447000, 0x40 },
{ 1615000, 0x60 },
{ 1791000, 0x80 },
{ 1972000, 0xa0 },
};
static u8 va1j5jf8007s_lookup_cb(u32 frequency)
{
int i;
const struct va1j5jf8007s_cb_map *map;
for (i = 0; i < ARRAY_SIZE(va1j5jf8007s_cb_maps); i++) {
map = &va1j5jf8007s_cb_maps[i];
if (frequency < map->frequency)
return map->cb;
}
return 0xc0;
}
static int va1j5jf8007s_set_frequency_1(struct va1j5jf8007s_state *state)
{
u32 frequency;
u16 word;
u8 buf[6];
struct i2c_msg msg;
frequency = state->fe.dtv_property_cache.frequency;
word = (frequency + 500) / 1000;
if (frequency < 1072000)
word = (word << 1 & ~0x1f) | (word & 0x0f);
buf[0] = 0xfe;
buf[1] = 0xc0;
buf[2] = 0x40 | word >> 8;
buf[3] = word;
buf[4] = 0xe0;
buf[5] = va1j5jf8007s_lookup_cb(frequency);
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int va1j5jf8007s_set_frequency_2(struct va1j5jf8007s_state *state)
{
u8 buf[3];
struct i2c_msg msg;
buf[0] = 0xfe;
buf[1] = 0xc0;
buf[2] = 0xe4;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int va1j5jf8007s_set_frequency_3(struct va1j5jf8007s_state *state)
{
u32 frequency;
u8 buf[4];
struct i2c_msg msg;
frequency = state->fe.dtv_property_cache.frequency;
buf[0] = 0xfe;
buf[1] = 0xc0;
buf[2] = 0xf4;
buf[3] = va1j5jf8007s_lookup_cb(frequency) | 0x4;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int
va1j5jf8007s_check_frequency(struct va1j5jf8007s_state *state, int *lock)
{
u8 addr;
u8 write_buf[2], read_buf[1];
struct i2c_msg msgs[2];
addr = state->config->demod_address;
write_buf[0] = 0xfe;
write_buf[1] = 0xc1;
msgs[0].addr = addr;
msgs[0].flags = 0;
msgs[0].len = sizeof(write_buf);
msgs[0].buf = write_buf;
msgs[1].addr = addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = sizeof(read_buf);
msgs[1].buf = read_buf;
if (i2c_transfer(state->adap, msgs, 2) != 2)
return -EREMOTEIO;
*lock = read_buf[0] & 0x40;
return 0;
}
static int va1j5jf8007s_set_modulation(struct va1j5jf8007s_state *state)
{
u8 buf[2];
struct i2c_msg msg;
buf[0] = 0x03;
buf[1] = 0x01;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int
va1j5jf8007s_check_modulation(struct va1j5jf8007s_state *state, int *lock)
{
u8 addr;
u8 write_buf[1], read_buf[1];
struct i2c_msg msgs[2];
addr = state->config->demod_address;
write_buf[0] = 0xc3;
msgs[0].addr = addr;
msgs[0].flags = 0;
msgs[0].len = sizeof(write_buf);
msgs[0].buf = write_buf;
msgs[1].addr = addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = sizeof(read_buf);
msgs[1].buf = read_buf;
if (i2c_transfer(state->adap, msgs, 2) != 2)
return -EREMOTEIO;
*lock = !(read_buf[0] & 0x10);
return 0;
}
static int
va1j5jf8007s_set_ts_id(struct va1j5jf8007s_state *state)
{
u32 ts_id;
u8 buf[3];
struct i2c_msg msg;
ts_id = state->fe.dtv_property_cache.isdbs_ts_id;
if (!ts_id)
return 0;
buf[0] = 0x8f;
buf[1] = ts_id >> 8;
buf[2] = ts_id;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int
va1j5jf8007s_check_ts_id(struct va1j5jf8007s_state *state, int *lock)
{
u8 addr;
u8 write_buf[1], read_buf[2];
struct i2c_msg msgs[2];
u32 ts_id;
ts_id = state->fe.dtv_property_cache.isdbs_ts_id;
if (!ts_id) {
*lock = 1;
return 0;
}
addr = state->config->demod_address;
write_buf[0] = 0xe6;
msgs[0].addr = addr;
msgs[0].flags = 0;
msgs[0].len = sizeof(write_buf);
msgs[0].buf = write_buf;
msgs[1].addr = addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = sizeof(read_buf);
msgs[1].buf = read_buf;
if (i2c_transfer(state->adap, msgs, 2) != 2)
return -EREMOTEIO;
*lock = (read_buf[0] << 8 | read_buf[1]) == ts_id;
return 0;
}
static int
va1j5jf8007s_tune(struct dvb_frontend *fe,
struct dvb_frontend_parameters *params,
unsigned int mode_flags, unsigned int *delay,
fe_status_t *status)
{
struct va1j5jf8007s_state *state;
int ret;
int lock;
state = fe->demodulator_priv;
if (params != NULL)
state->tune_state = VA1J5JF8007S_SET_FREQUENCY_1;
switch (state->tune_state) {
case VA1J5JF8007S_IDLE:
*delay = 3 * HZ;
*status = 0;
return 0;
case VA1J5JF8007S_SET_FREQUENCY_1:
ret = va1j5jf8007s_set_frequency_1(state);
if (ret < 0)
return ret;
state->tune_state = VA1J5JF8007S_SET_FREQUENCY_2;
*delay = 0;
*status = 0;
return 0;
case VA1J5JF8007S_SET_FREQUENCY_2:
ret = va1j5jf8007s_set_frequency_2(state);
if (ret < 0)
return ret;
state->tune_state = VA1J5JF8007S_SET_FREQUENCY_3;
*delay = (HZ + 99) / 100;
*status = 0;
return 0;
case VA1J5JF8007S_SET_FREQUENCY_3:
ret = va1j5jf8007s_set_frequency_3(state);
if (ret < 0)
return ret;
state->tune_state = VA1J5JF8007S_CHECK_FREQUENCY;
*delay = 0;
*status = 0;
return 0;
case VA1J5JF8007S_CHECK_FREQUENCY:
ret = va1j5jf8007s_check_frequency(state, &lock);
if (ret < 0)
return ret;
if (!lock) {
*delay = (HZ + 999) / 1000;
*status = 0;
return 0;
}
state->tune_state = VA1J5JF8007S_SET_MODULATION;
*delay = 0;
*status = FE_HAS_SIGNAL;
return 0;
case VA1J5JF8007S_SET_MODULATION:
ret = va1j5jf8007s_set_modulation(state);
if (ret < 0)
return ret;
state->tune_state = VA1J5JF8007S_CHECK_MODULATION;
*delay = 0;
*status = FE_HAS_SIGNAL;
return 0;
case VA1J5JF8007S_CHECK_MODULATION:
ret = va1j5jf8007s_check_modulation(state, &lock);
if (ret < 0)
return ret;
if (!lock) {
*delay = (HZ + 49) / 50;
*status = FE_HAS_SIGNAL;
return 0;
}
state->tune_state = VA1J5JF8007S_SET_TS_ID;
*delay = 0;
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
return 0;
case VA1J5JF8007S_SET_TS_ID:
ret = va1j5jf8007s_set_ts_id(state);
if (ret < 0)
return ret;
state->tune_state = VA1J5JF8007S_CHECK_TS_ID;
return 0;
case VA1J5JF8007S_CHECK_TS_ID:
ret = va1j5jf8007s_check_ts_id(state, &lock);
if (ret < 0)
return ret;
if (!lock) {
*delay = (HZ + 99) / 100;
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
return 0;
}
state->tune_state = VA1J5JF8007S_TRACK;
/* fall through */
case VA1J5JF8007S_TRACK:
*delay = 3 * HZ;
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
return 0;
}
BUG();
}
static int va1j5jf8007s_init_frequency(struct va1j5jf8007s_state *state)
{
u8 buf[4];
struct i2c_msg msg;
buf[0] = 0xfe;
buf[1] = 0xc0;
buf[2] = 0xf0;
buf[3] = 0x04;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int va1j5jf8007s_set_sleep(struct va1j5jf8007s_state *state, int sleep)
{
u8 buf[2];
struct i2c_msg msg;
buf[0] = 0x17;
buf[1] = sleep ? 0x01 : 0x00;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int va1j5jf8007s_sleep(struct dvb_frontend *fe)
{
struct va1j5jf8007s_state *state;
int ret;
state = fe->demodulator_priv;
ret = va1j5jf8007s_init_frequency(state);
if (ret < 0)
return ret;
return va1j5jf8007s_set_sleep(state, 1);
}
static int va1j5jf8007s_init(struct dvb_frontend *fe)
{
struct va1j5jf8007s_state *state;
state = fe->demodulator_priv;
state->tune_state = VA1J5JF8007S_IDLE;
return va1j5jf8007s_set_sleep(state, 0);
}
static void va1j5jf8007s_release(struct dvb_frontend *fe)
{
struct va1j5jf8007s_state *state;
state = fe->demodulator_priv;
kfree(state);
}
static struct dvb_frontend_ops va1j5jf8007s_ops = {
.info = {
.name = "VA1J5JF8007 ISDB-S",
.type = FE_QPSK,
.frequency_min = 950000,
.frequency_max = 2150000,
.frequency_stepsize = 1000,
.caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO |
FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
},
.get_frontend_algo = va1j5jf8007s_get_frontend_algo,
.read_status = va1j5jf8007s_read_status,
.tune = va1j5jf8007s_tune,
.sleep = va1j5jf8007s_sleep,
.init = va1j5jf8007s_init,
.release = va1j5jf8007s_release,
};
static int va1j5jf8007s_prepare_1(struct va1j5jf8007s_state *state)
{
u8 addr;
u8 write_buf[1], read_buf[1];
struct i2c_msg msgs[2];
addr = state->config->demod_address;
write_buf[0] = 0x07;
msgs[0].addr = addr;
msgs[0].flags = 0;
msgs[0].len = sizeof(write_buf);
msgs[0].buf = write_buf;
msgs[1].addr = addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = sizeof(read_buf);
msgs[1].buf = read_buf;
if (i2c_transfer(state->adap, msgs, 2) != 2)
return -EREMOTEIO;
if (read_buf[0] != 0x41)
return -EIO;
return 0;
}
static const u8 va1j5jf8007s_prepare_bufs[][2] = {
{0x04, 0x02}, {0x0d, 0x55}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01},
{0x1c, 0x0a}, {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0},
{0x52, 0x89}, {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69},
{0x87, 0x04}, {0x8e, 0x02}, {0xa3, 0xf7}, {0xa5, 0xc0},
};
static int va1j5jf8007s_prepare_2(struct va1j5jf8007s_state *state)
{
u8 addr;
u8 buf[2];
struct i2c_msg msg;
int i;
addr = state->config->demod_address;
msg.addr = addr;
msg.flags = 0;
msg.len = 2;
msg.buf = buf;
for (i = 0; i < ARRAY_SIZE(va1j5jf8007s_prepare_bufs); i++) {
memcpy(buf, va1j5jf8007s_prepare_bufs[i], sizeof(buf));
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
}
return 0;
}
/* must be called after va1j5jf8007t_attach */
int va1j5jf8007s_prepare(struct dvb_frontend *fe)
{
struct va1j5jf8007s_state *state;
int ret;
state = fe->demodulator_priv;
ret = va1j5jf8007s_prepare_1(state);
if (ret < 0)
return ret;
ret = va1j5jf8007s_prepare_2(state);
if (ret < 0)
return ret;
return va1j5jf8007s_init_frequency(state);
}
struct dvb_frontend *
va1j5jf8007s_attach(const struct va1j5jf8007s_config *config,
struct i2c_adapter *adap)
{
struct va1j5jf8007s_state *state;
struct dvb_frontend *fe;
u8 buf[2];
struct i2c_msg msg;
state = kzalloc(sizeof(struct va1j5jf8007s_state), GFP_KERNEL);
if (!state)
return NULL;
state->config = config;
state->adap = adap;
fe = &state->fe;
memcpy(&fe->ops, &va1j5jf8007s_ops, sizeof(struct dvb_frontend_ops));
fe->demodulator_priv = state;
buf[0] = 0x01;
buf[1] = 0x80;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1) {
kfree(state);
return NULL;
}
return fe;
}

View File

@@ -0,0 +1,40 @@
/*
* ISDB-S driver for VA1J5JF8007
*
* Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
*
* based on pt1dvr - http://pt1dvr.sourceforge.jp/
* by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
*
* 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 VA1J5JF8007S_H
#define VA1J5JF8007S_H
struct va1j5jf8007s_config {
u8 demod_address;
};
struct i2c_adapter;
struct dvb_frontend *
va1j5jf8007s_attach(const struct va1j5jf8007s_config *config,
struct i2c_adapter *adap);
/* must be called after va1j5jf8007t_attach */
int va1j5jf8007s_prepare(struct dvb_frontend *fe);
#endif

View File

@@ -0,0 +1,468 @@
/*
* ISDB-T driver for VA1J5JF8007
*
* Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
*
* based on pt1dvr - http://pt1dvr.sourceforge.jp/
* by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
*
* 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 <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include "dvb_frontend.h"
#include "dvb_math.h"
#include "va1j5jf8007t.h"
enum va1j5jf8007t_tune_state {
VA1J5JF8007T_IDLE,
VA1J5JF8007T_SET_FREQUENCY,
VA1J5JF8007T_CHECK_FREQUENCY,
VA1J5JF8007T_SET_MODULATION,
VA1J5JF8007T_CHECK_MODULATION,
VA1J5JF8007T_TRACK,
VA1J5JF8007T_ABORT,
};
struct va1j5jf8007t_state {
const struct va1j5jf8007t_config *config;
struct i2c_adapter *adap;
struct dvb_frontend fe;
enum va1j5jf8007t_tune_state tune_state;
};
static int va1j5jf8007t_get_frontend_algo(struct dvb_frontend *fe)
{
return DVBFE_ALGO_HW;
}
static int
va1j5jf8007t_read_status(struct dvb_frontend *fe, fe_status_t *status)
{
struct va1j5jf8007t_state *state;
state = fe->demodulator_priv;
switch (state->tune_state) {
case VA1J5JF8007T_IDLE:
case VA1J5JF8007T_SET_FREQUENCY:
case VA1J5JF8007T_CHECK_FREQUENCY:
*status = 0;
return 0;
case VA1J5JF8007T_SET_MODULATION:
case VA1J5JF8007T_CHECK_MODULATION:
case VA1J5JF8007T_ABORT:
*status |= FE_HAS_SIGNAL;
return 0;
case VA1J5JF8007T_TRACK:
*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
return 0;
}
BUG();
}
struct va1j5jf8007t_cb_map {
u32 frequency;
u8 cb;
};
static const struct va1j5jf8007t_cb_map va1j5jf8007t_cb_maps[] = {
{ 90000000, 0x80 },
{ 140000000, 0x81 },
{ 170000000, 0xa1 },
{ 220000000, 0x62 },
{ 330000000, 0xa2 },
{ 402000000, 0xe2 },
{ 450000000, 0x64 },
{ 550000000, 0x84 },
{ 600000000, 0xa4 },
{ 700000000, 0xc4 },
};
static u8 va1j5jf8007t_lookup_cb(u32 frequency)
{
int i;
const struct va1j5jf8007t_cb_map *map;
for (i = 0; i < ARRAY_SIZE(va1j5jf8007t_cb_maps); i++) {
map = &va1j5jf8007t_cb_maps[i];
if (frequency < map->frequency)
return map->cb;
}
return 0xe4;
}
static int va1j5jf8007t_set_frequency(struct va1j5jf8007t_state *state)
{
u32 frequency;
u16 word;
u8 buf[6];
struct i2c_msg msg;
frequency = state->fe.dtv_property_cache.frequency;
word = (frequency + 71428) / 142857 + 399;
buf[0] = 0xfe;
buf[1] = 0xc2;
buf[2] = word >> 8;
buf[3] = word;
buf[4] = 0x80;
buf[5] = va1j5jf8007t_lookup_cb(frequency);
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int
va1j5jf8007t_check_frequency(struct va1j5jf8007t_state *state, int *lock)
{
u8 addr;
u8 write_buf[2], read_buf[1];
struct i2c_msg msgs[2];
addr = state->config->demod_address;
write_buf[0] = 0xfe;
write_buf[1] = 0xc3;
msgs[0].addr = addr;
msgs[0].flags = 0;
msgs[0].len = sizeof(write_buf);
msgs[0].buf = write_buf;
msgs[1].addr = addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = sizeof(read_buf);
msgs[1].buf = read_buf;
if (i2c_transfer(state->adap, msgs, 2) != 2)
return -EREMOTEIO;
*lock = read_buf[0] & 0x40;
return 0;
}
static int va1j5jf8007t_set_modulation(struct va1j5jf8007t_state *state)
{
u8 buf[2];
struct i2c_msg msg;
buf[0] = 0x01;
buf[1] = 0x40;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int va1j5jf8007t_check_modulation(struct va1j5jf8007t_state *state,
int *lock, int *retry)
{
u8 addr;
u8 write_buf[1], read_buf[1];
struct i2c_msg msgs[2];
addr = state->config->demod_address;
write_buf[0] = 0x80;
msgs[0].addr = addr;
msgs[0].flags = 0;
msgs[0].len = sizeof(write_buf);
msgs[0].buf = write_buf;
msgs[1].addr = addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = sizeof(read_buf);
msgs[1].buf = read_buf;
if (i2c_transfer(state->adap, msgs, 2) != 2)
return -EREMOTEIO;
*lock = !(read_buf[0] & 0x10);
*retry = read_buf[0] & 0x80;
return 0;
}
static int
va1j5jf8007t_tune(struct dvb_frontend *fe,
struct dvb_frontend_parameters *params,
unsigned int mode_flags, unsigned int *delay,
fe_status_t *status)
{
struct va1j5jf8007t_state *state;
int ret;
int lock, retry;
state = fe->demodulator_priv;
if (params != NULL)
state->tune_state = VA1J5JF8007T_SET_FREQUENCY;
switch (state->tune_state) {
case VA1J5JF8007T_IDLE:
*delay = 3 * HZ;
*status = 0;
return 0;
case VA1J5JF8007T_SET_FREQUENCY:
ret = va1j5jf8007t_set_frequency(state);
if (ret < 0)
return ret;
state->tune_state = VA1J5JF8007T_CHECK_FREQUENCY;
*delay = 0;
*status = 0;
return 0;
case VA1J5JF8007T_CHECK_FREQUENCY:
ret = va1j5jf8007t_check_frequency(state, &lock);
if (ret < 0)
return ret;
if (!lock) {
*delay = (HZ + 999) / 1000;
*status = 0;
return 0;
}
state->tune_state = VA1J5JF8007T_SET_MODULATION;
*delay = 0;
*status = FE_HAS_SIGNAL;
return 0;
case VA1J5JF8007T_SET_MODULATION:
ret = va1j5jf8007t_set_modulation(state);
if (ret < 0)
return ret;
state->tune_state = VA1J5JF8007T_CHECK_MODULATION;
*delay = 0;
*status = FE_HAS_SIGNAL;
return 0;
case VA1J5JF8007T_CHECK_MODULATION:
ret = va1j5jf8007t_check_modulation(state, &lock, &retry);
if (ret < 0)
return ret;
if (!lock) {
if (!retry) {
state->tune_state = VA1J5JF8007T_ABORT;
*delay = 3 * HZ;
*status = FE_HAS_SIGNAL;
return 0;
}
*delay = (HZ + 999) / 1000;
*status = FE_HAS_SIGNAL;
return 0;
}
state->tune_state = VA1J5JF8007T_TRACK;
/* fall through */
case VA1J5JF8007T_TRACK:
*delay = 3 * HZ;
*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
return 0;
case VA1J5JF8007T_ABORT:
*delay = 3 * HZ;
*status = FE_HAS_SIGNAL;
return 0;
}
BUG();
}
static int va1j5jf8007t_init_frequency(struct va1j5jf8007t_state *state)
{
u8 buf[7];
struct i2c_msg msg;
buf[0] = 0xfe;
buf[1] = 0xc2;
buf[2] = 0x01;
buf[3] = 0x8f;
buf[4] = 0xc1;
buf[5] = 0x80;
buf[6] = 0x80;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int va1j5jf8007t_set_sleep(struct va1j5jf8007t_state *state, int sleep)
{
u8 buf[2];
struct i2c_msg msg;
buf[0] = 0x03;
buf[1] = sleep ? 0x90 : 0x80;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
return 0;
}
static int va1j5jf8007t_sleep(struct dvb_frontend *fe)
{
struct va1j5jf8007t_state *state;
int ret;
state = fe->demodulator_priv;
ret = va1j5jf8007t_init_frequency(state);
if (ret < 0)
return ret;
return va1j5jf8007t_set_sleep(state, 1);
}
static int va1j5jf8007t_init(struct dvb_frontend *fe)
{
struct va1j5jf8007t_state *state;
state = fe->demodulator_priv;
state->tune_state = VA1J5JF8007T_IDLE;
return va1j5jf8007t_set_sleep(state, 0);
}
static void va1j5jf8007t_release(struct dvb_frontend *fe)
{
struct va1j5jf8007t_state *state;
state = fe->demodulator_priv;
kfree(state);
}
static struct dvb_frontend_ops va1j5jf8007t_ops = {
.info = {
.name = "VA1J5JF8007 ISDB-T",
.type = FE_OFDM,
.frequency_min = 90000000,
.frequency_max = 770000000,
.frequency_stepsize = 142857,
.caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO |
FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
},
.get_frontend_algo = va1j5jf8007t_get_frontend_algo,
.read_status = va1j5jf8007t_read_status,
.tune = va1j5jf8007t_tune,
.sleep = va1j5jf8007t_sleep,
.init = va1j5jf8007t_init,
.release = va1j5jf8007t_release,
};
static const u8 va1j5jf8007t_prepare_bufs[][2] = {
{0x03, 0x90}, {0x14, 0x8f}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2},
{0x22, 0x83}, {0x31, 0x0d}, {0x32, 0xe0}, {0x39, 0xd3}, {0x3a, 0x00},
{0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x02}, {0x76, 0x4e}, {0x77, 0x03},
{0xef, 0x01}
};
int va1j5jf8007t_prepare(struct dvb_frontend *fe)
{
struct va1j5jf8007t_state *state;
u8 buf[2];
struct i2c_msg msg;
int i;
state = fe->demodulator_priv;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
for (i = 0; i < ARRAY_SIZE(va1j5jf8007t_prepare_bufs); i++) {
memcpy(buf, va1j5jf8007t_prepare_bufs[i], sizeof(buf));
if (i2c_transfer(state->adap, &msg, 1) != 1)
return -EREMOTEIO;
}
return va1j5jf8007t_init_frequency(state);
}
struct dvb_frontend *
va1j5jf8007t_attach(const struct va1j5jf8007t_config *config,
struct i2c_adapter *adap)
{
struct va1j5jf8007t_state *state;
struct dvb_frontend *fe;
u8 buf[2];
struct i2c_msg msg;
state = kzalloc(sizeof(struct va1j5jf8007t_state), GFP_KERNEL);
if (!state)
return NULL;
state->config = config;
state->adap = adap;
fe = &state->fe;
memcpy(&fe->ops, &va1j5jf8007t_ops, sizeof(struct dvb_frontend_ops));
fe->demodulator_priv = state;
buf[0] = 0x01;
buf[1] = 0x80;
msg.addr = state->config->demod_address;
msg.flags = 0;
msg.len = sizeof(buf);
msg.buf = buf;
if (i2c_transfer(state->adap, &msg, 1) != 1) {
kfree(state);
return NULL;
}
return fe;
}

View File

@@ -0,0 +1,40 @@
/*
* ISDB-T driver for VA1J5JF8007
*
* Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
*
* based on pt1dvr - http://pt1dvr.sourceforge.jp/
* by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
*
* 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 VA1J5JF8007T_H
#define VA1J5JF8007T_H
struct va1j5jf8007t_config {
u8 demod_address;
};
struct i2c_adapter;
struct dvb_frontend *
va1j5jf8007t_attach(const struct va1j5jf8007t_config *config,
struct i2c_adapter *adap);
/* must be called after va1j5jf8007s_attach */
int va1j5jf8007t_prepare(struct dvb_frontend *fe);
#endif

View File

@@ -346,7 +346,7 @@ config RADIO_SI4713
---help---
Say Y here if you want support to Si4713 FM Radio Transmitter.
This device can transmit audio through FM. It can transmit
EDS and EBDS signals as well. This module is the v4l2 radio
RDS and RBDS signals as well. This module is the v4l2 radio
interface for the i2c driver of this device.
To compile this driver as a module, choose M here: the

View File

@@ -24,7 +24,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>

View File

@@ -265,6 +265,15 @@ config VIDEO_SAA6588
comment "Video decoders"
config VIDEO_ADV7180
tristate "Analog Devices ADV7180 decoder"
depends on VIDEO_V4L2 && I2C
---help---
Support for the Analog Devices ADV7180 video decoder.
To compile this driver as a module, choose M here: the
module will be called adv7180.
config VIDEO_BT819
tristate "BT819A VideoStream decoder"
depends on VIDEO_V4L2 && I2C
@@ -493,6 +502,39 @@ config VIDEO_UPD64083
endmenu # encoder / decoder chips
config DISPLAY_DAVINCI_DM646X_EVM
tristate "DM646x EVM Video Display"
depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM
select VIDEOBUF_DMA_CONTIG
select VIDEO_DAVINCI_VPIF
select VIDEO_ADV7343
select VIDEO_THS7303
help
Support for DM6467 based display device.
To compile this driver as a module, choose M here: the
module will be called vpif_display.
config CAPTURE_DAVINCI_DM646X_EVM
tristate "DM646x EVM Video Capture"
depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM
select VIDEOBUF_DMA_CONTIG
select VIDEO_DAVINCI_VPIF
help
Support for DM6467 based capture device.
To compile this driver as a module, choose M here: the
module will be called vpif_capture.
config VIDEO_DAVINCI_VPIF
tristate "DaVinci VPIF Driver"
depends on DISPLAY_DAVINCI_DM646X_EVM
help
Support for DaVinci VPIF Driver.
To compile this driver as a module, choose M here: the
module will be called vpif.
config VIDEO_VIVI
tristate "Virtual Video Driver"
depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64
@@ -505,6 +547,55 @@ config VIDEO_VIVI
Say Y here if you want to test video apps or debug V4L devices.
In doubt, say N.
config VIDEO_VPSS_SYSTEM
tristate "VPSS System module driver"
depends on ARCH_DAVINCI
help
Support for vpss system module for video driver
default y
config VIDEO_VPFE_CAPTURE
tristate "VPFE Video Capture Driver"
depends on VIDEO_V4L2 && ARCH_DAVINCI
select VIDEOBUF_DMA_CONTIG
help
Support for DMXXXX VPFE based frame grabber. This is the
common V4L2 module for following DMXXX SoCs from Texas
Instruments:- DM6446 & DM355.
To compile this driver as a module, choose M here: the
module will be called vpfe-capture.
config VIDEO_DM6446_CCDC
tristate "DM6446 CCDC HW module"
depends on ARCH_DAVINCI_DM644x && VIDEO_VPFE_CAPTURE
select VIDEO_VPSS_SYSTEM
default y
help
Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces
with decoder modules such as TVP5146 over BT656 or
sensor module such as MT9T001 over a raw interface. This
module configures the interface and CCDC/ISIF to do
video frame capture from slave decoders.
To compile this driver as a module, choose M here: the
module will be called vpfe.
config VIDEO_DM355_CCDC
tristate "DM355 CCDC HW module"
depends on ARCH_DAVINCI_DM355 && VIDEO_VPFE_CAPTURE
select VIDEO_VPSS_SYSTEM
default y
help
Enables DM355 CCD hw module. DM355 CCDC hw interfaces
with decoder modules such as TVP5146 over BT656 or
sensor module such as MT9T001 over a raw interface. This
module configures the interface and CCDC/ISIF to do
video frame capture from a slave decoders
To compile this driver as a module, choose M here: the
module will be called vpfe.
source "drivers/media/video/bt8xx/Kconfig"
config VIDEO_PMS
@@ -690,6 +781,8 @@ source "drivers/media/video/ivtv/Kconfig"
source "drivers/media/video/cx18/Kconfig"
source "drivers/media/video/saa7164/Kconfig"
config VIDEO_M32R_AR
tristate "AR devices"
depends on M32R && VIDEO_V4L1

View File

@@ -45,6 +45,7 @@ obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o
obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o
obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o
obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o
obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
obj-$(CONFIG_VIDEO_BT819) += bt819.o
@@ -154,12 +155,17 @@ obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o
obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
obj-$(CONFIG_ARCH_DAVINCI) += davinci/
obj-$(CONFIG_VIDEO_AU0828) += au0828/
obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/
obj-$(CONFIG_VIDEO_SAA7164) += saa7164/
obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
obj-$(CONFIG_ARCH_DAVINCI) += davinci/
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
EXTRA_CFLAGS += -Idrivers/media/common/tuners

View File

@@ -0,0 +1,202 @@
/*
* adv7180.c Analog Devices ADV7180 video decoder driver
* Copyright (c) 2009 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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 <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <media/v4l2-ioctl.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#define DRIVER_NAME "adv7180"
#define ADV7180_INPUT_CONTROL_REG 0x00
#define ADV7180_INPUT_CONTROL_PAL_BG_NTSC_J_SECAM 0x00
#define ADV7180_AUTODETECT_ENABLE_REG 0x07
#define ADV7180_AUTODETECT_DEFAULT 0x7f
#define ADV7180_STATUS1_REG 0x10
#define ADV7180_STATUS1_AUTOD_MASK 0x70
#define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00
#define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10
#define ADV7180_STATUS1_AUTOD_PAL_M 0x20
#define ADV7180_STATUS1_AUTOD_PAL_60 0x30
#define ADV7180_STATUS1_AUTOD_PAL_B_G 0x40
#define ADV7180_STATUS1_AUTOD_SECAM 0x50
#define ADV7180_STATUS1_AUTOD_PAL_COMB 0x60
#define ADV7180_STATUS1_AUTOD_SECAM_525 0x70
#define ADV7180_IDENT_REG 0x11
#define ADV7180_ID_7180 0x18
struct adv7180_state {
struct v4l2_subdev sd;
};
static v4l2_std_id determine_norm(struct i2c_client *client)
{
u8 status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG);
switch (status1 & ADV7180_STATUS1_AUTOD_MASK) {
case ADV7180_STATUS1_AUTOD_NTSM_M_J:
return V4L2_STD_NTSC_M_JP;
case ADV7180_STATUS1_AUTOD_NTSC_4_43:
return V4L2_STD_NTSC_443;
case ADV7180_STATUS1_AUTOD_PAL_M:
return V4L2_STD_PAL_M;
case ADV7180_STATUS1_AUTOD_PAL_60:
return V4L2_STD_PAL_60;
case ADV7180_STATUS1_AUTOD_PAL_B_G:
return V4L2_STD_PAL;
case ADV7180_STATUS1_AUTOD_SECAM:
return V4L2_STD_SECAM;
case ADV7180_STATUS1_AUTOD_PAL_COMB:
return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N;
case ADV7180_STATUS1_AUTOD_SECAM_525:
return V4L2_STD_SECAM;
default:
return V4L2_STD_UNKNOWN;
}
}
static inline struct adv7180_state *to_state(struct v4l2_subdev *sd)
{
return container_of(sd, struct adv7180_state, sd);
}
static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
*std = determine_norm(client);
return 0;
}
static int adv7180_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *chip)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7180, 0);
}
static const struct v4l2_subdev_video_ops adv7180_video_ops = {
.querystd = adv7180_querystd,
};
static const struct v4l2_subdev_core_ops adv7180_core_ops = {
.g_chip_ident = adv7180_g_chip_ident,
};
static const struct v4l2_subdev_ops adv7180_ops = {
.core = &adv7180_core_ops,
.video = &adv7180_video_ops,
};
/*
* Generic i2c probe
* concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
*/
static int adv7180_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adv7180_state *state;
struct v4l2_subdev *sd;
int ret;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
v4l_info(client, "chip found @ 0x%02x (%s)\n",
client->addr << 1, client->adapter->name);
state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
/* Initialize adv7180 */
/* enable autodetection */
ret = i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
ADV7180_INPUT_CONTROL_PAL_BG_NTSC_J_SECAM);
if (ret > 0)
ret = i2c_smbus_write_byte_data(client,
ADV7180_AUTODETECT_ENABLE_REG,
ADV7180_AUTODETECT_DEFAULT);
if (ret < 0) {
printk(KERN_ERR DRIVER_NAME
": Failed to communicate to chip: %d\n", ret);
return ret;
}
return 0;
}
static int adv7180_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
kfree(to_state(sd));
return 0;
}
static const struct i2c_device_id adv7180_id[] = {
{DRIVER_NAME, 0},
{},
};
MODULE_DEVICE_TABLE(i2c, adv7180_id);
static struct i2c_driver adv7180_driver = {
.driver = {
.owner = THIS_MODULE,
.name = DRIVER_NAME,
},
.probe = adv7180_probe,
.remove = adv7180_remove,
.id_table = adv7180_id,
};
static __init int adv7180_init(void)
{
return i2c_add_driver(&adv7180_driver);
}
static __exit void adv7180_exit(void)
{
i2c_del_driver(&adv7180_driver);
}
module_init(adv7180_init);
module_exit(adv7180_exit);
MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver");
MODULE_AUTHOR("Mocean Laboratories");
MODULE_LICENSE("GPL v2");

View File

@@ -24,7 +24,6 @@
#include <linux/module.h>
#include <linux/videodev2.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include <media/adv7343.h>
#include <media/v4l2-device.h>

View File

@@ -212,7 +212,7 @@ void au0828_card_setup(struct au0828_dev *dev)
be abstracted out if we ever need to support a different
demod) */
sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"au8522", "au8522", 0x8e >> 1);
"au8522", "au8522", 0x8e >> 1, NULL);
if (sd == NULL)
printk(KERN_ERR "analog subdev registration failed\n");
}
@@ -221,7 +221,7 @@ void au0828_card_setup(struct au0828_dev *dev)
if (dev->board.tuner_type != TUNER_ABSENT) {
/* Load the tuner module, which does the attach */
sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"tuner", "tuner", dev->board.tuner_addr);
"tuner", "tuner", dev->board.tuner_addr, NULL);
if (sd == NULL)
printk(KERN_ERR "tuner subdev registration fail\n");

View File

@@ -3524,8 +3524,8 @@ void __devinit bttv_init_card2(struct bttv *btv)
};
struct v4l2_subdev *sd;
sd = v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "saa6588", "saa6588", addrs);
sd = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "saa6588", "saa6588", 0, addrs);
btv->has_saa6588 = (sd != NULL);
}
@@ -3549,8 +3549,8 @@ void __devinit bttv_init_card2(struct bttv *btv)
I2C_CLIENT_END
};
btv->sd_msp34xx = v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "msp3400", "msp3400", addrs);
btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "msp3400", "msp3400", 0, addrs);
if (btv->sd_msp34xx)
return;
goto no_audio;
@@ -3563,16 +3563,16 @@ void __devinit bttv_init_card2(struct bttv *btv)
I2C_CLIENT_END
};
if (v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "tda7432", "tda7432", addrs))
if (v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "tda7432", "tda7432", 0, addrs))
return;
goto no_audio;
}
case 3: {
/* The user specified that we should probe for tvaudio */
btv->sd_tvaudio = v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "tvaudio", "tvaudio", tvaudio_addrs());
btv->sd_tvaudio = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "tvaudio", "tvaudio", 0, tvaudio_addrs());
if (btv->sd_tvaudio)
return;
goto no_audio;
@@ -3591,13 +3591,13 @@ void __devinit bttv_init_card2(struct bttv *btv)
it really is a msp3400, so it will return NULL when the device
found is really something else (e.g. a tea6300). */
if (!bttv_tvcards[btv->c.type].no_msp34xx) {
btv->sd_msp34xx = v4l2_i2c_new_probed_subdev_addr(&btv->c.v4l2_dev,
btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "msp3400", "msp3400",
I2C_ADDR_MSP3400 >> 1);
0, I2C_ADDRS(I2C_ADDR_MSP3400 >> 1));
} else if (bttv_tvcards[btv->c.type].msp34xx_alt) {
btv->sd_msp34xx = v4l2_i2c_new_probed_subdev_addr(&btv->c.v4l2_dev,
btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "msp3400", "msp3400",
I2C_ADDR_MSP3400_ALT >> 1);
0, I2C_ADDRS(I2C_ADDR_MSP3400_ALT >> 1));
}
/* If we found a msp34xx, then we're done. */
@@ -3611,14 +3611,14 @@ void __devinit bttv_init_card2(struct bttv *btv)
I2C_CLIENT_END
};
if (v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "tda7432", "tda7432", addrs))
if (v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "tda7432", "tda7432", 0, addrs))
return;
}
/* Now see if we can find one of the tvaudio devices. */
btv->sd_tvaudio = v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "tvaudio", "tvaudio", tvaudio_addrs());
btv->sd_tvaudio = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "tvaudio", "tvaudio", 0, tvaudio_addrs());
if (btv->sd_tvaudio)
return;
@@ -3641,15 +3641,15 @@ void __devinit bttv_init_tuner(struct bttv *btv)
/* Load tuner module before issuing tuner config call! */
if (bttv_tvcards[btv->c.type].has_radio)
v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "tuner", "tuner",
v4l2_i2c_tuner_addrs(ADDRS_RADIO));
v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
0, v4l2_i2c_tuner_addrs(ADDRS_RADIO));
v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "tuner", "tuner",
v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev,
0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
&btv->c.i2c_adap, "tuner", "tuner",
v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD));
0, v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD));
tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV;
tun_setup.type = btv->tuner_type;

View File

@@ -1955,7 +1955,7 @@ static int cafe_pci_probe(struct pci_dev *pdev,
cam->sensor_addr = 0x42;
cam->sensor = v4l2_i2c_new_subdev(&cam->v4l2_dev, &cam->i2c_adapter,
"ov7670", "ov7670", cam->sensor_addr);
"ov7670", "ov7670", cam->sensor_addr, NULL);
if (cam->sensor == NULL) {
ret = -ENODEV;
goto out_smbus;

View File

@@ -231,7 +231,7 @@ MODULE_PARM_DESC(enc_pcm_bufs,
"Number of encoder PCM buffers\n"
"\t\t\tDefault is computed from other enc_pcm_* parameters");
MODULE_PARM_DESC(cx18_first_minor, "Set kernel number assigned to first card");
MODULE_PARM_DESC(cx18_first_minor, "Set device node number assigned to first card");
MODULE_AUTHOR("Hans Verkuil");
MODULE_DESCRIPTION("CX23418 driver");

View File

@@ -116,7 +116,7 @@ static int cx18_i2c_new_ir(struct i2c_adapter *adap, u32 hw, const char *type,
/* Our default information for ir-kbd-i2c.c to use */
switch (hw) {
case CX18_HW_Z8F0811_IR_RX_HAUP:
info.platform_data = &z8f0811_ir_init_data;
info.platform_data = (void *) &z8f0811_ir_init_data;
break;
default:
break;
@@ -139,16 +139,16 @@ int cx18_i2c_register(struct cx18 *cx, unsigned idx)
if (hw == CX18_HW_TUNER) {
/* special tuner group handling */
sd = v4l2_i2c_new_probed_subdev(&cx->v4l2_dev,
adap, mod, type, cx->card_i2c->radio);
sd = v4l2_i2c_new_subdev(&cx->v4l2_dev,
adap, mod, type, 0, cx->card_i2c->radio);
if (sd != NULL)
sd->grp_id = hw;
sd = v4l2_i2c_new_probed_subdev(&cx->v4l2_dev,
adap, mod, type, cx->card_i2c->demod);
sd = v4l2_i2c_new_subdev(&cx->v4l2_dev,
adap, mod, type, 0, cx->card_i2c->demod);
if (sd != NULL)
sd->grp_id = hw;
sd = v4l2_i2c_new_probed_subdev(&cx->v4l2_dev,
adap, mod, type, cx->card_i2c->tv);
sd = v4l2_i2c_new_subdev(&cx->v4l2_dev,
adap, mod, type, 0, cx->card_i2c->tv);
if (sd != NULL)
sd->grp_id = hw;
return sd != NULL ? 0 : -1;
@@ -162,7 +162,7 @@ int cx18_i2c_register(struct cx18 *cx, unsigned idx)
return -1;
/* It's an I2C device other than an analog tuner or IR chip */
sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, adap, mod, type, hw_addrs[idx]);
sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, adap, mod, type, hw_addrs[idx], NULL);
if (sd != NULL)
sd->grp_id = hw;
return sd != NULL ? 0 : -1;

View File

@@ -245,9 +245,9 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
video_set_drvdata(s->video_dev, s);
/* Register device. First try the desired minor, then any free one. */
ret = video_register_device(s->video_dev, vfl_type, num);
ret = video_register_device_no_warn(s->video_dev, vfl_type, num);
if (ret < 0) {
CX18_ERR("Couldn't register v4l2 device for %s kernel number %d\n",
CX18_ERR("Couldn't register v4l2 device for %s (device node number %d)\n",
s->name, num);
video_device_release(s->video_dev);
s->video_dev = NULL;

View File

@@ -313,7 +313,7 @@ void cx231xx_card_setup(struct cx231xx *dev)
if (dev->board.decoder == CX231XX_AVDECODER) {
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[0].i2c_adap,
"cx25840", "cx25840", 0x88 >> 1);
"cx25840", "cx25840", 0x88 >> 1, NULL);
if (dev->sd_cx25840 == NULL)
cx231xx_info("cx25840 subdev registration failure\n");
cx25840_call(dev, core, load_fw);
@@ -323,7 +323,7 @@ void cx231xx_card_setup(struct cx231xx *dev)
if (dev->board.tuner_type != TUNER_ABSENT) {
dev->sd_tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[1].i2c_adap,
"tuner", "tuner", 0xc2 >> 1);
"tuner", "tuner", 0xc2 >> 1, NULL);
if (dev->sd_tuner == NULL)
cx231xx_info("tuner subdev registration failure\n");

View File

@@ -75,7 +75,6 @@ struct netup_ci_state {
void *priv;
};
struct mutex gpio_mutex;/* Two CiMax's uses same GPIO lines */
int netup_read_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg,
u8 *buf, int len)
@@ -183,10 +182,11 @@ int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
if (ret != 0)
return ret;
mutex_lock(&gpio_mutex);
mutex_lock(&dev->gpio_lock);
/* write addr */
cx_write(MC417_OEN, NETUP_EN_ALL);
msleep(2);
cx_write(MC417_RWD, NETUP_CTRL_OFF |
NETUP_ADLO | (0xff & addr));
cx_clear(MC417_RWD, NETUP_ADLO);
@@ -194,9 +194,10 @@ int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
NETUP_ADHI | (0xff & (addr >> 8)));
cx_clear(MC417_RWD, NETUP_ADHI);
if (read) /* data in */
if (read) { /* data in */
cx_write(MC417_OEN, NETUP_EN_ALL | NETUP_DATA);
else /* data out */
msleep(2);
} else /* data out */
cx_write(MC417_RWD, NETUP_CTRL_OFF | data);
/* choose chip */
@@ -206,7 +207,7 @@ int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
cx_clear(MC417_RWD, (read) ? NETUP_RD : NETUP_WR);
mem = netup_ci_get_mem(dev);
mutex_unlock(&gpio_mutex);
mutex_unlock(&dev->gpio_lock);
if (!read)
if (mem < 0)
@@ -403,7 +404,6 @@ int netup_ci_init(struct cx23885_tsport *port)
switch (port->nr) {
case 1:
state->ci_i2c_addr = 0x40;
mutex_init(&gpio_mutex);
break;
case 2:
state->ci_i2c_addr = 0x41;

View File

@@ -210,6 +210,10 @@ struct cx23885_board cx23885_boards[] = {
.portb = CX23885_MPEG_ENCODER,
.portc = CX23885_MPEG_DVB,
},
[CX23885_BOARD_COMPRO_VIDEOMATE_E800] = {
.name = "Compro VideoMate E800",
.portc = CX23885_MPEG_DVB,
},
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
@@ -341,6 +345,10 @@ struct cx23885_subid cx23885_subids[] = {
.subvendor = 0x0070,
.subdevice = 0x8541,
.card = CX23885_BOARD_HAUPPAUGE_HVR1850,
}, {
.subvendor = 0x1858,
.subdevice = 0xe800,
.card = CX23885_BOARD_COMPRO_VIDEOMATE_E800,
},
};
const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -536,6 +544,7 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg)
case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
/* Tuner Reset Command */
bitmask = 0x04;
break;
@@ -687,6 +696,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
break;
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
/* GPIO-2 xc3028 tuner reset */
/* The following GPIO's are on the internal AVCore (cx25840) */
@@ -911,6 +921,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1255:
case CX23885_BOARD_HAUPPAUGE_HVR1210:
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
default:
ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
@@ -927,9 +938,10 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[2].i2c_adap,
"cx25840", "cx25840", 0x88 >> 1);
"cx25840", "cx25840", 0x88 >> 1, NULL);
v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
break;
}

View File

@@ -758,6 +758,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
int i;
mutex_init(&dev->lock);
mutex_init(&dev->gpio_lock);
atomic_inc(&dev->refcount);

View File

@@ -255,15 +255,18 @@ static struct tda18271_std_map hauppauge_hvr1200_tda18271_std_map = {
static struct tda18271_config hauppauge_tda18271_config = {
.std_map = &hauppauge_tda18271_std_map,
.gate = TDA18271_GATE_ANALOG,
.output_opt = TDA18271_OUTPUT_LT_OFF,
};
static struct tda18271_config hauppauge_hvr1200_tuner_config = {
.std_map = &hauppauge_hvr1200_tda18271_std_map,
.gate = TDA18271_GATE_ANALOG,
.output_opt = TDA18271_OUTPUT_LT_OFF,
};
static struct tda18271_config hauppauge_hvr1210_tuner_config = {
.gate = TDA18271_GATE_DIGITAL,
.output_opt = TDA18271_OUTPUT_LT_OFF,
};
static struct tda18271_std_map hauppauge_hvr127x_std_map = {
@@ -275,6 +278,7 @@ static struct tda18271_std_map hauppauge_hvr127x_std_map = {
static struct tda18271_config hauppauge_hvr127x_config = {
.std_map = &hauppauge_hvr127x_std_map,
.output_opt = TDA18271_OUTPUT_LT_OFF,
};
static struct lgdt3305_config hauppauge_lgdt3305_config = {
@@ -743,6 +747,7 @@ static int dvb_register(struct cx23885_tsport *port)
}
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
i2c_bus = &dev->i2c_bus[0];
fe0->dvb.frontend = dvb_attach(zl10353_attach,

View File

@@ -1521,11 +1521,11 @@ int cx23885_video_register(struct cx23885_dev *dev)
if (dev->tuner_addr)
sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[1].i2c_adap,
"tuner", "tuner", dev->tuner_addr);
"tuner", "tuner", dev->tuner_addr, NULL);
else
sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev,
sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[1].i2c_adap,
"tuner", "tuner", v4l2_i2c_tuner_addrs(ADDRS_TV));
"tuner", "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_TV));
if (sd) {
struct tuner_setup tun_setup;

View File

@@ -78,6 +78,7 @@
#define CX23885_BOARD_MYGICA_X8506 22
#define CX23885_BOARD_MAGICPRO_PROHDTVE2 23
#define CX23885_BOARD_HAUPPAUGE_HVR1850 24
#define CX23885_BOARD_COMPRO_VIDEOMATE_E800 25
#define GPIO_0 0x00000001
#define GPIO_1 0x00000002
@@ -325,6 +326,7 @@ struct cx23885_dev {
int nr;
struct mutex lock;
struct mutex gpio_lock;
/* board details */
unsigned int board;

View File

@@ -97,11 +97,11 @@ void netup_get_card_info(struct i2c_adapter *i2c_adap,
{
int i, j;
cinfo->rev = netup_eeprom_read(i2c_adap, 13);
cinfo->rev = netup_eeprom_read(i2c_adap, 63);
for (i = 0, j = 0; i < 6; i++, j++)
for (i = 64, j = 0; i < 70; i++, j++)
cinfo->port[0].mac[j] = netup_eeprom_read(i2c_adap, i);
for (i = 6, j = 0; i < 12; i++, j++)
for (i = 70, j = 0; i < 76; i++, j++)
cinfo->port[1].mac[j] = netup_eeprom_read(i2c_adap, i);
};

View File

@@ -3439,20 +3439,20 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr)
The radio_type is sometimes missing, or set to UNSET but
later code configures a tea5767.
*/
v4l2_i2c_new_probed_subdev(&core->v4l2_dev, &core->i2c_adap,
v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
"tuner", "tuner",
v4l2_i2c_tuner_addrs(ADDRS_RADIO));
0, v4l2_i2c_tuner_addrs(ADDRS_RADIO));
if (has_demod)
v4l2_i2c_new_probed_subdev(&core->v4l2_dev,
v4l2_i2c_new_subdev(&core->v4l2_dev,
&core->i2c_adap, "tuner", "tuner",
v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
if (core->board.tuner_addr == ADDR_UNSET) {
v4l2_i2c_new_probed_subdev(&core->v4l2_dev,
v4l2_i2c_new_subdev(&core->v4l2_dev,
&core->i2c_adap, "tuner", "tuner",
has_demod ? tv_addrs + 4 : tv_addrs);
0, has_demod ? tv_addrs + 4 : tv_addrs);
} else {
v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
"tuner", "tuner", core->board.tuner_addr);
"tuner", "tuner", core->board.tuner_addr, NULL);
}
}

View File

@@ -1881,14 +1881,14 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
if (core->board.audio_chip == V4L2_IDENT_WM8775)
v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
"wm8775", "wm8775", 0x36 >> 1);
"wm8775", "wm8775", 0x36 >> 1, NULL);
if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) {
/* This probes for a tda9874 as is used on some
Pixelview Ultra boards. */
v4l2_i2c_new_probed_subdev_addr(&core->v4l2_dev,
v4l2_i2c_new_subdev(&core->v4l2_dev,
&core->i2c_adap,
"tvaudio", "tvaudio", 0xb0 >> 1);
"tvaudio", "tvaudio", 0, I2C_ADDRS(0xb0 >> 1));
}
switch (core->boardnr) {

View File

@@ -0,0 +1,17 @@
#
# Makefile for the davinci video device drivers.
#
# VPIF
obj-$(CONFIG_VIDEO_DAVINCI_VPIF) += vpif.o
#DM646x EVM Display driver
obj-$(CONFIG_DISPLAY_DAVINCI_DM646X_EVM) += vpif_display.o
#DM646x EVM Capture driver
obj-$(CONFIG_CAPTURE_DAVINCI_DM646X_EVM) += vpif_capture.o
# Capture: DM6446 and DM355
obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o
obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o
obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o
obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o

View File

@@ -0,0 +1,110 @@
/*
* Copyright (C) 2008-2009 Texas Instruments Inc
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ccdc device API
*/
#ifndef _CCDC_HW_DEVICE_H
#define _CCDC_HW_DEVICE_H
#ifdef __KERNEL__
#include <linux/videodev2.h>
#include <linux/device.h>
#include <media/davinci/vpfe_types.h>
#include <media/davinci/ccdc_types.h>
/*
* ccdc hw operations
*/
struct ccdc_hw_ops {
/* Pointer to initialize function to initialize ccdc device */
int (*open) (struct device *dev);
/* Pointer to deinitialize function */
int (*close) (struct device *dev);
/* set ccdc base address */
void (*set_ccdc_base)(void *base, int size);
/* Pointer to function to enable or disable ccdc */
void (*enable) (int en);
/* reset sbl. only for 6446 */
void (*reset) (void);
/* enable output to sdram */
void (*enable_out_to_sdram) (int en);
/* Pointer to function to set hw parameters */
int (*set_hw_if_params) (struct vpfe_hw_if_param *param);
/* get interface parameters */
int (*get_hw_if_params) (struct vpfe_hw_if_param *param);
/*
* Pointer to function to set parameters. Used
* for implementing VPFE_S_CCDC_PARAMS
*/
int (*set_params) (void *params);
/*
* Pointer to function to get parameter. Used
* for implementing VPFE_G_CCDC_PARAMS
*/
int (*get_params) (void *params);
/* Pointer to function to configure ccdc */
int (*configure) (void);
/* Pointer to function to set buffer type */
int (*set_buftype) (enum ccdc_buftype buf_type);
/* Pointer to function to get buffer type */
enum ccdc_buftype (*get_buftype) (void);
/* Pointer to function to set frame format */
int (*set_frame_format) (enum ccdc_frmfmt frm_fmt);
/* Pointer to function to get frame format */
enum ccdc_frmfmt (*get_frame_format) (void);
/* enumerate hw pix formats */
int (*enum_pix)(u32 *hw_pix, int i);
/* Pointer to function to set buffer type */
u32 (*get_pixel_format) (void);
/* Pointer to function to get pixel format. */
int (*set_pixel_format) (u32 pixfmt);
/* Pointer to function to set image window */
int (*set_image_window) (struct v4l2_rect *win);
/* Pointer to function to set image window */
void (*get_image_window) (struct v4l2_rect *win);
/* Pointer to function to get line length */
unsigned int (*get_line_length) (void);
/* Query CCDC control IDs */
int (*queryctrl)(struct v4l2_queryctrl *qctrl);
/* Set CCDC control */
int (*set_control)(struct v4l2_control *ctrl);
/* Get CCDC control */
int (*get_control)(struct v4l2_control *ctrl);
/* Pointer to function to set frame buffer address */
void (*setfbaddr) (unsigned long addr);
/* Pointer to function to get field id */
int (*getfid) (void);
};
struct ccdc_hw_device {
/* ccdc device name */
char name[32];
/* module owner */
struct module *owner;
/* hw ops */
struct ccdc_hw_ops hw_ops;
};
/* Used by CCDC module to register & unregister with vpfe capture driver */
int vpfe_register_ccdc_device(struct ccdc_hw_device *dev);
void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev);
#endif
#endif

View File

@@ -0,0 +1,978 @@
/*
* Copyright (C) 2005-2009 Texas Instruments Inc
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* CCDC hardware module for DM355
* ------------------------------
*
* This module is for configuring DM355 CCD controller of VPFE to capture
* Raw yuv or Bayer RGB data from a decoder. CCDC has several modules
* such as Defect Pixel Correction, Color Space Conversion etc to
* pre-process the Bayer RGB data, before writing it to SDRAM. This
* module also allows application to configure individual
* module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL.
* To do so, application include dm355_ccdc.h and vpfe_capture.h header
* files. The setparams() API is called by vpfe_capture driver
* to configure module parameters
*
* TODO: 1) Raw bayer parameter settings and bayer capture
* 2) Split module parameter structure to module specific ioctl structs
* 3) add support for lense shading correction
* 4) investigate if enum used for user space type definition
* to be replaced by #defines or integer
*/
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/videodev2.h>
#include <media/davinci/dm355_ccdc.h>
#include <media/davinci/vpss.h>
#include "dm355_ccdc_regs.h"
#include "ccdc_hw_device.h"
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CCDC Driver for DM355");
MODULE_AUTHOR("Texas Instruments");
static struct device *dev;
/* Object for CCDC raw mode */
static struct ccdc_params_raw ccdc_hw_params_raw = {
.pix_fmt = CCDC_PIXFMT_RAW,
.frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
.win = CCDC_WIN_VGA,
.fid_pol = VPFE_PINPOL_POSITIVE,
.vd_pol = VPFE_PINPOL_POSITIVE,
.hd_pol = VPFE_PINPOL_POSITIVE,
.gain = {
.r_ye = 256,
.gb_g = 256,
.gr_cy = 256,
.b_mg = 256
},
.config_params = {
.datasft = 2,
.data_sz = CCDC_DATA_10BITS,
.mfilt1 = CCDC_NO_MEDIAN_FILTER1,
.mfilt2 = CCDC_NO_MEDIAN_FILTER2,
.alaw = {
.gama_wd = 2,
},
.blk_clamp = {
.sample_pixel = 1,
.dc_sub = 25
},
.col_pat_field0 = {
.olop = CCDC_GREEN_BLUE,
.olep = CCDC_BLUE,
.elop = CCDC_RED,
.elep = CCDC_GREEN_RED
},
.col_pat_field1 = {
.olop = CCDC_GREEN_BLUE,
.olep = CCDC_BLUE,
.elop = CCDC_RED,
.elep = CCDC_GREEN_RED
},
},
};
/* Object for CCDC ycbcr mode */
static struct ccdc_params_ycbcr ccdc_hw_params_ycbcr = {
.win = CCDC_WIN_PAL,
.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
.frm_fmt = CCDC_FRMFMT_INTERLACED,
.fid_pol = VPFE_PINPOL_POSITIVE,
.vd_pol = VPFE_PINPOL_POSITIVE,
.hd_pol = VPFE_PINPOL_POSITIVE,
.bt656_enable = 1,
.pix_order = CCDC_PIXORDER_CBYCRY,
.buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED
};
static enum vpfe_hw_if_type ccdc_if_type;
static void *__iomem ccdc_base_addr;
static int ccdc_addr_size;
/* Raw Bayer formats */
static u32 ccdc_raw_bayer_pix_formats[] =
{V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16};
/* Raw YUV formats */
static u32 ccdc_raw_yuv_pix_formats[] =
{V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV};
/* register access routines */
static inline u32 regr(u32 offset)
{
return __raw_readl(ccdc_base_addr + offset);
}
static inline void regw(u32 val, u32 offset)
{
__raw_writel(val, ccdc_base_addr + offset);
}
static void ccdc_set_ccdc_base(void *addr, int size)
{
ccdc_base_addr = addr;
ccdc_addr_size = size;
}
static void ccdc_enable(int en)
{
unsigned int temp;
temp = regr(SYNCEN);
temp &= (~CCDC_SYNCEN_VDHDEN_MASK);
temp |= (en & CCDC_SYNCEN_VDHDEN_MASK);
regw(temp, SYNCEN);
}
static void ccdc_enable_output_to_sdram(int en)
{
unsigned int temp;
temp = regr(SYNCEN);
temp &= (~(CCDC_SYNCEN_WEN_MASK));
temp |= ((en << CCDC_SYNCEN_WEN_SHIFT) & CCDC_SYNCEN_WEN_MASK);
regw(temp, SYNCEN);
}
static void ccdc_config_gain_offset(void)
{
/* configure gain */
regw(ccdc_hw_params_raw.gain.r_ye, RYEGAIN);
regw(ccdc_hw_params_raw.gain.gr_cy, GRCYGAIN);
regw(ccdc_hw_params_raw.gain.gb_g, GBGGAIN);
regw(ccdc_hw_params_raw.gain.b_mg, BMGGAIN);
/* configure offset */
regw(ccdc_hw_params_raw.ccdc_offset, OFFSET);
}
/*
* ccdc_restore_defaults()
* This function restore power on defaults in the ccdc registers
*/
static int ccdc_restore_defaults(void)
{
int i;
dev_dbg(dev, "\nstarting ccdc_restore_defaults...");
/* set all registers to zero */
for (i = 0; i <= CCDC_REG_LAST; i += 4)
regw(0, i);
/* now override the values with power on defaults in registers */
regw(MODESET_DEFAULT, MODESET);
/* no culling support */
regw(CULH_DEFAULT, CULH);
regw(CULV_DEFAULT, CULV);
/* Set default Gain and Offset */
ccdc_hw_params_raw.gain.r_ye = GAIN_DEFAULT;
ccdc_hw_params_raw.gain.gb_g = GAIN_DEFAULT;
ccdc_hw_params_raw.gain.gr_cy = GAIN_DEFAULT;
ccdc_hw_params_raw.gain.b_mg = GAIN_DEFAULT;
ccdc_config_gain_offset();
regw(OUTCLIP_DEFAULT, OUTCLIP);
regw(LSCCFG2_DEFAULT, LSCCFG2);
/* select ccdc input */
if (vpss_select_ccdc_source(VPSS_CCDCIN)) {
dev_dbg(dev, "\ncouldn't select ccdc input source");
return -EFAULT;
}
/* select ccdc clock */
if (vpss_enable_clock(VPSS_CCDC_CLOCK, 1) < 0) {
dev_dbg(dev, "\ncouldn't enable ccdc clock");
return -EFAULT;
}
dev_dbg(dev, "\nEnd of ccdc_restore_defaults...");
return 0;
}
static int ccdc_open(struct device *device)
{
dev = device;
return ccdc_restore_defaults();
}
static int ccdc_close(struct device *device)
{
/* disable clock */
vpss_enable_clock(VPSS_CCDC_CLOCK, 0);
/* do nothing for now */
return 0;
}
/*
* ccdc_setwin()
* This function will configure the window size to
* be capture in CCDC reg.
*/
static void ccdc_setwin(struct v4l2_rect *image_win,
enum ccdc_frmfmt frm_fmt, int ppc)
{
int horz_start, horz_nr_pixels;
int vert_start, vert_nr_lines;
int mid_img = 0;
dev_dbg(dev, "\nStarting ccdc_setwin...");
/*
* ppc - per pixel count. indicates how many pixels per cell
* output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
* raw capture this is 1
*/
horz_start = image_win->left << (ppc - 1);
horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1;
/* Writing the horizontal info into the registers */
regw(horz_start, SPH);
regw(horz_nr_pixels, NPH);
vert_start = image_win->top;
if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
vert_nr_lines = (image_win->height >> 1) - 1;
vert_start >>= 1;
/* Since first line doesn't have any data */
vert_start += 1;
/* configure VDINT0 and VDINT1 */
regw(vert_start, VDINT0);
} else {
/* Since first line doesn't have any data */
vert_start += 1;
vert_nr_lines = image_win->height - 1;
/* configure VDINT0 and VDINT1 */
mid_img = vert_start + (image_win->height / 2);
regw(vert_start, VDINT0);
regw(mid_img, VDINT1);
}
regw(vert_start & CCDC_START_VER_ONE_MASK, SLV0);
regw(vert_start & CCDC_START_VER_TWO_MASK, SLV1);
regw(vert_nr_lines & CCDC_NUM_LINES_VER, NLV);
dev_dbg(dev, "\nEnd of ccdc_setwin...");
}
static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
{
if (ccdcparam->datasft < CCDC_DATA_NO_SHIFT ||
ccdcparam->datasft > CCDC_DATA_SHIFT_6BIT) {
dev_dbg(dev, "Invalid value of data shift\n");
return -EINVAL;
}
if (ccdcparam->mfilt1 < CCDC_NO_MEDIAN_FILTER1 ||
ccdcparam->mfilt1 > CCDC_MEDIAN_FILTER1) {
dev_dbg(dev, "Invalid value of median filter1\n");
return -EINVAL;
}
if (ccdcparam->mfilt2 < CCDC_NO_MEDIAN_FILTER2 ||
ccdcparam->mfilt2 > CCDC_MEDIAN_FILTER2) {
dev_dbg(dev, "Invalid value of median filter2\n");
return -EINVAL;
}
if ((ccdcparam->med_filt_thres < 0) ||
(ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) {
dev_dbg(dev, "Invalid value of median filter thresold\n");
return -EINVAL;
}
if (ccdcparam->data_sz < CCDC_DATA_16BITS ||
ccdcparam->data_sz > CCDC_DATA_8BITS) {
dev_dbg(dev, "Invalid value of data size\n");
return -EINVAL;
}
if (ccdcparam->alaw.enable) {
if (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_13_4 ||
ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) {
dev_dbg(dev, "Invalid value of ALAW\n");
return -EINVAL;
}
}
if (ccdcparam->blk_clamp.b_clamp_enable) {
if (ccdcparam->blk_clamp.sample_pixel < CCDC_SAMPLE_1PIXELS ||
ccdcparam->blk_clamp.sample_pixel > CCDC_SAMPLE_16PIXELS) {
dev_dbg(dev, "Invalid value of sample pixel\n");
return -EINVAL;
}
if (ccdcparam->blk_clamp.sample_ln < CCDC_SAMPLE_1LINES ||
ccdcparam->blk_clamp.sample_ln > CCDC_SAMPLE_16LINES) {
dev_dbg(dev, "Invalid value of sample lines\n");
return -EINVAL;
}
}
return 0;
}
/* Parameter operations */
static int ccdc_set_params(void __user *params)
{
struct ccdc_config_params_raw ccdc_raw_params;
int x;
/* only raw module parameters can be set through the IOCTL */
if (ccdc_if_type != VPFE_RAW_BAYER)
return -EINVAL;
x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params));
if (x) {
dev_dbg(dev, "ccdc_set_params: error in copying ccdc"
"params, %d\n", x);
return -EFAULT;
}
if (!validate_ccdc_param(&ccdc_raw_params)) {
memcpy(&ccdc_hw_params_raw.config_params,
&ccdc_raw_params,
sizeof(ccdc_raw_params));
return 0;
}
return -EINVAL;
}
/* This function will configure CCDC for YCbCr video capture */
static void ccdc_config_ycbcr(void)
{
struct ccdc_params_ycbcr *params = &ccdc_hw_params_ycbcr;
u32 temp;
/* first set the CCDC power on defaults values in all registers */
dev_dbg(dev, "\nStarting ccdc_config_ycbcr...");
ccdc_restore_defaults();
/* configure pixel format & video frame format */
temp = (((params->pix_fmt & CCDC_INPUT_MODE_MASK) <<
CCDC_INPUT_MODE_SHIFT) |
((params->frm_fmt & CCDC_FRM_FMT_MASK) <<
CCDC_FRM_FMT_SHIFT));
/* setup BT.656 sync mode */
if (params->bt656_enable) {
regw(CCDC_REC656IF_BT656_EN, REC656IF);
/*
* configure the FID, VD, HD pin polarity fld,hd pol positive,
* vd negative, 8-bit pack mode
*/
temp |= CCDC_VD_POL_NEGATIVE;
} else { /* y/c external sync mode */
temp |= (((params->fid_pol & CCDC_FID_POL_MASK) <<
CCDC_FID_POL_SHIFT) |
((params->hd_pol & CCDC_HD_POL_MASK) <<
CCDC_HD_POL_SHIFT) |
((params->vd_pol & CCDC_VD_POL_MASK) <<
CCDC_VD_POL_SHIFT));
}
/* pack the data to 8-bit */
temp |= CCDC_DATA_PACK_ENABLE;
regw(temp, MODESET);
/* configure video window */
ccdc_setwin(&params->win, params->frm_fmt, 2);
/* configure the order of y cb cr in SD-RAM */
temp = (params->pix_order << CCDC_Y8POS_SHIFT);
temp |= CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC;
regw(temp, CCDCFG);
/*
* configure the horizontal line offset. This is done by rounding up
* width to a multiple of 16 pixels and multiply by two to account for
* y:cb:cr 4:2:2 data
*/
regw(((params->win.width * 2 + 31) >> 5), HSIZE);
/* configure the memory line offset */
if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) {
/* two fields are interleaved in memory */
regw(CCDC_SDOFST_FIELD_INTERLEAVED, SDOFST);
}
dev_dbg(dev, "\nEnd of ccdc_config_ycbcr...\n");
}
/*
* ccdc_config_black_clamp()
* configure parameters for Optical Black Clamp
*/
static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp)
{
u32 val;
if (!bclamp->b_clamp_enable) {
/* configure DCSub */
regw(bclamp->dc_sub & CCDC_BLK_DC_SUB_MASK, DCSUB);
regw(0x0000, CLAMP);
return;
}
/* Enable the Black clamping, set sample lines and pixels */
val = (bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) |
((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) <<
CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE;
regw(val, CLAMP);
/* If Black clamping is enable then make dcsub 0 */
val = (bclamp->sample_ln & CCDC_NUM_LINE_CALC_MASK)
<< CCDC_NUM_LINE_CALC_SHIFT;
regw(val, DCSUB);
}
/*
* ccdc_config_black_compense()
* configure parameters for Black Compensation
*/
static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp)
{
u32 val;
val = (bcomp->b & CCDC_BLK_COMP_MASK) |
((bcomp->gb & CCDC_BLK_COMP_MASK) <<
CCDC_BLK_COMP_GB_COMP_SHIFT);
regw(val, BLKCMP1);
val = ((bcomp->gr & CCDC_BLK_COMP_MASK) <<
CCDC_BLK_COMP_GR_COMP_SHIFT) |
((bcomp->r & CCDC_BLK_COMP_MASK) <<
CCDC_BLK_COMP_R_COMP_SHIFT);
regw(val, BLKCMP0);
}
/*
* ccdc_write_dfc_entry()
* write an entry in the dfc table.
*/
int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc)
{
/* TODO This is to be re-visited and adjusted */
#define DFC_WRITE_WAIT_COUNT 1000
u32 val, count = DFC_WRITE_WAIT_COUNT;
regw(dfc->dft_corr_vert[index], DFCMEM0);
regw(dfc->dft_corr_horz[index], DFCMEM1);
regw(dfc->dft_corr_sub1[index], DFCMEM2);
regw(dfc->dft_corr_sub2[index], DFCMEM3);
regw(dfc->dft_corr_sub3[index], DFCMEM4);
/* set WR bit to write */
val = regr(DFCMEMCTL) | CCDC_DFCMEMCTL_DFCMWR_MASK;
regw(val, DFCMEMCTL);
/*
* Assume, it is very short. If we get an error, we need to
* adjust this value
*/
while (regr(DFCMEMCTL) & CCDC_DFCMEMCTL_DFCMWR_MASK)
count--;
/*
* TODO We expect the count to be non-zero to be successful. Adjust
* the count if write requires more time
*/
if (count) {
dev_err(dev, "defect table write timeout !!!\n");
return -1;
}
return 0;
}
/*
* ccdc_config_vdfc()
* configure parameters for Vertical Defect Correction
*/
static int ccdc_config_vdfc(struct ccdc_vertical_dft *dfc)
{
u32 val;
int i;
/* Configure General Defect Correction. The table used is from IPIPE */
val = dfc->gen_dft_en & CCDC_DFCCTL_GDFCEN_MASK;
/* Configure Vertical Defect Correction if needed */
if (!dfc->ver_dft_en) {
/* Enable only General Defect Correction */
regw(val, DFCCTL);
return 0;
}
if (dfc->table_size > CCDC_DFT_TABLE_SIZE)
return -EINVAL;
val |= CCDC_DFCCTL_VDFC_DISABLE;
val |= (dfc->dft_corr_ctl.vdfcsl & CCDC_DFCCTL_VDFCSL_MASK) <<
CCDC_DFCCTL_VDFCSL_SHIFT;
val |= (dfc->dft_corr_ctl.vdfcuda & CCDC_DFCCTL_VDFCUDA_MASK) <<
CCDC_DFCCTL_VDFCUDA_SHIFT;
val |= (dfc->dft_corr_ctl.vdflsft & CCDC_DFCCTL_VDFLSFT_MASK) <<
CCDC_DFCCTL_VDFLSFT_SHIFT;
regw(val , DFCCTL);
/* clear address ptr to offset 0 */
val = CCDC_DFCMEMCTL_DFCMARST_MASK << CCDC_DFCMEMCTL_DFCMARST_SHIFT;
/* write defect table entries */
for (i = 0; i < dfc->table_size; i++) {
/* increment address for non zero index */
if (i != 0)
val = CCDC_DFCMEMCTL_INC_ADDR;
regw(val, DFCMEMCTL);
if (ccdc_write_dfc_entry(i, dfc) < 0)
return -EFAULT;
}
/* update saturation level and enable dfc */
regw(dfc->saturation_ctl & CCDC_VDC_DFCVSAT_MASK, DFCVSAT);
val = regr(DFCCTL) | (CCDC_DFCCTL_VDFCEN_MASK <<
CCDC_DFCCTL_VDFCEN_SHIFT);
regw(val, DFCCTL);
return 0;
}
/*
* ccdc_config_csc()
* configure parameters for color space conversion
* Each register CSCM0-7 has two values in S8Q5 format.
*/
static void ccdc_config_csc(struct ccdc_csc *csc)
{
u32 val1, val2;
int i;
if (!csc->enable)
return;
/* Enable the CSC sub-module */
regw(CCDC_CSC_ENABLE, CSCCTL);
/* Converting the co-eff as per the format of the register */
for (i = 0; i < CCDC_CSC_COEFF_TABLE_SIZE; i++) {
if ((i % 2) == 0) {
/* CSCM - LSB */
val1 = (csc->coeff[i].integer &
CCDC_CSC_COEF_INTEG_MASK)
<< CCDC_CSC_COEF_INTEG_SHIFT;
/*
* convert decimal part to binary. Use 2 decimal
* precision, user values range from .00 - 0.99
*/
val1 |= (((csc->coeff[i].decimal &
CCDC_CSC_COEF_DECIMAL_MASK) *
CCDC_CSC_DEC_MAX) / 100);
} else {
/* CSCM - MSB */
val2 = (csc->coeff[i].integer &
CCDC_CSC_COEF_INTEG_MASK)
<< CCDC_CSC_COEF_INTEG_SHIFT;
val2 |= (((csc->coeff[i].decimal &
CCDC_CSC_COEF_DECIMAL_MASK) *
CCDC_CSC_DEC_MAX) / 100);
val2 <<= CCDC_CSCM_MSB_SHIFT;
val2 |= val1;
regw(val2, (CSCM0 + ((i - 1) << 1)));
}
}
}
/*
* ccdc_config_color_patterns()
* configure parameters for color patterns
*/
static void ccdc_config_color_patterns(struct ccdc_col_pat *pat0,
struct ccdc_col_pat *pat1)
{
u32 val;
val = (pat0->olop | (pat0->olep << 2) | (pat0->elop << 4) |
(pat0->elep << 6) | (pat1->olop << 8) | (pat1->olep << 10) |
(pat1->elop << 12) | (pat1->elep << 14));
regw(val, COLPTN);
}
/* This function will configure CCDC for Raw mode image capture */
static int ccdc_config_raw(void)
{
struct ccdc_params_raw *params = &ccdc_hw_params_raw;
struct ccdc_config_params_raw *config_params =
&ccdc_hw_params_raw.config_params;
unsigned int val;
dev_dbg(dev, "\nStarting ccdc_config_raw...");
/* restore power on defaults to register */
ccdc_restore_defaults();
/* CCDCFG register:
* set CCD Not to swap input since input is RAW data
* set FID detection function to Latch at V-Sync
* set WENLOG - ccdc valid area to AND
* set TRGSEL to WENBIT
* set EXTRG to DISABLE
* disable latching function on VSYNC - shadowed registers
*/
regw(CCDC_YCINSWP_RAW | CCDC_CCDCFG_FIDMD_LATCH_VSYNC |
CCDC_CCDCFG_WENLOG_AND | CCDC_CCDCFG_TRGSEL_WEN |
CCDC_CCDCFG_EXTRG_DISABLE | CCDC_LATCH_ON_VSYNC_DISABLE, CCDCFG);
/*
* Set VDHD direction to input, input type to raw input
* normal data polarity, do not use external WEN
*/
val = (CCDC_VDHDOUT_INPUT | CCDC_RAW_IP_MODE | CCDC_DATAPOL_NORMAL |
CCDC_EXWEN_DISABLE);
/*
* Configure the vertical sync polarity (MODESET.VDPOL), horizontal
* sync polarity (MODESET.HDPOL), field id polarity (MODESET.FLDPOL),
* frame format(progressive or interlace), & pixel format (Input mode)
*/
val |= (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) |
((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) |
((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) |
((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) |
((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT));
/* set pack for alaw compression */
if ((config_params->data_sz == CCDC_DATA_8BITS) ||
config_params->alaw.enable)
val |= CCDC_DATA_PACK_ENABLE;
/* Configure for LPF */
if (config_params->lpf_enable)
val |= (config_params->lpf_enable & CCDC_LPF_MASK) <<
CCDC_LPF_SHIFT;
/* Configure the data shift */
val |= (config_params->datasft & CCDC_DATASFT_MASK) <<
CCDC_DATASFT_SHIFT;
regw(val , MODESET);
dev_dbg(dev, "\nWriting 0x%x to MODESET...\n", val);
/* Configure the Median Filter threshold */
regw((config_params->med_filt_thres) & CCDC_MED_FILT_THRESH, MEDFILT);
/* Configure GAMMAWD register. defaur 11-2, and Mosaic cfa pattern */
val = CCDC_GAMMA_BITS_11_2 << CCDC_GAMMAWD_INPUT_SHIFT |
CCDC_CFA_MOSAIC;
/* Enable and configure aLaw register if needed */
if (config_params->alaw.enable) {
val |= (CCDC_ALAW_ENABLE |
((config_params->alaw.gama_wd &
CCDC_ALAW_GAMA_WD_MASK) <<
CCDC_GAMMAWD_INPUT_SHIFT));
}
/* Configure Median filter1 & filter2 */
val |= ((config_params->mfilt1 << CCDC_MFILT1_SHIFT) |
(config_params->mfilt2 << CCDC_MFILT2_SHIFT));
regw(val, GAMMAWD);
dev_dbg(dev, "\nWriting 0x%x to GAMMAWD...\n", val);
/* configure video window */
ccdc_setwin(&params->win, params->frm_fmt, 1);
/* Optical Clamp Averaging */
ccdc_config_black_clamp(&config_params->blk_clamp);
/* Black level compensation */
ccdc_config_black_compense(&config_params->blk_comp);
/* Vertical Defect Correction if needed */
if (ccdc_config_vdfc(&config_params->vertical_dft) < 0)
return -EFAULT;
/* color space conversion */
ccdc_config_csc(&config_params->csc);
/* color pattern */
ccdc_config_color_patterns(&config_params->col_pat_field0,
&config_params->col_pat_field1);
/* Configure the Gain & offset control */
ccdc_config_gain_offset();
dev_dbg(dev, "\nWriting %x to COLPTN...\n", val);
/* Configure DATAOFST register */
val = (config_params->data_offset.horz_offset & CCDC_DATAOFST_MASK) <<
CCDC_DATAOFST_H_SHIFT;
val |= (config_params->data_offset.vert_offset & CCDC_DATAOFST_MASK) <<
CCDC_DATAOFST_V_SHIFT;
regw(val, DATAOFST);
/* configuring HSIZE register */
val = (params->horz_flip_enable & CCDC_HSIZE_FLIP_MASK) <<
CCDC_HSIZE_FLIP_SHIFT;
/* If pack 8 is enable then 1 pixel will take 1 byte */
if ((config_params->data_sz == CCDC_DATA_8BITS) ||
config_params->alaw.enable) {
val |= (((params->win.width) + 31) >> 5) &
CCDC_HSIZE_VAL_MASK;
/* adjust to multiple of 32 */
dev_dbg(dev, "\nWriting 0x%x to HSIZE...\n",
(((params->win.width) + 31) >> 5) &
CCDC_HSIZE_VAL_MASK);
} else {
/* else one pixel will take 2 byte */
val |= (((params->win.width * 2) + 31) >> 5) &
CCDC_HSIZE_VAL_MASK;
dev_dbg(dev, "\nWriting 0x%x to HSIZE...\n",
(((params->win.width * 2) + 31) >> 5) &
CCDC_HSIZE_VAL_MASK);
}
regw(val, HSIZE);
/* Configure SDOFST register */
if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {
if (params->image_invert_enable) {
/* For interlace inverse mode */
regw(CCDC_SDOFST_INTERLACE_INVERSE, SDOFST);
dev_dbg(dev, "\nWriting %x to SDOFST...\n",
CCDC_SDOFST_INTERLACE_INVERSE);
} else {
/* For interlace non inverse mode */
regw(CCDC_SDOFST_INTERLACE_NORMAL, SDOFST);
dev_dbg(dev, "\nWriting %x to SDOFST...\n",
CCDC_SDOFST_INTERLACE_NORMAL);
}
} else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
if (params->image_invert_enable) {
/* For progessive inverse mode */
regw(CCDC_SDOFST_PROGRESSIVE_INVERSE, SDOFST);
dev_dbg(dev, "\nWriting %x to SDOFST...\n",
CCDC_SDOFST_PROGRESSIVE_INVERSE);
} else {
/* For progessive non inverse mode */
regw(CCDC_SDOFST_PROGRESSIVE_NORMAL, SDOFST);
dev_dbg(dev, "\nWriting %x to SDOFST...\n",
CCDC_SDOFST_PROGRESSIVE_NORMAL);
}
}
dev_dbg(dev, "\nend of ccdc_config_raw...");
return 0;
}
static int ccdc_configure(void)
{
if (ccdc_if_type == VPFE_RAW_BAYER)
return ccdc_config_raw();
else
ccdc_config_ycbcr();
return 0;
}
static int ccdc_set_buftype(enum ccdc_buftype buf_type)
{
if (ccdc_if_type == VPFE_RAW_BAYER)
ccdc_hw_params_raw.buf_type = buf_type;
else
ccdc_hw_params_ycbcr.buf_type = buf_type;
return 0;
}
static enum ccdc_buftype ccdc_get_buftype(void)
{
if (ccdc_if_type == VPFE_RAW_BAYER)
return ccdc_hw_params_raw.buf_type;
return ccdc_hw_params_ycbcr.buf_type;
}
static int ccdc_enum_pix(u32 *pix, int i)
{
int ret = -EINVAL;
if (ccdc_if_type == VPFE_RAW_BAYER) {
if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) {
*pix = ccdc_raw_bayer_pix_formats[i];
ret = 0;
}
} else {
if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) {
*pix = ccdc_raw_yuv_pix_formats[i];
ret = 0;
}
}
return ret;
}
static int ccdc_set_pixel_format(u32 pixfmt)
{
struct ccdc_a_law *alaw =
&ccdc_hw_params_raw.config_params.alaw;
if (ccdc_if_type == VPFE_RAW_BAYER) {
ccdc_hw_params_raw.pix_fmt = CCDC_PIXFMT_RAW;
if (pixfmt == V4L2_PIX_FMT_SBGGR8)
alaw->enable = 1;
else if (pixfmt != V4L2_PIX_FMT_SBGGR16)
return -EINVAL;
} else {
if (pixfmt == V4L2_PIX_FMT_YUYV)
ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
else if (pixfmt == V4L2_PIX_FMT_UYVY)
ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
else
return -EINVAL;
}
return 0;
}
static u32 ccdc_get_pixel_format(void)
{
struct ccdc_a_law *alaw =
&ccdc_hw_params_raw.config_params.alaw;
u32 pixfmt;
if (ccdc_if_type == VPFE_RAW_BAYER)
if (alaw->enable)
pixfmt = V4L2_PIX_FMT_SBGGR8;
else
pixfmt = V4L2_PIX_FMT_SBGGR16;
else {
if (ccdc_hw_params_ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
pixfmt = V4L2_PIX_FMT_YUYV;
else
pixfmt = V4L2_PIX_FMT_UYVY;
}
return pixfmt;
}
static int ccdc_set_image_window(struct v4l2_rect *win)
{
if (ccdc_if_type == VPFE_RAW_BAYER)
ccdc_hw_params_raw.win = *win;
else
ccdc_hw_params_ycbcr.win = *win;
return 0;
}
static void ccdc_get_image_window(struct v4l2_rect *win)
{
if (ccdc_if_type == VPFE_RAW_BAYER)
*win = ccdc_hw_params_raw.win;
else
*win = ccdc_hw_params_ycbcr.win;
}
static unsigned int ccdc_get_line_length(void)
{
struct ccdc_config_params_raw *config_params =
&ccdc_hw_params_raw.config_params;
unsigned int len;
if (ccdc_if_type == VPFE_RAW_BAYER) {
if ((config_params->alaw.enable) ||
(config_params->data_sz == CCDC_DATA_8BITS))
len = ccdc_hw_params_raw.win.width;
else
len = ccdc_hw_params_raw.win.width * 2;
} else
len = ccdc_hw_params_ycbcr.win.width * 2;
return ALIGN(len, 32);
}
static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt)
{
if (ccdc_if_type == VPFE_RAW_BAYER)
ccdc_hw_params_raw.frm_fmt = frm_fmt;
else
ccdc_hw_params_ycbcr.frm_fmt = frm_fmt;
return 0;
}
static enum ccdc_frmfmt ccdc_get_frame_format(void)
{
if (ccdc_if_type == VPFE_RAW_BAYER)
return ccdc_hw_params_raw.frm_fmt;
else
return ccdc_hw_params_ycbcr.frm_fmt;
}
static int ccdc_getfid(void)
{
return (regr(MODESET) >> 15) & 1;
}
/* misc operations */
static inline void ccdc_setfbaddr(unsigned long addr)
{
regw((addr >> 21) & 0x007f, STADRH);
regw((addr >> 5) & 0x0ffff, STADRL);
}
static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params)
{
ccdc_if_type = params->if_type;
switch (params->if_type) {
case VPFE_BT656:
case VPFE_YCBCR_SYNC_16:
case VPFE_YCBCR_SYNC_8:
ccdc_hw_params_ycbcr.vd_pol = params->vdpol;
ccdc_hw_params_ycbcr.hd_pol = params->hdpol;
break;
default:
/* TODO add support for raw bayer here */
return -EINVAL;
}
return 0;
}
static struct ccdc_hw_device ccdc_hw_dev = {
.name = "DM355 CCDC",
.owner = THIS_MODULE,
.hw_ops = {
.open = ccdc_open,
.close = ccdc_close,
.set_ccdc_base = ccdc_set_ccdc_base,
.enable = ccdc_enable,
.enable_out_to_sdram = ccdc_enable_output_to_sdram,
.set_hw_if_params = ccdc_set_hw_if_params,
.set_params = ccdc_set_params,
.configure = ccdc_configure,
.set_buftype = ccdc_set_buftype,
.get_buftype = ccdc_get_buftype,
.enum_pix = ccdc_enum_pix,
.set_pixel_format = ccdc_set_pixel_format,
.get_pixel_format = ccdc_get_pixel_format,
.set_frame_format = ccdc_set_frame_format,
.get_frame_format = ccdc_get_frame_format,
.set_image_window = ccdc_set_image_window,
.get_image_window = ccdc_get_image_window,
.get_line_length = ccdc_get_line_length,
.setfbaddr = ccdc_setfbaddr,
.getfid = ccdc_getfid,
},
};
static int dm355_ccdc_init(void)
{
printk(KERN_NOTICE "dm355_ccdc_init\n");
if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0)
return -1;
printk(KERN_NOTICE "%s is registered with vpfe.\n",
ccdc_hw_dev.name);
return 0;
}
static void dm355_ccdc_exit(void)
{
vpfe_unregister_ccdc_device(&ccdc_hw_dev);
}
module_init(dm355_ccdc_init);
module_exit(dm355_ccdc_exit);

View File

@@ -0,0 +1,310 @@
/*
* Copyright (C) 2005-2009 Texas Instruments Inc
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _DM355_CCDC_REGS_H
#define _DM355_CCDC_REGS_H
/**************************************************************************\
* Register OFFSET Definitions
\**************************************************************************/
#define SYNCEN 0x00
#define MODESET 0x04
#define HDWIDTH 0x08
#define VDWIDTH 0x0c
#define PPLN 0x10
#define LPFR 0x14
#define SPH 0x18
#define NPH 0x1c
#define SLV0 0x20
#define SLV1 0x24
#define NLV 0x28
#define CULH 0x2c
#define CULV 0x30
#define HSIZE 0x34
#define SDOFST 0x38
#define STADRH 0x3c
#define STADRL 0x40
#define CLAMP 0x44
#define DCSUB 0x48
#define COLPTN 0x4c
#define BLKCMP0 0x50
#define BLKCMP1 0x54
#define MEDFILT 0x58
#define RYEGAIN 0x5c
#define GRCYGAIN 0x60
#define GBGGAIN 0x64
#define BMGGAIN 0x68
#define OFFSET 0x6c
#define OUTCLIP 0x70
#define VDINT0 0x74
#define VDINT1 0x78
#define RSV0 0x7c
#define GAMMAWD 0x80
#define REC656IF 0x84
#define CCDCFG 0x88
#define FMTCFG 0x8c
#define FMTPLEN 0x90
#define FMTSPH 0x94
#define FMTLNH 0x98
#define FMTSLV 0x9c
#define FMTLNV 0xa0
#define FMTRLEN 0xa4
#define FMTHCNT 0xa8
#define FMT_ADDR_PTR_B 0xac
#define FMT_ADDR_PTR(i) (FMT_ADDR_PTR_B + (i * 4))
#define FMTPGM_VF0 0xcc
#define FMTPGM_VF1 0xd0
#define FMTPGM_AP0 0xd4
#define FMTPGM_AP1 0xd8
#define FMTPGM_AP2 0xdc
#define FMTPGM_AP3 0xe0
#define FMTPGM_AP4 0xe4
#define FMTPGM_AP5 0xe8
#define FMTPGM_AP6 0xec
#define FMTPGM_AP7 0xf0
#define LSCCFG1 0xf4
#define LSCCFG2 0xf8
#define LSCH0 0xfc
#define LSCV0 0x100
#define LSCKH 0x104
#define LSCKV 0x108
#define LSCMEMCTL 0x10c
#define LSCMEMD 0x110
#define LSCMEMQ 0x114
#define DFCCTL 0x118
#define DFCVSAT 0x11c
#define DFCMEMCTL 0x120
#define DFCMEM0 0x124
#define DFCMEM1 0x128
#define DFCMEM2 0x12c
#define DFCMEM3 0x130
#define DFCMEM4 0x134
#define CSCCTL 0x138
#define CSCM0 0x13c
#define CSCM1 0x140
#define CSCM2 0x144
#define CSCM3 0x148
#define CSCM4 0x14c
#define CSCM5 0x150
#define CSCM6 0x154
#define CSCM7 0x158
#define DATAOFST 0x15c
#define CCDC_REG_LAST DATAOFST
/**************************************************************
* Define for various register bit mask and shifts for CCDC
*
**************************************************************/
#define CCDC_RAW_IP_MODE 0
#define CCDC_VDHDOUT_INPUT 0
#define CCDC_YCINSWP_RAW (0 << 4)
#define CCDC_EXWEN_DISABLE 0
#define CCDC_DATAPOL_NORMAL 0
#define CCDC_CCDCFG_FIDMD_LATCH_VSYNC 0
#define CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC (1 << 6)
#define CCDC_CCDCFG_WENLOG_AND 0
#define CCDC_CCDCFG_TRGSEL_WEN 0
#define CCDC_CCDCFG_EXTRG_DISABLE 0
#define CCDC_CFA_MOSAIC 0
#define CCDC_Y8POS_SHIFT 11
#define CCDC_VDC_DFCVSAT_MASK 0x3fff
#define CCDC_DATAOFST_MASK 0x0ff
#define CCDC_DATAOFST_H_SHIFT 0
#define CCDC_DATAOFST_V_SHIFT 8
#define CCDC_GAMMAWD_CFA_MASK 1
#define CCDC_GAMMAWD_CFA_SHIFT 5
#define CCDC_GAMMAWD_INPUT_SHIFT 2
#define CCDC_FID_POL_MASK 1
#define CCDC_FID_POL_SHIFT 4
#define CCDC_HD_POL_MASK 1
#define CCDC_HD_POL_SHIFT 3
#define CCDC_VD_POL_MASK 1
#define CCDC_VD_POL_SHIFT 2
#define CCDC_VD_POL_NEGATIVE (1 << 2)
#define CCDC_FRM_FMT_MASK 1
#define CCDC_FRM_FMT_SHIFT 7
#define CCDC_DATA_SZ_MASK 7
#define CCDC_DATA_SZ_SHIFT 8
#define CCDC_VDHDOUT_MASK 1
#define CCDC_VDHDOUT_SHIFT 0
#define CCDC_EXWEN_MASK 1
#define CCDC_EXWEN_SHIFT 5
#define CCDC_INPUT_MODE_MASK 3
#define CCDC_INPUT_MODE_SHIFT 12
#define CCDC_PIX_FMT_MASK 3
#define CCDC_PIX_FMT_SHIFT 12
#define CCDC_DATAPOL_MASK 1
#define CCDC_DATAPOL_SHIFT 6
#define CCDC_WEN_ENABLE (1 << 1)
#define CCDC_VDHDEN_ENABLE (1 << 16)
#define CCDC_LPF_ENABLE (1 << 14)
#define CCDC_ALAW_ENABLE 1
#define CCDC_ALAW_GAMA_WD_MASK 7
#define CCDC_REC656IF_BT656_EN 3
#define CCDC_FMTCFG_FMTMODE_MASK 3
#define CCDC_FMTCFG_FMTMODE_SHIFT 1
#define CCDC_FMTCFG_LNUM_MASK 3
#define CCDC_FMTCFG_LNUM_SHIFT 4
#define CCDC_FMTCFG_ADDRINC_MASK 7
#define CCDC_FMTCFG_ADDRINC_SHIFT 8
#define CCDC_CCDCFG_FIDMD_SHIFT 6
#define CCDC_CCDCFG_WENLOG_SHIFT 8
#define CCDC_CCDCFG_TRGSEL_SHIFT 9
#define CCDC_CCDCFG_EXTRG_SHIFT 10
#define CCDC_CCDCFG_MSBINVI_SHIFT 13
#define CCDC_HSIZE_FLIP_SHIFT 12
#define CCDC_HSIZE_FLIP_MASK 1
#define CCDC_HSIZE_VAL_MASK 0xFFF
#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249
#define CCDC_SDOFST_INTERLACE_INVERSE 0x4B6D
#define CCDC_SDOFST_INTERLACE_NORMAL 0x0B6D
#define CCDC_SDOFST_PROGRESSIVE_INVERSE 0x4000
#define CCDC_SDOFST_PROGRESSIVE_NORMAL 0
#define CCDC_START_PX_HOR_MASK 0x7FFF
#define CCDC_NUM_PX_HOR_MASK 0x7FFF
#define CCDC_START_VER_ONE_MASK 0x7FFF
#define CCDC_START_VER_TWO_MASK 0x7FFF
#define CCDC_NUM_LINES_VER 0x7FFF
#define CCDC_BLK_CLAMP_ENABLE (1 << 15)
#define CCDC_BLK_SGAIN_MASK 0x1F
#define CCDC_BLK_ST_PXL_MASK 0x1FFF
#define CCDC_BLK_SAMPLE_LN_MASK 3
#define CCDC_BLK_SAMPLE_LN_SHIFT 13
#define CCDC_NUM_LINE_CALC_MASK 3
#define CCDC_NUM_LINE_CALC_SHIFT 14
#define CCDC_BLK_DC_SUB_MASK 0x3FFF
#define CCDC_BLK_COMP_MASK 0xFF
#define CCDC_BLK_COMP_GB_COMP_SHIFT 8
#define CCDC_BLK_COMP_GR_COMP_SHIFT 0
#define CCDC_BLK_COMP_R_COMP_SHIFT 8
#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15)
#define CCDC_LATCH_ON_VSYNC_ENABLE (0 << 15)
#define CCDC_FPC_ENABLE (1 << 15)
#define CCDC_FPC_FPC_NUM_MASK 0x7FFF
#define CCDC_DATA_PACK_ENABLE (1 << 11)
#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF
#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF
#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16
#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF
#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF
#define CCDC_FMT_VERT_FMTSLV_SHIFT 16
#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF
#define CCDC_VP_OUT_VERT_NUM_SHIFT 17
#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF
#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4
#define CCDC_VP_OUT_HORZ_ST_MASK 0xF
#define CCDC_CSC_COEF_INTEG_MASK 7
#define CCDC_CSC_COEF_DECIMAL_MASK 0x1f
#define CCDC_CSC_COEF_INTEG_SHIFT 5
#define CCDC_CSCM_MSB_SHIFT 8
#define CCDC_CSC_ENABLE 1
#define CCDC_CSC_DEC_MAX 32
#define CCDC_MFILT1_SHIFT 10
#define CCDC_MFILT2_SHIFT 8
#define CCDC_MED_FILT_THRESH 0x3FFF
#define CCDC_LPF_MASK 1
#define CCDC_LPF_SHIFT 14
#define CCDC_OFFSET_MASK 0x3FF
#define CCDC_DATASFT_MASK 7
#define CCDC_DATASFT_SHIFT 8
#define CCDC_DF_ENABLE 1
#define CCDC_FMTPLEN_P0_MASK 0xF
#define CCDC_FMTPLEN_P1_MASK 0xF
#define CCDC_FMTPLEN_P2_MASK 7
#define CCDC_FMTPLEN_P3_MASK 7
#define CCDC_FMTPLEN_P0_SHIFT 0
#define CCDC_FMTPLEN_P1_SHIFT 4
#define CCDC_FMTPLEN_P2_SHIFT 8
#define CCDC_FMTPLEN_P3_SHIFT 12
#define CCDC_FMTSPH_MASK 0x1FFF
#define CCDC_FMTLNH_MASK 0x1FFF
#define CCDC_FMTSLV_MASK 0x1FFF
#define CCDC_FMTLNV_MASK 0x7FFF
#define CCDC_FMTRLEN_MASK 0x1FFF
#define CCDC_FMTHCNT_MASK 0x1FFF
#define CCDC_ADP_INIT_MASK 0x1FFF
#define CCDC_ADP_LINE_SHIFT 13
#define CCDC_ADP_LINE_MASK 3
#define CCDC_FMTPGN_APTR_MASK 7
#define CCDC_DFCCTL_GDFCEN_MASK 1
#define CCDC_DFCCTL_VDFCEN_MASK 1
#define CCDC_DFCCTL_VDFC_DISABLE (0 << 4)
#define CCDC_DFCCTL_VDFCEN_SHIFT 4
#define CCDC_DFCCTL_VDFCSL_MASK 3
#define CCDC_DFCCTL_VDFCSL_SHIFT 5
#define CCDC_DFCCTL_VDFCUDA_MASK 1
#define CCDC_DFCCTL_VDFCUDA_SHIFT 7
#define CCDC_DFCCTL_VDFLSFT_MASK 3
#define CCDC_DFCCTL_VDFLSFT_SHIFT 8
#define CCDC_DFCMEMCTL_DFCMARST_MASK 1
#define CCDC_DFCMEMCTL_DFCMARST_SHIFT 2
#define CCDC_DFCMEMCTL_DFCMWR_MASK 1
#define CCDC_DFCMEMCTL_DFCMWR_SHIFT 0
#define CCDC_DFCMEMCTL_INC_ADDR (0 << 2)
#define CCDC_LSCCFG_GFTSF_MASK 7
#define CCDC_LSCCFG_GFTSF_SHIFT 1
#define CCDC_LSCCFG_GFTINV_MASK 0xf
#define CCDC_LSCCFG_GFTINV_SHIFT 4
#define CCDC_LSC_GFTABLE_SEL_MASK 3
#define CCDC_LSC_GFTABLE_EPEL_SHIFT 8
#define CCDC_LSC_GFTABLE_OPEL_SHIFT 10
#define CCDC_LSC_GFTABLE_EPOL_SHIFT 12
#define CCDC_LSC_GFTABLE_OPOL_SHIFT 14
#define CCDC_LSC_GFMODE_MASK 3
#define CCDC_LSC_GFMODE_SHIFT 4
#define CCDC_LSC_DISABLE 0
#define CCDC_LSC_ENABLE 1
#define CCDC_LSC_TABLE1_SLC 0
#define CCDC_LSC_TABLE2_SLC 1
#define CCDC_LSC_TABLE3_SLC 2
#define CCDC_LSC_MEMADDR_RESET (1 << 2)
#define CCDC_LSC_MEMADDR_INCR (0 << 2)
#define CCDC_LSC_FRAC_MASK_T1 0xFF
#define CCDC_LSC_INT_MASK 3
#define CCDC_LSC_FRAC_MASK 0x3FFF
#define CCDC_LSC_CENTRE_MASK 0x3FFF
#define CCDC_LSC_COEF_MASK 0xff
#define CCDC_LSC_COEFL_SHIFT 0
#define CCDC_LSC_COEFU_SHIFT 8
#define CCDC_GAIN_MASK 0x7FF
#define CCDC_SYNCEN_VDHDEN_MASK (1 << 0)
#define CCDC_SYNCEN_WEN_MASK (1 << 1)
#define CCDC_SYNCEN_WEN_SHIFT 1
/* Power on Defaults in hardware */
#define MODESET_DEFAULT 0x200
#define CULH_DEFAULT 0xFFFF
#define CULV_DEFAULT 0xFF
#define GAIN_DEFAULT 256
#define OUTCLIP_DEFAULT 0x3FFF
#define LSCCFG2_DEFAULT 0xE
#endif

View File

@@ -0,0 +1,878 @@
/*
* Copyright (C) 2006-2009 Texas Instruments Inc
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* CCDC hardware module for DM6446
* ------------------------------
*
* This module is for configuring CCD controller of DM6446 VPFE to capture
* Raw yuv or Bayer RGB data from a decoder. CCDC has several modules
* such as Defect Pixel Correction, Color Space Conversion etc to
* pre-process the Raw Bayer RGB data, before writing it to SDRAM. This
* module also allows application to configure individual
* module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL.
* To do so, application includes dm644x_ccdc.h and vpfe_capture.h header
* files. The setparams() API is called by vpfe_capture driver
* to configure module parameters. This file is named DM644x so that other
* variants such DM6443 may be supported using the same module.
*
* TODO: Test Raw bayer parameter settings and bayer capture
* Split module parameter structure to module specific ioctl structs
* investigate if enum used for user space type definition
* to be replaced by #defines or integer
*/
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/videodev2.h>
#include <media/davinci/dm644x_ccdc.h>
#include <media/davinci/vpss.h>
#include "dm644x_ccdc_regs.h"
#include "ccdc_hw_device.h"
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CCDC Driver for DM6446");
MODULE_AUTHOR("Texas Instruments");
static struct device *dev;
/* Object for CCDC raw mode */
static struct ccdc_params_raw ccdc_hw_params_raw = {
.pix_fmt = CCDC_PIXFMT_RAW,
.frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
.win = CCDC_WIN_VGA,
.fid_pol = VPFE_PINPOL_POSITIVE,
.vd_pol = VPFE_PINPOL_POSITIVE,
.hd_pol = VPFE_PINPOL_POSITIVE,
.config_params = {
.data_sz = CCDC_DATA_10BITS,
},
};
/* Object for CCDC ycbcr mode */
static struct ccdc_params_ycbcr ccdc_hw_params_ycbcr = {
.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
.frm_fmt = CCDC_FRMFMT_INTERLACED,
.win = CCDC_WIN_PAL,
.fid_pol = VPFE_PINPOL_POSITIVE,
.vd_pol = VPFE_PINPOL_POSITIVE,
.hd_pol = VPFE_PINPOL_POSITIVE,
.bt656_enable = 1,
.pix_order = CCDC_PIXORDER_CBYCRY,
.buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED
};
#define CCDC_MAX_RAW_YUV_FORMATS 2
/* Raw Bayer formats */
static u32 ccdc_raw_bayer_pix_formats[] =
{V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16};
/* Raw YUV formats */
static u32 ccdc_raw_yuv_pix_formats[] =
{V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV};
static void *__iomem ccdc_base_addr;
static int ccdc_addr_size;
static enum vpfe_hw_if_type ccdc_if_type;
/* register access routines */
static inline u32 regr(u32 offset)
{
return __raw_readl(ccdc_base_addr + offset);
}
static inline void regw(u32 val, u32 offset)
{
__raw_writel(val, ccdc_base_addr + offset);
}
static void ccdc_set_ccdc_base(void *addr, int size)
{
ccdc_base_addr = addr;
ccdc_addr_size = size;
}
static void ccdc_enable(int flag)
{
regw(flag, CCDC_PCR);
}
static void ccdc_enable_vport(int flag)
{
if (flag)
/* enable video port */
regw(CCDC_ENABLE_VIDEO_PORT, CCDC_FMTCFG);
else
regw(CCDC_DISABLE_VIDEO_PORT, CCDC_FMTCFG);
}
/*
* ccdc_setwin()
* This function will configure the window size
* to be capture in CCDC reg
*/
void ccdc_setwin(struct v4l2_rect *image_win,
enum ccdc_frmfmt frm_fmt,
int ppc)
{
int horz_start, horz_nr_pixels;
int vert_start, vert_nr_lines;
int val = 0, mid_img = 0;
dev_dbg(dev, "\nStarting ccdc_setwin...");
/*
* ppc - per pixel count. indicates how many pixels per cell
* output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
* raw capture this is 1
*/
horz_start = image_win->left << (ppc - 1);
horz_nr_pixels = (image_win->width << (ppc - 1)) - 1;
regw((horz_start << CCDC_HORZ_INFO_SPH_SHIFT) | horz_nr_pixels,
CCDC_HORZ_INFO);
vert_start = image_win->top;
if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
vert_nr_lines = (image_win->height >> 1) - 1;
vert_start >>= 1;
/* Since first line doesn't have any data */
vert_start += 1;
/* configure VDINT0 */
val = (vert_start << CCDC_VDINT_VDINT0_SHIFT);
regw(val, CCDC_VDINT);
} else {
/* Since first line doesn't have any data */
vert_start += 1;
vert_nr_lines = image_win->height - 1;
/*
* configure VDINT0 and VDINT1. VDINT1 will be at half
* of image height
*/
mid_img = vert_start + (image_win->height / 2);
val = (vert_start << CCDC_VDINT_VDINT0_SHIFT) |
(mid_img & CCDC_VDINT_VDINT1_MASK);
regw(val, CCDC_VDINT);
}
regw((vert_start << CCDC_VERT_START_SLV0_SHIFT) | vert_start,
CCDC_VERT_START);
regw(vert_nr_lines, CCDC_VERT_LINES);
dev_dbg(dev, "\nEnd of ccdc_setwin...");
}
static void ccdc_readregs(void)
{
unsigned int val = 0;
val = regr(CCDC_ALAW);
dev_notice(dev, "\nReading 0x%x to ALAW...\n", val);
val = regr(CCDC_CLAMP);
dev_notice(dev, "\nReading 0x%x to CLAMP...\n", val);
val = regr(CCDC_DCSUB);
dev_notice(dev, "\nReading 0x%x to DCSUB...\n", val);
val = regr(CCDC_BLKCMP);
dev_notice(dev, "\nReading 0x%x to BLKCMP...\n", val);
val = regr(CCDC_FPC_ADDR);
dev_notice(dev, "\nReading 0x%x to FPC_ADDR...\n", val);
val = regr(CCDC_FPC);
dev_notice(dev, "\nReading 0x%x to FPC...\n", val);
val = regr(CCDC_FMTCFG);
dev_notice(dev, "\nReading 0x%x to FMTCFG...\n", val);
val = regr(CCDC_COLPTN);
dev_notice(dev, "\nReading 0x%x to COLPTN...\n", val);
val = regr(CCDC_FMT_HORZ);
dev_notice(dev, "\nReading 0x%x to FMT_HORZ...\n", val);
val = regr(CCDC_FMT_VERT);
dev_notice(dev, "\nReading 0x%x to FMT_VERT...\n", val);
val = regr(CCDC_HSIZE_OFF);
dev_notice(dev, "\nReading 0x%x to HSIZE_OFF...\n", val);
val = regr(CCDC_SDOFST);
dev_notice(dev, "\nReading 0x%x to SDOFST...\n", val);
val = regr(CCDC_VP_OUT);
dev_notice(dev, "\nReading 0x%x to VP_OUT...\n", val);
val = regr(CCDC_SYN_MODE);
dev_notice(dev, "\nReading 0x%x to SYN_MODE...\n", val);
val = regr(CCDC_HORZ_INFO);
dev_notice(dev, "\nReading 0x%x to HORZ_INFO...\n", val);
val = regr(CCDC_VERT_START);
dev_notice(dev, "\nReading 0x%x to VERT_START...\n", val);
val = regr(CCDC_VERT_LINES);
dev_notice(dev, "\nReading 0x%x to VERT_LINES...\n", val);
}
static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
{
if (ccdcparam->alaw.enable) {
if ((ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) ||
(ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_15_6) ||
(ccdcparam->alaw.gama_wd < ccdcparam->data_sz)) {
dev_dbg(dev, "\nInvalid data line select");
return -1;
}
}
return 0;
}
static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params)
{
struct ccdc_config_params_raw *config_params =
&ccdc_hw_params_raw.config_params;
unsigned int *fpc_virtaddr = NULL;
unsigned int *fpc_physaddr = NULL;
memcpy(config_params, raw_params, sizeof(*raw_params));
/*
* allocate memory for fault pixel table and copy the user
* values to the table
*/
if (!config_params->fault_pxl.enable)
return 0;
fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr;
fpc_virtaddr = (unsigned int *)phys_to_virt(
(unsigned long)fpc_physaddr);
/*
* Allocate memory for FPC table if current
* FPC table buffer is not big enough to
* accomodate FPC Number requested
*/
if (raw_params->fault_pxl.fp_num != config_params->fault_pxl.fp_num) {
if (fpc_physaddr != NULL) {
free_pages((unsigned long)fpc_physaddr,
get_order
(config_params->fault_pxl.fp_num *
FP_NUM_BYTES));
}
/* Allocate memory for FPC table */
fpc_virtaddr =
(unsigned int *)__get_free_pages(GFP_KERNEL | GFP_DMA,
get_order(raw_params->
fault_pxl.fp_num *
FP_NUM_BYTES));
if (fpc_virtaddr == NULL) {
dev_dbg(dev,
"\nUnable to allocate memory for FPC");
return -EFAULT;
}
fpc_physaddr =
(unsigned int *)virt_to_phys((void *)fpc_virtaddr);
}
/* Copy number of fault pixels and FPC table */
config_params->fault_pxl.fp_num = raw_params->fault_pxl.fp_num;
if (copy_from_user(fpc_virtaddr,
(void __user *)raw_params->fault_pxl.fpc_table_addr,
config_params->fault_pxl.fp_num * FP_NUM_BYTES)) {
dev_dbg(dev, "\n copy_from_user failed");
return -EFAULT;
}
config_params->fault_pxl.fpc_table_addr = (unsigned int)fpc_physaddr;
return 0;
}
static int ccdc_close(struct device *dev)
{
struct ccdc_config_params_raw *config_params =
&ccdc_hw_params_raw.config_params;
unsigned int *fpc_physaddr = NULL, *fpc_virtaddr = NULL;
fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr;
if (fpc_physaddr != NULL) {
fpc_virtaddr = (unsigned int *)
phys_to_virt((unsigned long)fpc_physaddr);
free_pages((unsigned long)fpc_virtaddr,
get_order(config_params->fault_pxl.fp_num *
FP_NUM_BYTES));
}
return 0;
}
/*
* ccdc_restore_defaults()
* This function will write defaults to all CCDC registers
*/
static void ccdc_restore_defaults(void)
{
int i;
/* disable CCDC */
ccdc_enable(0);
/* set all registers to default value */
for (i = 4; i <= 0x94; i += 4)
regw(0, i);
regw(CCDC_NO_CULLING, CCDC_CULLING);
regw(CCDC_GAMMA_BITS_11_2, CCDC_ALAW);
}
static int ccdc_open(struct device *device)
{
dev = device;
ccdc_restore_defaults();
if (ccdc_if_type == VPFE_RAW_BAYER)
ccdc_enable_vport(1);
return 0;
}
static void ccdc_sbl_reset(void)
{
vpss_clear_wbl_overflow(VPSS_PCR_CCDC_WBL_O);
}
/* Parameter operations */
static int ccdc_set_params(void __user *params)
{
struct ccdc_config_params_raw ccdc_raw_params;
int x;
if (ccdc_if_type != VPFE_RAW_BAYER)
return -EINVAL;
x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params));
if (x) {
dev_dbg(dev, "ccdc_set_params: error in copying"
"ccdc params, %d\n", x);
return -EFAULT;
}
if (!validate_ccdc_param(&ccdc_raw_params)) {
if (!ccdc_update_raw_params(&ccdc_raw_params))
return 0;
}
return -EINVAL;
}
/*
* ccdc_config_ycbcr()
* This function will configure CCDC for YCbCr video capture
*/
void ccdc_config_ycbcr(void)
{
struct ccdc_params_ycbcr *params = &ccdc_hw_params_ycbcr;
u32 syn_mode;
dev_dbg(dev, "\nStarting ccdc_config_ycbcr...");
/*
* first restore the CCDC registers to default values
* This is important since we assume default values to be set in
* a lot of registers that we didn't touch
*/
ccdc_restore_defaults();
/*
* configure pixel format, frame format, configure video frame
* format, enable output to SDRAM, enable internal timing generator
* and 8bit pack mode
*/
syn_mode = (((params->pix_fmt & CCDC_SYN_MODE_INPMOD_MASK) <<
CCDC_SYN_MODE_INPMOD_SHIFT) |
((params->frm_fmt & CCDC_SYN_FLDMODE_MASK) <<
CCDC_SYN_FLDMODE_SHIFT) | CCDC_VDHDEN_ENABLE |
CCDC_WEN_ENABLE | CCDC_DATA_PACK_ENABLE);
/* setup BT.656 sync mode */
if (params->bt656_enable) {
regw(CCDC_REC656IF_BT656_EN, CCDC_REC656IF);
/*
* configure the FID, VD, HD pin polarity,
* fld,hd pol positive, vd negative, 8-bit data
*/
syn_mode |= CCDC_SYN_MODE_VD_POL_NEGATIVE | CCDC_SYN_MODE_8BITS;
} else {
/* y/c external sync mode */
syn_mode |= (((params->fid_pol & CCDC_FID_POL_MASK) <<
CCDC_FID_POL_SHIFT) |
((params->hd_pol & CCDC_HD_POL_MASK) <<
CCDC_HD_POL_SHIFT) |
((params->vd_pol & CCDC_VD_POL_MASK) <<
CCDC_VD_POL_SHIFT));
}
regw(syn_mode, CCDC_SYN_MODE);
/* configure video window */
ccdc_setwin(&params->win, params->frm_fmt, 2);
/*
* configure the order of y cb cr in SDRAM, and disable latch
* internal register on vsync
*/
regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) |
CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG);
/*
* configure the horizontal line offset. This should be a
* on 32 byte bondary. So clear LSB 5 bits
*/
regw(((params->win.width * 2 + 31) & ~0x1f), CCDC_HSIZE_OFF);
/* configure the memory line offset */
if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED)
/* two fields are interleaved in memory */
regw(CCDC_SDOFST_FIELD_INTERLEAVED, CCDC_SDOFST);
ccdc_sbl_reset();
dev_dbg(dev, "\nEnd of ccdc_config_ycbcr...\n");
ccdc_readregs();
}
static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp)
{
u32 val;
if (!bclamp->enable) {
/* configure DCSub */
val = (bclamp->dc_sub) & CCDC_BLK_DC_SUB_MASK;
regw(val, CCDC_DCSUB);
dev_dbg(dev, "\nWriting 0x%x to DCSUB...\n", val);
regw(CCDC_CLAMP_DEFAULT_VAL, CCDC_CLAMP);
dev_dbg(dev, "\nWriting 0x0000 to CLAMP...\n");
return;
}
/*
* Configure gain, Start pixel, No of line to be avg,
* No of pixel/line to be avg, & Enable the Black clamping
*/
val = ((bclamp->sgain & CCDC_BLK_SGAIN_MASK) |
((bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) <<
CCDC_BLK_ST_PXL_SHIFT) |
((bclamp->sample_ln & CCDC_BLK_SAMPLE_LINE_MASK) <<
CCDC_BLK_SAMPLE_LINE_SHIFT) |
((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) <<
CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE);
regw(val, CCDC_CLAMP);
dev_dbg(dev, "\nWriting 0x%x to CLAMP...\n", val);
/* If Black clamping is enable then make dcsub 0 */
regw(CCDC_DCSUB_DEFAULT_VAL, CCDC_DCSUB);
dev_dbg(dev, "\nWriting 0x00000000 to DCSUB...\n");
}
static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp)
{
u32 val;
val = ((bcomp->b & CCDC_BLK_COMP_MASK) |
((bcomp->gb & CCDC_BLK_COMP_MASK) <<
CCDC_BLK_COMP_GB_COMP_SHIFT) |
((bcomp->gr & CCDC_BLK_COMP_MASK) <<
CCDC_BLK_COMP_GR_COMP_SHIFT) |
((bcomp->r & CCDC_BLK_COMP_MASK) <<
CCDC_BLK_COMP_R_COMP_SHIFT));
regw(val, CCDC_BLKCMP);
}
static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc)
{
u32 val;
/* Initially disable FPC */
val = CCDC_FPC_DISABLE;
regw(val, CCDC_FPC);
if (!fpc->enable)
return;
/* Configure Fault pixel if needed */
regw(fpc->fpc_table_addr, CCDC_FPC_ADDR);
dev_dbg(dev, "\nWriting 0x%x to FPC_ADDR...\n",
(fpc->fpc_table_addr));
/* Write the FPC params with FPC disable */
val = fpc->fp_num & CCDC_FPC_FPC_NUM_MASK;
regw(val, CCDC_FPC);
dev_dbg(dev, "\nWriting 0x%x to FPC...\n", val);
/* read the FPC register */
val = regr(CCDC_FPC) | CCDC_FPC_ENABLE;
regw(val, CCDC_FPC);
dev_dbg(dev, "\nWriting 0x%x to FPC...\n", val);
}
/*
* ccdc_config_raw()
* This function will configure CCDC for Raw capture mode
*/
void ccdc_config_raw(void)
{
struct ccdc_params_raw *params = &ccdc_hw_params_raw;
struct ccdc_config_params_raw *config_params =
&ccdc_hw_params_raw.config_params;
unsigned int syn_mode = 0;
unsigned int val;
dev_dbg(dev, "\nStarting ccdc_config_raw...");
/* Reset CCDC */
ccdc_restore_defaults();
/* Disable latching function registers on VSYNC */
regw(CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG);
/*
* Configure the vertical sync polarity(SYN_MODE.VDPOL),
* horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity
* (SYN_MODE.FLDPOL), frame format(progressive or interlace),
* data size(SYNMODE.DATSIZ), &pixel format (Input mode), output
* SDRAM, enable internal timing generator
*/
syn_mode =
(((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) |
((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) |
((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) |
((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) |
((config_params->data_sz & CCDC_DATA_SZ_MASK) <<
CCDC_DATA_SZ_SHIFT) |
((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT) |
CCDC_WEN_ENABLE | CCDC_VDHDEN_ENABLE);
/* Enable and configure aLaw register if needed */
if (config_params->alaw.enable) {
val = ((config_params->alaw.gama_wd &
CCDC_ALAW_GAMA_WD_MASK) | CCDC_ALAW_ENABLE);
regw(val, CCDC_ALAW);
dev_dbg(dev, "\nWriting 0x%x to ALAW...\n", val);
}
/* Configure video window */
ccdc_setwin(&params->win, params->frm_fmt, CCDC_PPC_RAW);
/* Configure Black Clamp */
ccdc_config_black_clamp(&config_params->blk_clamp);
/* Configure Black level compensation */
ccdc_config_black_compense(&config_params->blk_comp);
/* Configure Fault Pixel Correction */
ccdc_config_fpc(&config_params->fault_pxl);
/* If data size is 8 bit then pack the data */
if ((config_params->data_sz == CCDC_DATA_8BITS) ||
config_params->alaw.enable)
syn_mode |= CCDC_DATA_PACK_ENABLE;
#ifdef CONFIG_DM644X_VIDEO_PORT_ENABLE
/* enable video port */
val = CCDC_ENABLE_VIDEO_PORT;
#else
/* disable video port */
val = CCDC_DISABLE_VIDEO_PORT;
#endif
if (config_params->data_sz == CCDC_DATA_8BITS)
val |= (CCDC_DATA_10BITS & CCDC_FMTCFG_VPIN_MASK)
<< CCDC_FMTCFG_VPIN_SHIFT;
else
val |= (config_params->data_sz & CCDC_FMTCFG_VPIN_MASK)
<< CCDC_FMTCFG_VPIN_SHIFT;
/* Write value in FMTCFG */
regw(val, CCDC_FMTCFG);
dev_dbg(dev, "\nWriting 0x%x to FMTCFG...\n", val);
/* Configure the color pattern according to mt9t001 sensor */
regw(CCDC_COLPTN_VAL, CCDC_COLPTN);
dev_dbg(dev, "\nWriting 0xBB11BB11 to COLPTN...\n");
/*
* Configure Data formatter(Video port) pixel selection
* (FMT_HORZ, FMT_VERT)
*/
val = ((params->win.left & CCDC_FMT_HORZ_FMTSPH_MASK) <<
CCDC_FMT_HORZ_FMTSPH_SHIFT) |
(params->win.width & CCDC_FMT_HORZ_FMTLNH_MASK);
regw(val, CCDC_FMT_HORZ);
dev_dbg(dev, "\nWriting 0x%x to FMT_HORZ...\n", val);
val = (params->win.top & CCDC_FMT_VERT_FMTSLV_MASK)
<< CCDC_FMT_VERT_FMTSLV_SHIFT;
if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE)
val |= (params->win.height) & CCDC_FMT_VERT_FMTLNV_MASK;
else
val |= (params->win.height >> 1) & CCDC_FMT_VERT_FMTLNV_MASK;
dev_dbg(dev, "\nparams->win.height 0x%x ...\n",
params->win.height);
regw(val, CCDC_FMT_VERT);
dev_dbg(dev, "\nWriting 0x%x to FMT_VERT...\n", val);
dev_dbg(dev, "\nbelow regw(val, FMT_VERT)...");
/*
* Configure Horizontal offset register. If pack 8 is enabled then
* 1 pixel will take 1 byte
*/
if ((config_params->data_sz == CCDC_DATA_8BITS) ||
config_params->alaw.enable)
regw((params->win.width + CCDC_32BYTE_ALIGN_VAL) &
CCDC_HSIZE_OFF_MASK, CCDC_HSIZE_OFF);
else
/* else one pixel will take 2 byte */
regw(((params->win.width * CCDC_TWO_BYTES_PER_PIXEL) +
CCDC_32BYTE_ALIGN_VAL) & CCDC_HSIZE_OFF_MASK,
CCDC_HSIZE_OFF);
/* Set value for SDOFST */
if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {
if (params->image_invert_enable) {
/* For intelace inverse mode */
regw(CCDC_INTERLACED_IMAGE_INVERT, CCDC_SDOFST);
dev_dbg(dev, "\nWriting 0x4B6D to SDOFST...\n");
}
else {
/* For intelace non inverse mode */
regw(CCDC_INTERLACED_NO_IMAGE_INVERT, CCDC_SDOFST);
dev_dbg(dev, "\nWriting 0x0249 to SDOFST...\n");
}
} else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
regw(CCDC_PROGRESSIVE_NO_IMAGE_INVERT, CCDC_SDOFST);
dev_dbg(dev, "\nWriting 0x0000 to SDOFST...\n");
}
/*
* Configure video port pixel selection (VPOUT)
* Here -1 is to make the height value less than FMT_VERT.FMTLNV
*/
if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE)
val = (((params->win.height - 1) & CCDC_VP_OUT_VERT_NUM_MASK))
<< CCDC_VP_OUT_VERT_NUM_SHIFT;
else
val =
((((params->win.height >> CCDC_INTERLACED_HEIGHT_SHIFT) -
1) & CCDC_VP_OUT_VERT_NUM_MASK)) <<
CCDC_VP_OUT_VERT_NUM_SHIFT;
val |= ((((params->win.width))) & CCDC_VP_OUT_HORZ_NUM_MASK)
<< CCDC_VP_OUT_HORZ_NUM_SHIFT;
val |= (params->win.left) & CCDC_VP_OUT_HORZ_ST_MASK;
regw(val, CCDC_VP_OUT);
dev_dbg(dev, "\nWriting 0x%x to VP_OUT...\n", val);
regw(syn_mode, CCDC_SYN_MODE);
dev_dbg(dev, "\nWriting 0x%x to SYN_MODE...\n", syn_mode);
ccdc_sbl_reset();
dev_dbg(dev, "\nend of ccdc_config_raw...");
ccdc_readregs();
}
static int ccdc_configure(void)
{
if (ccdc_if_type == VPFE_RAW_BAYER)
ccdc_config_raw();
else
ccdc_config_ycbcr();
return 0;
}
static int ccdc_set_buftype(enum ccdc_buftype buf_type)
{
if (ccdc_if_type == VPFE_RAW_BAYER)
ccdc_hw_params_raw.buf_type = buf_type;
else
ccdc_hw_params_ycbcr.buf_type = buf_type;
return 0;
}
static enum ccdc_buftype ccdc_get_buftype(void)
{
if (ccdc_if_type == VPFE_RAW_BAYER)
return ccdc_hw_params_raw.buf_type;
return ccdc_hw_params_ycbcr.buf_type;
}
static int ccdc_enum_pix(u32 *pix, int i)
{
int ret = -EINVAL;
if (ccdc_if_type == VPFE_RAW_BAYER) {
if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) {
*pix = ccdc_raw_bayer_pix_formats[i];
ret = 0;
}
} else {
if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) {
*pix = ccdc_raw_yuv_pix_formats[i];
ret = 0;
}
}
return ret;
}
static int ccdc_set_pixel_format(u32 pixfmt)
{
if (ccdc_if_type == VPFE_RAW_BAYER) {
ccdc_hw_params_raw.pix_fmt = CCDC_PIXFMT_RAW;
if (pixfmt == V4L2_PIX_FMT_SBGGR8)
ccdc_hw_params_raw.config_params.alaw.enable = 1;
else if (pixfmt != V4L2_PIX_FMT_SBGGR16)
return -EINVAL;
} else {
if (pixfmt == V4L2_PIX_FMT_YUYV)
ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
else if (pixfmt == V4L2_PIX_FMT_UYVY)
ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
else
return -EINVAL;
}
return 0;
}
static u32 ccdc_get_pixel_format(void)
{
struct ccdc_a_law *alaw =
&ccdc_hw_params_raw.config_params.alaw;
u32 pixfmt;
if (ccdc_if_type == VPFE_RAW_BAYER)
if (alaw->enable)
pixfmt = V4L2_PIX_FMT_SBGGR8;
else
pixfmt = V4L2_PIX_FMT_SBGGR16;
else {
if (ccdc_hw_params_ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
pixfmt = V4L2_PIX_FMT_YUYV;
else
pixfmt = V4L2_PIX_FMT_UYVY;
}
return pixfmt;
}
static int ccdc_set_image_window(struct v4l2_rect *win)
{
if (ccdc_if_type == VPFE_RAW_BAYER)
ccdc_hw_params_raw.win = *win;
else
ccdc_hw_params_ycbcr.win = *win;
return 0;
}
static void ccdc_get_image_window(struct v4l2_rect *win)
{
if (ccdc_if_type == VPFE_RAW_BAYER)
*win = ccdc_hw_params_raw.win;
else
*win = ccdc_hw_params_ycbcr.win;
}
static unsigned int ccdc_get_line_length(void)
{
struct ccdc_config_params_raw *config_params =
&ccdc_hw_params_raw.config_params;
unsigned int len;
if (ccdc_if_type == VPFE_RAW_BAYER) {
if ((config_params->alaw.enable) ||
(config_params->data_sz == CCDC_DATA_8BITS))
len = ccdc_hw_params_raw.win.width;
else
len = ccdc_hw_params_raw.win.width * 2;
} else
len = ccdc_hw_params_ycbcr.win.width * 2;
return ALIGN(len, 32);
}
static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt)
{
if (ccdc_if_type == VPFE_RAW_BAYER)
ccdc_hw_params_raw.frm_fmt = frm_fmt;
else
ccdc_hw_params_ycbcr.frm_fmt = frm_fmt;
return 0;
}
static enum ccdc_frmfmt ccdc_get_frame_format(void)
{
if (ccdc_if_type == VPFE_RAW_BAYER)
return ccdc_hw_params_raw.frm_fmt;
else
return ccdc_hw_params_ycbcr.frm_fmt;
}
static int ccdc_getfid(void)
{
return (regr(CCDC_SYN_MODE) >> 15) & 1;
}
/* misc operations */
static inline void ccdc_setfbaddr(unsigned long addr)
{
regw(addr & 0xffffffe0, CCDC_SDR_ADDR);
}
static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params)
{
ccdc_if_type = params->if_type;
switch (params->if_type) {
case VPFE_BT656:
case VPFE_YCBCR_SYNC_16:
case VPFE_YCBCR_SYNC_8:
ccdc_hw_params_ycbcr.vd_pol = params->vdpol;
ccdc_hw_params_ycbcr.hd_pol = params->hdpol;
break;
default:
/* TODO add support for raw bayer here */
return -EINVAL;
}
return 0;
}
static struct ccdc_hw_device ccdc_hw_dev = {
.name = "DM6446 CCDC",
.owner = THIS_MODULE,
.hw_ops = {
.open = ccdc_open,
.close = ccdc_close,
.set_ccdc_base = ccdc_set_ccdc_base,
.reset = ccdc_sbl_reset,
.enable = ccdc_enable,
.set_hw_if_params = ccdc_set_hw_if_params,
.set_params = ccdc_set_params,
.configure = ccdc_configure,
.set_buftype = ccdc_set_buftype,
.get_buftype = ccdc_get_buftype,
.enum_pix = ccdc_enum_pix,
.set_pixel_format = ccdc_set_pixel_format,
.get_pixel_format = ccdc_get_pixel_format,
.set_frame_format = ccdc_set_frame_format,
.get_frame_format = ccdc_get_frame_format,
.set_image_window = ccdc_set_image_window,
.get_image_window = ccdc_get_image_window,
.get_line_length = ccdc_get_line_length,
.setfbaddr = ccdc_setfbaddr,
.getfid = ccdc_getfid,
},
};
static int dm644x_ccdc_init(void)
{
printk(KERN_NOTICE "dm644x_ccdc_init\n");
if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0)
return -1;
printk(KERN_NOTICE "%s is registered with vpfe.\n",
ccdc_hw_dev.name);
return 0;
}
static void dm644x_ccdc_exit(void)
{
vpfe_unregister_ccdc_device(&ccdc_hw_dev);
}
module_init(dm644x_ccdc_init);
module_exit(dm644x_ccdc_exit);

View File

@@ -0,0 +1,145 @@
/*
* Copyright (C) 2006-2009 Texas Instruments Inc
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _DM644X_CCDC_REGS_H
#define _DM644X_CCDC_REGS_H
/**************************************************************************\
* Register OFFSET Definitions
\**************************************************************************/
#define CCDC_PID 0x0
#define CCDC_PCR 0x4
#define CCDC_SYN_MODE 0x8
#define CCDC_HD_VD_WID 0xc
#define CCDC_PIX_LINES 0x10
#define CCDC_HORZ_INFO 0x14
#define CCDC_VERT_START 0x18
#define CCDC_VERT_LINES 0x1c
#define CCDC_CULLING 0x20
#define CCDC_HSIZE_OFF 0x24
#define CCDC_SDOFST 0x28
#define CCDC_SDR_ADDR 0x2c
#define CCDC_CLAMP 0x30
#define CCDC_DCSUB 0x34
#define CCDC_COLPTN 0x38
#define CCDC_BLKCMP 0x3c
#define CCDC_FPC 0x40
#define CCDC_FPC_ADDR 0x44
#define CCDC_VDINT 0x48
#define CCDC_ALAW 0x4c
#define CCDC_REC656IF 0x50
#define CCDC_CCDCFG 0x54
#define CCDC_FMTCFG 0x58
#define CCDC_FMT_HORZ 0x5c
#define CCDC_FMT_VERT 0x60
#define CCDC_FMT_ADDR0 0x64
#define CCDC_FMT_ADDR1 0x68
#define CCDC_FMT_ADDR2 0x6c
#define CCDC_FMT_ADDR3 0x70
#define CCDC_FMT_ADDR4 0x74
#define CCDC_FMT_ADDR5 0x78
#define CCDC_FMT_ADDR6 0x7c
#define CCDC_FMT_ADDR7 0x80
#define CCDC_PRGEVEN_0 0x84
#define CCDC_PRGEVEN_1 0x88
#define CCDC_PRGODD_0 0x8c
#define CCDC_PRGODD_1 0x90
#define CCDC_VP_OUT 0x94
/***************************************************************
* Define for various register bit mask and shifts for CCDC
****************************************************************/
#define CCDC_FID_POL_MASK 1
#define CCDC_FID_POL_SHIFT 4
#define CCDC_HD_POL_MASK 1
#define CCDC_HD_POL_SHIFT 3
#define CCDC_VD_POL_MASK 1
#define CCDC_VD_POL_SHIFT 2
#define CCDC_HSIZE_OFF_MASK 0xffffffe0
#define CCDC_32BYTE_ALIGN_VAL 31
#define CCDC_FRM_FMT_MASK 0x1
#define CCDC_FRM_FMT_SHIFT 7
#define CCDC_DATA_SZ_MASK 7
#define CCDC_DATA_SZ_SHIFT 8
#define CCDC_PIX_FMT_MASK 3
#define CCDC_PIX_FMT_SHIFT 12
#define CCDC_VP2SDR_DISABLE 0xFFFBFFFF
#define CCDC_WEN_ENABLE (1 << 17)
#define CCDC_SDR2RSZ_DISABLE 0xFFF7FFFF
#define CCDC_VDHDEN_ENABLE (1 << 16)
#define CCDC_LPF_ENABLE (1 << 14)
#define CCDC_ALAW_ENABLE (1 << 3)
#define CCDC_ALAW_GAMA_WD_MASK 7
#define CCDC_BLK_CLAMP_ENABLE (1 << 31)
#define CCDC_BLK_SGAIN_MASK 0x1F
#define CCDC_BLK_ST_PXL_MASK 0x7FFF
#define CCDC_BLK_ST_PXL_SHIFT 10
#define CCDC_BLK_SAMPLE_LN_MASK 7
#define CCDC_BLK_SAMPLE_LN_SHIFT 28
#define CCDC_BLK_SAMPLE_LINE_MASK 7
#define CCDC_BLK_SAMPLE_LINE_SHIFT 25
#define CCDC_BLK_DC_SUB_MASK 0x03FFF
#define CCDC_BLK_COMP_MASK 0xFF
#define CCDC_BLK_COMP_GB_COMP_SHIFT 8
#define CCDC_BLK_COMP_GR_COMP_SHIFT 16
#define CCDC_BLK_COMP_R_COMP_SHIFT 24
#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15)
#define CCDC_FPC_ENABLE (1 << 15)
#define CCDC_FPC_DISABLE 0
#define CCDC_FPC_FPC_NUM_MASK 0x7FFF
#define CCDC_DATA_PACK_ENABLE (1 << 11)
#define CCDC_FMTCFG_VPIN_MASK 7
#define CCDC_FMTCFG_VPIN_SHIFT 12
#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF
#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF
#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16
#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF
#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF
#define CCDC_FMT_VERT_FMTSLV_SHIFT 16
#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF
#define CCDC_VP_OUT_VERT_NUM_SHIFT 17
#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF
#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4
#define CCDC_VP_OUT_HORZ_ST_MASK 0xF
#define CCDC_HORZ_INFO_SPH_SHIFT 16
#define CCDC_VERT_START_SLV0_SHIFT 16
#define CCDC_VDINT_VDINT0_SHIFT 16
#define CCDC_VDINT_VDINT1_MASK 0xFFFF
#define CCDC_PPC_RAW 1
#define CCDC_DCSUB_DEFAULT_VAL 0
#define CCDC_CLAMP_DEFAULT_VAL 0
#define CCDC_ENABLE_VIDEO_PORT 0x8000
#define CCDC_DISABLE_VIDEO_PORT 0
#define CCDC_COLPTN_VAL 0xBB11BB11
#define CCDC_TWO_BYTES_PER_PIXEL 2
#define CCDC_INTERLACED_IMAGE_INVERT 0x4B6D
#define CCDC_INTERLACED_NO_IMAGE_INVERT 0x0249
#define CCDC_PROGRESSIVE_IMAGE_INVERT 0x4000
#define CCDC_PROGRESSIVE_NO_IMAGE_INVERT 0
#define CCDC_INTERLACED_HEIGHT_SHIFT 1
#define CCDC_SYN_MODE_INPMOD_SHIFT 12
#define CCDC_SYN_MODE_INPMOD_MASK 3
#define CCDC_SYN_MODE_8BITS (7 << 8)
#define CCDC_SYN_FLDMODE_MASK 1
#define CCDC_SYN_FLDMODE_SHIFT 7
#define CCDC_REC656IF_BT656_EN 3
#define CCDC_SYN_MODE_VD_POL_NEGATIVE (1 << 2)
#define CCDC_CCDCFG_Y8POS_SHIFT 11
#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249
#define CCDC_NO_CULLING 0xffff00ff
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,296 @@
/*
* vpif - DM646x Video Port Interface driver
* VPIF is a receiver and transmitter for video data. It has two channels(0, 1)
* that receiveing video byte stream and two channels(2, 3) for video output.
* The hardware supports SDTV, HDTV formats, raw data capture.
* Currently, the driver supports NTSC and PAL standards.
*
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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.
*
* This program is distributed .as is. WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/kernel.h>
#include <linux/io.h>
#include <mach/hardware.h>
#include "vpif.h"
MODULE_DESCRIPTION("TI DaVinci Video Port Interface driver");
MODULE_LICENSE("GPL");
#define VPIF_CH0_MAX_MODES (22)
#define VPIF_CH1_MAX_MODES (02)
#define VPIF_CH2_MAX_MODES (15)
#define VPIF_CH3_MAX_MODES (02)
static resource_size_t res_len;
static struct resource *res;
spinlock_t vpif_lock;
void __iomem *vpif_base;
static inline void vpif_wr_bit(u32 reg, u32 bit, u32 val)
{
if (val)
vpif_set_bit(reg, bit);
else
vpif_clr_bit(reg, bit);
}
/* This structure is used to keep track of VPIF size register's offsets */
struct vpif_registers {
u32 h_cfg, v_cfg_00, v_cfg_01, v_cfg_02, v_cfg, ch_ctrl;
u32 line_offset, vanc0_strt, vanc0_size, vanc1_strt;
u32 vanc1_size, width_mask, len_mask;
u8 max_modes;
};
static const struct vpif_registers vpifregs[VPIF_NUM_CHANNELS] = {
/* Channel0 */
{
VPIF_CH0_H_CFG, VPIF_CH0_V_CFG_00, VPIF_CH0_V_CFG_01,
VPIF_CH0_V_CFG_02, VPIF_CH0_V_CFG_03, VPIF_CH0_CTRL,
VPIF_CH0_IMG_ADD_OFST, 0, 0, 0, 0, 0x1FFF, 0xFFF,
VPIF_CH0_MAX_MODES,
},
/* Channel1 */
{
VPIF_CH1_H_CFG, VPIF_CH1_V_CFG_00, VPIF_CH1_V_CFG_01,
VPIF_CH1_V_CFG_02, VPIF_CH1_V_CFG_03, VPIF_CH1_CTRL,
VPIF_CH1_IMG_ADD_OFST, 0, 0, 0, 0, 0x1FFF, 0xFFF,
VPIF_CH1_MAX_MODES,
},
/* Channel2 */
{
VPIF_CH2_H_CFG, VPIF_CH2_V_CFG_00, VPIF_CH2_V_CFG_01,
VPIF_CH2_V_CFG_02, VPIF_CH2_V_CFG_03, VPIF_CH2_CTRL,
VPIF_CH2_IMG_ADD_OFST, VPIF_CH2_VANC0_STRT, VPIF_CH2_VANC0_SIZE,
VPIF_CH2_VANC1_STRT, VPIF_CH2_VANC1_SIZE, 0x7FF, 0x7FF,
VPIF_CH2_MAX_MODES
},
/* Channel3 */
{
VPIF_CH3_H_CFG, VPIF_CH3_V_CFG_00, VPIF_CH3_V_CFG_01,
VPIF_CH3_V_CFG_02, VPIF_CH3_V_CFG_03, VPIF_CH3_CTRL,
VPIF_CH3_IMG_ADD_OFST, VPIF_CH3_VANC0_STRT, VPIF_CH3_VANC0_SIZE,
VPIF_CH3_VANC1_STRT, VPIF_CH3_VANC1_SIZE, 0x7FF, 0x7FF,
VPIF_CH3_MAX_MODES
},
};
/* vpif_set_mode_info:
* This function is used to set horizontal and vertical config parameters
* As per the standard in the channel, configure the values of L1, L3,
* L5, L7 L9, L11 in VPIF Register , also write width and height
*/
static void vpif_set_mode_info(const struct vpif_channel_config_params *config,
u8 channel_id, u8 config_channel_id)
{
u32 value;
value = (config->eav2sav & vpifregs[config_channel_id].width_mask);
value <<= VPIF_CH_LEN_SHIFT;
value |= (config->sav2eav & vpifregs[config_channel_id].width_mask);
regw(value, vpifregs[channel_id].h_cfg);
value = (config->l1 & vpifregs[config_channel_id].len_mask);
value <<= VPIF_CH_LEN_SHIFT;
value |= (config->l3 & vpifregs[config_channel_id].len_mask);
regw(value, vpifregs[channel_id].v_cfg_00);
value = (config->l5 & vpifregs[config_channel_id].len_mask);
value <<= VPIF_CH_LEN_SHIFT;
value |= (config->l7 & vpifregs[config_channel_id].len_mask);
regw(value, vpifregs[channel_id].v_cfg_01);
value = (config->l9 & vpifregs[config_channel_id].len_mask);
value <<= VPIF_CH_LEN_SHIFT;
value |= (config->l11 & vpifregs[config_channel_id].len_mask);
regw(value, vpifregs[channel_id].v_cfg_02);
value = (config->vsize & vpifregs[config_channel_id].len_mask);
regw(value, vpifregs[channel_id].v_cfg);
}
/* config_vpif_params
* Function to set the parameters of a channel
* Mainly modifies the channel ciontrol register
* It sets frame format, yc mux mode
*/
static void config_vpif_params(struct vpif_params *vpifparams,
u8 channel_id, u8 found)
{
const struct vpif_channel_config_params *config = &vpifparams->std_info;
u32 value, ch_nip, reg;
u8 start, end;
int i;
start = channel_id;
end = channel_id + found;
for (i = start; i < end; i++) {
reg = vpifregs[i].ch_ctrl;
if (channel_id < 2)
ch_nip = VPIF_CAPTURE_CH_NIP;
else
ch_nip = VPIF_DISPLAY_CH_NIP;
vpif_wr_bit(reg, ch_nip, config->frm_fmt);
vpif_wr_bit(reg, VPIF_CH_YC_MUX_BIT, config->ycmux_mode);
vpif_wr_bit(reg, VPIF_CH_INPUT_FIELD_FRAME_BIT,
vpifparams->video_params.storage_mode);
/* Set raster scanning SDR Format */
vpif_clr_bit(reg, VPIF_CH_SDR_FMT_BIT);
vpif_wr_bit(reg, VPIF_CH_DATA_MODE_BIT, config->capture_format);
if (channel_id > 1) /* Set the Pixel enable bit */
vpif_set_bit(reg, VPIF_DISPLAY_PIX_EN_BIT);
else if (config->capture_format) {
/* Set the polarity of various pins */
vpif_wr_bit(reg, VPIF_CH_FID_POLARITY_BIT,
vpifparams->iface.fid_pol);
vpif_wr_bit(reg, VPIF_CH_V_VALID_POLARITY_BIT,
vpifparams->iface.vd_pol);
vpif_wr_bit(reg, VPIF_CH_H_VALID_POLARITY_BIT,
vpifparams->iface.hd_pol);
value = regr(reg);
/* Set data width */
value &= ((~(unsigned int)(0x3)) <<
VPIF_CH_DATA_WIDTH_BIT);
value |= ((vpifparams->params.data_sz) <<
VPIF_CH_DATA_WIDTH_BIT);
regw(value, reg);
}
/* Write the pitch in the driver */
regw((vpifparams->video_params.hpitch),
vpifregs[i].line_offset);
}
}
/* vpif_set_video_params
* This function is used to set video parameters in VPIF register
*/
int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id)
{
const struct vpif_channel_config_params *config = &vpifparams->std_info;
int found = 1;
vpif_set_mode_info(config, channel_id, channel_id);
if (!config->ycmux_mode) {
/* YC are on separate channels (HDTV formats) */
vpif_set_mode_info(config, channel_id + 1, channel_id);
found = 2;
}
config_vpif_params(vpifparams, channel_id, found);
regw(0x80, VPIF_REQ_SIZE);
regw(0x01, VPIF_EMULATION_CTRL);
return found;
}
EXPORT_SYMBOL(vpif_set_video_params);
void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams,
u8 channel_id)
{
u32 value;
value = 0x3F8 & (vbiparams->hstart0);
value |= 0x3FFFFFF & ((vbiparams->vstart0) << 16);
regw(value, vpifregs[channel_id].vanc0_strt);
value = 0x3F8 & (vbiparams->hstart1);
value |= 0x3FFFFFF & ((vbiparams->vstart1) << 16);
regw(value, vpifregs[channel_id].vanc1_strt);
value = 0x3F8 & (vbiparams->hsize0);
value |= 0x3FFFFFF & ((vbiparams->vsize0) << 16);
regw(value, vpifregs[channel_id].vanc0_size);
value = 0x3F8 & (vbiparams->hsize1);
value |= 0x3FFFFFF & ((vbiparams->vsize1) << 16);
regw(value, vpifregs[channel_id].vanc1_size);
}
EXPORT_SYMBOL(vpif_set_vbi_display_params);
int vpif_channel_getfid(u8 channel_id)
{
return (regr(vpifregs[channel_id].ch_ctrl) & VPIF_CH_FID_MASK)
>> VPIF_CH_FID_SHIFT;
}
EXPORT_SYMBOL(vpif_channel_getfid);
static int __init vpif_probe(struct platform_device *pdev)
{
int status = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENOENT;
res_len = res->end - res->start + 1;
res = request_mem_region(res->start, res_len, res->name);
if (!res)
return -EBUSY;
vpif_base = ioremap(res->start, res_len);
if (!vpif_base) {
status = -EBUSY;
goto fail;
}
spin_lock_init(&vpif_lock);
dev_info(&pdev->dev, "vpif probe success\n");
return 0;
fail:
release_mem_region(res->start, res_len);
return status;
}
static int vpif_remove(struct platform_device *pdev)
{
iounmap(vpif_base);
release_mem_region(res->start, res_len);
return 0;
}
static struct platform_driver vpif_driver = {
.driver = {
.name = "vpif",
.owner = THIS_MODULE,
},
.remove = __devexit_p(vpif_remove),
.probe = vpif_probe,
};
static void vpif_exit(void)
{
platform_driver_unregister(&vpif_driver);
}
static int __init vpif_init(void)
{
return platform_driver_register(&vpif_driver);
}
subsys_initcall(vpif_init);
module_exit(vpif_exit);

View File

@@ -0,0 +1,642 @@
/*
* VPIF header file
*
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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.
*
* This program is distributed .as is. WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef VPIF_H
#define VPIF_H
#include <linux/io.h>
#include <linux/videodev2.h>
#include <mach/hardware.h>
#include <mach/dm646x.h>
/* Maximum channel allowed */
#define VPIF_NUM_CHANNELS (4)
#define VPIF_CAPTURE_NUM_CHANNELS (2)
#define VPIF_DISPLAY_NUM_CHANNELS (2)
/* Macros to read/write registers */
extern void __iomem *vpif_base;
extern spinlock_t vpif_lock;
#define regr(reg) readl((reg) + vpif_base)
#define regw(value, reg) writel(value, (reg + vpif_base))
/* Register Addresss Offsets */
#define VPIF_PID (0x0000)
#define VPIF_CH0_CTRL (0x0004)
#define VPIF_CH1_CTRL (0x0008)
#define VPIF_CH2_CTRL (0x000C)
#define VPIF_CH3_CTRL (0x0010)
#define VPIF_INTEN (0x0020)
#define VPIF_INTEN_SET (0x0024)
#define VPIF_INTEN_CLR (0x0028)
#define VPIF_STATUS (0x002C)
#define VPIF_STATUS_CLR (0x0030)
#define VPIF_EMULATION_CTRL (0x0034)
#define VPIF_REQ_SIZE (0x0038)
#define VPIF_CH0_TOP_STRT_ADD_LUMA (0x0040)
#define VPIF_CH0_BTM_STRT_ADD_LUMA (0x0044)
#define VPIF_CH0_TOP_STRT_ADD_CHROMA (0x0048)
#define VPIF_CH0_BTM_STRT_ADD_CHROMA (0x004c)
#define VPIF_CH0_TOP_STRT_ADD_HANC (0x0050)
#define VPIF_CH0_BTM_STRT_ADD_HANC (0x0054)
#define VPIF_CH0_TOP_STRT_ADD_VANC (0x0058)
#define VPIF_CH0_BTM_STRT_ADD_VANC (0x005c)
#define VPIF_CH0_SP_CFG (0x0060)
#define VPIF_CH0_IMG_ADD_OFST (0x0064)
#define VPIF_CH0_HANC_ADD_OFST (0x0068)
#define VPIF_CH0_H_CFG (0x006c)
#define VPIF_CH0_V_CFG_00 (0x0070)
#define VPIF_CH0_V_CFG_01 (0x0074)
#define VPIF_CH0_V_CFG_02 (0x0078)
#define VPIF_CH0_V_CFG_03 (0x007c)
#define VPIF_CH1_TOP_STRT_ADD_LUMA (0x0080)
#define VPIF_CH1_BTM_STRT_ADD_LUMA (0x0084)
#define VPIF_CH1_TOP_STRT_ADD_CHROMA (0x0088)
#define VPIF_CH1_BTM_STRT_ADD_CHROMA (0x008c)
#define VPIF_CH1_TOP_STRT_ADD_HANC (0x0090)
#define VPIF_CH1_BTM_STRT_ADD_HANC (0x0094)
#define VPIF_CH1_TOP_STRT_ADD_VANC (0x0098)
#define VPIF_CH1_BTM_STRT_ADD_VANC (0x009c)
#define VPIF_CH1_SP_CFG (0x00a0)
#define VPIF_CH1_IMG_ADD_OFST (0x00a4)
#define VPIF_CH1_HANC_ADD_OFST (0x00a8)
#define VPIF_CH1_H_CFG (0x00ac)
#define VPIF_CH1_V_CFG_00 (0x00b0)
#define VPIF_CH1_V_CFG_01 (0x00b4)
#define VPIF_CH1_V_CFG_02 (0x00b8)
#define VPIF_CH1_V_CFG_03 (0x00bc)
#define VPIF_CH2_TOP_STRT_ADD_LUMA (0x00c0)
#define VPIF_CH2_BTM_STRT_ADD_LUMA (0x00c4)
#define VPIF_CH2_TOP_STRT_ADD_CHROMA (0x00c8)
#define VPIF_CH2_BTM_STRT_ADD_CHROMA (0x00cc)
#define VPIF_CH2_TOP_STRT_ADD_HANC (0x00d0)
#define VPIF_CH2_BTM_STRT_ADD_HANC (0x00d4)
#define VPIF_CH2_TOP_STRT_ADD_VANC (0x00d8)
#define VPIF_CH2_BTM_STRT_ADD_VANC (0x00dc)
#define VPIF_CH2_SP_CFG (0x00e0)
#define VPIF_CH2_IMG_ADD_OFST (0x00e4)
#define VPIF_CH2_HANC_ADD_OFST (0x00e8)
#define VPIF_CH2_H_CFG (0x00ec)
#define VPIF_CH2_V_CFG_00 (0x00f0)
#define VPIF_CH2_V_CFG_01 (0x00f4)
#define VPIF_CH2_V_CFG_02 (0x00f8)
#define VPIF_CH2_V_CFG_03 (0x00fc)
#define VPIF_CH2_HANC0_STRT (0x0100)
#define VPIF_CH2_HANC0_SIZE (0x0104)
#define VPIF_CH2_HANC1_STRT (0x0108)
#define VPIF_CH2_HANC1_SIZE (0x010c)
#define VPIF_CH2_VANC0_STRT (0x0110)
#define VPIF_CH2_VANC0_SIZE (0x0114)
#define VPIF_CH2_VANC1_STRT (0x0118)
#define VPIF_CH2_VANC1_SIZE (0x011c)
#define VPIF_CH3_TOP_STRT_ADD_LUMA (0x0140)
#define VPIF_CH3_BTM_STRT_ADD_LUMA (0x0144)
#define VPIF_CH3_TOP_STRT_ADD_CHROMA (0x0148)
#define VPIF_CH3_BTM_STRT_ADD_CHROMA (0x014c)
#define VPIF_CH3_TOP_STRT_ADD_HANC (0x0150)
#define VPIF_CH3_BTM_STRT_ADD_HANC (0x0154)
#define VPIF_CH3_TOP_STRT_ADD_VANC (0x0158)
#define VPIF_CH3_BTM_STRT_ADD_VANC (0x015c)
#define VPIF_CH3_SP_CFG (0x0160)
#define VPIF_CH3_IMG_ADD_OFST (0x0164)
#define VPIF_CH3_HANC_ADD_OFST (0x0168)
#define VPIF_CH3_H_CFG (0x016c)
#define VPIF_CH3_V_CFG_00 (0x0170)
#define VPIF_CH3_V_CFG_01 (0x0174)
#define VPIF_CH3_V_CFG_02 (0x0178)
#define VPIF_CH3_V_CFG_03 (0x017c)
#define VPIF_CH3_HANC0_STRT (0x0180)
#define VPIF_CH3_HANC0_SIZE (0x0184)
#define VPIF_CH3_HANC1_STRT (0x0188)
#define VPIF_CH3_HANC1_SIZE (0x018c)
#define VPIF_CH3_VANC0_STRT (0x0190)
#define VPIF_CH3_VANC0_SIZE (0x0194)
#define VPIF_CH3_VANC1_STRT (0x0198)
#define VPIF_CH3_VANC1_SIZE (0x019c)
#define VPIF_IODFT_CTRL (0x01c0)
/* Functions for bit Manipulation */
static inline void vpif_set_bit(u32 reg, u32 bit)
{
regw((regr(reg)) | (0x01 << bit), reg);
}
static inline void vpif_clr_bit(u32 reg, u32 bit)
{
regw(((regr(reg)) & ~(0x01 << bit)), reg);
}
/* Macro for Generating mask */
#ifdef GENERATE_MASK
#undef GENERATE_MASK
#endif
#define GENERATE_MASK(bits, pos) \
((((0xFFFFFFFF) << (32 - bits)) >> (32 - bits)) << pos)
/* Bit positions in the channel control registers */
#define VPIF_CH_DATA_MODE_BIT (2)
#define VPIF_CH_YC_MUX_BIT (3)
#define VPIF_CH_SDR_FMT_BIT (4)
#define VPIF_CH_HANC_EN_BIT (8)
#define VPIF_CH_VANC_EN_BIT (9)
#define VPIF_CAPTURE_CH_NIP (10)
#define VPIF_DISPLAY_CH_NIP (11)
#define VPIF_DISPLAY_PIX_EN_BIT (10)
#define VPIF_CH_INPUT_FIELD_FRAME_BIT (12)
#define VPIF_CH_FID_POLARITY_BIT (15)
#define VPIF_CH_V_VALID_POLARITY_BIT (14)
#define VPIF_CH_H_VALID_POLARITY_BIT (13)
#define VPIF_CH_DATA_WIDTH_BIT (28)
#define VPIF_CH_CLK_EDGE_CTRL_BIT (31)
/* Mask various length */
#define VPIF_CH_EAVSAV_MASK GENERATE_MASK(13, 0)
#define VPIF_CH_LEN_MASK GENERATE_MASK(12, 0)
#define VPIF_CH_WIDTH_MASK GENERATE_MASK(13, 0)
#define VPIF_CH_LEN_SHIFT (16)
/* VPIF masks for registers */
#define VPIF_REQ_SIZE_MASK (0x1ff)
/* bit posotion of interrupt vpif_ch_intr register */
#define VPIF_INTEN_FRAME_CH0 (0x00000001)
#define VPIF_INTEN_FRAME_CH1 (0x00000002)
#define VPIF_INTEN_FRAME_CH2 (0x00000004)
#define VPIF_INTEN_FRAME_CH3 (0x00000008)
/* bit position of clock and channel enable in vpif_chn_ctrl register */
#define VPIF_CH0_CLK_EN (0x00000002)
#define VPIF_CH0_EN (0x00000001)
#define VPIF_CH1_CLK_EN (0x00000002)
#define VPIF_CH1_EN (0x00000001)
#define VPIF_CH2_CLK_EN (0x00000002)
#define VPIF_CH2_EN (0x00000001)
#define VPIF_CH3_CLK_EN (0x00000002)
#define VPIF_CH3_EN (0x00000001)
#define VPIF_CH_CLK_EN (0x00000002)
#define VPIF_CH_EN (0x00000001)
#define VPIF_INT_TOP (0x00)
#define VPIF_INT_BOTTOM (0x01)
#define VPIF_INT_BOTH (0x02)
#define VPIF_CH0_INT_CTRL_SHIFT (6)
#define VPIF_CH1_INT_CTRL_SHIFT (6)
#define VPIF_CH2_INT_CTRL_SHIFT (6)
#define VPIF_CH3_INT_CTRL_SHIFT (6)
#define VPIF_CH_INT_CTRL_SHIFT (6)
/* enabled interrupt on both the fields on vpid_ch0_ctrl register */
#define channel0_intr_assert() (regw((regr(VPIF_CH0_CTRL)|\
(VPIF_INT_BOTH << VPIF_CH0_INT_CTRL_SHIFT)), VPIF_CH0_CTRL))
/* enabled interrupt on both the fields on vpid_ch1_ctrl register */
#define channel1_intr_assert() (regw((regr(VPIF_CH1_CTRL)|\
(VPIF_INT_BOTH << VPIF_CH1_INT_CTRL_SHIFT)), VPIF_CH1_CTRL))
/* enabled interrupt on both the fields on vpid_ch0_ctrl register */
#define channel2_intr_assert() (regw((regr(VPIF_CH2_CTRL)|\
(VPIF_INT_BOTH << VPIF_CH2_INT_CTRL_SHIFT)), VPIF_CH2_CTRL))
/* enabled interrupt on both the fields on vpid_ch1_ctrl register */
#define channel3_intr_assert() (regw((regr(VPIF_CH3_CTRL)|\
(VPIF_INT_BOTH << VPIF_CH3_INT_CTRL_SHIFT)), VPIF_CH3_CTRL))
#define VPIF_CH_FID_MASK (0x20)
#define VPIF_CH_FID_SHIFT (5)
#define VPIF_NTSC_VBI_START_FIELD0 (1)
#define VPIF_NTSC_VBI_START_FIELD1 (263)
#define VPIF_PAL_VBI_START_FIELD0 (624)
#define VPIF_PAL_VBI_START_FIELD1 (311)
#define VPIF_NTSC_HBI_START_FIELD0 (1)
#define VPIF_NTSC_HBI_START_FIELD1 (263)
#define VPIF_PAL_HBI_START_FIELD0 (624)
#define VPIF_PAL_HBI_START_FIELD1 (311)
#define VPIF_NTSC_VBI_COUNT_FIELD0 (20)
#define VPIF_NTSC_VBI_COUNT_FIELD1 (19)
#define VPIF_PAL_VBI_COUNT_FIELD0 (24)
#define VPIF_PAL_VBI_COUNT_FIELD1 (25)
#define VPIF_NTSC_HBI_COUNT_FIELD0 (263)
#define VPIF_NTSC_HBI_COUNT_FIELD1 (262)
#define VPIF_PAL_HBI_COUNT_FIELD0 (312)
#define VPIF_PAL_HBI_COUNT_FIELD1 (313)
#define VPIF_NTSC_VBI_SAMPLES_PER_LINE (720)
#define VPIF_PAL_VBI_SAMPLES_PER_LINE (720)
#define VPIF_NTSC_HBI_SAMPLES_PER_LINE (268)
#define VPIF_PAL_HBI_SAMPLES_PER_LINE (280)
#define VPIF_CH_VANC_EN (0x20)
#define VPIF_DMA_REQ_SIZE (0x080)
#define VPIF_EMULATION_DISABLE (0x01)
extern u8 irq_vpif_capture_channel[VPIF_NUM_CHANNELS];
/* inline function to enable/disable channel0 */
static inline void enable_channel0(int enable)
{
if (enable)
regw((regr(VPIF_CH0_CTRL) | (VPIF_CH0_EN)), VPIF_CH0_CTRL);
else
regw((regr(VPIF_CH0_CTRL) & (~VPIF_CH0_EN)), VPIF_CH0_CTRL);
}
/* inline function to enable/disable channel1 */
static inline void enable_channel1(int enable)
{
if (enable)
regw((regr(VPIF_CH1_CTRL) | (VPIF_CH1_EN)), VPIF_CH1_CTRL);
else
regw((regr(VPIF_CH1_CTRL) & (~VPIF_CH1_EN)), VPIF_CH1_CTRL);
}
/* inline function to enable interrupt for channel0 */
static inline void channel0_intr_enable(int enable)
{
unsigned long flags;
spin_lock_irqsave(&vpif_lock, flags);
if (enable) {
regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH0), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0),
VPIF_INTEN_SET);
} else {
regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH0)), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0),
VPIF_INTEN_SET);
}
spin_unlock_irqrestore(&vpif_lock, flags);
}
/* inline function to enable interrupt for channel1 */
static inline void channel1_intr_enable(int enable)
{
unsigned long flags;
spin_lock_irqsave(&vpif_lock, flags);
if (enable) {
regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH1), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1),
VPIF_INTEN_SET);
} else {
regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH1)), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1),
VPIF_INTEN_SET);
}
spin_unlock_irqrestore(&vpif_lock, flags);
}
/* inline function to set buffer addresses in case of Y/C non mux mode */
static inline void ch0_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma,
unsigned long btm_strt_luma,
unsigned long top_strt_chroma,
unsigned long btm_strt_chroma)
{
regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA);
regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA);
regw(top_strt_chroma, VPIF_CH1_TOP_STRT_ADD_CHROMA);
regw(btm_strt_chroma, VPIF_CH1_BTM_STRT_ADD_CHROMA);
}
/* inline function to set buffer addresses in VPIF registers for video data */
static inline void ch0_set_videobuf_addr(unsigned long top_strt_luma,
unsigned long btm_strt_luma,
unsigned long top_strt_chroma,
unsigned long btm_strt_chroma)
{
regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA);
regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA);
regw(top_strt_chroma, VPIF_CH0_TOP_STRT_ADD_CHROMA);
regw(btm_strt_chroma, VPIF_CH0_BTM_STRT_ADD_CHROMA);
}
static inline void ch1_set_videobuf_addr(unsigned long top_strt_luma,
unsigned long btm_strt_luma,
unsigned long top_strt_chroma,
unsigned long btm_strt_chroma)
{
regw(top_strt_luma, VPIF_CH1_TOP_STRT_ADD_LUMA);
regw(btm_strt_luma, VPIF_CH1_BTM_STRT_ADD_LUMA);
regw(top_strt_chroma, VPIF_CH1_TOP_STRT_ADD_CHROMA);
regw(btm_strt_chroma, VPIF_CH1_BTM_STRT_ADD_CHROMA);
}
static inline void ch0_set_vbi_addr(unsigned long top_vbi,
unsigned long btm_vbi, unsigned long a, unsigned long b)
{
regw(top_vbi, VPIF_CH0_TOP_STRT_ADD_VANC);
regw(btm_vbi, VPIF_CH0_BTM_STRT_ADD_VANC);
}
static inline void ch0_set_hbi_addr(unsigned long top_vbi,
unsigned long btm_vbi, unsigned long a, unsigned long b)
{
regw(top_vbi, VPIF_CH0_TOP_STRT_ADD_HANC);
regw(btm_vbi, VPIF_CH0_BTM_STRT_ADD_HANC);
}
static inline void ch1_set_vbi_addr(unsigned long top_vbi,
unsigned long btm_vbi, unsigned long a, unsigned long b)
{
regw(top_vbi, VPIF_CH1_TOP_STRT_ADD_VANC);
regw(btm_vbi, VPIF_CH1_BTM_STRT_ADD_VANC);
}
static inline void ch1_set_hbi_addr(unsigned long top_vbi,
unsigned long btm_vbi, unsigned long a, unsigned long b)
{
regw(top_vbi, VPIF_CH1_TOP_STRT_ADD_HANC);
regw(btm_vbi, VPIF_CH1_BTM_STRT_ADD_HANC);
}
/* Inline function to enable raw vbi in the given channel */
static inline void disable_raw_feature(u8 channel_id, u8 index)
{
u32 ctrl_reg;
if (0 == channel_id)
ctrl_reg = VPIF_CH0_CTRL;
else
ctrl_reg = VPIF_CH1_CTRL;
if (1 == index)
vpif_clr_bit(ctrl_reg, VPIF_CH_VANC_EN_BIT);
else
vpif_clr_bit(ctrl_reg, VPIF_CH_HANC_EN_BIT);
}
static inline void enable_raw_feature(u8 channel_id, u8 index)
{
u32 ctrl_reg;
if (0 == channel_id)
ctrl_reg = VPIF_CH0_CTRL;
else
ctrl_reg = VPIF_CH1_CTRL;
if (1 == index)
vpif_set_bit(ctrl_reg, VPIF_CH_VANC_EN_BIT);
else
vpif_set_bit(ctrl_reg, VPIF_CH_HANC_EN_BIT);
}
/* inline function to enable/disable channel2 */
static inline void enable_channel2(int enable)
{
if (enable) {
regw((regr(VPIF_CH2_CTRL) | (VPIF_CH2_CLK_EN)), VPIF_CH2_CTRL);
regw((regr(VPIF_CH2_CTRL) | (VPIF_CH2_EN)), VPIF_CH2_CTRL);
} else {
regw((regr(VPIF_CH2_CTRL) & (~VPIF_CH2_CLK_EN)), VPIF_CH2_CTRL);
regw((regr(VPIF_CH2_CTRL) & (~VPIF_CH2_EN)), VPIF_CH2_CTRL);
}
}
/* inline function to enable/disable channel3 */
static inline void enable_channel3(int enable)
{
if (enable) {
regw((regr(VPIF_CH3_CTRL) | (VPIF_CH3_CLK_EN)), VPIF_CH3_CTRL);
regw((regr(VPIF_CH3_CTRL) | (VPIF_CH3_EN)), VPIF_CH3_CTRL);
} else {
regw((regr(VPIF_CH3_CTRL) & (~VPIF_CH3_CLK_EN)), VPIF_CH3_CTRL);
regw((regr(VPIF_CH3_CTRL) & (~VPIF_CH3_EN)), VPIF_CH3_CTRL);
}
}
/* inline function to enable interrupt for channel2 */
static inline void channel2_intr_enable(int enable)
{
unsigned long flags;
spin_lock_irqsave(&vpif_lock, flags);
if (enable) {
regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH2), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2),
VPIF_INTEN_SET);
} else {
regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH2)), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2),
VPIF_INTEN_SET);
}
spin_unlock_irqrestore(&vpif_lock, flags);
}
/* inline function to enable interrupt for channel3 */
static inline void channel3_intr_enable(int enable)
{
unsigned long flags;
spin_lock_irqsave(&vpif_lock, flags);
if (enable) {
regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH3), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3),
VPIF_INTEN_SET);
} else {
regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH3)), VPIF_INTEN);
regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3),
VPIF_INTEN_SET);
}
spin_unlock_irqrestore(&vpif_lock, flags);
}
/* inline function to enable raw vbi data for channel2 */
static inline void channel2_raw_enable(int enable, u8 index)
{
u32 mask;
if (1 == index)
mask = VPIF_CH_VANC_EN_BIT;
else
mask = VPIF_CH_HANC_EN_BIT;
if (enable)
vpif_set_bit(VPIF_CH2_CTRL, mask);
else
vpif_clr_bit(VPIF_CH2_CTRL, mask);
}
/* inline function to enable raw vbi data for channel3*/
static inline void channel3_raw_enable(int enable, u8 index)
{
u32 mask;
if (1 == index)
mask = VPIF_CH_VANC_EN_BIT;
else
mask = VPIF_CH_HANC_EN_BIT;
if (enable)
vpif_set_bit(VPIF_CH3_CTRL, mask);
else
vpif_clr_bit(VPIF_CH3_CTRL, mask);
}
/* inline function to set buffer addresses in case of Y/C non mux mode */
static inline void ch2_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma,
unsigned long btm_strt_luma,
unsigned long top_strt_chroma,
unsigned long btm_strt_chroma)
{
regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA);
regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA);
regw(top_strt_chroma, VPIF_CH3_TOP_STRT_ADD_CHROMA);
regw(btm_strt_chroma, VPIF_CH3_BTM_STRT_ADD_CHROMA);
}
/* inline function to set buffer addresses in VPIF registers for video data */
static inline void ch2_set_videobuf_addr(unsigned long top_strt_luma,
unsigned long btm_strt_luma,
unsigned long top_strt_chroma,
unsigned long btm_strt_chroma)
{
regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA);
regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA);
regw(top_strt_chroma, VPIF_CH2_TOP_STRT_ADD_CHROMA);
regw(btm_strt_chroma, VPIF_CH2_BTM_STRT_ADD_CHROMA);
}
static inline void ch3_set_videobuf_addr(unsigned long top_strt_luma,
unsigned long btm_strt_luma,
unsigned long top_strt_chroma,
unsigned long btm_strt_chroma)
{
regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_LUMA);
regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_LUMA);
regw(top_strt_chroma, VPIF_CH3_TOP_STRT_ADD_CHROMA);
regw(btm_strt_chroma, VPIF_CH3_BTM_STRT_ADD_CHROMA);
}
/* inline function to set buffer addresses in VPIF registers for vbi data */
static inline void ch2_set_vbi_addr(unsigned long top_strt_luma,
unsigned long btm_strt_luma,
unsigned long top_strt_chroma,
unsigned long btm_strt_chroma)
{
regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_VANC);
regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_VANC);
}
static inline void ch3_set_vbi_addr(unsigned long top_strt_luma,
unsigned long btm_strt_luma,
unsigned long top_strt_chroma,
unsigned long btm_strt_chroma)
{
regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_VANC);
regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_VANC);
}
#define VPIF_MAX_NAME (30)
/* This structure will store size parameters as per the mode selected by user */
struct vpif_channel_config_params {
char name[VPIF_MAX_NAME]; /* Name of the mode */
u16 width; /* Indicates width of the image */
u16 height; /* Indicates height of the image */
u8 fps;
u8 frm_fmt; /* Indicates whether this is interlaced
* or progressive format */
u8 ycmux_mode; /* Indicates whether this mode requires
* single or two channels */
u16 eav2sav; /* length of sav 2 eav */
u16 sav2eav; /* length of sav 2 eav */
u16 l1, l3, l5, l7, l9, l11; /* Other parameter configurations */
u16 vsize; /* Vertical size of the image */
u8 capture_format; /* Indicates whether capture format
* is in BT or in CCD/CMOS */
u8 vbi_supported; /* Indicates whether this mode
* supports capturing vbi or not */
u8 hd_sd;
v4l2_std_id stdid;
};
struct vpif_video_params;
struct vpif_params;
struct vpif_vbi_params;
int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id);
void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams,
u8 channel_id);
int vpif_channel_getfid(u8 channel_id);
enum data_size {
_8BITS = 0,
_10BITS,
_12BITS,
};
/* Structure for vpif parameters for raw vbi data */
struct vpif_vbi_params {
__u32 hstart0; /* Horizontal start of raw vbi data for first field */
__u32 vstart0; /* Vertical start of raw vbi data for first field */
__u32 hsize0; /* Horizontal size of raw vbi data for first field */
__u32 vsize0; /* Vertical size of raw vbi data for first field */
__u32 hstart1; /* Horizontal start of raw vbi data for second field */
__u32 vstart1; /* Vertical start of raw vbi data for second field */
__u32 hsize1; /* Horizontal size of raw vbi data for second field */
__u32 vsize1; /* Vertical size of raw vbi data for second field */
};
/* structure for vpif parameters */
struct vpif_video_params {
__u8 storage_mode; /* Indicates field or frame mode */
unsigned long hpitch;
v4l2_std_id stdid;
};
struct vpif_params {
struct vpif_interface iface;
struct vpif_video_params video_params;
struct vpif_channel_config_params std_info;
union param {
struct vpif_vbi_params vbi_params;
enum data_size data_sz;
} params;
};
#endif /* End of #ifndef VPIF_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,165 @@
/*
* Copyright (C) 2009 Texas Instruments Inc
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef VPIF_CAPTURE_H
#define VPIF_CAPTURE_H
#ifdef __KERNEL__
/* Header files */
#include <linux/videodev2.h>
#include <linux/version.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/videobuf-core.h>
#include <media/videobuf-dma-contig.h>
#include <mach/dm646x.h>
#include "vpif.h"
/* Macros */
#define VPIF_MAJOR_RELEASE 0
#define VPIF_MINOR_RELEASE 0
#define VPIF_BUILD 1
#define VPIF_CAPTURE_VERSION_CODE ((VPIF_MAJOR_RELEASE << 16) | \
(VPIF_MINOR_RELEASE << 8) | VPIF_BUILD)
#define VPIF_VALID_FIELD(field) (((V4L2_FIELD_ANY == field) || \
(V4L2_FIELD_NONE == field)) || \
(((V4L2_FIELD_INTERLACED == field) || \
(V4L2_FIELD_SEQ_TB == field)) || \
(V4L2_FIELD_SEQ_BT == field)))
#define VPIF_CAPTURE_MAX_DEVICES 2
#define VPIF_VIDEO_INDEX 0
#define VPIF_NUMBER_OF_OBJECTS 1
/* Enumerated data type to give id to each device per channel */
enum vpif_channel_id {
VPIF_CHANNEL0_VIDEO = 0,
VPIF_CHANNEL1_VIDEO,
};
struct video_obj {
enum v4l2_field buf_field;
/* Currently selected or default standard */
v4l2_std_id stdid;
/* This is to track the last input that is passed to application */
u32 input_idx;
};
struct common_obj {
/* Pointer pointing to current v4l2_buffer */
struct videobuf_buffer *cur_frm;
/* Pointer pointing to current v4l2_buffer */
struct videobuf_buffer *next_frm;
/*
* This field keeps track of type of buffer exchange mechanism
* user has selected
*/
enum v4l2_memory memory;
/* Used to store pixel format */
struct v4l2_format fmt;
/* Buffer queue used in video-buf */
struct videobuf_queue buffer_queue;
/* Queue of filled frames */
struct list_head dma_queue;
/* Used in video-buf */
spinlock_t irqlock;
/* lock used to access this structure */
struct mutex lock;
/* number of users performing IO */
u32 io_usrs;
/* Indicates whether streaming started */
u8 started;
/* Function pointer to set the addresses */
void (*set_addr) (unsigned long, unsigned long, unsigned long,
unsigned long);
/* offset where Y top starts from the starting of the buffer */
u32 ytop_off;
/* offset where Y bottom starts from the starting of the buffer */
u32 ybtm_off;
/* offset where C top starts from the starting of the buffer */
u32 ctop_off;
/* offset where C bottom starts from the starting of the buffer */
u32 cbtm_off;
/* Indicates width of the image data */
u32 width;
/* Indicates height of the image data */
u32 height;
};
struct channel_obj {
/* Identifies video device for this channel */
struct video_device *video_dev;
/* Used to keep track of state of the priority */
struct v4l2_prio_state prio;
/* number of open instances of the channel */
int usrs;
/* Indicates id of the field which is being displayed */
u32 field_id;
/* flag to indicate whether decoder is initialized */
u8 initialized;
/* Identifies channel */
enum vpif_channel_id channel_id;
/* index into sd table */
int curr_sd_index;
/* ptr to current sub device information */
struct vpif_subdev_info *curr_subdev_info;
/* vpif configuration params */
struct vpif_params vpifparams;
/* common object array */
struct common_obj common[VPIF_NUMBER_OF_OBJECTS];
/* video object */
struct video_obj video;
};
/* File handle structure */
struct vpif_fh {
/* pointer to channel object for opened device */
struct channel_obj *channel;
/* Indicates whether this file handle is doing IO */
u8 io_allowed[VPIF_NUMBER_OF_OBJECTS];
/* Used to keep track priority of this instance */
enum v4l2_priority prio;
/* Used to indicate channel is initialize or not */
u8 initialized;
};
struct vpif_device {
struct v4l2_device v4l2_dev;
struct channel_obj *dev[VPIF_CAPTURE_NUM_CHANNELS];
struct v4l2_subdev **sd;
};
struct vpif_config_params {
u8 min_numbuffers;
u8 numbuffers[VPIF_CAPTURE_NUM_CHANNELS];
s8 device_type;
u32 min_bufsize[VPIF_CAPTURE_NUM_CHANNELS];
u32 channel_bufsize[VPIF_CAPTURE_NUM_CHANNELS];
u8 default_device[VPIF_CAPTURE_NUM_CHANNELS];
u8 max_device_type;
};
/* Struct which keeps track of the line numbers for the sliced vbi service */
struct vpif_service_line {
u16 service_id;
u16 service_line[2];
};
#endif /* End of __KERNEL__ */
#endif /* VPIF_CAPTURE_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,175 @@
/*
* DM646x display header file
*
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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.
*
* This program is distributed .as is. WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef DAVINCIHD_DISPLAY_H
#define DAVINCIHD_DISPLAY_H
/* Header files */
#include <linux/videodev2.h>
#include <linux/version.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/videobuf-core.h>
#include <media/videobuf-dma-contig.h>
#include "vpif.h"
/* Macros */
#define VPIF_MAJOR_RELEASE (0)
#define VPIF_MINOR_RELEASE (0)
#define VPIF_BUILD (1)
#define VPIF_DISPLAY_VERSION_CODE \
((VPIF_MAJOR_RELEASE << 16) | (VPIF_MINOR_RELEASE << 8) | VPIF_BUILD)
#define VPIF_VALID_FIELD(field) \
(((V4L2_FIELD_ANY == field) || (V4L2_FIELD_NONE == field)) || \
(((V4L2_FIELD_INTERLACED == field) || (V4L2_FIELD_SEQ_TB == field)) || \
(V4L2_FIELD_SEQ_BT == field)))
#define VPIF_DISPLAY_MAX_DEVICES (2)
#define VPIF_SLICED_BUF_SIZE (256)
#define VPIF_SLICED_MAX_SERVICES (3)
#define VPIF_VIDEO_INDEX (0)
#define VPIF_VBI_INDEX (1)
#define VPIF_HBI_INDEX (2)
/* Setting it to 1 as HBI/VBI support yet to be added , else 3*/
#define VPIF_NUMOBJECTS (1)
/* Macros */
#define ISALIGNED(a) (0 == ((a) & 7))
/* enumerated data types */
/* Enumerated data type to give id to each device per channel */
enum vpif_channel_id {
VPIF_CHANNEL2_VIDEO = 0, /* Channel2 Video */
VPIF_CHANNEL3_VIDEO, /* Channel3 Video */
};
/* structures */
struct video_obj {
enum v4l2_field buf_field;
u32 latest_only; /* indicate whether to return
* most recent displayed frame only */
v4l2_std_id stdid; /* Currently selected or default
* standard */
u32 output_id; /* Current output id */
};
struct vbi_obj {
int num_services;
struct vpif_vbi_params vbiparams; /* vpif parameters for the raw
* vbi data */
};
struct common_obj {
/* Buffer specific parameters */
u8 *fbuffers[VIDEO_MAX_FRAME]; /* List of buffer pointers for
* storing frames */
u32 numbuffers; /* number of buffers */
struct videobuf_buffer *cur_frm; /* Pointer pointing to current
* videobuf_buffer */
struct videobuf_buffer *next_frm; /* Pointer pointing to next
* videobuf_buffer */
enum v4l2_memory memory; /* This field keeps track of
* type of buffer exchange
* method user has selected */
struct v4l2_format fmt; /* Used to store the format */
struct videobuf_queue buffer_queue; /* Buffer queue used in
* video-buf */
struct list_head dma_queue; /* Queue of filled frames */
spinlock_t irqlock; /* Used in video-buf */
/* channel specific parameters */
struct mutex lock; /* lock used to access this
* structure */
u32 io_usrs; /* number of users performing
* IO */
u8 started; /* Indicates whether streaming
* started */
u32 ytop_off; /* offset of Y top from the
* starting of the buffer */
u32 ybtm_off; /* offset of Y bottom from the
* starting of the buffer */
u32 ctop_off; /* offset of C top from the
* starting of the buffer */
u32 cbtm_off; /* offset of C bottom from the
* starting of the buffer */
/* Function pointer to set the addresses */
void (*set_addr) (unsigned long, unsigned long,
unsigned long, unsigned long);
u32 height;
u32 width;
};
struct channel_obj {
/* V4l2 specific parameters */
struct video_device *video_dev; /* Identifies video device for
* this channel */
struct v4l2_prio_state prio; /* Used to keep track of state of
* the priority */
atomic_t usrs; /* number of open instances of
* the channel */
u32 field_id; /* Indicates id of the field
* which is being displayed */
u8 initialized; /* flag to indicate whether
* encoder is initialized */
enum vpif_channel_id channel_id;/* Identifies channel */
struct vpif_params vpifparams;
struct common_obj common[VPIF_NUMOBJECTS];
struct video_obj video;
struct vbi_obj vbi;
};
/* File handle structure */
struct vpif_fh {
struct channel_obj *channel; /* pointer to channel object for
* opened device */
u8 io_allowed[VPIF_NUMOBJECTS]; /* Indicates whether this file handle
* is doing IO */
enum v4l2_priority prio; /* Used to keep track priority of
* this instance */
u8 initialized; /* Used to keep track of whether this
* file handle has initialized
* channel or not */
};
/* vpif device structure */
struct vpif_device {
struct v4l2_device v4l2_dev;
struct channel_obj *dev[VPIF_DISPLAY_NUM_CHANNELS];
struct v4l2_subdev **sd;
};
struct vpif_config_params {
u32 min_bufsize[VPIF_DISPLAY_NUM_CHANNELS];
u32 channel_bufsize[VPIF_DISPLAY_NUM_CHANNELS];
u8 numbuffers[VPIF_DISPLAY_NUM_CHANNELS];
u8 min_numbuffers;
};
/* Struct which keeps track of the line numbers for the sliced vbi service */
struct vpif_service_line {
u16 service_id;
u16 service_line[2];
u16 enc_service_id;
u8 bytestowrite;
};
#endif /* DAVINCIHD_DISPLAY_H */

View File

@@ -0,0 +1,301 @@
/*
* Copyright (C) 2009 Texas Instruments.
*
* 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.
*
* common vpss driver for all video drivers.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/compiler.h>
#include <linux/io.h>
#include <mach/hardware.h>
#include <media/davinci/vpss.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("VPSS Driver");
MODULE_AUTHOR("Texas Instruments");
/* DM644x defines */
#define DM644X_SBL_PCR_VPSS (4)
/* vpss BL register offsets */
#define DM355_VPSSBL_CCDCMUX 0x1c
/* vpss CLK register offsets */
#define DM355_VPSSCLK_CLKCTRL 0x04
/* masks and shifts */
#define VPSS_HSSISEL_SHIFT 4
/*
* vpss operations. Depends on platform. Not all functions are available
* on all platforms. The api, first check if a functio is available before
* invoking it. In the probe, the function ptrs are intialized based on
* vpss name. vpss name can be "dm355_vpss", "dm644x_vpss" etc.
*/
struct vpss_hw_ops {
/* enable clock */
int (*enable_clock)(enum vpss_clock_sel clock_sel, int en);
/* select input to ccdc */
void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel);
/* clear wbl overlflow bit */
int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel);
};
/* vpss configuration */
struct vpss_oper_config {
__iomem void *vpss_bl_regs_base;
__iomem void *vpss_regs_base;
struct resource *r1;
resource_size_t len1;
struct resource *r2;
resource_size_t len2;
char vpss_name[32];
spinlock_t vpss_lock;
struct vpss_hw_ops hw_ops;
};
static struct vpss_oper_config oper_cfg;
/* register access routines */
static inline u32 bl_regr(u32 offset)
{
return __raw_readl(oper_cfg.vpss_bl_regs_base + offset);
}
static inline void bl_regw(u32 val, u32 offset)
{
__raw_writel(val, oper_cfg.vpss_bl_regs_base + offset);
}
static inline u32 vpss_regr(u32 offset)
{
return __raw_readl(oper_cfg.vpss_regs_base + offset);
}
static inline void vpss_regw(u32 val, u32 offset)
{
__raw_writel(val, oper_cfg.vpss_regs_base + offset);
}
static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
{
bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX);
}
int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
{
if (!oper_cfg.hw_ops.select_ccdc_source)
return -1;
dm355_select_ccdc_source(src_sel);
return 0;
}
EXPORT_SYMBOL(vpss_select_ccdc_source);
static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel)
{
u32 mask = 1, val;
if (wbl_sel < VPSS_PCR_AEW_WBL_0 ||
wbl_sel > VPSS_PCR_CCDC_WBL_O)
return -1;
/* writing a 0 clear the overflow */
mask = ~(mask << wbl_sel);
val = bl_regr(DM644X_SBL_PCR_VPSS) & mask;
bl_regw(val, DM644X_SBL_PCR_VPSS);
return 0;
}
int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel)
{
if (!oper_cfg.hw_ops.clear_wbl_overflow)
return -1;
return oper_cfg.hw_ops.clear_wbl_overflow(wbl_sel);
}
EXPORT_SYMBOL(vpss_clear_wbl_overflow);
/*
* dm355_enable_clock - Enable VPSS Clock
* @clock_sel: CLock to be enabled/disabled
* @en: enable/disable flag
*
* This is called to enable or disable a vpss clock
*/
static int dm355_enable_clock(enum vpss_clock_sel clock_sel, int en)
{
unsigned long flags;
u32 utemp, mask = 0x1, shift = 0;
switch (clock_sel) {
case VPSS_VPBE_CLOCK:
/* nothing since lsb */
break;
case VPSS_VENC_CLOCK_SEL:
shift = 2;
break;
case VPSS_CFALD_CLOCK:
shift = 3;
break;
case VPSS_H3A_CLOCK:
shift = 4;
break;
case VPSS_IPIPE_CLOCK:
shift = 5;
break;
case VPSS_CCDC_CLOCK:
shift = 6;
break;
default:
printk(KERN_ERR "dm355_enable_clock:"
" Invalid selector: %d\n", clock_sel);
return -1;
}
spin_lock_irqsave(&oper_cfg.vpss_lock, flags);
utemp = vpss_regr(DM355_VPSSCLK_CLKCTRL);
if (!en)
utemp &= ~(mask << shift);
else
utemp |= (mask << shift);
vpss_regw(utemp, DM355_VPSSCLK_CLKCTRL);
spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags);
return 0;
}
int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en)
{
if (!oper_cfg.hw_ops.enable_clock)
return -1;
return oper_cfg.hw_ops.enable_clock(clock_sel, en);
}
EXPORT_SYMBOL(vpss_enable_clock);
static int __init vpss_probe(struct platform_device *pdev)
{
int status, dm355 = 0;
if (!pdev->dev.platform_data) {
dev_err(&pdev->dev, "no platform data\n");
return -ENOENT;
}
strcpy(oper_cfg.vpss_name, pdev->dev.platform_data);
if (!strcmp(oper_cfg.vpss_name, "dm355_vpss"))
dm355 = 1;
else if (strcmp(oper_cfg.vpss_name, "dm644x_vpss")) {
dev_err(&pdev->dev, "vpss driver not supported on"
" this platform\n");
return -ENODEV;
}
dev_info(&pdev->dev, "%s vpss probed\n", oper_cfg.vpss_name);
oper_cfg.r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!oper_cfg.r1)
return -ENOENT;
oper_cfg.len1 = oper_cfg.r1->end - oper_cfg.r1->start + 1;
oper_cfg.r1 = request_mem_region(oper_cfg.r1->start, oper_cfg.len1,
oper_cfg.r1->name);
if (!oper_cfg.r1)
return -EBUSY;
oper_cfg.vpss_bl_regs_base = ioremap(oper_cfg.r1->start, oper_cfg.len1);
if (!oper_cfg.vpss_bl_regs_base) {
status = -EBUSY;
goto fail1;
}
if (dm355) {
oper_cfg.r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!oper_cfg.r2) {
status = -ENOENT;
goto fail2;
}
oper_cfg.len2 = oper_cfg.r2->end - oper_cfg.r2->start + 1;
oper_cfg.r2 = request_mem_region(oper_cfg.r2->start,
oper_cfg.len2,
oper_cfg.r2->name);
if (!oper_cfg.r2) {
status = -EBUSY;
goto fail2;
}
oper_cfg.vpss_regs_base = ioremap(oper_cfg.r2->start,
oper_cfg.len2);
if (!oper_cfg.vpss_regs_base) {
status = -EBUSY;
goto fail3;
}
}
if (dm355) {
oper_cfg.hw_ops.enable_clock = dm355_enable_clock;
oper_cfg.hw_ops.select_ccdc_source = dm355_select_ccdc_source;
} else
oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow;
spin_lock_init(&oper_cfg.vpss_lock);
dev_info(&pdev->dev, "%s vpss probe success\n", oper_cfg.vpss_name);
return 0;
fail3:
release_mem_region(oper_cfg.r2->start, oper_cfg.len2);
fail2:
iounmap(oper_cfg.vpss_bl_regs_base);
fail1:
release_mem_region(oper_cfg.r1->start, oper_cfg.len1);
return status;
}
static int vpss_remove(struct platform_device *pdev)
{
iounmap(oper_cfg.vpss_bl_regs_base);
release_mem_region(oper_cfg.r1->start, oper_cfg.len1);
if (!strcmp(oper_cfg.vpss_name, "dm355_vpss")) {
iounmap(oper_cfg.vpss_regs_base);
release_mem_region(oper_cfg.r2->start, oper_cfg.len2);
}
return 0;
}
static struct platform_driver vpss_driver = {
.driver = {
.name = "vpss",
.owner = THIS_MODULE,
},
.remove = __devexit_p(vpss_remove),
.probe = vpss_probe,
};
static void vpss_exit(void)
{
platform_driver_unregister(&vpss_driver);
}
static int __init vpss_init(void)
{
return platform_driver_register(&vpss_driver);
}
subsys_initcall(vpss_init);
module_exit(vpss_exit);

View File

@@ -36,6 +36,7 @@ config VIDEO_EM28XX_DVB
depends on VIDEO_EM28XX && DVB_CORE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
select DVB_TDA10023 if !DVB_FE_CUSTOMISE
select VIDEOBUF_DVB
---help---
This adds support for DVB cards based on the

View File

@@ -1,5 +1,5 @@
em28xx-objs := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \
em28xx-input.o
em28xx-input.o em28xx-vbi.o
em28xx-alsa-objs := em28xx-audio.o

View File

@@ -170,6 +170,19 @@ static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = {
{ -1, -1, -1, -1},
};
/* eb1a:2868 Reddo DVB-C USB TV Box
GPIO4 - CU1216L NIM
Other GPIOs seems to be don't care. */
static struct em28xx_reg_seq reddo_dvb_c_usb_box[] = {
{EM28XX_R08_GPIO, 0xfe, 0xff, 10},
{EM28XX_R08_GPIO, 0xde, 0xff, 10},
{EM28XX_R08_GPIO, 0xfe, 0xff, 10},
{EM28XX_R08_GPIO, 0xff, 0xff, 10},
{EM28XX_R08_GPIO, 0x7f, 0xff, 10},
{EM28XX_R08_GPIO, 0x6f, 0xff, 10},
{EM28XX_R08_GPIO, 0xff, 0xff, 10},
{-1, -1, -1, -1},
};
/* Callback for the most boards */
static struct em28xx_reg_seq default_tuner_gpio[] = {
@@ -1566,6 +1579,14 @@ struct em28xx_board em28xx_boards[] = {
.gpio = evga_indtube_analog,
} },
},
/* eb1a:2868 Empia EM2870 + Philips CU1216L NIM (Philips TDA10023 +
Infineon TUA6034) */
[EM2870_BOARD_REDDO_DVB_C_USB_BOX] = {
.name = "Reddo DVB-C USB TV Box",
.tuner_type = TUNER_ABSENT,
.has_dvb = 1,
.dvb_gpio = reddo_dvb_c_usb_box,
},
};
const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
@@ -1593,6 +1614,8 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2883),
.driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2868),
.driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0xe300),
.driver_info = EM2861_BOARD_KWORLD_PVRTV_300U },
{ USB_DEVICE(0xeb1a, 0xe303),
@@ -1696,6 +1719,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = {
{0x166a0441, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028},
{0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028},
{0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028},
{0x63f653bd, EM2870_BOARD_REDDO_DVB_C_USB_BOX, TUNER_ABSENT},
};
/* I2C devicelist hash table for devices with generic USB IDs */
@@ -2348,55 +2372,55 @@ void em28xx_card_setup(struct em28xx *dev)
/* request some modules */
if (dev->board.has_msp34xx)
v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"msp3400", "msp3400", msp3400_addrs);
v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"msp3400", "msp3400", 0, msp3400_addrs);
if (dev->board.decoder == EM28XX_SAA711X)
v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"saa7115", "saa7115_auto", saa711x_addrs);
v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"saa7115", "saa7115_auto", 0, saa711x_addrs);
if (dev->board.decoder == EM28XX_TVP5150)
v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"tvp5150", "tvp5150", tvp5150_addrs);
v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"tvp5150", "tvp5150", 0, tvp5150_addrs);
if (dev->em28xx_sensor == EM28XX_MT9V011) {
struct v4l2_subdev *sd;
sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev,
&dev->i2c_adap, "mt9v011", "mt9v011", mt9v011_addrs);
sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_adap, "mt9v011", "mt9v011", 0, mt9v011_addrs);
v4l2_subdev_call(sd, core, s_config, 0, &dev->sensor_xtal);
}
if (dev->board.adecoder == EM28XX_TVAUDIO)
v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"tvaudio", "tvaudio", dev->board.tvaudio_addr);
"tvaudio", "tvaudio", dev->board.tvaudio_addr, NULL);
if (dev->board.tuner_type != TUNER_ABSENT) {
int has_demod = (dev->tda9887_conf & TDA9887_PRESENT);
if (dev->board.radio.type)
v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"tuner", "tuner", dev->board.radio_addr);
"tuner", "tuner", dev->board.radio_addr, NULL);
if (has_demod)
v4l2_i2c_new_probed_subdev(&dev->v4l2_dev,
v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_adap, "tuner", "tuner",
v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
if (dev->tuner_addr == 0) {
enum v4l2_i2c_tuner_type type =
has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
struct v4l2_subdev *sd;
sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev,
sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_adap, "tuner", "tuner",
v4l2_i2c_tuner_addrs(type));
0, v4l2_i2c_tuner_addrs(type));
if (sd)
dev->tuner_addr = v4l2_i2c_subdev_addr(sd);
} else {
v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"tuner", "tuner", dev->tuner_addr);
"tuner", "tuner", dev->tuner_addr, NULL);
}
}
@@ -2570,7 +2594,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
* Default format, used for tvp5150 or saa711x output formats
*/
dev->vinmode = 0x10;
dev->vinctl = 0x11;
dev->vinctl = EM28XX_VINCTRL_INTERLACED |
EM28XX_VINCTRL_CCIR656_ENABLE;
/* Do board specific init and eeprom reading */
em28xx_card_setup(dev);
@@ -2589,6 +2614,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
/* init video dma queues */
INIT_LIST_HEAD(&dev->vidq.active);
INIT_LIST_HEAD(&dev->vidq.queued);
INIT_LIST_HEAD(&dev->vbiq.active);
INIT_LIST_HEAD(&dev->vbiq.queued);
if (dev->board.has_msp34xx) {

View File

@@ -54,6 +54,10 @@ static int alt = EM28XX_PINOUT;
module_param(alt, int, 0644);
MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
static unsigned int disable_vbi;
module_param(disable_vbi, int, 0644);
MODULE_PARM_DESC(disable_vbi, "disable vbi support");
/* FIXME */
#define em28xx_isocdbg(fmt, arg...) do {\
if (core_debug) \
@@ -648,9 +652,24 @@ int em28xx_capture_start(struct em28xx *dev, int start)
return rc;
}
int em28xx_vbi_supported(struct em28xx *dev)
{
/* Modprobe option to manually disable */
if (disable_vbi == 1)
return 0;
if (dev->chip_id == CHIP_ID_EM2860 ||
dev->chip_id == CHIP_ID_EM2883)
return 1;
/* Version of em28xx that does not support VBI */
return 0;
}
int em28xx_set_outfmt(struct em28xx *dev)
{
int ret;
u8 vinctrl;
ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT,
dev->format->reg | 0x20, 0xff);
@@ -661,7 +680,16 @@ int em28xx_set_outfmt(struct em28xx *dev)
if (ret < 0)
return ret;
return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, dev->vinctl);
vinctrl = dev->vinctl;
if (em28xx_vbi_supported(dev) == 1) {
vinctrl |= EM28XX_VINCTRL_VBI_RAW;
em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00);
em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09);
em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, 0xb4);
em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, 0x0c);
}
return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl);
}
static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax,
@@ -732,7 +760,14 @@ int em28xx_resolution_set(struct em28xx *dev)
em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2);
em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2);
/* If we don't set the start position to 4 in VBI mode, we end up
with line 21 being YUYV encoded instead of being in 8-bit
greyscale */
if (em28xx_vbi_supported(dev) == 1)
em28xx_capture_area_set(dev, 0, 4, width >> 2, height >> 2);
else
em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2);
return em28xx_scaler_set(dev, dev->hscale, dev->vscale);
}
@@ -844,8 +879,7 @@ EXPORT_SYMBOL_GPL(em28xx_set_mode);
*/
static void em28xx_irq_callback(struct urb *urb)
{
struct em28xx_dmaqueue *dma_q = urb->context;
struct em28xx *dev = container_of(dma_q, struct em28xx, vidq);
struct em28xx *dev = urb->context;
int rc, i;
switch (urb->status) {
@@ -930,6 +964,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
int (*isoc_copy) (struct em28xx *dev, struct urb *urb))
{
struct em28xx_dmaqueue *dma_q = &dev->vidq;
struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
int i;
int sb_size, pipe;
struct urb *urb;
@@ -959,7 +994,8 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
}
dev->isoc_ctl.max_pkt_size = max_pkt_size;
dev->isoc_ctl.buf = NULL;
dev->isoc_ctl.vid_buf = NULL;
dev->isoc_ctl.vbi_buf = NULL;
sb_size = max_packets * dev->isoc_ctl.max_pkt_size;
@@ -994,7 +1030,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
usb_fill_int_urb(urb, dev->udev, pipe,
dev->isoc_ctl.transfer_buffer[i], sb_size,
em28xx_irq_callback, dma_q, 1);
em28xx_irq_callback, dev, 1);
urb->number_of_packets = max_packets;
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
@@ -1009,6 +1045,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
}
init_waitqueue_head(&dma_q->wq);
init_waitqueue_head(&vbi_dma_q->wq);
em28xx_capture_start(dev, 1);
@@ -1094,7 +1131,7 @@ struct em28xx *em28xx_get_device(int minor,
list_for_each_entry(h, &em28xx_devlist, devlist) {
if (h->vdev->minor == minor)
dev = h;
if (h->vbi_dev->minor == minor) {
if (h->vbi_dev && h->vbi_dev->minor == minor) {
dev = h;
*fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
}

View File

@@ -33,6 +33,7 @@
#include "s5h1409.h"
#include "mt352.h"
#include "mt352_priv.h" /* FIXME */
#include "tda1002x.h"
MODULE_DESCRIPTION("driver for em28xx based DVB cards");
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
@@ -295,6 +296,11 @@ static struct mt352_config terratec_xs_mt352_cfg = {
.demod_init = mt352_terratec_xs_init,
};
static struct tda10023_config em28xx_tda10023_config = {
.demod_address = 0x0c,
.invert = 1,
};
/* ------------------------------------------------------------------ */
static int attach_xc3028(u8 addr, struct em28xx *dev)
@@ -549,6 +555,19 @@ static int dvb_init(struct em28xx *dev)
}
break;
#endif
case EM2870_BOARD_REDDO_DVB_C_USB_BOX:
/* Philips CU1216L NIM (Philips TDA10023 + Infineon TUA6034) */
dvb->frontend = dvb_attach(tda10023_attach,
&em28xx_tda10023_config,
&dev->i2c_adap, 0x48);
if (dvb->frontend) {
if (!dvb_attach(simple_tuner_attach, dvb->frontend,
&dev->i2c_adap, 0x60, TUNER_PHILIPS_CU1216L)) {
result = -EINVAL;
goto out_free;
}
}
break;
default:
printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card"
" isn't supported yet\n",

View File

@@ -86,7 +86,19 @@
#define EM28XX_XCLK_FREQUENCY_24MHZ 0x0b
#define EM28XX_R10_VINMODE 0x10
#define EM28XX_R11_VINCTRL 0x11
/* em28xx Video Input Control Register 0x11 */
#define EM28XX_VINCTRL_VBI_SLICED 0x80
#define EM28XX_VINCTRL_VBI_RAW 0x40
#define EM28XX_VINCTRL_VOUT_MODE_IN 0x20 /* HREF,VREF,VACT in output */
#define EM28XX_VINCTRL_CCIR656_ENABLE 0x10
#define EM28XX_VINCTRL_VBI_16BIT_RAW 0x08 /* otherwise 8-bit raw */
#define EM28XX_VINCTRL_FID_ON_HREF 0x04
#define EM28XX_VINCTRL_DUAL_EDGE_STROBE 0x02
#define EM28XX_VINCTRL_INTERLACED 0x01
#define EM28XX_R12_VINENABLE 0x12 /* */
#define EM28XX_R14_GAMMA 0x14
@@ -135,6 +147,10 @@
#define EM28XX_R31_HSCALEHIGH 0x31
#define EM28XX_R32_VSCALELOW 0x32
#define EM28XX_R33_VSCALEHIGH 0x33
#define EM28XX_R34_VBI_START_H 0x34
#define EM28XX_R35_VBI_START_V 0x35
#define EM28XX_R36_VBI_WIDTH 0x36
#define EM28XX_R37_VBI_HEIGHT 0x37
#define EM28XX_R40_AC97LSB 0x40
#define EM28XX_R41_AC97MSB 0x41

View File

@@ -0,0 +1,142 @@
/*
em28xx-vbi.c - VBI driver for em28xx
Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
This work was sponsored by EyeMagnet Limited.
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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include "em28xx.h"
static unsigned int vbibufs = 5;
module_param(vbibufs, int, 0644);
MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32");
static unsigned int vbi_debug;
module_param(vbi_debug, int, 0644);
MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]");
#define dprintk(level, fmt, arg...) if (vbi_debug >= level) \
printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg)
/* ------------------------------------------------------------------ */
static void
free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf)
{
struct em28xx_fh *fh = vq->priv_data;
struct em28xx *dev = fh->dev;
unsigned long flags = 0;
if (in_interrupt())
BUG();
/* We used to wait for the buffer to finish here, but this didn't work
because, as we were keeping the state as VIDEOBUF_QUEUED,
videobuf_queue_cancel marked it as finished for us.
(Also, it could wedge forever if the hardware was misconfigured.)
This should be safe; by the time we get here, the buffer isn't
queued anymore. If we ever start marking the buffers as
VIDEOBUF_ACTIVE, it won't be, though.
*/
spin_lock_irqsave(&dev->slock, flags);
if (dev->isoc_ctl.vbi_buf == buf)
dev->isoc_ctl.vbi_buf = NULL;
spin_unlock_irqrestore(&dev->slock, flags);
videobuf_vmalloc_free(&buf->vb);
buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
static int
vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
{
*size = 720 * 12 * 2;
if (0 == *count)
*count = vbibufs;
if (*count < 2)
*count = 2;
if (*count > 32)
*count = 32;
return 0;
}
static int
vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
enum v4l2_field field)
{
struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
int rc = 0;
unsigned int size;
size = 720 * 12 * 2;
buf->vb.size = size;
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
return -EINVAL;
buf->vb.width = 720;
buf->vb.height = 12;
buf->vb.field = field;
if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
rc = videobuf_iolock(q, &buf->vb, NULL);
if (rc < 0)
goto fail;
}
buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
free_buffer(q, buf);
return rc;
}
static void
vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct em28xx_buffer *buf = container_of(vb,
struct em28xx_buffer,
vb);
struct em28xx_fh *fh = vq->priv_data;
struct em28xx *dev = fh->dev;
struct em28xx_dmaqueue *vbiq = &dev->vbiq;
buf->vb.state = VIDEOBUF_QUEUED;
list_add_tail(&buf->vb.queue, &vbiq->active);
}
static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
{
struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
free_buffer(q, buf);
}
struct videobuf_queue_ops em28xx_vbi_qops = {
.buf_setup = vbi_setup,
.buf_prepare = vbi_prepare,
.buf_queue = vbi_queue,
.buf_release = vbi_release,
};

View File

@@ -163,7 +163,24 @@ static inline void buffer_filled(struct em28xx *dev,
buf->vb.field_count++;
do_gettimeofday(&buf->vb.ts);
dev->isoc_ctl.buf = NULL;
dev->isoc_ctl.vid_buf = NULL;
list_del(&buf->vb.queue);
wake_up(&buf->vb.done);
}
static inline void vbi_buffer_filled(struct em28xx *dev,
struct em28xx_dmaqueue *dma_q,
struct em28xx_buffer *buf)
{
/* Advice that buffer was filled */
em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
buf->vb.state = VIDEOBUF_DONE;
buf->vb.field_count++;
do_gettimeofday(&buf->vb.ts);
dev->isoc_ctl.vbi_buf = NULL;
list_del(&buf->vb.queue);
wake_up(&buf->vb.done);
@@ -239,7 +256,8 @@ static void em28xx_copy_video(struct em28xx *dev,
if ((char *)startwrite + lencopy > (char *)outp +
buf->vb.size) {
em28xx_isocdbg("Overflow of %zi bytes past buffer end (2)\n",
em28xx_isocdbg("Overflow of %zi bytes past buffer end"
"(2)\n",
((char *)startwrite + lencopy) -
((char *)outp + buf->vb.size));
lencopy = remain = (char *)outp + buf->vb.size -
@@ -256,6 +274,63 @@ static void em28xx_copy_video(struct em28xx *dev,
dma_q->pos += len;
}
static void em28xx_copy_vbi(struct em28xx *dev,
struct em28xx_dmaqueue *dma_q,
struct em28xx_buffer *buf,
unsigned char *p,
unsigned char *outp, unsigned long len)
{
void *startwrite, *startread;
int offset;
int bytesperline = 720;
if (dev == NULL) {
em28xx_isocdbg("dev is null\n");
return;
}
if (dma_q == NULL) {
em28xx_isocdbg("dma_q is null\n");
return;
}
if (buf == NULL) {
return;
}
if (p == NULL) {
em28xx_isocdbg("p is null\n");
return;
}
if (outp == NULL) {
em28xx_isocdbg("outp is null\n");
return;
}
if (dma_q->pos + len > buf->vb.size)
len = buf->vb.size - dma_q->pos;
if ((p[0] == 0x33 && p[1] == 0x95) ||
(p[0] == 0x88 && p[1] == 0x88)) {
/* Header field, advance past it */
p += 4;
} else {
len += 4;
}
startread = p;
startwrite = outp + dma_q->pos;
offset = dma_q->pos;
/* Make sure the bottom field populates the second half of the frame */
if (buf->top_field == 0) {
startwrite += bytesperline * 0x0c;
offset += bytesperline * 0x0c;
}
memcpy(startwrite, startread, len);
dma_q->pos += len;
}
static inline void print_err_status(struct em28xx *dev,
int packet, int status)
{
@@ -306,7 +381,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q,
if (list_empty(&dma_q->active)) {
em28xx_isocdbg("No active queue to serve\n");
dev->isoc_ctl.buf = NULL;
dev->isoc_ctl.vid_buf = NULL;
*buf = NULL;
return;
}
@@ -318,7 +393,34 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q,
outp = videobuf_to_vmalloc(&(*buf)->vb);
memset(outp, 0, (*buf)->vb.size);
dev->isoc_ctl.buf = *buf;
dev->isoc_ctl.vid_buf = *buf;
return;
}
/*
* video-buf generic routine to get the next available VBI buffer
*/
static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q,
struct em28xx_buffer **buf)
{
struct em28xx *dev = container_of(dma_q, struct em28xx, vbiq);
char *outp;
if (list_empty(&dma_q->active)) {
em28xx_isocdbg("No active queue to serve\n");
dev->isoc_ctl.vbi_buf = NULL;
*buf = NULL;
return;
}
/* Get the next buffer */
*buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue);
/* Cleans up buffer - Usefull for testing for frame/URB loss */
outp = videobuf_to_vmalloc(&(*buf)->vb);
memset(outp, 0x00, (*buf)->vb.size);
dev->isoc_ctl.vbi_buf = *buf;
return;
}
@@ -329,7 +431,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q,
static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
{
struct em28xx_buffer *buf;
struct em28xx_dmaqueue *dma_q = urb->context;
struct em28xx_dmaqueue *dma_q = &dev->vidq;
unsigned char *outp = NULL;
int i, len = 0, rc = 1;
unsigned char *p;
@@ -346,7 +448,7 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
return 0;
}
buf = dev->isoc_ctl.buf;
buf = dev->isoc_ctl.vid_buf;
if (buf != NULL)
outp = videobuf_to_vmalloc(&buf->vb);
@@ -410,6 +512,153 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
return rc;
}
/* Version of isoc handler that takes into account a mixture of video and
VBI data */
static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb)
{
struct em28xx_buffer *buf, *vbi_buf;
struct em28xx_dmaqueue *dma_q = &dev->vidq;
struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
unsigned char *outp = NULL;
unsigned char *vbioutp = NULL;
int i, len = 0, rc = 1;
unsigned char *p;
int vbi_size;
if (!dev)
return 0;
if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
return 0;
if (urb->status < 0) {
print_err_status(dev, -1, urb->status);
if (urb->status == -ENOENT)
return 0;
}
buf = dev->isoc_ctl.vid_buf;
if (buf != NULL)
outp = videobuf_to_vmalloc(&buf->vb);
vbi_buf = dev->isoc_ctl.vbi_buf;
if (vbi_buf != NULL)
vbioutp = videobuf_to_vmalloc(&vbi_buf->vb);
for (i = 0; i < urb->number_of_packets; i++) {
int status = urb->iso_frame_desc[i].status;
if (status < 0) {
print_err_status(dev, i, status);
if (urb->iso_frame_desc[i].status != -EPROTO)
continue;
}
len = urb->iso_frame_desc[i].actual_length - 4;
if (urb->iso_frame_desc[i].actual_length <= 0) {
/* em28xx_isocdbg("packet %d is empty",i); - spammy */
continue;
}
if (urb->iso_frame_desc[i].actual_length >
dev->max_pkt_size) {
em28xx_isocdbg("packet bigger than packet size");
continue;
}
p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
/* capture type 0 = vbi start
capture type 1 = video start
capture type 2 = video in progress */
if (p[0] == 0x33 && p[1] == 0x95) {
dev->capture_type = 0;
dev->vbi_read = 0;
em28xx_isocdbg("VBI START HEADER!!!\n");
dev->cur_field = p[2];
}
/* FIXME: get rid of hard-coded value */
vbi_size = 720 * 0x0c;
if (dev->capture_type == 0) {
if (dev->vbi_read >= vbi_size) {
/* We've already read all the VBI data, so
treat the rest as video */
em28xx_isocdbg("dev->vbi_read > vbi_size\n");
} else if ((dev->vbi_read + len) < vbi_size) {
/* This entire frame is VBI data */
if (dev->vbi_read == 0 &&
(!(dev->cur_field & 1))) {
/* Brand new frame */
if (vbi_buf != NULL)
vbi_buffer_filled(dev,
vbi_dma_q,
vbi_buf);
vbi_get_next_buf(vbi_dma_q, &vbi_buf);
if (vbi_buf == NULL)
vbioutp = NULL;
else
vbioutp = videobuf_to_vmalloc(
&vbi_buf->vb);
}
if (dev->vbi_read == 0) {
vbi_dma_q->pos = 0;
if (vbi_buf != NULL) {
if (dev->cur_field & 1)
vbi_buf->top_field = 0;
else
vbi_buf->top_field = 1;
}
}
dev->vbi_read += len;
em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p,
vbioutp, len);
} else {
/* Some of this frame is VBI data and some is
video data */
int vbi_data_len = vbi_size - dev->vbi_read;
dev->vbi_read += vbi_data_len;
em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p,
vbioutp, vbi_data_len);
dev->capture_type = 1;
p += vbi_data_len;
len -= vbi_data_len;
}
}
if (dev->capture_type == 1) {
dev->capture_type = 2;
em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2],
len, (p[2] & 1) ? "odd" : "even");
if (dev->progressive || !(dev->cur_field & 1)) {
if (buf != NULL)
buffer_filled(dev, dma_q, buf);
get_next_buf(dma_q, &buf);
if (buf == NULL)
outp = NULL;
else
outp = videobuf_to_vmalloc(&buf->vb);
}
if (buf != NULL) {
if (dev->cur_field & 1)
buf->top_field = 0;
else
buf->top_field = 1;
}
dma_q->pos = 0;
}
if (buf != NULL && dev->capture_type == 2)
em28xx_copy_video(dev, dma_q, buf, p, outp, len);
}
return rc;
}
/* ------------------------------------------------------------------
Videobuf operations
------------------------------------------------------------------*/
@@ -421,7 +670,8 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
struct em28xx *dev = fh->dev;
struct v4l2_frequency f;
*size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3;
*size = (fh->dev->width * fh->dev->height * dev->format->depth + 7)
>> 3;
if (0 == *count)
*count = EM28XX_DEF_BUF;
@@ -458,8 +708,8 @@ static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf)
VIDEOBUF_ACTIVE, it won't be, though.
*/
spin_lock_irqsave(&dev->slock, flags);
if (dev->isoc_ctl.buf == buf)
dev->isoc_ctl.buf = NULL;
if (dev->isoc_ctl.vid_buf == buf)
dev->isoc_ctl.vid_buf = NULL;
spin_unlock_irqrestore(&dev->slock, flags);
videobuf_vmalloc_free(&buf->vb);
@@ -475,7 +725,8 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
struct em28xx *dev = fh->dev;
int rc = 0, urb_init = 0;
buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3;
buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth
+ 7) >> 3;
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
return -EINVAL;
@@ -494,9 +745,16 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
urb_init = 1;
if (urb_init) {
rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
EM28XX_NUM_BUFS, dev->max_pkt_size,
em28xx_isoc_copy);
if (em28xx_vbi_supported(dev) == 1)
rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
EM28XX_NUM_BUFS,
dev->max_pkt_size,
em28xx_isoc_copy_vbi);
else
rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
EM28XX_NUM_BUFS,
dev->max_pkt_size,
em28xx_isoc_copy);
if (rc < 0)
goto fail;
}
@@ -578,34 +836,63 @@ static void video_mux(struct em28xx *dev, int index)
}
/* Usage lock check functions */
static int res_get(struct em28xx_fh *fh)
{
struct em28xx *dev = fh->dev;
int rc = 0;
/* This instance already has stream_on */
if (fh->stream_on)
return rc;
if (dev->stream_on)
return -EBUSY;
dev->stream_on = 1;
fh->stream_on = 1;
return rc;
}
static int res_check(struct em28xx_fh *fh)
{
return fh->stream_on;
}
static void res_free(struct em28xx_fh *fh)
static int res_get(struct em28xx_fh *fh, unsigned int bit)
{
struct em28xx *dev = fh->dev;
fh->stream_on = 0;
dev->stream_on = 0;
if (fh->resources & bit)
/* have it already allocated */
return 1;
/* is it free? */
mutex_lock(&dev->lock);
if (dev->resources & bit) {
/* no, someone else uses it */
mutex_unlock(&dev->lock);
return 0;
}
/* it's free, grab it */
fh->resources |= bit;
dev->resources |= bit;
em28xx_videodbg("res: get %d\n", bit);
mutex_unlock(&dev->lock);
return 1;
}
static int res_check(struct em28xx_fh *fh, unsigned int bit)
{
return fh->resources & bit;
}
static int res_locked(struct em28xx *dev, unsigned int bit)
{
return dev->resources & bit;
}
static void res_free(struct em28xx_fh *fh, unsigned int bits)
{
struct em28xx *dev = fh->dev;
BUG_ON((fh->resources & bits) != bits);
mutex_lock(&dev->lock);
fh->resources &= ~bits;
dev->resources &= ~bits;
em28xx_videodbg("res: put %d\n", bits);
mutex_unlock(&dev->lock);
}
static int get_ressource(struct em28xx_fh *fh)
{
switch (fh->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
return EM28XX_RESOURCE_VIDEO;
case V4L2_BUF_TYPE_VBI_CAPTURE:
return EM28XX_RESOURCE_VBI;
default:
BUG();
return 0;
}
}
/*
@@ -782,7 +1069,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
} else {
/* width must even because of the YUYV format
height must be even because of interlacing */
v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0);
v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh,
1, 0);
}
get_scale(dev, width, height, &hscale, &vscale);
@@ -848,12 +1136,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
goto out;
}
if (dev->stream_on && !fh->stream_on) {
em28xx_errdev("%s device in use by another fh\n", __func__);
rc = -EBUSY;
goto out;
}
rc = em28xx_set_video_format(dev, f->fmt.pix.pixelformat,
f->fmt.pix.width, f->fmt.pix.height);
@@ -862,6 +1144,21 @@ out:
return rc;
}
static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
int rc;
rc = check_dev(dev);
if (rc < 0)
return rc;
*norm = dev->norm;
return 0;
}
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
{
struct em28xx_fh *fh = priv;
@@ -1413,20 +1710,25 @@ static int vidioc_streamon(struct file *file, void *priv,
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
int rc;
int rc = -EINVAL;
rc = check_dev(dev);
if (rc < 0)
return rc;
if (unlikely(type != fh->type))
return -EINVAL;
mutex_lock(&dev->lock);
rc = res_get(fh);
em28xx_videodbg("vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n",
fh, type, fh->resources, dev->resources);
if (likely(rc >= 0))
if (unlikely(!res_get(fh, get_ressource(fh))))
return -EBUSY;
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
rc = videobuf_streamon(&fh->vb_vidq);
mutex_unlock(&dev->lock);
else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
rc = videobuf_streamon(&fh->vb_vbiq);
return rc;
}
@@ -1442,17 +1744,22 @@ static int vidioc_streamoff(struct file *file, void *priv,
if (rc < 0)
return rc;
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)
return -EINVAL;
if (type != fh->type)
return -EINVAL;
mutex_lock(&dev->lock);
em28xx_videodbg("vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n",
fh, type, fh->resources, dev->resources);
videobuf_streamoff(&fh->vb_vidq);
res_free(fh);
mutex_unlock(&dev->lock);
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
videobuf_streamoff(&fh->vb_vidq);
res_free(fh, EM28XX_RESOURCE_VIDEO);
} else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
videobuf_streamoff(&fh->vb_vbiq);
res_free(fh, EM28XX_RESOURCE_VBI);
}
return 0;
}
@@ -1474,6 +1781,9 @@ static int vidioc_querycap(struct file *file, void *priv,
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
if (dev->vbi_dev)
cap->capabilities |= V4L2_CAP_VBI_CAPTURE;
if (dev->audio_mode.has_audio)
cap->capabilities |= V4L2_CAP_AUDIO;
@@ -1541,6 +1851,45 @@ static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv,
return 0;
}
/* RAW VBI ioctls */
static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
struct v4l2_format *format)
{
format->fmt.vbi.samples_per_line = 720;
format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
format->fmt.vbi.offset = 0;
format->fmt.vbi.flags = 0;
/* Varies by video standard (NTSC, PAL, etc.) */
/* FIXME: hard-coded for NTSC support */
format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; /* FIXME: ??? */
format->fmt.vbi.count[0] = 12;
format->fmt.vbi.count[1] = 12;
format->fmt.vbi.start[0] = 10;
format->fmt.vbi.start[1] = 273;
return 0;
}
static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv,
struct v4l2_format *format)
{
format->fmt.vbi.samples_per_line = 720;
format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
format->fmt.vbi.offset = 0;
format->fmt.vbi.flags = 0;
/* Varies by video standard (NTSC, PAL, etc.) */
/* FIXME: hard-coded for NTSC support */
format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; /* FIXME: ??? */
format->fmt.vbi.count[0] = 12;
format->fmt.vbi.count[1] = 12;
format->fmt.vbi.start[0] = 10;
format->fmt.vbi.start[1] = 273;
return 0;
}
static int vidioc_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *rb)
@@ -1553,7 +1902,10 @@ static int vidioc_reqbufs(struct file *file, void *priv,
if (rc < 0)
return rc;
return videobuf_reqbufs(&fh->vb_vidq, rb);
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
return videobuf_reqbufs(&fh->vb_vidq, rb);
else
return videobuf_reqbufs(&fh->vb_vbiq, rb);
}
static int vidioc_querybuf(struct file *file, void *priv,
@@ -1567,7 +1919,18 @@ static int vidioc_querybuf(struct file *file, void *priv,
if (rc < 0)
return rc;
return videobuf_querybuf(&fh->vb_vidq, b);
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
return videobuf_querybuf(&fh->vb_vidq, b);
else {
/* FIXME: I'm not sure yet whether this is a bug in zvbi or
the videobuf framework, but we probably shouldn't be
returning a buffer larger than that which was asked for.
At a minimum, it causes a crash in zvbi since it does
a memcpy based on the source buffer length */
int result = videobuf_querybuf(&fh->vb_vbiq, b);
b->length = 17280;
return result;
}
}
static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
@@ -1580,7 +1943,10 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
if (rc < 0)
return rc;
return videobuf_qbuf(&fh->vb_vidq, b);
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
return videobuf_qbuf(&fh->vb_vidq, b);
else
return videobuf_qbuf(&fh->vb_vbiq, b);
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
@@ -1593,7 +1959,12 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
if (rc < 0)
return rc;
return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags &
O_NONBLOCK);
else
return videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags &
O_NONBLOCK);
}
#ifdef CONFIG_VIDEO_V4L1_COMPAT
@@ -1601,7 +1972,10 @@ static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
{
struct em28xx_fh *fh = priv;
return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8);
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8);
else
return videobuf_cgmbuf(&fh->vb_vbiq, mbuf, 8);
}
#endif
@@ -1766,8 +2140,15 @@ static int em28xx_v4l2_open(struct file *filp)
field = V4L2_FIELD_INTERLACED;
videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops,
NULL, &dev->slock, fh->type, field,
sizeof(struct em28xx_buffer), fh);
NULL, &dev->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE, field,
sizeof(struct em28xx_buffer), fh);
videobuf_queue_vmalloc_init(&fh->vb_vbiq, &em28xx_vbi_qops,
NULL, &dev->slock,
V4L2_BUF_TYPE_VBI_CAPTURE,
V4L2_FIELD_SEQ_TB,
sizeof(struct em28xx_buffer), fh);
mutex_unlock(&dev->lock);
@@ -1824,20 +2205,21 @@ static int em28xx_v4l2_close(struct file *filp)
em28xx_videodbg("users=%d\n", dev->users);
if (res_check(fh, EM28XX_RESOURCE_VIDEO)) {
videobuf_stop(&fh->vb_vidq);
res_free(fh, EM28XX_RESOURCE_VIDEO);
}
mutex_lock(&dev->lock);
if (res_check(fh))
res_free(fh);
if (res_check(fh, EM28XX_RESOURCE_VBI)) {
videobuf_stop(&fh->vb_vbiq);
res_free(fh, EM28XX_RESOURCE_VBI);
}
if (dev->users == 1) {
videobuf_stop(&fh->vb_vidq);
videobuf_mmap_free(&fh->vb_vidq);
/* the device is already disconnect,
free the remaining resources */
if (dev->state & DEV_DISCONNECTED) {
em28xx_release_resources(dev);
mutex_unlock(&dev->lock);
kfree(dev);
return 0;
}
@@ -1858,10 +2240,12 @@ static int em28xx_v4l2_close(struct file *filp)
"0 (error=%i)\n", errCode);
}
}
videobuf_mmap_free(&fh->vb_vidq);
videobuf_mmap_free(&fh->vb_vbiq);
kfree(fh);
dev->users--;
wake_up_interruptible_nr(&dev->open, 1);
mutex_unlock(&dev->lock);
return 0;
}
@@ -1886,16 +2270,22 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
*/
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
mutex_lock(&dev->lock);
rc = res_get(fh);
mutex_unlock(&dev->lock);
if (unlikely(rc < 0))
return rc;
if (res_locked(dev, EM28XX_RESOURCE_VIDEO))
return -EBUSY;
return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
filp->f_flags & O_NONBLOCK);
}
if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
if (!res_get(fh, EM28XX_RESOURCE_VBI))
return -EBUSY;
return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0,
filp->f_flags & O_NONBLOCK);
}
return 0;
}
@@ -1913,17 +2303,17 @@ static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait)
if (rc < 0)
return rc;
mutex_lock(&dev->lock);
rc = res_get(fh);
mutex_unlock(&dev->lock);
if (unlikely(rc < 0))
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
if (!res_get(fh, EM28XX_RESOURCE_VIDEO))
return POLLERR;
return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
} else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
if (!res_get(fh, EM28XX_RESOURCE_VBI))
return POLLERR;
return videobuf_poll_stream(filp, &fh->vb_vbiq, wait);
} else {
return POLLERR;
if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
return POLLERR;
return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
}
}
/*
@@ -1939,14 +2329,10 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
if (rc < 0)
return rc;
mutex_lock(&dev->lock);
rc = res_get(fh);
mutex_unlock(&dev->lock);
if (unlikely(rc < 0))
return rc;
rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma);
em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n",
(unsigned long)vma->vm_start,
@@ -1972,6 +2358,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
.vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap,
.vidioc_g_audio = vidioc_g_audio,
.vidioc_s_audio = vidioc_s_audio,
.vidioc_cropcap = vidioc_cropcap,
@@ -1984,6 +2372,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_g_std = vidioc_g_std,
.vidioc_s_std = vidioc_s_std,
.vidioc_g_parm = vidioc_g_parm,
.vidioc_s_parm = vidioc_s_parm,
@@ -2105,13 +2494,10 @@ int em28xx_register_analog_devices(struct em28xx *dev)
dev->mute = 1;
dev->volume = 0x1f;
/* enable vbi capturing */
/* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */
val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK);
em28xx_write_reg(dev, EM28XX_R0F_XCLK,
(EM28XX_XCLK_AUDIO_UNMUTE | val));
em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51);
em28xx_set_outfmt(dev);
em28xx_colorlevels_set_default(dev);
@@ -2134,14 +2520,17 @@ int em28xx_register_analog_devices(struct em28xx *dev)
}
/* Allocate and fill vbi video_device struct */
dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, "vbi");
if (em28xx_vbi_supported(dev) == 1) {
dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template,
"vbi");
/* register v4l2 vbi video_device */
ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
vbi_nr[dev->devno]);
if (ret < 0) {
em28xx_errdev("unable to register vbi device\n");
return ret;
/* register v4l2 vbi video_device */
ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
vbi_nr[dev->devno]);
if (ret < 0) {
em28xx_errdev("unable to register vbi device\n");
return ret;
}
}
if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) {
@@ -2161,8 +2550,12 @@ int em28xx_register_analog_devices(struct em28xx *dev)
dev->radio_dev->num);
}
em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n",
dev->vdev->num, dev->vbi_dev->num);
em28xx_info("V4L2 video device registered as /dev/video%d\n",
dev->vdev->num);
if (dev->vbi_dev)
em28xx_info("V4L2 VBI device registered as /dev/vbi%d\n",
dev->vbi_dev->num);
return 0;
}

View File

@@ -109,6 +109,7 @@
#define EM2882_BOARD_EVGA_INDTUBE 70
#define EM2820_BOARD_SILVERCREST_WEBCAM 71
#define EM2861_BOARD_GADMEI_UTV330PLUS 72
#define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
@@ -214,7 +215,8 @@ struct em28xx_usb_isoc_ctl {
int tmp_buf_len;
/* Stores already requested buffers */
struct em28xx_buffer *buf;
struct em28xx_buffer *vid_buf;
struct em28xx_buffer *vbi_buf;
/* Stores the number of received fields */
int nfields;
@@ -443,6 +445,10 @@ enum em28xx_dev_state {
#define EM28XX_AUDIO 0x10
#define EM28XX_DVB 0x20
/* em28xx resource types (used for res_get/res_lock etc */
#define EM28XX_RESOURCE_VIDEO 0x01
#define EM28XX_RESOURCE_VBI 0x02
struct em28xx_audio {
char name[50];
char *transfer_buffer[EM28XX_AUDIO_BUFS];
@@ -463,10 +469,11 @@ struct em28xx;
struct em28xx_fh {
struct em28xx *dev;
unsigned int stream_on:1; /* Locks streams */
int radio;
unsigned int resources;
struct videobuf_queue vb_vidq;
struct videobuf_queue vb_vbiq;
enum v4l2_buf_type type;
};
@@ -493,7 +500,6 @@ struct em28xx {
/* Vinmode/Vinctl used at the driver */
int vinmode, vinctl;
unsigned int stream_on:1; /* Locks streams */
unsigned int has_audio_class:1;
unsigned int has_alsa_audio:1;
@@ -544,6 +550,12 @@ struct em28xx {
enum em28xx_dev_state state;
enum em28xx_io_method io;
/* vbi related state tracking */
int capture_type;
int vbi_read;
unsigned char cur_field;
struct work_struct request_module_wk;
/* locks */
@@ -555,10 +567,14 @@ struct em28xx {
struct video_device *vbi_dev;
struct video_device *radio_dev;
/* resources in use */
unsigned int resources;
unsigned char eedata[256];
/* Isoc control struct */
struct em28xx_dmaqueue vidq;
struct em28xx_dmaqueue vbiq;
struct em28xx_usb_isoc_ctl isoc_ctl;
spinlock_t slock;
@@ -639,6 +655,7 @@ int em28xx_audio_setup(struct em28xx *dev);
int em28xx_colorlevels_set_default(struct em28xx *dev);
int em28xx_capture_start(struct em28xx *dev, int start);
int em28xx_vbi_supported(struct em28xx *dev);
int em28xx_set_outfmt(struct em28xx *dev);
int em28xx_resolution_set(struct em28xx *dev);
int em28xx_set_alternate(struct em28xx *dev);
@@ -686,6 +703,9 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev);
int em28xx_ir_init(struct em28xx *dev);
int em28xx_ir_fini(struct em28xx *dev);
/* Provided by em28xx-vbi.c */
extern struct videobuf_queue_ops em28xx_vbi_qops;
/* printk macros */
#define em28xx_err(fmt, arg...) do {\

View File

@@ -1379,8 +1379,10 @@ et61x251_read(struct file* filp, char __user * buf,
(!list_empty(&cam->outqueue)) ||
(cam->state & DEV_DISCONNECTED) ||
(cam->state & DEV_MISCONFIGURED),
cam->module_param.frame_timeout *
1000 * msecs_to_jiffies(1) );
msecs_to_jiffies(
cam->module_param.frame_timeout * 1000
)
);
if (timeout < 0) {
mutex_unlock(&cam->fileop_mutex);
return timeout;

View File

@@ -19,6 +19,7 @@ if USB_GSPCA && VIDEO_V4L2
source "drivers/media/video/gspca/m5602/Kconfig"
source "drivers/media/video/gspca/stv06xx/Kconfig"
source "drivers/media/video/gspca/gl860/Kconfig"
config USB_GSPCA_CONEX
tristate "Conexant Camera Driver"

View File

@@ -58,3 +58,4 @@ gspca_zc3xx-objs := zc3xx.o
obj-$(CONFIG_USB_M5602) += m5602/
obj-$(CONFIG_USB_STV06XX) += stv06xx/
obj-$(CONFIG_USB_GL860) += gl860/

View File

@@ -0,0 +1,8 @@
config USB_GL860
tristate "GL860 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
Say Y here if you want support for cameras based on the GL860 chip.
To compile this driver as a module, choose M here: the
module will be called gspca_gl860.

View File

@@ -0,0 +1,10 @@
obj-$(CONFIG_USB_GL860) += gspca_gl860.o
gspca_gl860-objs := gl860.o \
gl860-mi1320.o \
gl860-ov2640.o \
gl860-ov9655.o \
gl860-mi2020.o
EXTRA_CFLAGS += -Idrivers/media/video/gspca

View File

@@ -0,0 +1,537 @@
/* @file gl860-mi1320.c
* @author Olivier LORIN from my logs
* @date 2009-08-27
*
* 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
* 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, see <http://www.gnu.org/licenses/>.
*/
/* Sensor : MI1320 */
#include "gl860.h"
static struct validx tbl_common[] = {
{0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba51, 0x0066}, {0xba02, 0x00f1},
{0xba05, 0x0067}, {0xba05, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1},
{0xffff, 0xffff},
{0xba00, 0x00f0}, {0xba02, 0x00f1}, {0xbafa, 0x0028}, {0xba02, 0x00f1},
{0xba00, 0x00f0}, {0xba01, 0x00f1}, {0xbaf0, 0x0006}, {0xba0e, 0x00f1},
{0xba70, 0x0006}, {0xba0e, 0x00f1},
{0xffff, 0xffff},
{0xba74, 0x0006}, {0xba0e, 0x00f1},
{0xffff, 0xffff},
{0x0061, 0x0000}, {0x0068, 0x000d},
};
static struct validx tbl_init_at_startup[] = {
{0x0000, 0x0000}, {0x0010, 0x0010},
{35, 0xffff},
{0x0008, 0x00c0}, {0x0001, 0x00c1}, {0x0001, 0x00c2}, {0x0020, 0x0006},
{0x006a, 0x000d},
};
static struct validx tbl_sensor_settings_common[] = {
{0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, {0x0040, 0x0000},
{0x006a, 0x0007}, {0x006a, 0x000d}, {0x0063, 0x0006},
};
static struct validx tbl_sensor_settings_1280[] = {
{0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba5a, 0x0066}, {0xba02, 0x00f1},
{0xba05, 0x0067}, {0xba05, 0x00f1}, {0xba20, 0x0065}, {0xba00, 0x00f1},
};
static struct validx tbl_sensor_settings_800[] = {
{0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba5a, 0x0066}, {0xba02, 0x00f1},
{0xba05, 0x0067}, {0xba05, 0x00f1}, {0xba20, 0x0065}, {0xba00, 0x00f1},
};
static struct validx tbl_sensor_settings_640[] = {
{0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1},
{0xba51, 0x0066}, {0xba02, 0x00f1}, {0xba05, 0x0067}, {0xba05, 0x00f1},
{0xba20, 0x0065}, {0xba00, 0x00f1},
};
static struct validx tbl_post_unset_alt[] = {
{0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1},
{0x0061, 0x0000}, {0x0068, 0x000d},
};
static u8 *tbl_1280[] = {
"\x0d\x80\xf1\x08\x03\x04\xf1\x00" "\x04\x05\xf1\x02\x05\x00\xf1\xf1"
"\x06\x00\xf1\x0d\x20\x01\xf1\x00" "\x21\x84\xf1\x00\x0d\x00\xf1\x08"
"\xf0\x00\xf1\x01\x34\x00\xf1\x00" "\x9b\x43\xf1\x00\xa6\x05\xf1\x00"
"\xa9\x04\xf1\x00\xa1\x05\xf1\x00" "\xa4\x04\xf1\x00\xae\x0a\xf1\x08"
,
"\xf0\x00\xf1\x02\x3a\x05\xf1\xf1" "\x3c\x05\xf1\xf1\x59\x01\xf1\x47"
"\x5a\x01\xf1\x88\x5c\x0a\xf1\x06" "\x5d\x0e\xf1\x0a\x64\x5e\xf1\x1c"
"\xd2\x00\xf1\xcf\xcb\x00\xf1\x01"
,
"\xd3\x02\xd4\x28\xd5\x01\xd0\x02" "\xd1\x18\xd2\xc1"
};
static u8 *tbl_800[] = {
"\x0d\x80\xf1\x08\x03\x03\xf1\xc0" "\x04\x05\xf1\x02\x05\x00\xf1\xf1"
"\x06\x00\xf1\x0d\x20\x01\xf1\x00" "\x21\x84\xf1\x00\x0d\x00\xf1\x08"
"\xf0\x00\xf1\x01\x34\x00\xf1\x00" "\x9b\x43\xf1\x00\xa6\x05\xf1\x00"
"\xa9\x03\xf1\xc0\xa1\x03\xf1\x20" "\xa4\x02\xf1\x5a\xae\x0a\xf1\x08"
,
"\xf0\x00\xf1\x02\x3a\x05\xf1\xf1" "\x3c\x05\xf1\xf1\x59\x01\xf1\x47"
"\x5a\x01\xf1\x88\x5c\x0a\xf1\x06" "\x5d\x0e\xf1\x0a\x64\x5e\xf1\x1c"
"\xd2\x00\xf1\xcf\xcb\x00\xf1\x01"
,
"\xd3\x02\xd4\x18\xd5\x21\xd0\x02" "\xd1\x10\xd2\x59"
};
static u8 *tbl_640[] = {
"\x0d\x80\xf1\x08\x03\x04\xf1\x04" "\x04\x05\xf1\x02\x07\x01\xf1\x7c"
"\x08\x00\xf1\x0e\x21\x80\xf1\x00" "\x0d\x00\xf1\x08\xf0\x00\xf1\x01"
"\x34\x10\xf1\x10\x3a\x43\xf1\x00" "\xa6\x05\xf1\x02\xa9\x04\xf1\x04"
"\xa7\x02\xf1\x81\xaa\x01\xf1\xe2" "\xae\x0c\xf1\x09"
,
"\xf0\x00\xf1\x02\x39\x03\xf1\xfc" "\x3b\x04\xf1\x04\x57\x01\xf1\xb6"
"\x58\x02\xf1\x0d\x5c\x1f\xf1\x19" "\x5d\x24\xf1\x1e\x64\x5e\xf1\x1c"
"\xd2\x00\xf1\x00\xcb\x00\xf1\x01"
,
"\xd3\x02\xd4\x10\xd5\x81\xd0\x02" "\xd1\x08\xd2\xe1"
};
static s32 tbl_sat[] = {0x25, 0x1d, 0x15, 0x0d, 0x05, 0x4d, 0x55, 0x5d, 0x2d};
static s32 tbl_bright[] = {0, 8, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70};
static s32 tbl_backlight[] = {0x0e, 0x06, 0x02};
static s32 tbl_cntr1[] = {
0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0, 0xc8, 0xd0, 0xe0, 0xf0};
static s32 tbl_cntr2[] = {
0x70, 0x68, 0x60, 0x58, 0x50, 0x48, 0x40, 0x38, 0x30, 0x20, 0x10};
static u8 dat_wbalNL[] =
"\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x3b\x04\xf1\x2a\x47\x10\xf1\x10"
"\x9d\x3c\xf1\xae\xaf\x10\xf1\x00" "\xf0\x00\xf1\x02\x2f\x91\xf1\x20"
"\x9c\x91\xf1\x20\x37\x03\xf1\x00" "\x9d\xc5\xf1\x0f\xf0\x00\xf1\x00";
static u8 dat_wbalLL[] =
"\xf0\x00\xf1\x01\x05\x00\xf1\x0c" "\x3b\x04\xf1\x2a\x47\x40\xf1\x40"
"\x9d\x20\xf1\xae\xaf\x10\xf1\x00" "\xf0\x00\xf1\x02\x2f\xd1\xf1\x00"
"\x9c\xd1\xf1\x00\x37\x03\xf1\x00" "\x9d\xc5\xf1\x3f\xf0\x00\xf1\x00";
static u8 dat_wbalBL[] =
"\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x47\x10\xf1\x30\x9d\x3c\xf1\xae"
"\xaf\x10\xf1\x00\xf0\x00\xf1\x02" "\x2f\x91\xf1\x20\x9c\x91\xf1\x20"
"\x37\x03\xf1\x00\x9d\xc5\xf1\x2f" "\xf0\x00\xf1\x00";
static u8 dat_hvflip1[] = {0xf0, 0x00, 0xf1, 0x00};
static u8 s000[] =
"\x00\x01\x07\x6a\x06\x63\x0d\x6a" "\xc0\x00\x10\x10\xc1\x03\xc2\x42"
"\xd8\x04\x58\x00\x04\x02";
static u8 s001[] =
"\x0d\x00\xf1\x0b\x0d\x00\xf1\x08" "\x35\x00\xf1\x22\x68\x00\xf1\x5d"
"\xf0\x00\xf1\x01\x06\x70\xf1\x0e" "\xf0\x00\xf1\x02\xdd\x18\xf1\xe0";
static u8 s002[] =
"\x05\x01\xf1\x84\x06\x00\xf1\x44" "\x07\x00\xf1\xbe\x08\x00\xf1\x1e"
"\x20\x01\xf1\x03\x21\x84\xf1\x00" "\x22\x0d\xf1\x0f\x24\x80\xf1\x00"
"\x34\x18\xf1\x2d\x35\x00\xf1\x22" "\x43\x83\xf1\x83\x59\x00\xf1\xff";
static u8 s003[] =
"\xf0\x00\xf1\x02\x39\x06\xf1\x8c" "\x3a\x06\xf1\x8c\x3b\x03\xf1\xda"
"\x3c\x05\xf1\x30\x57\x01\xf1\x0c" "\x58\x01\xf1\x42\x59\x01\xf1\x0c"
"\x5a\x01\xf1\x42\x5c\x13\xf1\x0e" "\x5d\x17\xf1\x12\x64\x1e\xf1\x1c";
static u8 s004[] =
"\xf0\x00\xf1\x02\x24\x5f\xf1\x20" "\x28\xea\xf1\x02\x5f\x41\xf1\x43";
static u8 s005[] =
"\x02\x00\xf1\xee\x03\x29\xf1\x1a" "\x04\x02\xf1\xa4\x09\x00\xf1\x68"
"\x0a\x00\xf1\x2a\x0b\x00\xf1\x04" "\x0c\x00\xf1\x93\x0d\x00\xf1\x82"
"\x0e\x00\xf1\x40\x0f\x00\xf1\x5f" "\x10\x00\xf1\x4e\x11\x00\xf1\x5b";
static u8 s006[] =
"\x15\x00\xf1\xc9\x16\x00\xf1\x5e" "\x17\x00\xf1\x9d\x18\x00\xf1\x06"
"\x19\x00\xf1\x89\x1a\x00\xf1\x12" "\x1b\x00\xf1\xa1\x1c\x00\xf1\xe4"
"\x1d\x00\xf1\x7a\x1e\x00\xf1\x64" "\xf6\x00\xf1\x5f";
static u8 s007[] =
"\xf0\x00\xf1\x01\x53\x09\xf1\x03" "\x54\x3d\xf1\x1c\x55\x99\xf1\x72"
"\x56\xc1\xf1\xb1\x57\xd8\xf1\xce" "\x58\xe0\xf1\x00\xdc\x0a\xf1\x03"
"\xdd\x45\xf1\x20\xde\xae\xf1\x82" "\xdf\xdc\xf1\xc9\xe0\xf6\xf1\xea"
"\xe1\xff\xf1\x00";
static u8 s008[] =
"\xf0\x00\xf1\x01\x80\x00\xf1\x06" "\x81\xf6\xf1\x08\x82\xfb\xf1\xf7"
"\x83\x00\xf1\xfe\xb6\x07\xf1\x03" "\xb7\x18\xf1\x0c\x84\xfb\xf1\x06"
"\x85\xfb\xf1\xf9\x86\x00\xf1\xff" "\xb8\x07\xf1\x04\xb9\x16\xf1\x0a";
static u8 s009[] =
"\x87\xfa\xf1\x05\x88\xfc\xf1\xf9" "\x89\x00\xf1\xff\xba\x06\xf1\x03"
"\xbb\x17\xf1\x09\x8a\xe8\xf1\x14" "\x8b\xf7\xf1\xf0\x8c\xfd\xf1\xfa"
"\x8d\x00\xf1\x00\xbc\x05\xf1\x01" "\xbd\x0c\xf1\x08\xbe\x00\xf1\x14";
static u8 s010[] =
"\x8e\xea\xf1\x13\x8f\xf7\xf1\xf2" "\x90\xfd\xf1\xfa\x91\x00\xf1\x00"
"\xbf\x05\xf1\x01\xc0\x0a\xf1\x08" "\xc1\x00\xf1\x0c\x92\xed\xf1\x0f"
"\x93\xf9\xf1\xf4\x94\xfe\xf1\xfb" "\x95\x00\xf1\x00\xc2\x04\xf1\x01"
"\xc3\x0a\xf1\x07\xc4\x00\xf1\x10";
static u8 s011[] =
"\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x25\x00\xf1\x55\x34\x10\xf1\x10"
"\x35\xf0\xf1\x10\x3a\x02\xf1\x03" "\x3b\x04\xf1\x2a\x9b\x43\xf1\x00"
"\xa4\x03\xf1\xc0\xa7\x02\xf1\x81";
static int mi1320_init_at_startup(struct gspca_dev *gspca_dev);
static int mi1320_configure_alt(struct gspca_dev *gspca_dev);
static int mi1320_init_pre_alt(struct gspca_dev *gspca_dev);
static int mi1320_init_post_alt(struct gspca_dev *gspca_dev);
static void mi1320_post_unset_alt(struct gspca_dev *gspca_dev);
static int mi1320_sensor_settings(struct gspca_dev *gspca_dev);
static int mi1320_camera_settings(struct gspca_dev *gspca_dev);
/*==========================================================================*/
void mi1320_init_settings(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->vcur.backlight = 0;
sd->vcur.brightness = 0;
sd->vcur.sharpness = 6;
sd->vcur.contrast = 10;
sd->vcur.gamma = 20;
sd->vcur.hue = 0;
sd->vcur.saturation = 6;
sd->vcur.whitebal = 0;
sd->vcur.mirror = 0;
sd->vcur.flip = 0;
sd->vcur.AC50Hz = 1;
sd->vmax.backlight = 2;
sd->vmax.brightness = 8;
sd->vmax.sharpness = 7;
sd->vmax.contrast = 0; /* 10 but not working with tihs driver */
sd->vmax.gamma = 40;
sd->vmax.hue = 5 + 1;
sd->vmax.saturation = 8;
sd->vmax.whitebal = 2;
sd->vmax.mirror = 1;
sd->vmax.flip = 1;
sd->vmax.AC50Hz = 1;
sd->dev_camera_settings = mi1320_camera_settings;
sd->dev_init_at_startup = mi1320_init_at_startup;
sd->dev_configure_alt = mi1320_configure_alt;
sd->dev_init_pre_alt = mi1320_init_pre_alt;
sd->dev_post_unset_alt = mi1320_post_unset_alt;
}
/*==========================================================================*/
static void common(struct gspca_dev *gspca_dev)
{
s32 n; /* reserved for FETCH macros */
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, s000);
ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, s001);
n = fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common));
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s002);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s003);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, s004);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s005);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, s006);
keep_on_fetching_validx(gspca_dev, tbl_common,
ARRAY_SIZE(tbl_common), n);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, s007);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s008);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s009);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, s010);
keep_on_fetching_validx(gspca_dev, tbl_common,
ARRAY_SIZE(tbl_common), n);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, s011);
keep_on_fetching_validx(gspca_dev, tbl_common,
ARRAY_SIZE(tbl_common), n);
}
static int mi1320_init_at_startup(struct gspca_dev *gspca_dev)
{
fetch_validx(gspca_dev, tbl_init_at_startup,
ARRAY_SIZE(tbl_init_at_startup));
common(gspca_dev);
/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */
return 0;
}
static int mi1320_init_pre_alt(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->mirrorMask = 0;
sd->vold.backlight = -1;
sd->vold.brightness = -1;
sd->vold.sharpness = -1;
sd->vold.contrast = -1;
sd->vold.saturation = -1;
sd->vold.gamma = -1;
sd->vold.hue = -1;
sd->vold.whitebal = -1;
sd->vold.mirror = -1;
sd->vold.flip = -1;
sd->vold.AC50Hz = -1;
common(gspca_dev);
mi1320_sensor_settings(gspca_dev);
mi1320_init_post_alt(gspca_dev);
return 0;
}
static int mi1320_init_post_alt(struct gspca_dev *gspca_dev)
{
mi1320_camera_settings(gspca_dev);
return 0;
}
static int mi1320_sensor_settings(struct gspca_dev *gspca_dev)
{
s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL);
fetch_validx(gspca_dev, tbl_sensor_settings_common,
ARRAY_SIZE(tbl_sensor_settings_common));
switch (reso) {
case IMAGE_1280:
fetch_validx(gspca_dev, tbl_sensor_settings_1280,
ARRAY_SIZE(tbl_sensor_settings_1280));
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 64, tbl_1280[0]);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_1280[1]);
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_1280[2]);
break;
case IMAGE_800:
fetch_validx(gspca_dev, tbl_sensor_settings_800,
ARRAY_SIZE(tbl_sensor_settings_800));
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 64, tbl_800[0]);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_800[1]);
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_800[2]);
break;
default:
fetch_validx(gspca_dev, tbl_sensor_settings_640,
ARRAY_SIZE(tbl_sensor_settings_640));
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 60, tbl_640[0]);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_640[1]);
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_640[2]);
break;
}
return 0;
}
static int mi1320_configure_alt(struct gspca_dev *gspca_dev)
{
s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
switch (reso) {
case IMAGE_640:
gspca_dev->alt = 3 + 1;
break;
case IMAGE_800:
case IMAGE_1280:
gspca_dev->alt = 1 + 1;
break;
}
return 0;
}
int mi1320_camera_settings(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 backlight = sd->vcur.backlight;
s32 bright = sd->vcur.brightness;
s32 sharp = sd->vcur.sharpness;
s32 cntr = sd->vcur.contrast;
s32 gam = sd->vcur.gamma;
s32 hue = sd->vcur.hue;
s32 sat = sd->vcur.saturation;
s32 wbal = sd->vcur.whitebal;
s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0);
s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0);
s32 freq = (sd->vcur.AC50Hz > 0);
s32 i;
if (freq != sd->vold.AC50Hz) {
sd->vold.AC50Hz = freq;
freq = 2 * (freq == 0);
ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba02, 0x00f1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba00 , 0x005b, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba01 + freq, 0x00f1, 0, NULL);
}
if (wbal != sd->vold.whitebal) {
sd->vold.whitebal = wbal;
if (wbal < 0 || wbal > sd->vmax.whitebal)
wbal = 0;
for (i = 0; i < 2; i++) {
if (wbal == 0) { /* Normal light */
ctrl_out(gspca_dev, 0x40, 1,
0x0010, 0x0010, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1,
0x0003, 0x00c1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1,
0x0042, 0x00c2, 0, NULL);
ctrl_out(gspca_dev, 0x40, 3,
0xba00, 0x0200, 48, dat_wbalNL);
}
if (wbal == 1) { /* Low light */
ctrl_out(gspca_dev, 0x40, 1,
0x0010, 0x0010, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1,
0x0004, 0x00c1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1,
0x0043, 0x00c2, 0, NULL);
ctrl_out(gspca_dev, 0x40, 3,
0xba00, 0x0200, 48, dat_wbalLL);
}
if (wbal == 2) { /* Back light */
ctrl_out(gspca_dev, 0x40, 1,
0x0010, 0x0010, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1,
0x0003, 0x00c1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1,
0x0042, 0x00c2, 0, NULL);
ctrl_out(gspca_dev, 0x40, 3,
0xba00, 0x0200, 44, dat_wbalBL);
}
}
}
if (bright != sd->vold.brightness) {
sd->vold.brightness = bright;
if (bright < 0 || bright > sd->vmax.brightness)
bright = 0;
bright = tbl_bright[bright];
ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba00 + bright, 0x0034, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba00 + bright, 0x00f1, 0, NULL);
}
if (sat != sd->vold.saturation) {
sd->vold.saturation = sat;
if (sat < 0 || sat > sd->vmax.saturation)
sat = 0;
sat = tbl_sat[sat];
ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba00 , 0x0025, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba00 + sat, 0x00f1, 0, NULL);
}
if (sharp != sd->vold.sharpness) {
sd->vold.sharpness = sharp;
if (sharp < 0 || sharp > sd->vmax.sharpness)
sharp = 0;
ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba00 , 0x0005, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba00 + sharp, 0x00f1, 0, NULL);
}
if (hue != sd->vold.hue) {
/* 0=normal 1=NB 2="sepia" 3=negative 4=other 5=other2 */
if (hue < 0 || hue > sd->vmax.hue)
hue = 0;
if (hue == sd->vmax.hue)
sd->swapRB = 1;
else
sd->swapRB = 0;
ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba70, 0x00e2, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba00 + hue * (hue < 6), 0x00f1,
0, NULL);
}
if (backlight != sd->vold.backlight) {
sd->vold.backlight = backlight;
if (backlight < 0 || backlight > sd->vmax.backlight)
backlight = 0;
backlight = tbl_backlight[backlight];
for (i = 0; i < 2; i++) {
ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba74, 0x0006, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba80 + backlight, 0x00f1,
0, NULL);
}
}
if (hue != sd->vold.hue) {
sd->vold.hue = hue;
ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba70, 0x00e2, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba00 + hue * (hue < 6), 0x00f1,
0, NULL);
}
if (mirror != sd->vold.mirror || flip != sd->vold.flip) {
u8 dat_hvflip2[4] = {0x20, 0x01, 0xf1, 0x00};
sd->vold.mirror = mirror;
sd->vold.flip = flip;
dat_hvflip2[3] = flip + 2 * mirror;
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 4, dat_hvflip1);
ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 4, dat_hvflip2);
}
if (gam != sd->vold.gamma) {
sd->vold.gamma = gam;
if (gam < 0 || gam > sd->vmax.gamma)
gam = 0;
gam = 2 * gam;
ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba04 , 0x003b, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba02 + gam, 0x00f1, 0, NULL);
}
if (cntr != sd->vold.contrast) {
sd->vold.contrast = cntr;
if (cntr < 0 || cntr > sd->vmax.contrast)
cntr = 0;
ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba00 + tbl_cntr1[cntr], 0x0035,
0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0xba00 + tbl_cntr2[cntr], 0x00f1,
0, NULL);
}
return 0;
}
static void mi1320_post_unset_alt(struct gspca_dev *gspca_dev)
{
ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL);
fetch_validx(gspca_dev, tbl_post_unset_alt,
ARRAY_SIZE(tbl_post_unset_alt));
}

View File

@@ -0,0 +1,937 @@
/* @file gl860-mi2020.c
* @author Olivier LORIN, from Ice/Soro2005's logs(A), Fret_saw/Hulkie's
* logs(B) and Tricid"s logs(C). With the help of Kytrix/BUGabundo/Blazercist.
* @date 2009-08-27
*
* 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
* 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, see <http://www.gnu.org/licenses/>.
*/
/* Sensor : MI2020 */
#include "gl860.h"
static u8 dat_bright1[] = {0x8c, 0xa2, 0x06};
static u8 dat_bright3[] = {0x8c, 0xa1, 0x02};
static u8 dat_bright4[] = {0x90, 0x00, 0x0f};
static u8 dat_bright5[] = {0x8c, 0xa1, 0x03};
static u8 dat_bright6[] = {0x90, 0x00, 0x05};
static u8 dat_dummy1[] = {0x90, 0x00, 0x06};
/*static u8 dummy2[] = {0x8c, 0xa1, 0x02};*/
/*static u8 dummy3[] = {0x90, 0x00, 0x1f};*/
static u8 dat_hvflip1[] = {0x8c, 0x27, 0x19};
static u8 dat_hvflip3[] = {0x8c, 0x27, 0x3b};
static u8 dat_hvflip5[] = {0x8c, 0xa1, 0x03};
static u8 dat_hvflip6[] = {0x90, 0x00, 0x06};
static u8 dat_freq1[] = { 0x8c, 0xa4, 0x04 };
static u8 dat_multi5[] = { 0x8c, 0xa1, 0x03 };
static u8 dat_multi6[] = { 0x90, 0x00, 0x05 };
static struct validx tbl_common_a[] = {
{0x0000, 0x0000},
{1, 0xffff}, /* msleep(35); */
{0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d}, {0x0000, 0x00c0},
{0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, {0x0004, 0x00d8},
{0x0000, 0x0058}, {0x0002, 0x0004}, {0x0041, 0x0000},
};
static struct validx tbl_common_b[] = {
{0x006a, 0x0007},
{35, 0xffff},
{0x00ef, 0x0006},
{35, 0xffff},
{0x006a, 0x000d},
{35, 0xffff},
{0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2},
{0x0004, 0x00d8}, {0x0000, 0x0058}, {0x0041, 0x0000},
};
static struct idxdata tbl_common_c[] = {
{0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"},
{6, "\xff\xff\xff"}, /* 12 */
{0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"},
{2, "\xff\xff\xff"}, /* - */
{0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\x22\x23"},
{0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0f"}, {0x33, "\x90\x00\x0d"},
{0x33, "\x8c\xa2\x10"}, {0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x11"},
{0x33, "\x90\x00\x07"}, {0x33, "\xf4\x03\x1d"}, {0x35, "\xa2\x00\xe2"},
{0x33, "\x8c\xab\x05"}, {0x33, "\x90\x00\x01"}, {0x32, "\x6e\x00\x86"},
{0x32, "\x70\x0f\xaa"}, {0x32, "\x72\x0f\xe4"}, {0x33, "\x8c\xa3\x4a"},
{0x33, "\x90\x00\x5a"}, {0x33, "\x8c\xa3\x4b"}, {0x33, "\x90\x00\xa6"},
{0x33, "\x8c\xa3\x61"}, {0x33, "\x90\x00\xc8"}, {0x33, "\x8c\xa3\x62"},
{0x33, "\x90\x00\xe1"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"},
{0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"},
{0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"},
{0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"},
{0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"},
{0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"},
{0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"},
{0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"},
{0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"},
{0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"},
{0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"},
{0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"},
{0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"},
{0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"},
{0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"},
{0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"},
{0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"},
{0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"},
{0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"},
{0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"},
{0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"},
{0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"},
{0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"},
{0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"},
{0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"},
{0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"},
{1, "\xff\xff\xff"},
{0x33, "\x78\x00\x00"},
{1, "\xff\xff\xff"},
{0x35, "\xb8\x1f\x20"}, {0x33, "\x8c\xa2\x06"}, {0x33, "\x90\x00\x10"},
{0x33, "\x8c\xa2\x07"}, {0x33, "\x90\x00\x08"}, {0x33, "\x8c\xa2\x42"},
{0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x4a"}, {0x33, "\x90\x00\x8c"},
{0x35, "\xba\xfa\x08"}, {0x33, "\x8c\xa2\x02"}, {0x33, "\x90\x00\x22"},
{0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"},
};
static struct idxdata tbl_common_d[] = {
{0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\xa4\x08"},
{0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x21"},
{0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\xa4\x0b"},
{0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\xa0"},
{0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc0"}, {0x33, "\x8c\x24\x15"},
{0x33, "\x90\x00\xa0"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\xc0"},
};
static struct idxdata tbl_common_e[] = {
{0x33, "\x8c\xa4\x04"}, {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"},
{0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"},
{0x33, "\x8c\xa2\x0c"}, {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"},
{0x33, "\x90\x00\x04"}, {0x33, "\x8c\xa2\x14"}, {0x33, "\x90\x00\x20"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"},
/* msleep(53); */
{0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"},
{0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x39"},
{0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"},
{0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x03"},
{0x33, "\x90\x02\x84"}, {0x33, "\x8c\x27\x05"}, {0x33, "\x90\x01\xe2"},
{0x33, "\x8c\x27\x07"}, {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x09"},
{0x33, "\x90\x04\xb0"}, {0x33, "\x8c\x27\x0d"}, {0x33, "\x90\x00\x00"},
{0x33, "\x8c\x27\x0f"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x11"},
{0x33, "\x90\x04\xbd"}, {0x33, "\x8c\x27\x13"}, {0x33, "\x90\x06\x4d"},
{0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"},
{0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"},
{0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x1d"},
{0x33, "\x90\x01\x02"}, {0x33, "\x8c\x27\x1f"}, {0x33, "\x90\x02\x79"},
{0x33, "\x8c\x27\x21"}, {0x33, "\x90\x01\x55"}, {0x33, "\x8c\x27\x23"},
{0x33, "\x90\x02\x85"}, {0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"},
{0x33, "\x8c\x27\x27"}, {0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x29"},
{0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x2b"}, {0x33, "\x90\x10\x20"},
{0x33, "\x8c\x27\x2d"}, {0x33, "\x90\x20\x07"}, {0x33, "\x8c\x27\x2f"},
{0x33, "\x90\x00\x04"}, {0x33, "\x8c\x27\x31"}, {0x33, "\x90\x00\x04"},
{0x33, "\x8c\x27\x33"}, {0x33, "\x90\x04\xbb"}, {0x33, "\x8c\x27\x35"},
{0x33, "\x90\x06\x4b"}, {0x33, "\x8c\x27\x37"}, {0x33, "\x90\x00\x00"},
{0x33, "\x8c\x27\x39"}, {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3b"},
{0x33, "\x90\x00\x24"}, {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"},
{0x33, "\x8c\x27\x41"}, {0x33, "\x90\x01\x69"}, {0x33, "\x8c\x27\x45"},
{0x33, "\x90\x04\xed"}, {0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"},
{0x33, "\x8c\x27\x51"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x53"},
{0x33, "\x90\x03\x20"}, {0x33, "\x8c\x27\x55"}, {0x33, "\x90\x00\x00"},
{0x33, "\x8c\x27\x57"}, {0x33, "\x90\x02\x58"}, {0x33, "\x8c\x27\x5f"},
{0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x61"}, {0x33, "\x90\x06\x40"},
{0x33, "\x8c\x27\x63"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x65"},
{0x33, "\x90\x04\xb0"}, {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa1"},
{0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"},
{0x33, "\x90\x00\x21"}, {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"},
{0x33, "\x8c\xa4\x0b"}, {0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"},
{0x33, "\x90\x00\xa1"}, {0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc1"},
{0x33, "\x8c\x24\x15"},
};
static struct validx tbl_init_at_startup[] = {
{0x0000, 0x0000},
{53, 0xffff},
{0x0010, 0x0010},
{53, 0xffff},
{0x0008, 0x00c0},
{53, 0xffff},
{0x0001, 0x00c1},
{53, 0xffff},
{0x0001, 0x00c2},
{53, 0xffff},
{0x0020, 0x0006},
{53, 0xffff},
{0x006a, 0x000d},
{53, 0xffff},
};
static struct idxdata tbl_init_post_alt_low_a[] = {
{0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\x22\x2e"},
{0x33, "\x90\x00\x81"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x17"},
{0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x1a"}, {0x33, "\x8c\xa4\x0a"},
{0x33, "\x90\x00\x1d"}, {0x33, "\x8c\xa4\x0b"}, {0x33, "\x90\x00\x20"},
{0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\x81"}, {0x33, "\x8c\x24\x13"},
{0x33, "\x90\x00\x9b"},
};
static struct idxdata tbl_init_post_alt_low_b[] = {
{0x33, "\x8c\x27\x03"}, {0x33, "\x90\x03\x24"}, {0x33, "\x8c\x27\x05"},
{0x33, "\x90\x02\x58"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
{2, "\xff\xff\xff"},
};
static struct idxdata tbl_init_post_alt_low_c[] = {
{0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"},
{2, "\xff\xff\xff"},
{0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x20"},
{0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x01"},
{0x33, "\x2e\x01\x00"}, {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"},
{0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x95"}, {0x33, "\x90\x01\x00"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x03"},
{0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"},
{0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"},
{2, "\xff\xff\xff"}, /* - * */
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
{1, "\xff\xff\xff"},
};
static struct idxdata tbl_init_post_alt_low_d[] = {
{0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"},
{0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"},
{0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"},
{0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"},
{0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"},
{0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"},
{0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"},
{0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"},
{0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"},
{0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"},
{0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"},
{0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"},
{0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"},
{0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"},
{0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"},
{0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"},
{0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"},
{0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"},
{0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"},
{0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"},
{0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"},
{0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"},
{0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"},
{0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"},
{0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"},
{0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"},
/* Flip/Mirror h/v=1 */
{0x33, "\x90\x00\x3c"}, {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"},
{0x33, "\x8c\x27\x3b"}, {0x33, "\x90\x00\x24"}, {0x33, "\x8c\xa1\x03"},
{0x33, "\x90\x00\x06"},
{130, "\xff\xff\xff"},
{0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"},
{0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"},
{100, "\xff\xff\xff"},
/* ?? */
{0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa1\x02"},
{0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"},
{0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"},
/* Brigthness=70 */
{0x33, "\x8c\xa2\x06"}, {0x33, "\x90\x00\x46"}, {0x33, "\x8c\xa1\x02"},
{0x33, "\x90\x00\x0f"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
/* Sharpness=20 */
{0x32, "\x6c\x14\x08"},
};
static struct idxdata tbl_init_post_alt_big_a[] = {
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
{2, "\xff\xff\xff"},
{0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"},
{0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x03"},
{0x33, "\x90\x00\x05"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, {0x33, "\x8c\xa1\x20"},
{0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x30"}, {0x33, "\x90\x00\x03"},
{0x33, "\x8c\xa1\x31"}, {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa1\x32"},
{0x33, "\x90\x00\x03"}, {0x33, "\x8c\xa1\x34"}, {0x33, "\x90\x00\x03"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x02"}, {0x33, "\x2e\x01\x00"},
{0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"},
};
static struct idxdata tbl_init_post_alt_big_b[] = {
{0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"},
{0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"},
{0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"},
{0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"},
{0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"},
{0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"},
{0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"},
{0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"},
{0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"},
{0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"},
{0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"},
{0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"},
{0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"},
{0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"},
{0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"},
{0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"},
{0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"},
{0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"},
{0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"},
{0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"},
{0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"},
{0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"},
{0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"},
{0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"},
{0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"},
{0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"},
};
static struct idxdata tbl_init_post_alt_big_c[] = {
{0x33, "\x8c\xa1\x02"},
{0x33, "\x90\x00\x1f"},
{0x33, "\x8c\xa1\x02"},
{0x33, "\x90\x00\x1f"},
{0x33, "\x8c\xa1\x02"},
{0x33, "\x90\x00\x1f"},
{0x33, "\x8c\xa1\x02"},
{0x33, "\x90\x00\x1f"},
};
static u8 *dat_640 = "\xd0\x02\xd1\x08\xd2\xe1\xd3\x02\xd4\x10\xd5\x81";
static u8 *dat_800 = "\xd0\x02\xd1\x10\xd2\x57\xd3\x02\xd4\x18\xd5\x21";
static u8 *dat_1280 = "\xd0\x02\xd1\x20\xd2\x01\xd3\x02\xd4\x28\xd5\x01";
static u8 *dat_1600 = "\xd0\x02\xd1\x20\xd2\xaf\xd3\x02\xd4\x30\xd5\x41";
static int mi2020_init_at_startup(struct gspca_dev *gspca_dev);
static int mi2020_configure_alt(struct gspca_dev *gspca_dev);
static int mi2020_init_pre_alt(struct gspca_dev *gspca_dev);
static int mi2020_init_post_alt(struct gspca_dev *gspca_dev);
static void mi2020_post_unset_alt(struct gspca_dev *gspca_dev);
static int mi2020_camera_settings(struct gspca_dev *gspca_dev);
/*==========================================================================*/
void mi2020_init_settings(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->vcur.backlight = 0;
sd->vcur.brightness = 70;
sd->vcur.sharpness = 20;
sd->vcur.contrast = 0;
sd->vcur.gamma = 0;
sd->vcur.hue = 0;
sd->vcur.saturation = 60;
sd->vcur.whitebal = 50;
sd->vcur.mirror = 0;
sd->vcur.flip = 0;
sd->vcur.AC50Hz = 1;
sd->vmax.backlight = 64;
sd->vmax.brightness = 128;
sd->vmax.sharpness = 40;
sd->vmax.contrast = 3;
sd->vmax.gamma = 2;
sd->vmax.hue = 0 + 1; /* 200 */
sd->vmax.saturation = 0; /* 100 */
sd->vmax.whitebal = 0; /* 100 */
sd->vmax.mirror = 1;
sd->vmax.flip = 1;
sd->vmax.AC50Hz = 1;
if (_MI2020b_) {
sd->vmax.contrast = 0;
sd->vmax.gamma = 0;
sd->vmax.backlight = 0;
}
sd->dev_camera_settings = mi2020_camera_settings;
sd->dev_init_at_startup = mi2020_init_at_startup;
sd->dev_configure_alt = mi2020_configure_alt;
sd->dev_init_pre_alt = mi2020_init_pre_alt;
sd->dev_post_unset_alt = mi2020_post_unset_alt;
}
/*==========================================================================*/
static void common(struct gspca_dev *gspca_dev)
{
s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
if (_MI2020b_) {
fetch_validx(gspca_dev, tbl_common_a, ARRAY_SIZE(tbl_common_a));
} else {
if (_MI2020_)
ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x0004, 0, NULL);
else
ctrl_out(gspca_dev, 0x40, 1, 0x0002, 0x0004, 0, NULL);
msleep(35);
fetch_validx(gspca_dev, tbl_common_b, ARRAY_SIZE(tbl_common_b));
}
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x01");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x00");
msleep(2); /* - * */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0030, 3, "\x1a\x0a\xcc");
if (reso == IMAGE_1600)
msleep(2); /* 1600 */
fetch_idxdata(gspca_dev, tbl_common_c, ARRAY_SIZE(tbl_common_c));
if (_MI2020b_ || _MI2020_)
fetch_idxdata(gspca_dev, tbl_common_d,
ARRAY_SIZE(tbl_common_d));
fetch_idxdata(gspca_dev, tbl_common_e, ARRAY_SIZE(tbl_common_e));
if (_MI2020b_ || _MI2020_) {
/* Different from fret */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x78");
/* Same as fret */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\x24\x17");
/* Different from fret */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x90");
} else {
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x6a");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\x24\x17");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x80");
}
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x05");
msleep(2);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03");
if (reso == IMAGE_1600)
msleep(14); /* 1600 */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x06");
msleep(2);
}
static int mi2020_init_at_startup(struct gspca_dev *gspca_dev)
{
u8 c;
ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &c);
ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &c);
fetch_validx(gspca_dev, tbl_init_at_startup,
ARRAY_SIZE(tbl_init_at_startup));
common(gspca_dev);
return 0;
}
static int mi2020_init_pre_alt(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->mirrorMask = 0;
sd->vold.backlight = -1;
sd->vold.brightness = -1;
sd->vold.sharpness = -1;
sd->vold.contrast = -1;
sd->vold.gamma = -1;
sd->vold.hue = -1;
sd->vold.mirror = -1;
sd->vold.flip = -1;
sd->vold.AC50Hz = -1;
mi2020_init_post_alt(gspca_dev);
return 0;
}
static int mi2020_init_post_alt(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
s32 backlight = sd->vcur.backlight;
s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0);
s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0);
s32 freq = (sd->vcur.AC50Hz > 0);
u8 dat_freq2[] = {0x90, 0x00, 0x80};
u8 dat_multi1[] = {0x8c, 0xa7, 0x00};
u8 dat_multi2[] = {0x90, 0x00, 0x00};
u8 dat_multi3[] = {0x8c, 0xa7, 0x00};
u8 dat_multi4[] = {0x90, 0x00, 0x00};
u8 dat_hvflip2[] = {0x90, 0x04, 0x6c};
u8 dat_hvflip4[] = {0x90, 0x00, 0x24};
u8 c;
sd->nbIm = -1;
dat_freq2[2] = freq ? 0xc0 : 0x80;
dat_multi1[2] = 0x9d;
dat_multi3[2] = dat_multi1[2] + 1;
dat_multi4[2] = dat_multi2[2] = backlight;
dat_hvflip2[2] = 0x6c + 2 * (1 - flip) + (1 - mirror);
dat_hvflip4[2] = 0x24 + 2 * (1 - flip) + (1 - mirror);
msleep(200);
ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL);
msleep(3); /* 35 * */
common(gspca_dev);
ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL);
msleep(70);
if (_MI2020b_)
ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x0003, 0x00c1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x0042, 0x00c2, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL);
switch (reso) {
case IMAGE_640:
case IMAGE_800:
if (reso != IMAGE_800)
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
12, dat_640);
else
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
12, dat_800);
if (_MI2020c_)
fetch_idxdata(gspca_dev, tbl_init_post_alt_low_a,
ARRAY_SIZE(tbl_init_post_alt_low_a));
if (reso == IMAGE_800)
fetch_idxdata(gspca_dev, tbl_init_post_alt_low_b,
ARRAY_SIZE(tbl_init_post_alt_low_b));
fetch_idxdata(gspca_dev, tbl_init_post_alt_low_c,
ARRAY_SIZE(tbl_init_post_alt_low_c));
if (_MI2020b_) {
ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL);
msleep(150);
} else if (_MI2020c_) {
ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL);
msleep(120);
ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL);
msleep(30);
} else if (_MI2020_) {
ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL);
msleep(120);
ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL);
msleep(30);
}
/* AC power frequency */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2);
msleep(20);
/* backlight */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4);
/* at init time but not after */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa2\x0c");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x17");
/* finish the backlight */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6);
msleep(5);/* " */
if (_MI2020c_) {
fetch_idxdata(gspca_dev, tbl_init_post_alt_low_d,
ARRAY_SIZE(tbl_init_post_alt_low_d));
} else {
ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c);
msleep(14); /* 0xd8 */
/* flip/mirror */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, dat_hvflip1);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, dat_hvflip2);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, dat_hvflip3);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, dat_hvflip4);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, dat_hvflip5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, dat_hvflip6);
msleep(21);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, dat_dummy1);
msleep(5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, dat_dummy1);
msleep(5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, dat_dummy1);
msleep(5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, dat_dummy1);
msleep(5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, dat_dummy1);
msleep(5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, dat_dummy1);
/* end of flip/mirror main part */
msleep(246); /* 146 */
sd->nbIm = 0;
}
break;
case IMAGE_1280:
case IMAGE_1600:
if (reso == IMAGE_1280) {
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
12, dat_1280);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, "\x8c\x27\x07");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, "\x90\x05\x04");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, "\x8c\x27\x09");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, "\x90\x04\x02");
} else {
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
12, dat_1600);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, "\x8c\x27\x07");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, "\x90\x06\x40");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, "\x8c\x27\x09");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033,
3, "\x90\x04\xb0");
}
fetch_idxdata(gspca_dev, tbl_init_post_alt_big_a,
ARRAY_SIZE(tbl_init_post_alt_big_a));
if (reso == IMAGE_1600)
msleep(13); /* 1600 */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\x27\x97");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x01\x00");
msleep(53);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01");
if (reso == IMAGE_1600)
msleep(13); /* 1600 */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00");
msleep(53);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x72");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x02");
if (reso == IMAGE_1600)
msleep(13); /* 1600 */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01");
msleep(53);
if (_MI2020b_) {
ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL);
if (reso == IMAGE_1600)
msleep(500); /* 1600 */
ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL);
msleep(1850);
} else if (_MI2020c_ || _MI2020_) {
ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL);
msleep(1850);
ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL);
msleep(30);
}
/* AC power frequency */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2);
msleep(20);
/* backlight */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4);
/* at init time but not after */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa2\x0c");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x17");
/* finish the backlight */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6);
msleep(6); /* " */
ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c);
msleep(14);
if (_MI2020c_)
fetch_idxdata(gspca_dev, tbl_init_post_alt_big_b,
ARRAY_SIZE(tbl_init_post_alt_big_b));
/* flip/mirror */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6);
/* end of flip/mirror main part */
msleep(16);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00");
if (reso == IMAGE_1600)
msleep(25); /* 1600 */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00");
msleep(103);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x02");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x72");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01");
sd->nbIm = 0;
if (_MI2020c_)
fetch_idxdata(gspca_dev, tbl_init_post_alt_big_c,
ARRAY_SIZE(tbl_init_post_alt_big_c));
}
sd->vold.mirror = mirror;
sd->vold.flip = flip;
sd->vold.AC50Hz = freq;
sd->vold.backlight = backlight;
mi2020_camera_settings(gspca_dev);
return 0;
}
static int mi2020_configure_alt(struct gspca_dev *gspca_dev)
{
s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
switch (reso) {
case IMAGE_640:
gspca_dev->alt = 3 + 1;
break;
case IMAGE_800:
case IMAGE_1280:
case IMAGE_1600:
gspca_dev->alt = 1 + 1;
break;
}
return 0;
}
int mi2020_camera_settings(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 backlight = sd->vcur.backlight;
s32 bright = sd->vcur.brightness;
s32 sharp = sd->vcur.sharpness;
s32 cntr = sd->vcur.contrast;
s32 gam = sd->vcur.gamma;
s32 hue = (sd->vcur.hue > 0);
s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0);
s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0);
s32 freq = (sd->vcur.AC50Hz > 0);
u8 dat_sharp[] = {0x6c, 0x00, 0x08};
u8 dat_bright2[] = {0x90, 0x00, 0x00};
u8 dat_freq2[] = {0x90, 0x00, 0x80};
u8 dat_multi1[] = {0x8c, 0xa7, 0x00};
u8 dat_multi2[] = {0x90, 0x00, 0x00};
u8 dat_multi3[] = {0x8c, 0xa7, 0x00};
u8 dat_multi4[] = {0x90, 0x00, 0x00};
u8 dat_hvflip2[] = {0x90, 0x04, 0x6c};
u8 dat_hvflip4[] = {0x90, 0x00, 0x24};
/* Less than 4 images received -> too early to set the settings */
if (sd->nbIm < 4) {
sd->waitSet = 1;
return 0;
}
sd->waitSet = 0;
if (freq != sd->vold.AC50Hz) {
sd->vold.AC50Hz = freq;
dat_freq2[2] = freq ? 0xc0 : 0x80;
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2);
msleep(20);
}
if (mirror != sd->vold.mirror || flip != sd->vold.flip) {
sd->vold.mirror = mirror;
sd->vold.flip = flip;
dat_hvflip2[2] = 0x6c + 2 * (1 - flip) + (1 - mirror);
dat_hvflip4[2] = 0x24 + 2 * (1 - flip) + (1 - mirror);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6);
msleep(130);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1);
msleep(6);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1);
msleep(6);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1);
msleep(6);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1);
msleep(6);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1);
msleep(6);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1);
msleep(6);
/* Sometimes present, sometimes not, useful? */
/* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2);
* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3);
* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2);
* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3);
* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2);
* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3);
* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2);
* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3);*/
}
if (backlight != sd->vold.backlight) {
sd->vold.backlight = backlight;
if (backlight < 0 || backlight > sd->vmax.backlight)
backlight = 0;
dat_multi1[2] = 0x9d;
dat_multi3[2] = dat_multi1[2] + 1;
dat_multi4[2] = dat_multi2[2] = backlight;
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6);
}
if (gam != sd->vold.gamma) {
sd->vold.gamma = gam;
if (gam < 0 || gam > sd->vmax.gamma)
gam = 0;
dat_multi1[2] = 0x6d;
dat_multi3[2] = dat_multi1[2] + 1;
dat_multi4[2] = dat_multi2[2] = 0x40 + gam;
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6);
}
if (cntr != sd->vold.contrast) {
sd->vold.contrast = cntr;
if (cntr < 0 || cntr > sd->vmax.contrast)
cntr = 0;
dat_multi1[2] = 0x6d;
dat_multi3[2] = dat_multi1[2] + 1;
dat_multi4[2] = dat_multi2[2] = 0x12 + 16 * cntr;
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6);
}
if (bright != sd->vold.brightness) {
sd->vold.brightness = bright;
if (bright < 0 || bright > sd->vmax.brightness)
bright = 0;
dat_bright2[2] = bright;
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright1);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright2);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright3);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright4);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright5);
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright6);
}
if (sharp != sd->vold.sharpness) {
sd->vold.sharpness = sharp;
if (sharp < 0 || sharp > sd->vmax.sharpness)
sharp = 0;
dat_sharp[1] = sharp;
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0032, 3, dat_sharp);
}
if (hue != sd->vold.hue) {
sd->swapRB = hue;
sd->vold.hue = hue;
}
return 0;
}
static void mi2020_post_unset_alt(struct gspca_dev *gspca_dev)
{
ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL);
msleep(20);
if (_MI2020c_ || _MI2020_)
ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0000, 0, NULL);
else
ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL);
}

View File

@@ -0,0 +1,505 @@
/* @file gl860-ov2640.c
* @author Olivier LORIN, from Malmostoso's logs
* @date 2009-08-27
*
* 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
* 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, see <http://www.gnu.org/licenses/>.
*/
/* Sensor : OV2640 */
#include "gl860.h"
static u8 dat_init1[] = "\x00\x41\x07\x6a\x06\x61\x0d\x6a" "\x10\x10\xc1\x01";
static u8 dat_init2[] = {0x61}; /* expected */
static u8 dat_init3[] = {0x51}; /* expected */
static u8 dat_post[] =
"\x00\x41\x07\x6a\x06\xef\x0d\x6a" "\x10\x10\xc1\x01";
static u8 dat_640[] = "\xd0\x01\xd1\x08\xd2\xe0\xd3\x02\xd4\x10\xd5\x81";
static u8 dat_800[] = "\xd0\x01\xd1\x10\xd2\x58\xd3\x02\xd4\x18\xd5\x21";
static u8 dat_1280[] = "\xd0\x01\xd1\x18\xd2\xc0\xd3\x02\xd4\x28\xd5\x01";
static u8 dat_1600[] = "\xd0\x01\xd1\x20\xd2\xb0\xd3\x02\xd4\x30\xd5\x41";
static u8 c50[] = {0x50}; /* expected */
static u8 c28[] = {0x28}; /* expected */
static u8 ca8[] = {0xa8}; /* expected */
static struct validx tbl_init_at_startup[] = {
{0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1},
{0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d},
{0x0050, 0x0000}, {0x0041, 0x0000}, {0x006a, 0x0007}, {0x0061, 0x0006},
{0x006a, 0x000d}, {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1},
{0x0041, 0x00c2}, {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058},
{0x0041, 0x0000}, {0x0061, 0x0000},
};
static struct validx tbl_common[] = {
{0x6000, 0x00ff}, {0x60ff, 0x002c}, {0x60df, 0x002e}, {0x6001, 0x00ff},
{0x6080, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045}, {0x6000, 0x0010},
{0x6035, 0x003c}, {0x6000, 0x0011}, {0x6028, 0x0004}, {0x60e5, 0x0013},
{0x6088, 0x0014}, {0x600c, 0x002c}, {0x6078, 0x0033}, {0x60f7, 0x003b},
{0x6000, 0x003e}, {0x6011, 0x0043}, {0x6010, 0x0016}, {0x6082, 0x0039},
{0x6088, 0x0035}, {0x600a, 0x0022}, {0x6040, 0x0037}, {0x6000, 0x0023},
{0x60a0, 0x0034}, {0x601a, 0x0036}, {0x6002, 0x0006}, {0x60c0, 0x0007},
{0x60b7, 0x000d}, {0x6001, 0x000e}, {0x6000, 0x004c}, {0x6081, 0x004a},
{0x6099, 0x0021}, {0x6002, 0x0009}, {0x603e, 0x0024}, {0x6034, 0x0025},
{0x6081, 0x0026}, {0x6000, 0x0000}, {0x6000, 0x0045}, {0x6000, 0x0010},
{0x6000, 0x005c}, {0x6000, 0x0063}, {0x6000, 0x007c}, {0x6070, 0x0061},
{0x6080, 0x0062}, {0x6080, 0x0020}, {0x6030, 0x0028}, {0x6000, 0x006c},
{0x6000, 0x006e}, {0x6002, 0x0070}, {0x6094, 0x0071}, {0x60c1, 0x0073},
{0x6034, 0x003d}, {0x6057, 0x005a}, {0x60bb, 0x004f}, {0x609c, 0x0050},
{0x6080, 0x006d}, {0x6002, 0x0039}, {0x6033, 0x003a}, {0x60f1, 0x003b},
{0x6031, 0x003c}, {0x6000, 0x00ff}, {0x6014, 0x00e0}, {0x60ff, 0x0076},
{0x60a0, 0x0033}, {0x6020, 0x0042}, {0x6018, 0x0043}, {0x6000, 0x004c},
{0x60d0, 0x0087}, {0x600f, 0x0088}, {0x6003, 0x00d7}, {0x6010, 0x00d9},
{0x6005, 0x00da}, {0x6082, 0x00d3}, {0x60c0, 0x00f9}, {0x6006, 0x0044},
{0x6007, 0x00d1}, {0x6002, 0x00d2}, {0x6000, 0x00d2}, {0x6011, 0x00d8},
{0x6008, 0x00c8}, {0x6080, 0x00c9}, {0x6008, 0x007c}, {0x6020, 0x007d},
{0x6020, 0x007d}, {0x6000, 0x0090}, {0x600e, 0x0091}, {0x601a, 0x0091},
{0x6031, 0x0091}, {0x605a, 0x0091}, {0x6069, 0x0091}, {0x6075, 0x0091},
{0x607e, 0x0091}, {0x6088, 0x0091}, {0x608f, 0x0091}, {0x6096, 0x0091},
{0x60a3, 0x0091}, {0x60af, 0x0091}, {0x60c4, 0x0091}, {0x60d7, 0x0091},
{0x60e8, 0x0091}, {0x6020, 0x0091}, {0x6000, 0x0092}, {0x6006, 0x0093},
{0x60e3, 0x0093}, {0x6005, 0x0093}, {0x6005, 0x0093}, {0x6000, 0x0093},
{0x6004, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093},
{0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093},
{0x6000, 0x0096}, {0x6008, 0x0097}, {0x6019, 0x0097}, {0x6002, 0x0097},
{0x600c, 0x0097}, {0x6024, 0x0097}, {0x6030, 0x0097}, {0x6028, 0x0097},
{0x6026, 0x0097}, {0x6002, 0x0097}, {0x6098, 0x0097}, {0x6080, 0x0097},
{0x6000, 0x0097}, {0x6000, 0x0097}, {0x60ed, 0x00c3}, {0x609a, 0x00c4},
{0x6000, 0x00a4}, {0x6011, 0x00c5}, {0x6051, 0x00c6}, {0x6010, 0x00c7},
{0x6066, 0x00b6}, {0x60a5, 0x00b8}, {0x6064, 0x00b7}, {0x607c, 0x00b9},
{0x60af, 0x00b3}, {0x6097, 0x00b4}, {0x60ff, 0x00b5}, {0x60c5, 0x00b0},
{0x6094, 0x00b1}, {0x600f, 0x00b2}, {0x605c, 0x00c4}, {0x6000, 0x00a8},
{0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x601d, 0x0086}, {0x6000, 0x0050},
{0x6090, 0x0051}, {0x6018, 0x0052}, {0x6000, 0x0053}, {0x6000, 0x0054},
{0x6088, 0x0055}, {0x6000, 0x0057}, {0x6090, 0x005a}, {0x6018, 0x005b},
{0x6005, 0x005c}, {0x60ed, 0x00c3}, {0x6000, 0x007f}, {0x6005, 0x00da},
{0x601f, 0x00e5}, {0x6067, 0x00e1}, {0x6000, 0x00e0}, {0x60ff, 0x00dd},
{0x6000, 0x0005}, {0x6001, 0x00ff}, {0x6000, 0x0000}, {0x6000, 0x0045},
{0x6000, 0x0010},
};
static struct validx tbl_sensor_settings_common_a[] = {
{0x0041, 0x0000}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a, 0x000d},
{0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2},
{0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0041, 0x0000},
{50, 0xffff},
{0x0061, 0x0000},
{0xffff, 0xffff},
{0x6000, 0x00ff}, {0x6000, 0x007c}, {0x6007, 0x007d},
{30, 0xffff},
{0x0040, 0x0000},
};
static struct validx tbl_sensor_settings_common_b[] = {
{0x6001, 0x00ff}, {0x6038, 0x000c},
{10, 0xffff},
{0x6000, 0x0011},
/* backlight=31/64 */
{0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025},
/* bright=0/256 */
{0x6000, 0x00ff}, {0x6009, 0x007c}, {0x6000, 0x007d},
/* wbal=64/128 */
{0x6000, 0x00ff}, {0x6003, 0x007c}, {0x6040, 0x007d},
/* cntr=0/256 */
{0x6000, 0x00ff}, {0x6007, 0x007c}, {0x6000, 0x007d},
/* sat=128/256 */
{0x6000, 0x00ff}, {0x6001, 0x007c}, {0x6080, 0x007d},
/* sharpness=0/32 */
{0x6000, 0x00ff}, {0x6001, 0x0092}, {0x60c0, 0x0093},
/* hue=0/256 */
{0x6000, 0x00ff}, {0x6002, 0x007c}, {0x6000, 0x007d},
/* gam=32/64 */
{0x6000, 0x00ff}, {0x6008, 0x007c}, {0x6020, 0x007d},
/* image right up */
{0xffff, 0xffff},
{15, 0xffff},
{0x6001, 0x00ff}, {0x6000, 0x8004},
{0xffff, 0xffff},
{0x60a8, 0x0004},
{15, 0xffff},
{0x6001, 0x00ff}, {0x6000, 0x8004},
{0xffff, 0xffff},
{0x60f8, 0x0004},
/* image right up */
{0xffff, 0xffff},
/* backlight=31/64 */
{0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025},
};
static struct validx tbl_640[] = {
{0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6067, 0x00e1},
{0x6004, 0x00da}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0},
{0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0011}, {0x6011, 0x0017},
{0x6075, 0x0018}, {0x6001, 0x0019}, {0x6097, 0x001a}, {0x6036, 0x0032},
{0x60bb, 0x004f}, {0x6057, 0x005a}, {0x609c, 0x0050}, {0x6080, 0x006d},
{0x6092, 0x0026}, {0x60ff, 0x0020}, {0x6000, 0x0027}, {0x6000, 0x00ff},
{0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c}, {0x603d, 0x0086},
{0x6089, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052}, {0x6000, 0x0053},
{0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057}, {0x60a0, 0x005a},
{0x6078, 0x005b}, {0x6000, 0x005c}, {0x6004, 0x00d3}, {0x6000, 0x00e0},
{0x60ff, 0x00dd}, {0x60a1, 0x005a},
};
static struct validx tbl_800[] = {
{0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6067, 0x00e1},
{0x6004, 0x00da}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0},
{0x6001, 0x00ff}, {0x6040, 0x0012}, {0x6000, 0x0011}, {0x6011, 0x0017},
{0x6043, 0x0018}, {0x6000, 0x0019}, {0x604b, 0x001a}, {0x6009, 0x0032},
{0x60ca, 0x004f}, {0x60a8, 0x0050}, {0x6000, 0x006d}, {0x6038, 0x003d},
{0x60c8, 0x0035}, {0x6000, 0x0022}, {0x6092, 0x0026}, {0x60ff, 0x0020},
{0x6000, 0x0027}, {0x6000, 0x00ff}, {0x6064, 0x00c0}, {0x604b, 0x00c1},
{0x6000, 0x008c}, {0x601d, 0x0086}, {0x6082, 0x00d3}, {0x6000, 0x00e0},
{0x60ff, 0x00dd}, {0x6020, 0x008c}, {0x6001, 0x00ff}, {0x6044, 0x0018},
};
static struct validx tbl_big_a[] = {
{0x0002, 0x00c1}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0},
{0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045},
{0x6000, 0x0010}, {0x6000, 0x0011}, {0x6011, 0x0017}, {0x6075, 0x0018},
{0x6001, 0x0019}, {0x6097, 0x001a}, {0x6036, 0x0032}, {0x60bb, 0x004f},
{0x609c, 0x0050}, {0x6057, 0x005a}, {0x6080, 0x006d}, {0x6043, 0x000f},
{0x608f, 0x0003}, {0x6005, 0x007c}, {0x6081, 0x0026}, {0x6000, 0x00ff},
{0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c},
};
static struct validx tbl_big_b[] = {
{0x603d, 0x0086}, {0x6000, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052},
{0x6000, 0x0053}, {0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057},
{0x6040, 0x005a}, {0x60f0, 0x005b}, {0x6001, 0x005c}, {0x6082, 0x00d3},
{0x6000, 0x008e},
};
static struct validx tbl_big_c[] = {
{0x6004, 0x00da}, {0x6000, 0x00e0}, {0x6067, 0x00e1}, {0x60ff, 0x00dd},
{0x6001, 0x00ff}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0},
{0x6001, 0x00ff}, {0x6000, 0x0011}, {0x6000, 0x00ff}, {0x6010, 0x00c7},
{0x6000, 0x0092}, {0x6006, 0x0093}, {0x60e3, 0x0093}, {0x6005, 0x0093},
{0x6005, 0x0093}, {0x60ed, 0x00c3}, {0x6000, 0x00a4}, {0x60d0, 0x0087},
{0x6003, 0x0096}, {0x600c, 0x0097}, {0x6024, 0x0097}, {0x6030, 0x0097},
{0x6028, 0x0097}, {0x6026, 0x0097}, {0x6002, 0x0097}, {0x6001, 0x00ff},
{0x6043, 0x000f}, {0x608f, 0x0003}, {0x6000, 0x002d}, {0x6000, 0x002e},
{0x600a, 0x0022}, {0x6002, 0x0070}, {0x6008, 0x0014}, {0x6048, 0x0014},
{0x6000, 0x00ff}, {0x6000, 0x00e0}, {0x60ff, 0x00dd},
};
static struct validx tbl_post_unset_alt[] = {
{0x006a, 0x000d}, {0x6001, 0x00ff}, {0x6081, 0x0026}, {0x6000, 0x0000},
{0x6000, 0x0045}, {0x6000, 0x0010}, {0x6068, 0x000d},
{50, 0xffff},
{0x0021, 0x0000},
};
static int ov2640_init_at_startup(struct gspca_dev *gspca_dev);
static int ov2640_configure_alt(struct gspca_dev *gspca_dev);
static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev);
static int ov2640_init_post_alt(struct gspca_dev *gspca_dev);
static void ov2640_post_unset_alt(struct gspca_dev *gspca_dev);
static int ov2640_camera_settings(struct gspca_dev *gspca_dev);
/*==========================================================================*/
void ov2640_init_settings(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->vcur.backlight = 32;
sd->vcur.brightness = 0;
sd->vcur.sharpness = 6;
sd->vcur.contrast = 0;
sd->vcur.gamma = 32;
sd->vcur.hue = 0;
sd->vcur.saturation = 128;
sd->vcur.whitebal = 64;
sd->vmax.backlight = 64;
sd->vmax.brightness = 255;
sd->vmax.sharpness = 31;
sd->vmax.contrast = 255;
sd->vmax.gamma = 64;
sd->vmax.hue = 255 + 1;
sd->vmax.saturation = 255;
sd->vmax.whitebal = 128;
sd->vmax.mirror = 0;
sd->vmax.flip = 0;
sd->vmax.AC50Hz = 0;
sd->dev_camera_settings = ov2640_camera_settings;
sd->dev_init_at_startup = ov2640_init_at_startup;
sd->dev_configure_alt = ov2640_configure_alt;
sd->dev_init_pre_alt = ov2640_init_pre_alt;
sd->dev_post_unset_alt = ov2640_post_unset_alt;
}
/*==========================================================================*/
static void common(struct gspca_dev *gspca_dev)
{
fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common));
}
static int ov2640_init_at_startup(struct gspca_dev *gspca_dev)
{
fetch_validx(gspca_dev, tbl_init_at_startup,
ARRAY_SIZE(tbl_init_at_startup));
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_init1);
common(gspca_dev);
ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, dat_init2);
ctrl_out(gspca_dev, 0x40, 1, 0x00ef, 0x0006, 0, NULL);
ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, dat_init3);
ctrl_out(gspca_dev, 0x40, 1, 0x0051, 0x0000, 0, NULL);
/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */
return 0;
}
static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->vold.backlight = -1;
sd->vold.brightness = -1;
sd->vold.sharpness = -1;
sd->vold.contrast = -1;
sd->vold.saturation = -1;
sd->vold.gamma = -1;
sd->vold.hue = -1;
sd->vold.whitebal = -1;
ov2640_init_post_alt(gspca_dev);
return 0;
}
static int ov2640_init_post_alt(struct gspca_dev *gspca_dev)
{
s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
s32 n; /* reserved for FETCH macros */
ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL);
n = fetch_validx(gspca_dev, tbl_sensor_settings_common_a,
ARRAY_SIZE(tbl_sensor_settings_common_a));
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_post);
common(gspca_dev);
keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_a,
ARRAY_SIZE(tbl_sensor_settings_common_a), n);
switch (reso) {
case IMAGE_640:
n = fetch_validx(gspca_dev, tbl_640, ARRAY_SIZE(tbl_640));
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_640);
break;
case IMAGE_800:
n = fetch_validx(gspca_dev, tbl_800, ARRAY_SIZE(tbl_800));
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_800);
break;
case IMAGE_1600:
case IMAGE_1280:
n = fetch_validx(gspca_dev, tbl_big_a, ARRAY_SIZE(tbl_big_a));
if (reso == IMAGE_1280) {
n = fetch_validx(gspca_dev, tbl_big_b,
ARRAY_SIZE(tbl_big_b));
} else {
ctrl_out(gspca_dev, 0x40, 1, 0x601d, 0x0086, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00d7, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6082, 0x00d3, 0, NULL);
}
n = fetch_validx(gspca_dev, tbl_big_c, ARRAY_SIZE(tbl_big_c));
if (reso == IMAGE_1280) {
ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
12, dat_1280);
} else {
ctrl_out(gspca_dev, 0x40, 1, 0x6020, 0x008c, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6076, 0x0018, 0, NULL);
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
12, dat_1600);
}
break;
}
n = fetch_validx(gspca_dev, tbl_sensor_settings_common_b,
ARRAY_SIZE(tbl_sensor_settings_common_b));
ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50);
keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
ARRAY_SIZE(tbl_sensor_settings_common_b), n);
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28);
keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
ARRAY_SIZE(tbl_sensor_settings_common_b), n);
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8);
keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
ARRAY_SIZE(tbl_sensor_settings_common_b), n);
ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50);
keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
ARRAY_SIZE(tbl_sensor_settings_common_b), n);
ov2640_camera_settings(gspca_dev);
return 0;
}
static int ov2640_configure_alt(struct gspca_dev *gspca_dev)
{
s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
switch (reso) {
case IMAGE_640:
gspca_dev->alt = 3 + 1;
break;
case IMAGE_800:
case IMAGE_1280:
case IMAGE_1600:
gspca_dev->alt = 1 + 1;
break;
}
return 0;
}
static int ov2640_camera_settings(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 backlight = sd->vcur.backlight;
s32 bright = sd->vcur.brightness;
s32 sharp = sd->vcur.sharpness;
s32 gam = sd->vcur.gamma;
s32 cntr = sd->vcur.contrast;
s32 sat = sd->vcur.saturation;
s32 hue = sd->vcur.hue;
s32 wbal = sd->vcur.whitebal;
if (backlight != sd->vold.backlight) {
if (backlight < 0 || backlight > sd->vmax.backlight)
backlight = 0;
ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff,
0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight , 0x0024,
0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025,
0, NULL);
/* No sd->vold.backlight=backlight; (to be done again later) */
}
if (bright != sd->vold.brightness) {
sd->vold.brightness = bright;
if (bright < 0 || bright > sd->vmax.brightness)
bright = 0;
ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6009 , 0x007c, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6000 + bright, 0x007d, 0, NULL);
}
if (wbal != sd->vold.whitebal) {
sd->vold.whitebal = wbal;
if (wbal < 0 || wbal > sd->vmax.whitebal)
wbal = 0;
ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6003 , 0x007c, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6000 + wbal, 0x007d, 0, NULL);
}
if (cntr != sd->vold.contrast) {
sd->vold.contrast = cntr;
if (cntr < 0 || cntr > sd->vmax.contrast)
cntr = 0;
ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6007 , 0x007c, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6000 + cntr, 0x007d, 0, NULL);
}
if (sat != sd->vold.saturation) {
sd->vold.saturation = sat;
if (sat < 0 || sat > sd->vmax.saturation)
sat = 0;
ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x007c, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6000 + sat, 0x007d, 0, NULL);
}
if (sharp != sd->vold.sharpness) {
sd->vold.sharpness = sharp;
if (sharp < 0 || sharp > sd->vmax.sharpness)
sharp = 0;
ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x0092, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x60c0 + sharp, 0x0093, 0, NULL);
}
if (hue != sd->vold.hue) {
sd->vold.hue = hue;
if (hue < 0 || hue > sd->vmax.hue)
hue = 0;
ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6002 , 0x007c, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6000 + hue * (hue < 255), 0x007d,
0, NULL);
if (hue >= sd->vmax.hue)
sd->swapRB = 1;
else
sd->swapRB = 0;
}
if (gam != sd->vold.gamma) {
sd->vold.gamma = gam;
if (gam < 0 || gam > sd->vmax.gamma)
gam = 0;
ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6008 , 0x007c, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6000 + gam, 0x007d, 0, NULL);
}
if (backlight != sd->vold.backlight) {
sd->vold.backlight = backlight;
ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff,
0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight , 0x0024,
0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025,
0, NULL);
}
return 0;
}
static void ov2640_post_unset_alt(struct gspca_dev *gspca_dev)
{
ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL);
msleep(20);
fetch_validx(gspca_dev, tbl_post_unset_alt,
ARRAY_SIZE(tbl_post_unset_alt));
}

View File

@@ -0,0 +1,337 @@
/* @file gl860-ov9655.c
* @author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt
* on dsd's weblog
* @date 2009-08-27
*
* 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
* 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, see <http://www.gnu.org/licenses/>.
*/
/* Sensor : OV9655 */
#include "gl860.h"
static struct validx tbl_init_at_startup[] = {
{0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1},
{0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d},
{0x0040, 0x0000},
};
static struct validx tbl_commmon[] = {
{0x0041, 0x0000}, {0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d},
{0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2},
{0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0040, 0x0000},
{0x00f3, 0x0006}, {0x0058, 0x0000}, {0x0048, 0x0000}, {0x0061, 0x0000},
};
static s32 tbl_length[] = {12, 56, 52, 54, 56, 42, 32, 12};
static u8 *tbl_640[] = {
"\x00\x40\x07\x6a\x06\xf3\x0d\x6a" "\x10\x10\xc1\x01"
,
"\x12\x80\x00\x00\x01\x98\x02\x80" "\x03\x12\x04\x03\x0b\x57\x0e\x61"
"\x0f\x42\x11\x01\x12\x60\x13\x00" "\x14\x3a\x16\x24\x17\x14\x18\x00"
"\x19\x01\x1a\x3d\x1e\x04\x24\x3c" "\x25\x36\x26\x72\x27\x08\x28\x08"
"\x29\x15\x2a\x00\x2b\x00\x2c\x08"
,
"\x32\xff\x33\x00\x34\x3d\x35\x00" "\x36\xfa\x38\x72\x39\x57\x3a\x00"
"\x3b\x0c\x3d\x99\x3e\x0c\x3f\xc1" "\x40\xc0\x41\x00\x42\xc0\x43\x0a"
"\x44\xf0\x45\x46\x46\x62\x47\x2a" "\x48\x3c\x4a\xee\x4b\xe7\x4c\xe7"
"\x4d\xe7\x4e\xe7"
,
"\x4f\x98\x50\x98\x51\x00\x52\x28" "\x53\x70\x54\x98\x58\x1a\x59\x85"
"\x5a\xa9\x5b\x64\x5c\x84\x5d\x53" "\x5e\x0e\x5f\xf0\x60\xf0\x61\xf0"
"\x62\x00\x63\x00\x64\x02\x65\x20" "\x66\x00\x69\x0a\x6b\x5a\x6c\x04"
"\x6d\x55\x6e\x00\x6f\x9d"
,
"\x70\x15\x71\x78\x72\x00\x73\x00" "\x74\x3a\x75\x35\x76\x01\x77\x02"
"\x7a\x24\x7b\x04\x7c\x07\x7d\x10" "\x7e\x28\x7f\x36\x80\x44\x81\x52"
"\x82\x60\x83\x6c\x84\x78\x85\x8c" "\x86\x9e\x87\xbb\x88\xd2\x89\xe5"
"\x8a\x23\x8c\x8d\x90\x7c\x91\x7b"
,
"\x9d\x02\x9e\x02\x9f\x74\xa0\x73" "\xa1\x40\xa4\x50\xa5\x68\xa6\x70"
"\xa8\xc1\xa9\xef\xaa\x92\xab\x04" "\xac\x80\xad\x80\xae\x80\xaf\x80"
"\xb2\xf2\xb3\x20\xb4\x20\xb5\x00" "\xb6\xaf"
,
"\xbb\xae\xbc\x4f\xbd\x4e\xbe\x6a" "\xbf\x68\xc0\xaa\xc1\xc0\xc2\x01"
"\xc3\x4e\xc6\x85\xc7\x81\xc9\xe0" "\xca\xe8\xcb\xf0\xcc\xd8\xcd\x93"
,
"\xd0\x01\xd1\x08\xd2\xe0\xd3\x01" "\xd4\x10\xd5\x80"
};
static u8 *tbl_800[] = {
"\x00\x40\x07\x6a\x06\xf3\x0d\x6a" "\x10\x10\xc1\x01"
,
"\x12\x80\x00\x00\x01\x98\x02\x80" "\x03\x12\x04\x01\x0b\x57\x0e\x61"
"\x0f\x42\x11\x00\x12\x00\x13\x00" "\x14\x3a\x16\x24\x17\x1b\x18\xbb"
"\x19\x01\x1a\x81\x1e\x04\x24\x3c" "\x25\x36\x26\x72\x27\x08\x28\x08"
"\x29\x15\x2a\x00\x2b\x00\x2c\x08"
,
"\x32\xa4\x33\x00\x34\x3d\x35\x00" "\x36\xf8\x38\x72\x39\x57\x3a\x00"
"\x3b\x0c\x3d\x99\x3e\x0c\x3f\xc2" "\x40\xc0\x41\x00\x42\xc0\x43\x0a"
"\x44\xf0\x45\x46\x46\x62\x47\x2a" "\x48\x3c\x4a\xec\x4b\xe8\x4c\xe8"
"\x4d\xe8\x4e\xe8"
,
"\x4f\x98\x50\x98\x51\x00\x52\x28" "\x53\x70\x54\x98\x58\x1a\x59\x85"
"\x5a\xa9\x5b\x64\x5c\x84\x5d\x53" "\x5e\x0e\x5f\xf0\x60\xf0\x61\xf0"
"\x62\x00\x63\x00\x64\x02\x65\x20" "\x66\x00\x69\x02\x6b\x5a\x6c\x04"
"\x6d\x55\x6e\x00\x6f\x9d"
,
"\x70\x08\x71\x78\x72\x00\x73\x01" "\x74\x3a\x75\x35\x76\x01\x77\x02"
"\x7a\x24\x7b\x04\x7c\x07\x7d\x10" "\x7e\x28\x7f\x36\x80\x44\x81\x52"
"\x82\x60\x83\x6c\x84\x78\x85\x8c" "\x86\x9e\x87\xbb\x88\xd2\x89\xe5"
"\x8a\x23\x8c\x0d\x90\x90\x91\x90"
,
"\x9d\x02\x9e\x02\x9f\x94\xa0\x94" "\xa1\x01\xa4\x50\xa5\x68\xa6\x70"
"\xa8\xc1\xa9\xef\xaa\x92\xab\x04" "\xac\x80\xad\x80\xae\x80\xaf\x80"
"\xb2\xf2\xb3\x20\xb4\x20\xb5\x00" "\xb6\xaf"
,
"\xbb\xae\xbc\x38\xbd\x39\xbe\x01" "\xbf\x01\xc0\xe2\xc1\xc0\xc2\x01"
"\xc3\x4e\xc6\x85\xc7\x81\xc9\xe0" "\xca\xe8\xcb\xf0\xcc\xd8\xcd\x93"
,
"\xd0\x21\xd1\x18\xd2\xe0\xd3\x01" "\xd4\x28\xd5\x00"
};
static u8 c04[] = {0x04};
static u8 dat_post_1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02";
static u8 dat_post_2[] = "\x10\x10\xc1\x02";
static u8 dat_post_3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04";
static u8 dat_post_4[] = "\x10\x02\xc1\x06";
static u8 dat_post_5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08";
static u8 dat_post_6[] = "\x10\x10\xc1\x05";
static u8 dat_post_7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08";
static u8 dat_post_8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09";
static struct validx tbl_init_post_alt[] = {
{0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x603c, 0x00ff},
{0x6003, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6001, 0x00ff},
{0x6000, 0x801e},
{0xffff, 0xffff},
{0x6004, 0x001e}, {0x6000, 0x801e},
{0xffff, 0xffff},
{0x6004, 0x001e}, {0x6012, 0x0003}, {0x6000, 0x801e},
{0xffff, 0xffff},
{0x6004, 0x001e}, {0x6000, 0x801e},
{0xffff, 0xffff},
{0x6004, 0x001e}, {0x6012, 0x0003},
{0xffff, 0xffff},
{0x6000, 0x801e},
{0xffff, 0xffff},
{0x6004, 0x001e}, {0x6000, 0x801e},
{0xffff, 0xffff},
{0x6004, 0x001e}, {0x6012, 0x0003}, {0x6000, 0x801e},
{0xffff, 0xffff},
{0x6004, 0x001e}, {0x6000, 0x801e},
{0xffff, 0xffff},
{0x6004, 0x001e}, {0x6012, 0x0003},
{0xffff, 0xffff},
{0x6000, 0x801e},
{0xffff, 0xffff},
{0x6004, 0x001e}, {0x6000, 0x801e},
{0xffff, 0xffff},
{0x6004, 0x001e}, {0x6012, 0x0003},
};
static int ov9655_init_at_startup(struct gspca_dev *gspca_dev);
static int ov9655_configure_alt(struct gspca_dev *gspca_dev);
static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev);
static int ov9655_init_post_alt(struct gspca_dev *gspca_dev);
static void ov9655_post_unset_alt(struct gspca_dev *gspca_dev);
static int ov9655_camera_settings(struct gspca_dev *gspca_dev);
/*==========================================================================*/
void ov9655_init_settings(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->vcur.backlight = 0;
sd->vcur.brightness = 128;
sd->vcur.sharpness = 0;
sd->vcur.contrast = 0;
sd->vcur.gamma = 0;
sd->vcur.hue = 0;
sd->vcur.saturation = 0;
sd->vcur.whitebal = 0;
sd->vmax.backlight = 0;
sd->vmax.brightness = 255;
sd->vmax.sharpness = 0;
sd->vmax.contrast = 0;
sd->vmax.gamma = 0;
sd->vmax.hue = 0 + 1;
sd->vmax.saturation = 0;
sd->vmax.whitebal = 0;
sd->vmax.mirror = 0;
sd->vmax.flip = 0;
sd->vmax.AC50Hz = 0;
sd->dev_camera_settings = ov9655_camera_settings;
sd->dev_init_at_startup = ov9655_init_at_startup;
sd->dev_configure_alt = ov9655_configure_alt;
sd->dev_init_pre_alt = ov9655_init_pre_alt;
sd->dev_post_unset_alt = ov9655_post_unset_alt;
}
/*==========================================================================*/
static int ov9655_init_at_startup(struct gspca_dev *gspca_dev)
{
fetch_validx(gspca_dev, tbl_init_at_startup,
ARRAY_SIZE(tbl_init_at_startup));
fetch_validx(gspca_dev, tbl_commmon, ARRAY_SIZE(tbl_commmon));
/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL);*/
return 0;
}
static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->vold.brightness = -1;
sd->vold.hue = -1;
fetch_validx(gspca_dev, tbl_commmon, ARRAY_SIZE(tbl_commmon));
ov9655_init_post_alt(gspca_dev);
return 0;
}
static int ov9655_init_post_alt(struct gspca_dev *gspca_dev)
{
s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
s32 n; /* reserved for FETCH macros */
s32 i;
u8 **tbl;
ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL);
tbl = (reso == IMAGE_640) ? tbl_640 : tbl_800;
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
tbl_length[0], tbl[0]);
for (i = 1; i < 7; i++)
ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200,
tbl_length[i], tbl[i]);
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200,
tbl_length[7], tbl[7]);
n = fetch_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt));
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1);
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_2);
ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_3);
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_4);
ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_5);
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_6);
ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_7);
ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_8);
ov9655_camera_settings(gspca_dev);
return 0;
}
static int ov9655_configure_alt(struct gspca_dev *gspca_dev)
{
s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
switch (reso) {
case IMAGE_640:
gspca_dev->alt = 1 + 1;
break;
default:
gspca_dev->alt = 1 + 1;
break;
}
return 0;
}
static int ov9655_camera_settings(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 dat_bright[] = "\x04\x00\x10\x7c\xa1\x00\x00\x70";
s32 bright = sd->vcur.brightness;
s32 hue = sd->vcur.hue;
if (bright != sd->vold.brightness) {
sd->vold.brightness = bright;
if (bright < 0 || bright > sd->vmax.brightness)
bright = 0;
dat_bright[3] = bright;
ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_bright);
}
if (hue != sd->vold.hue) {
sd->vold.hue = hue;
sd->swapRB = (hue != 0);
}
return 0;
}
static void ov9655_post_unset_alt(struct gspca_dev *gspca_dev)
{
ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x0061, 0x0000, 0, NULL);
}

View File

@@ -0,0 +1,785 @@
/* @file gl860.c
* @date 2009-08-27
*
* Genesys Logic webcam with gl860 subdrivers
*
* Driver by Olivier Lorin <o.lorin@laposte.net>
* GSPCA by Jean-Francois Moine <http://moinejf.free.fr>
* Thanks BUGabundo and Malmostoso for your amazing help!
*
* 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
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "gspca.h"
#include "gl860.h"
MODULE_AUTHOR("Olivier Lorin <lorin@laposte.net>");
MODULE_DESCRIPTION("GSPCA/Genesys Logic GL860 USB Camera Driver");
MODULE_LICENSE("GPL");
/*======================== static function declarations ====================*/
static void (*dev_init_settings)(struct gspca_dev *gspca_dev);
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id);
static int sd_init(struct gspca_dev *gspca_dev);
static int sd_isoc_init(struct gspca_dev *gspca_dev);
static int sd_start(struct gspca_dev *gspca_dev);
static void sd_stop0(struct gspca_dev *gspca_dev);
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
struct gspca_frame *frame, u8 *data, s32 len);
static void sd_callback(struct gspca_dev *gspca_dev);
static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
s32 vendor_id, s32 product_id);
/*============================ driver options ==============================*/
static s32 AC50Hz = 0xff;
module_param(AC50Hz, int, 0644);
MODULE_PARM_DESC(AC50Hz, " Does AC power frequency is 50Hz? (0/1)");
static char sensor[7];
module_param_string(sensor, sensor, sizeof(sensor), 0644);
MODULE_PARM_DESC(sensor,
" Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640'/'')");
/*============================ webcam controls =============================*/
/* Functions to get and set a control value */
#define SD_SETGET(thename) \
static int sd_set_##thename(struct gspca_dev *gspca_dev, s32 val)\
{\
struct sd *sd = (struct sd *) gspca_dev;\
\
sd->vcur.thename = val;\
if (gspca_dev->streaming)\
sd->dev_camera_settings(gspca_dev);\
return 0;\
} \
static int sd_get_##thename(struct gspca_dev *gspca_dev, s32 *val)\
{\
struct sd *sd = (struct sd *) gspca_dev;\
\
*val = sd->vcur.thename;\
return 0;\
}
SD_SETGET(mirror)
SD_SETGET(flip)
SD_SETGET(AC50Hz)
SD_SETGET(backlight)
SD_SETGET(brightness)
SD_SETGET(gamma)
SD_SETGET(hue)
SD_SETGET(saturation)
SD_SETGET(sharpness)
SD_SETGET(whitebal)
SD_SETGET(contrast)
#define GL860_NCTRLS 11
/* control table */
static struct ctrl sd_ctrls_mi1320[GL860_NCTRLS];
static struct ctrl sd_ctrls_mi2020[GL860_NCTRLS];
static struct ctrl sd_ctrls_mi2020b[GL860_NCTRLS];
static struct ctrl sd_ctrls_ov2640[GL860_NCTRLS];
static struct ctrl sd_ctrls_ov9655[GL860_NCTRLS];
#define SET_MY_CTRL(theid, \
thetype, thelabel, thename) \
if (sd->vmax.thename != 0) {\
sd_ctrls[nCtrls].qctrl.id = theid;\
sd_ctrls[nCtrls].qctrl.type = thetype;\
strcpy(sd_ctrls[nCtrls].qctrl.name, thelabel);\
sd_ctrls[nCtrls].qctrl.minimum = 0;\
sd_ctrls[nCtrls].qctrl.maximum = sd->vmax.thename;\
sd_ctrls[nCtrls].qctrl.default_value = sd->vcur.thename;\
sd_ctrls[nCtrls].qctrl.step = \
(sd->vmax.thename < 16) ? 1 : sd->vmax.thename/16;\
sd_ctrls[nCtrls].set = sd_set_##thename;\
sd_ctrls[nCtrls].get = sd_get_##thename;\
nCtrls++;\
}
static int gl860_build_control_table(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct ctrl *sd_ctrls;
int nCtrls = 0;
if (_MI1320_)
sd_ctrls = sd_ctrls_mi1320;
else if (_MI2020_)
sd_ctrls = sd_ctrls_mi2020;
else if (_MI2020b_)
sd_ctrls = sd_ctrls_mi2020b;
else if (_OV2640_)
sd_ctrls = sd_ctrls_ov2640;
else if (_OV9655_)
sd_ctrls = sd_ctrls_ov9655;
else
return 0;
memset(sd_ctrls, 0, GL860_NCTRLS * sizeof(struct ctrl));
SET_MY_CTRL(V4L2_CID_BRIGHTNESS,
V4L2_CTRL_TYPE_INTEGER, "Brightness", brightness)
SET_MY_CTRL(V4L2_CID_SHARPNESS,
V4L2_CTRL_TYPE_INTEGER, "Sharpness", sharpness)
SET_MY_CTRL(V4L2_CID_CONTRAST,
V4L2_CTRL_TYPE_INTEGER, "Contrast", contrast)
SET_MY_CTRL(V4L2_CID_GAMMA,
V4L2_CTRL_TYPE_INTEGER, "Gamma", gamma)
SET_MY_CTRL(V4L2_CID_HUE,
V4L2_CTRL_TYPE_INTEGER, "Palette", hue)
SET_MY_CTRL(V4L2_CID_SATURATION,
V4L2_CTRL_TYPE_INTEGER, "Saturation", saturation)
SET_MY_CTRL(V4L2_CID_WHITE_BALANCE_TEMPERATURE,
V4L2_CTRL_TYPE_INTEGER, "White Bal.", whitebal)
SET_MY_CTRL(V4L2_CID_BACKLIGHT_COMPENSATION,
V4L2_CTRL_TYPE_INTEGER, "Backlight" , backlight)
SET_MY_CTRL(V4L2_CID_HFLIP,
V4L2_CTRL_TYPE_BOOLEAN, "Mirror", mirror)
SET_MY_CTRL(V4L2_CID_VFLIP,
V4L2_CTRL_TYPE_BOOLEAN, "Flip", flip)
SET_MY_CTRL(V4L2_CID_POWER_LINE_FREQUENCY,
V4L2_CTRL_TYPE_BOOLEAN, "50Hz", AC50Hz)
return nCtrls;
}
/*==================== sud-driver structure initialisation =================*/
static struct sd_desc sd_desc_mi1320 = {
.name = MODULE_NAME,
.ctrls = sd_ctrls_mi1320,
.nctrls = GL860_NCTRLS,
.config = sd_config,
.init = sd_init,
.isoc_init = sd_isoc_init,
.start = sd_start,
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
.dq_callback = sd_callback,
};
static struct sd_desc sd_desc_mi2020 = {
.name = MODULE_NAME,
.ctrls = sd_ctrls_mi2020,
.nctrls = GL860_NCTRLS,
.config = sd_config,
.init = sd_init,
.isoc_init = sd_isoc_init,
.start = sd_start,
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
.dq_callback = sd_callback,
};
static struct sd_desc sd_desc_mi2020b = {
.name = MODULE_NAME,
.ctrls = sd_ctrls_mi2020b,
.nctrls = GL860_NCTRLS,
.config = sd_config,
.init = sd_init,
.isoc_init = sd_isoc_init,
.start = sd_start,
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
.dq_callback = sd_callback,
};
static struct sd_desc sd_desc_ov2640 = {
.name = MODULE_NAME,
.ctrls = sd_ctrls_ov2640,
.nctrls = GL860_NCTRLS,
.config = sd_config,
.init = sd_init,
.isoc_init = sd_isoc_init,
.start = sd_start,
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
.dq_callback = sd_callback,
};
static struct sd_desc sd_desc_ov9655 = {
.name = MODULE_NAME,
.ctrls = sd_ctrls_ov9655,
.nctrls = GL860_NCTRLS,
.config = sd_config,
.init = sd_init,
.isoc_init = sd_isoc_init,
.start = sd_start,
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
.dq_callback = sd_callback,
};
/*=========================== sub-driver image sizes =======================*/
static struct v4l2_pix_format mi2020_mode[] = {
{ 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 640,
.sizeimage = 640 * 480,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 0
},
{ 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 800,
.sizeimage = 800 * 600,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 1
},
{1280, 1024, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 1280,
.sizeimage = 1280 * 1024,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 2
},
{1600, 1200, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 1600,
.sizeimage = 1600 * 1200,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 3
},
};
static struct v4l2_pix_format ov2640_mode[] = {
{ 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 640,
.sizeimage = 640 * 480,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 0
},
{ 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 800,
.sizeimage = 800 * 600,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 1
},
{1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 1280,
.sizeimage = 1280 * 960,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 2
},
{1600, 1200, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 1600,
.sizeimage = 1600 * 1200,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 3
},
};
static struct v4l2_pix_format mi1320_mode[] = {
{ 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 640,
.sizeimage = 640 * 480,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 0
},
{ 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 800,
.sizeimage = 800 * 600,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 1
},
{1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 1280,
.sizeimage = 1280 * 960,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 2
},
};
static struct v4l2_pix_format ov9655_mode[] = {
{ 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 640,
.sizeimage = 640 * 480,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 0
},
{1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
.bytesperline = 1280,
.sizeimage = 1280 * 960,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 1
},
};
/*========================= sud-driver functions ===========================*/
/* This function is called at probe time */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
s32 vendor_id, product_id;
/* Get USB VendorID and ProductID */
vendor_id = le16_to_cpu(id->idVendor);
product_id = le16_to_cpu(id->idProduct);
sd->nbRightUp = 1;
sd->nbIm = -1;
sd->sensor = 0xff;
if (strcmp(sensor, "MI1320") == 0)
sd->sensor = ID_MI1320;
else if (strcmp(sensor, "OV2640") == 0)
sd->sensor = ID_OV2640;
else if (strcmp(sensor, "OV9655") == 0)
sd->sensor = ID_OV9655;
else if (strcmp(sensor, "MI2020") == 0)
sd->sensor = ID_MI2020;
else if (strcmp(sensor, "MI2020b") == 0)
sd->sensor = ID_MI2020b;
/* Get sensor and set the suitable init/start/../stop functions */
if (gl860_guess_sensor(gspca_dev, vendor_id, product_id) == -1)
return -1;
cam = &gspca_dev->cam;
gspca_dev->nbalt = 4;
switch (sd->sensor) {
case ID_MI1320:
gspca_dev->sd_desc = &sd_desc_mi1320;
cam->cam_mode = mi1320_mode;
cam->nmodes = ARRAY_SIZE(mi1320_mode);
dev_init_settings = mi1320_init_settings;
break;
case ID_MI2020:
gspca_dev->sd_desc = &sd_desc_mi2020;
cam->cam_mode = mi2020_mode;
cam->nmodes = ARRAY_SIZE(mi2020_mode);
dev_init_settings = mi2020_init_settings;
break;
case ID_MI2020b:
gspca_dev->sd_desc = &sd_desc_mi2020b;
cam->cam_mode = mi2020_mode;
cam->nmodes = ARRAY_SIZE(mi2020_mode);
dev_init_settings = mi2020_init_settings;
break;
case ID_OV2640:
gspca_dev->sd_desc = &sd_desc_ov2640;
cam->cam_mode = ov2640_mode;
cam->nmodes = ARRAY_SIZE(ov2640_mode);
dev_init_settings = ov2640_init_settings;
break;
case ID_OV9655:
gspca_dev->sd_desc = &sd_desc_ov9655;
cam->cam_mode = ov9655_mode;
cam->nmodes = ARRAY_SIZE(ov9655_mode);
dev_init_settings = ov9655_init_settings;
break;
}
dev_init_settings(gspca_dev);
if (AC50Hz != 0xff)
((struct sd *) gspca_dev)->vcur.AC50Hz = AC50Hz;
gl860_build_control_table(gspca_dev);
return 0;
}
/* This function is called at probe time after sd_config */
static int sd_init(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
return sd->dev_init_at_startup(gspca_dev);
}
/* This function is called before to choose the alt setting */
static int sd_isoc_init(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
return sd->dev_configure_alt(gspca_dev);
}
/* This function is called to start the webcam */
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
return sd->dev_init_pre_alt(gspca_dev);
}
/* This function is called to stop the webcam */
static void sd_stop0(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
return sd->dev_post_unset_alt(gspca_dev);
}
/* This function is called when an image is being received */
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
struct gspca_frame *frame, u8 *data, s32 len)
{
struct sd *sd = (struct sd *) gspca_dev;
static s32 nSkipped;
s32 mode = (s32) gspca_dev->curr_mode;
s32 nToSkip =
sd->swapRB * (gspca_dev->cam.cam_mode[mode].bytesperline + 1);
/* Test only against 0202h, so endianess does not matter */
switch (*(s16 *) data) {
case 0x0202: /* End of frame, start a new one */
frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0);
nSkipped = 0;
if (sd->nbIm >= 0 && sd->nbIm < 10)
sd->nbIm++;
gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0);
break;
default:
data += 2;
len -= 2;
if (nSkipped + len <= nToSkip)
nSkipped += len;
else {
if (nSkipped < nToSkip && nSkipped + len > nToSkip) {
data += nToSkip - nSkipped;
len -= nToSkip - nSkipped;
nSkipped = nToSkip + 1;
}
gspca_frame_add(gspca_dev,
INTER_PACKET, frame, data, len);
}
break;
}
}
/* This function is called when an image has been read */
/* This function is used to monitor webcam orientation */
static void sd_callback(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
if (!_OV9655_) {
u8 state;
u8 upsideDown;
/* Probe sensor orientation */
ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, (void *)&state);
/* C8/40 means upside-down (looking backwards) */
/* D8/50 means right-up (looking onwards) */
upsideDown = (state == 0xc8 || state == 0x40);
if (upsideDown && sd->nbRightUp > -4) {
if (sd->nbRightUp > 0)
sd->nbRightUp = 0;
if (sd->nbRightUp == -3) {
sd->mirrorMask = 1;
sd->waitSet = 1;
}
sd->nbRightUp--;
}
if (!upsideDown && sd->nbRightUp < 4) {
if (sd->nbRightUp < 0)
sd->nbRightUp = 0;
if (sd->nbRightUp == 3) {
sd->mirrorMask = 0;
sd->waitSet = 1;
}
sd->nbRightUp++;
}
}
if (sd->waitSet)
sd->dev_camera_settings(gspca_dev);
}
/*=================== USB driver structure initialisation ==================*/
static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x05e3, 0x0503)},
{USB_DEVICE(0x05e3, 0xf191)},
{}
};
MODULE_DEVICE_TABLE(usb, device_table);
static int sd_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct gspca_dev *gspca_dev;
s32 ret;
ret = gspca_dev_probe(intf, id,
&sd_desc_mi1320, sizeof(struct sd), THIS_MODULE);
if (ret >= 0) {
gspca_dev = usb_get_intfdata(intf);
PDEBUG(D_PROBE,
"Camera is now controlling video device /dev/video%d",
gspca_dev->vdev.minor);
}
return ret;
}
static void sd_disconnect(struct usb_interface *intf)
{
gspca_disconnect(intf);
}
static struct usb_driver sd_driver = {
.name = MODULE_NAME,
.id_table = device_table,
.probe = sd_probe,
.disconnect = sd_disconnect,
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
#endif
};
/*====================== Init and Exit module functions ====================*/
static int __init sd_mod_init(void)
{
PDEBUG(D_PROBE, "driver startup - version %s", DRIVER_VERSION);
if (usb_register(&sd_driver) < 0)
return -1;
PDEBUG(D_PROBE, "driver registered");
return 0;
}
static void __exit sd_mod_exit(void)
{
usb_deregister(&sd_driver);
PDEBUG(D_PROBE, "driver deregistered");
}
module_init(sd_mod_init);
module_exit(sd_mod_exit);
/*==========================================================================*/
int gl860_RTx(struct gspca_dev *gspca_dev,
unsigned char pref, u32 req, u16 val, u16 index,
s32 len, void *pdata)
{
struct usb_device *udev = gspca_dev->dev;
s32 r = 0;
if (pref == 0x40) { /* Send */
if (len > 0) {
memcpy(gspca_dev->usb_buf, pdata, len);
r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
req, pref, val, index,
gspca_dev->usb_buf,
len, 400 + 200 * (len > 1));
} else {
r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
req, pref, val, index, NULL, len, 400);
}
} else { /* Receive */
if (len > 0) {
r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
req, pref, val, index,
gspca_dev->usb_buf,
len, 400 + 200 * (len > 1));
memcpy(pdata, gspca_dev->usb_buf, len);
} else {
r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
req, pref, val, index, NULL, len, 400);
}
}
if (r < 0)
PDEBUG(D_ERR,
"ctrl transfer failed %4d "
"[p%02x r%d v%04x i%04x len%d]",
r, pref, req, val, index, len);
else if (len > 1 && r < len)
PDEBUG(D_ERR, "short ctrl transfer %d/%d", r, len);
if ((_MI2020_ || _MI2020b_ || _MI2020c_) && (val || index))
msleep(1);
if (_OV2640_)
msleep(1);
return r;
}
int fetch_validx(struct gspca_dev *gspca_dev, struct validx *tbl, int len)
{
int n;
for (n = 0; n < len; n++) {
if (tbl[n].idx != 0xffff)
ctrl_out(gspca_dev, 0x40, 1, tbl[n].val,
tbl[n].idx, 0, NULL);
else if (tbl[n].val == 0xffff)
break;
else
msleep(tbl[n].val);
}
return n;
}
int keep_on_fetching_validx(struct gspca_dev *gspca_dev, struct validx *tbl,
int len, int n)
{
while (++n < len) {
if (tbl[n].idx != 0xffff)
ctrl_out(gspca_dev, 0x40, 1, tbl[n].val, tbl[n].idx,
0, NULL);
else if (tbl[n].val == 0xffff)
break;
else
msleep(tbl[n].val);
}
return n;
}
void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len)
{
int n;
for (n = 0; n < len; n++) {
if (memcmp(tbl[n].data, "\xff\xff\xff", 3) != 0)
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, tbl[n].idx,
3, tbl[n].data);
else
msleep(tbl[n].idx);
}
}
static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
s32 vendor_id, s32 product_id)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 probe, nb26, nb96, nOV, ntry;
if (product_id == 0xf191)
sd->sensor = ID_MI1320;
if (sd->sensor == 0xff) {
ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe);
ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe);
ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x0000, 0, NULL);
msleep(3);
ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL);
msleep(3);
ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x00c0, 0, NULL);
msleep(3);
ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c1, 0, NULL);
msleep(3);
ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c2, 0, NULL);
msleep(3);
ctrl_out(gspca_dev, 0x40, 1, 0x0020, 0x0006, 0, NULL);
msleep(3);
ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL);
msleep(56);
nOV = 0;
for (ntry = 0; ntry < 4; ntry++) {
ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL);
msleep(3);
ctrl_out(gspca_dev, 0x40, 1, 0x0063, 0x0006, 0, NULL);
msleep(3);
ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL);
msleep(10);
ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &probe);
PDEBUG(D_PROBE, "1st probe=%02x", probe);
if (probe == 0xff)
nOV++;
}
if (nOV) {
PDEBUG(D_PROBE, "0xff -> sensor OVXXXX");
PDEBUG(D_PROBE, "Probing for sensor OV2640 or OV9655");
nb26 = nb96 = 0;
for (ntry = 0; ntry < 4; ntry++) {
ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000,
0, NULL);
msleep(3);
ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x800a,
0, NULL);
msleep(10);
/* Wait for 26(OV2640) or 96(OV9655) */
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x800a,
1, &probe);
PDEBUG(D_PROBE, "2nd probe=%02x", probe);
if (probe == 0x00)
nb26++;
if (probe == 0x26 || probe == 0x40) {
sd->sensor = ID_OV2640;
nb26 += 4;
break;
}
if (probe == 0x96 || probe == 0x55) {
sd->sensor = ID_OV9655;
nb96 += 4;
break;
}
if (probe == 0xff)
nb96++;
msleep(3);
}
if (nb26 < 4 && nb96 < 4) {
PDEBUG(D_PROBE, "No relevant answer ");
PDEBUG(D_PROBE, "* 1.3Mpixels -> use OV9655");
PDEBUG(D_PROBE, "* 2.0Mpixels -> use OV2640");
PDEBUG(D_PROBE,
"To force a sensor, add that line to "
"/etc/modprobe.d/options.conf:");
PDEBUG(D_PROBE, "options gspca_gl860 "
"sensor=\"OV2640\" or \"OV9655\"");
return -1;
}
} else { /* probe = 0 */
PDEBUG(D_PROBE, "No 0xff -> sensor MI2020");
sd->sensor = ID_MI2020;
}
}
if (_MI1320_) {
PDEBUG(D_PROBE, "05e3:f191 sensor MI1320 (1.3M)");
} else if (_MI2020_) {
PDEBUG(D_PROBE, "05e3:0503 sensor MI2020 (2.0M)");
} else if (_MI2020b_) {
PDEBUG(D_PROBE, "05e3:0503 sensor MI2020 alt. driver (2.0M)");
} else if (_OV9655_) {
PDEBUG(D_PROBE, "05e3:0503 sensor OV9655 (1.3M)");
} else if (_OV2640_) {
PDEBUG(D_PROBE, "05e3:0503 sensor OV2640 (2.0M)");
} else {
PDEBUG(D_PROBE, "***** Unknown sensor *****");
return -1;
}
return 0;
}

View File

@@ -0,0 +1,108 @@
/* @file gl860.h
* @author Olivier LORIN, tiré du pilote Syntek par Nicolas VIVIEN
* @date 2009-08-27
*
* 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
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef GL860_DEV_H
#define GL860_DEV_H
#include <linux/version.h>
#include "gspca.h"
#define MODULE_NAME "gspca_gl860"
#define DRIVER_VERSION "0.9d10"
#define ctrl_in gl860_RTx
#define ctrl_out gl860_RTx
#define ID_MI1320 1
#define ID_OV2640 2
#define ID_OV9655 4
#define ID_MI2020 8
#define ID_MI2020b 16
#define _MI1320_ (((struct sd *) gspca_dev)->sensor == ID_MI1320)
#define _MI2020_ (((struct sd *) gspca_dev)->sensor == ID_MI2020)
#define _MI2020b_ (((struct sd *) gspca_dev)->sensor == ID_MI2020b)
#define _MI2020c_ 0
#define _OV2640_ (((struct sd *) gspca_dev)->sensor == ID_OV2640)
#define _OV9655_ (((struct sd *) gspca_dev)->sensor == ID_OV9655)
#define IMAGE_640 0
#define IMAGE_800 1
#define IMAGE_1280 2
#define IMAGE_1600 3
struct sd_gl860 {
u16 backlight;
u16 brightness;
u16 sharpness;
u16 contrast;
u16 gamma;
u16 hue;
u16 saturation;
u16 whitebal;
u8 mirror;
u8 flip;
u8 AC50Hz;
};
/* Specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
struct sd_gl860 vcur;
struct sd_gl860 vold;
struct sd_gl860 vmax;
int (*dev_configure_alt) (struct gspca_dev *);
int (*dev_init_at_startup)(struct gspca_dev *);
int (*dev_init_pre_alt) (struct gspca_dev *);
void (*dev_post_unset_alt) (struct gspca_dev *);
int (*dev_camera_settings)(struct gspca_dev *);
u8 swapRB;
u8 mirrorMask;
u8 sensor;
s32 nbIm;
s32 nbRightUp;
u8 waitSet;
};
struct validx {
u16 val;
u16 idx;
};
struct idxdata {
u8 idx;
u8 data[3];
};
int fetch_validx(struct gspca_dev *gspca_dev, struct validx *tbl, int len);
int keep_on_fetching_validx(struct gspca_dev *gspca_dev, struct validx *tbl,
int len, int n);
void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len);
int gl860_RTx(struct gspca_dev *gspca_dev,
unsigned char pref, u32 req, u16 val, u16 index,
s32 len, void *pdata);
void mi1320_init_settings(struct gspca_dev *);
void ov2640_init_settings(struct gspca_dev *);
void ov9655_init_settings(struct gspca_dev *);
void mi2020_init_settings(struct gspca_dev *);
#endif

View File

@@ -312,6 +312,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* create the JPEG header */
dev->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
if (dev->jpeg_hdr == NULL)
return -ENOMEM;
jpeg_define(dev->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x21); /* JPEG 422 */
jpeg_set_qual(dev->jpeg_hdr, dev->quality);

View File

@@ -20,6 +20,18 @@
static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val);
static int ov7660_get_auto_white_balance(struct gspca_dev *gspca_dev,
__s32 *val);
static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev,
__s32 val);
static int ov7660_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val);
static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val);
static int ov7660_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val);
static int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev, __s32 val);
static int ov7660_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
const static struct ctrl ov7660_ctrls[] = {
#define GAIN_IDX 1
@@ -37,6 +49,79 @@ const static struct ctrl ov7660_ctrls[] = {
.set = ov7660_set_gain,
.get = ov7660_get_gain
},
#define BLUE_BALANCE_IDX 2
#define RED_BALANCE_IDX 3
#define AUTO_WHITE_BALANCE_IDX 4
{
{
.id = V4L2_CID_AUTO_WHITE_BALANCE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "auto white balance",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1
},
.set = ov7660_set_auto_white_balance,
.get = ov7660_get_auto_white_balance
},
#define AUTO_GAIN_CTRL_IDX 5
{
{
.id = V4L2_CID_AUTOGAIN,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "auto gain control",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1
},
.set = ov7660_set_auto_gain,
.get = ov7660_get_auto_gain
},
#define AUTO_EXPOSURE_IDX 6
{
{
.id = V4L2_CID_EXPOSURE_AUTO,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "auto exposure",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1
},
.set = ov7660_set_auto_exposure,
.get = ov7660_get_auto_exposure
},
#define HFLIP_IDX 7
{
{
.id = V4L2_CID_HFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "horizontal flip",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0
},
.set = ov7660_set_hflip,
.get = ov7660_get_hflip
},
#define VFLIP_IDX 8
{
{
.id = V4L2_CID_VFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "vertical flip",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0
},
.set = ov7660_set_vflip,
.get = ov7660_get_vflip
},
};
static struct v4l2_pix_format ov7660_modes[] = {
@@ -137,7 +222,7 @@ int ov7660_init(struct sd *sd)
} else {
data[0] = init_ov7660[i][2];
err = m5602_write_sensor(sd,
init_ov7660[i][1], data, 1);
init_ov7660[i][1], data, 1);
}
}
@@ -148,6 +233,28 @@ int ov7660_init(struct sd *sd)
if (err < 0)
return err;
err = ov7660_set_auto_white_balance(&sd->gspca_dev,
sensor_settings[AUTO_WHITE_BALANCE_IDX]);
if (err < 0)
return err;
err = ov7660_set_auto_gain(&sd->gspca_dev,
sensor_settings[AUTO_GAIN_CTRL_IDX]);
if (err < 0)
return err;
err = ov7660_set_auto_exposure(&sd->gspca_dev,
sensor_settings[AUTO_EXPOSURE_IDX]);
if (err < 0)
return err;
err = ov7660_set_hflip(&sd->gspca_dev,
sensor_settings[HFLIP_IDX]);
if (err < 0)
return err;
err = ov7660_set_vflip(&sd->gspca_dev,
sensor_settings[VFLIP_IDX]);
return err;
}
@@ -194,6 +301,159 @@ static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val)
return err;
}
static int ov7660_get_auto_white_balance(struct gspca_dev *gspca_dev,
__s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[AUTO_WHITE_BALANCE_IDX];
return 0;
}
static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev,
__s32 val)
{
int err;
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set auto white balance to %d", val);
sensor_settings[AUTO_WHITE_BALANCE_IDX] = val;
err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1);
if (err < 0)
return err;
i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1));
err = m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1);
return err;
}
static int ov7660_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[AUTO_GAIN_CTRL_IDX];
PDEBUG(D_V4L2, "Read auto gain control %d", *val);
return 0;
}
static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set auto gain control to %d", val);
sensor_settings[AUTO_GAIN_CTRL_IDX] = val;
err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1);
if (err < 0)
return err;
i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2));
return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1);
}
static int ov7660_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[AUTO_EXPOSURE_IDX];
PDEBUG(D_V4L2, "Read auto exposure control %d", *val);
return 0;
}
static int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev,
__s32 val)
{
int err;
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set auto exposure control to %d", val);
sensor_settings[AUTO_EXPOSURE_IDX] = val;
err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1);
if (err < 0)
return err;
i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0));
return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1);
}
static int ov7660_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[HFLIP_IDX];
PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
return 0;
}
static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
sensor_settings[HFLIP_IDX] = val;
i2c_data = ((val & 0x01) << 5) |
(sensor_settings[VFLIP_IDX] << 4);
err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1);
return err;
}
static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
*val = sensor_settings[VFLIP_IDX];
PDEBUG(D_V4L2, "Read vertical flip %d", *val);
return 0;
}
static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
u8 i2c_data;
struct sd *sd = (struct sd *) gspca_dev;
s32 *sensor_settings = sd->sensor_priv;
PDEBUG(D_V4L2, "Set vertical flip to %d", val);
sensor_settings[VFLIP_IDX] = val;
i2c_data = ((val & 0x01) << 4) | (sensor_settings[VFLIP_IDX] << 5);
err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1);
if (err < 0)
return err;
/* When vflip is toggled we need to readjust the bridge hsync/vsync */
if (gspca_dev->streaming)
err = ov7660_start(sd);
return err;
}
static void ov7660_dump_registers(struct sd *sd)
{
int address;

View File

@@ -66,23 +66,23 @@
#define OV7660_RBIAS 0x2c
#define OV7660_HREF 0x32
#define OV7660_ADC 0x37
#define OV7660_OFON 0x39
#define OV7660_TSLB 0x3a
#define OV7660_COM12 0x3c
#define OV7660_COM13 0x3d
#define OV7660_OFON 0x39
#define OV7660_TSLB 0x3a
#define OV7660_COM12 0x3c
#define OV7660_COM13 0x3d
#define OV7660_LCC1 0x62
#define OV7660_LCC2 0x63
#define OV7660_LCC3 0x64
#define OV7660_LCC4 0x65
#define OV7660_LCC5 0x66
#define OV7660_HV 0x69
#define OV7660_RSVDA1 0xa1
#define OV7660_HV 0x69
#define OV7660_RSVDA1 0xa1
#define OV7660_DEFAULT_GAIN 0x0e
#define OV7660_DEFAULT_RED_GAIN 0x80
#define OV7660_DEFAULT_RED_GAIN 0x80
#define OV7660_DEFAULT_BLUE_GAIN 0x80
#define OV7660_DEFAULT_SATURATION 0x00
#define OV7660_DEFAULT_EXPOSURE 0x20
#define OV7660_DEFAULT_EXPOSURE 0x20
/* Kernel module parameters */
extern int force_sensor;
@@ -149,22 +149,8 @@ static const unsigned char init_ov7660[][4] =
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR, 0x03},
{BRIDGE, M5602_XB_GPIO_DIR, 0x03},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
{SENSOR, OV7660_OFON, 0x0c},
{SENSOR, OV7660_COM2, 0x11},
{SENSOR, OV7660_COM7, 0x05},
{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
@@ -173,34 +159,8 @@ static const unsigned char init_ov7660[][4] =
{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x02},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{SENSOR, OV7660_AECH, OV7660_DEFAULT_EXPOSURE},
{SENSOR, OV7660_COM1, 0x00},
{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
{SENSOR, OV7660_COM7, 0x80},
{SENSOR, OV7660_CLKRC, 0x80},
{SENSOR, OV7660_BLUE_GAIN, 0x80},
{SENSOR, OV7660_RED_GAIN, 0x80},
{SENSOR, OV7660_COM9, 0x4c},
{SENSOR, OV7660_OFON, 0x43},
{SENSOR, OV7660_COM12, 0x28},
@@ -212,17 +172,17 @@ static const unsigned char init_ov7660[][4] =
{SENSOR, OV7660_PSHFT, 0x0b},
{SENSOR, OV7660_VSTART, 0x01},
{SENSOR, OV7660_VSTOP, 0x7a},
{SENSOR, OV7660_VREF, 0x00},
{SENSOR, OV7660_VSTOP, 0x00},
{SENSOR, OV7660_COM7, 0x05},
{SENSOR, OV7660_COM6, 0x4b},
{SENSOR, OV7660_BBIAS, 0x98},
{SENSOR, OV7660_GbBIAS, 0x98},
{SENSOR, OV7660_RSVD29, 0x98},
{SENSOR, OV7660_RBIAS, 0x98},
{SENSOR, OV7660_COM6, 0x42},
{SENSOR, OV7660_BBIAS, 0x94},
{SENSOR, OV7660_GbBIAS, 0x94},
{SENSOR, OV7660_RSVD29, 0x94},
{SENSOR, OV7660_RBIAS, 0x94},
{SENSOR, OV7660_COM1, 0x00},
{SENSOR, OV7660_AECH, 0x00},
{SENSOR, OV7660_AECHH, 0x00},
{SENSOR, OV7660_ADC, 0x04},
{SENSOR, OV7660_ADC, 0x05},
{SENSOR, OV7660_COM13, 0x00},
{SENSOR, OV7660_RSVDA1, 0x23},
{SENSOR, OV7660_TSLB, 0x0d},
@@ -233,6 +193,47 @@ static const unsigned char init_ov7660[][4] =
{SENSOR, OV7660_LCC4, 0x40},
{SENSOR, OV7660_LCC5, 0x01},
{SENSOR, OV7660_AECH, 0x20},
{SENSOR, OV7660_COM1, 0x00},
{SENSOR, OV7660_OFON, 0x0c},
{SENSOR, OV7660_COM2, 0x11},
{SENSOR, OV7660_COM7, 0x05},
{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
{SENSOR, OV7660_AECH, 0x5f},
{SENSOR, OV7660_COM1, 0x03},
{SENSOR, OV7660_OFON, 0x0c},
{SENSOR, OV7660_COM2, 0x11},
{SENSOR, OV7660_COM7, 0x05},
{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
@@ -245,35 +246,18 @@ static const unsigned char init_ov7660[][4] =
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
{BRIDGE, M5602_XB_VSYNC_PARA, 0xe0}, /* 480 */
{BRIDGE, M5602_XB_VSYNC_PARA, 0xec},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x00},
{BRIDGE, M5602_XB_SIG_INI, 0x02},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
{BRIDGE, M5602_XB_VSYNC_PARA, 0x27}, /* 39 */
{BRIDGE, M5602_XB_VSYNC_PARA, 0x02},
{BRIDGE, M5602_XB_VSYNC_PARA, 0xa7}, /* 679 */
{BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x27},
{BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
{BRIDGE, M5602_XB_HSYNC_PARA, 0xa7},
{BRIDGE, M5602_XB_SIG_INI, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
{SENSOR, OV7660_AECH, 0x20},
{SENSOR, OV7660_COM1, 0x00},
{SENSOR, OV7660_OFON, 0x0c},
{SENSOR, OV7660_COM2, 0x11},
{SENSOR, OV7660_COM7, 0x05},
{SENSOR, OV7660_BLUE_GAIN, 0x80},
{SENSOR, OV7660_RED_GAIN, 0x80},
{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}
};
#endif

View File

@@ -46,6 +46,12 @@ static
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550")
}
}, {
.ident = "Fujitsu-Siemens Amilo Pa 2548",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 2548")
}
}, {
.ident = "MSI GX700",
.matches = {
@@ -53,6 +59,13 @@ static
DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
DMI_MATCH(DMI_BIOS_DATE, "07/26/2007")
}
}, {
.ident = "MSI GX700",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
DMI_MATCH(DMI_BIOS_DATE, "07/19/2007")
}
}, {
.ident = "MSI GX700/GX705/EX700",
.matches = {

Some files were not shown because too many files have changed in this diff Show More