// SPDX-License-Identifier: GPL-2.0-only /* * Driver for mt2063 Micronas tuner * * Copyright (c) 2011 Mauro Carvalho Chehab * * This driver came from a driver originally written by: * Henry Wang <Henry.wang@AzureWave.com> * Made publicly available by Terratec, at: * http://linux.terratec.de/files/TERRATEC_H7/20110323_TERRATEC_H7_Linux.tar.gz
*/
#define dprintk(level, fmt, arg...) do { \ if (debug >= level) \
printk(KERN_DEBUG "mt2063 %s: " fmt, __func__, ## arg); \
} while (0)
/* positive error codes used internally */
/* Info: Unavoidable LO-related spur may be present in the output */ #define MT2063_SPUR_PRESENT_ERR (0x00800000)
/* Info: Mask of bits used for # of LO-related spurs that were avoided during tuning */ #define MT2063_SPUR_CNT_MASK (0x001f0000) #define MT2063_SPUR_SHIFT (16)
/* Info: Upconverter frequency is out of range (may be reason for MT_UPC_UNLOCK) */ #define MT2063_UPC_RANGE (0x04000000)
/* Info: Downconverter frequency is out of range (may be reason for MT_DPC_UNLOCK) */ #define MT2063_DNC_RANGE (0x08000000)
/* * Constant defining the version of the following structure * and therefore the API for this code. * * When compiling the tuner driver, the preprocessor will * check against this version number to make sure that * it matches the version that the tuner driver knows about.
*/
/* DECT Frequency Avoidance */ #define MT2063_DECT_AVOID_US_FREQS 0x00000001
/* * mt2063_write - Write data into the I2C bus
*/ staticint mt2063_write(struct mt2063_state *state, u8 reg, u8 *data, u32 len)
{ struct dvb_frontend *fe = state->frontend; int ret;
u8 buf[60]; struct i2c_msg msg = {
.addr = state->config->tuner_address,
.flags = 0,
.buf = buf,
.len = len + 1
};
dprintk(2, "\n");
msg.buf[0] = reg;
memcpy(msg.buf + 1, data, len);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
ret = i2c_transfer(state->i2c, &msg, 1); if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
if (ret < 0)
printk(KERN_ERR "%s error ret=%d\n", __func__, ret);
return ret;
}
/* * mt2063_write - Write register data into the I2C bus, caching the value
*/ staticint mt2063_setreg(struct mt2063_state *state, u8 reg, u8 val)
{ int status;
dprintk(2, "\n");
if (reg >= MT2063_REG_END_REGS) return -ERANGE;
status = mt2063_write(state, reg, &val, 1); if (status < 0) return status;
state->reg[reg] = val;
return 0;
}
/* * mt2063_read - Read data from the I2C bus
*/ staticint mt2063_read(struct mt2063_state *state,
u8 subAddress, u8 *pData, u32 cnt)
{ int status = 0; /* Status to be returned */ struct dvb_frontend *fe = state->frontend;
u32 i = 0;
status = i2c_transfer(state->i2c, msg, 2);
dprintk(2, "addr 0x%02x, ret = %d, val = 0x%02x\n",
subAddress + i, status, *(pData + i)); if (status < 0) break;
} if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
if (status < 0)
printk(KERN_ERR "Can't read from address 0x%02x,\n",
subAddress + i);
return status;
}
/* * FIXME: Is this really needed?
*/ staticint MT2063_Sleep(struct dvb_frontend *fe)
{ /* * ToDo: Add code here to implement a OS blocking
*/
msleep(100);
/* Check for a node in the free list */ if (pAS_Info->freeZones != NULL) { /* Use one from the free list */
pNode = pAS_Info->freeZones;
pAS_Info->freeZones = pNode->next_;
} else { /* Grab a node from the array */
pNode = &pAS_Info->MT2063_ExclZones[pAS_Info->nZones];
}
if (pPrevNode != NULL) {
pNode->next_ = pPrevNode->next_;
pPrevNode->next_ = pNode;
} else { /* insert at the beginning of the list */
/* Make previous node point to the subsequent node */ if (pPrevNode != NULL)
pPrevNode->next_ = pNext;
/* Add pNodeToRemove to the beginning of the freeZones */
pNodeToRemove->next_ = pAS_Info->freeZones;
pAS_Info->freeZones = pNodeToRemove;
/* Decrement node count */
pAS_Info->nZones--;
return pNext;
}
/* * MT_AddExclZone() * * Add (and merge) an exclusion zone into the list. * If the range (f_min, f_max) is totally outside the * 1st IF BW, ignore the entry. * If the range (f_min, f_max) is negative, ignore the entry.
*/ staticvoid MT2063_AddExclZone(struct MT2063_AvoidSpursData_t *pAS_Info,
u32 f_min, u32 f_max)
{ struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones; struct MT2063_ExclZone_t *pPrev = NULL; struct MT2063_ExclZone_t *pNext = NULL;
dprintk(2, "\n");
/* Check to see if this overlaps the 1st IF filter */ if ((f_max > (pAS_Info->f_if1_Center - (pAS_Info->f_if1_bw / 2)))
&& (f_min < (pAS_Info->f_if1_Center + (pAS_Info->f_if1_bw / 2)))
&& (f_min < f_max)) { /* * 1 2 3 4 5 6 * * New entry: |---| |--| |--| |-| |---| |--| * or or or or or * Existing: |--| |--| |--| |---| |-| |--|
*/
/* Check for our place in the list */ while ((pNode != NULL) && (pNode->max_ < f_min)) {
pPrev = pNode;
pNode = pNode->next_;
}
if ((pNode != NULL) && (pNode->min_ < f_max)) { /* Combine me with pNode */ if (f_min < pNode->min_)
pNode->min_ = f_min; if (f_max > pNode->max_)
pNode->max_ = f_max;
} else {
pNode = InsertNode(pAS_Info, pPrev);
pNode->min_ = f_min;
pNode->max_ = f_max;
}
/* * Reset all exclusion zones. * Add zones to protect the PLL FracN regions near zero
*/ staticvoid MT2063_ResetExclZones(struct MT2063_AvoidSpursData_t *pAS_Info)
{
u32 center;
dprintk(2, "\n");
pAS_Info->nZones = 0; /* this clears the used list */
pAS_Info->usedZones = NULL; /* reset ptr */
pAS_Info->freeZones = NULL; /* reset ptr */
center =
pAS_Info->f_ref *
((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 +
pAS_Info->f_in) / pAS_Info->f_ref) - pAS_Info->f_in; while (center <
pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 +
pAS_Info->f_LO1_FracN_Avoid) { /* Exclude LO1 FracN */
MT2063_AddExclZone(pAS_Info,
center - pAS_Info->f_LO1_FracN_Avoid,
center - 1);
MT2063_AddExclZone(pAS_Info, center + 1,
center + pAS_Info->f_LO1_FracN_Avoid);
center += pAS_Info->f_ref;
}
center =
pAS_Info->f_ref *
((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 -
pAS_Info->f_out) / pAS_Info->f_ref) + pAS_Info->f_out; while (center <
pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 +
pAS_Info->f_LO2_FracN_Avoid) { /* Exclude LO2 FracN */
MT2063_AddExclZone(pAS_Info,
center - pAS_Info->f_LO2_FracN_Avoid,
center - 1);
MT2063_AddExclZone(pAS_Info, center + 1,
center + pAS_Info->f_LO2_FracN_Avoid);
center += pAS_Info->f_ref;
}
/* * MT_ChooseFirstIF - Choose the best available 1st IF * If f_Desired is not excluded, choose that first. * Otherwise, return the value closest to f_Center that is * not excluded
*/ static u32 MT2063_ChooseFirstIF(struct MT2063_AvoidSpursData_t *pAS_Info)
{ /* * Update "f_Desired" to be the nearest "combinational-multiple" of * "f_LO1_Step". * The resulting number, F_LO1 must be a multiple of f_LO1_Step. * And F_LO1 is the arithmetic sum of f_in + f_Center. * Neither f_in, nor f_Center must be a multiple of f_LO1_Step. * However, the sum must be.
*/ const u32 f_Desired =
pAS_Info->f_LO1_Step *
((pAS_Info->f_if1_Request + pAS_Info->f_in +
pAS_Info->f_LO1_Step / 2) / pAS_Info->f_LO1_Step) -
pAS_Info->f_in; const u32 f_Step =
(pAS_Info->f_LO1_Step >
pAS_Info->f_LO2_Step) ? pAS_Info->f_LO1_Step : pAS_Info->
f_LO2_Step;
u32 f_Center;
s32 i;
s32 j = 0;
u32 bDesiredExcluded = 0;
u32 bZeroExcluded = 0;
s32 tmpMin, tmpMax;
s32 bestDiff; struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones; struct MT2063_FIFZone_t zones[MT2063_MAX_ZONES];
dprintk(2, "\n");
if (pAS_Info->nZones == 0) return f_Desired;
/* * f_Center needs to be an integer multiple of f_Step away * from f_Desired
*/ if (pAS_Info->f_if1_Center > f_Desired)
f_Center =
f_Desired +
f_Step *
((pAS_Info->f_if1_Center - f_Desired +
f_Step / 2) / f_Step); else
f_Center =
f_Desired -
f_Step *
((f_Desired - pAS_Info->f_if1_Center +
f_Step / 2) / f_Step);
/* * Take MT_ExclZones, center around f_Center and change the * resolution to f_Step
*/ while (pNode != NULL) { /* floor function */
tmpMin =
floor((s32) (pNode->min_ - f_Center), (s32) f_Step);
/* See if this zone overlaps the previous */ if ((j > 0) && (tmpMin < zones[j - 1].max_))
zones[j - 1].max_ = tmpMax; else { /* Add new zone */
zones[j].min_ = tmpMin;
zones[j].max_ = tmpMax;
j++;
}
pNode = pNode->next_;
}
/* * If the desired is okay, return with it
*/ if (bDesiredExcluded == 0) return f_Desired;
/* * If the desired is excluded and the center is okay, return with it
*/ if (bZeroExcluded == 0) return f_Center;
/* Find the value closest to 0 (f_Center) */
bestDiff = zones[0].min_; for (i = 0; i < j; i++) { if (abs(zones[i].min_) < abs(bestDiff))
bestDiff = zones[i].min_; if (abs(zones[i].max_) < abs(bestDiff))
bestDiff = zones[i].max_;
}
/** * IsSpurInBand() - Checks to see if a spur will be present within the IF's * bandwidth. (fIFOut +/- fIFBW, -fIFOut +/- fIFBW) * * ma mb mc md * <--+-+-+-------------------+-------------------+-+-+--> * | ^ 0 ^ | * ^ b=-fIFOut+fIFBW/2 -b=+fIFOut-fIFBW/2 ^ * a=-fIFOut-fIFBW/2 -a=+fIFOut+fIFBW/2 * * Note that some equations are doubled to prevent round-off * problems when calculating fIFBW/2 * * @pAS_Info: Avoid Spurs information block * @fm: If spur, amount f_IF1 has to move negative * @fp: If spur, amount f_IF1 has to move positive * * Returns 1 if an LO spur would be present, otherwise 0.
*/ static u32 IsSpurInBand(struct MT2063_AvoidSpursData_t *pAS_Info,
u32 *fm, u32 * fp)
{ /* ** Calculate LO frequency settings.
*/
u32 n, n0; const u32 f_LO1 = pAS_Info->f_LO1; const u32 f_LO2 = pAS_Info->f_LO2; const u32 d = pAS_Info->f_out + pAS_Info->f_out_bw / 2; const u32 c = d - pAS_Info->f_out_bw; const u32 f = pAS_Info->f_zif_bw / 2; const u32 f_Scale = (f_LO1 / (UINT_MAX / 2 / pAS_Info->maxH1)) + 1;
s32 f_nsLO1, f_nsLO2;
s32 f_Spur;
u32 ma, mb, mc, md, me, mf;
u32 lo_gcd, gd_Scale, gc_Scale, gf_Scale, hgds, hgfs, hgcs;
dprintk(2, "\n");
*fm = 0;
/* ** For each edge (d, c & f), calculate a scale, based on the gcd ** of f_LO1, f_LO2 and the edge value. Use the larger of this ** gcd-based scale factor or f_Scale.
*/
lo_gcd = gcd(f_LO1, f_LO2);
gd_Scale = max((u32) gcd(lo_gcd, d), f_Scale);
hgds = gd_Scale / 2;
gc_Scale = max((u32) gcd(lo_gcd, c), f_Scale);
hgcs = gc_Scale / 2;
gf_Scale = max((u32) gcd(lo_gcd, f), f_Scale);
hgfs = gf_Scale / 2;
n0 = DIV_ROUND_UP(f_LO2 - d, f_LO1 - f_LO2);
/* Check out all multiples of LO1 from n0 to m_maxLOSpurHarmonic */ for (n = n0; n <= pAS_Info->maxH1; ++n) {
md = (n * ((f_LO1 + hgds) / gd_Scale) -
((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale);
/* If # fLO2 harmonics > m_maxLOSpurHarmonic, then no spurs present */ if (md >= pAS_Info->maxH1) break;
mb = (n * ((f_LO1 + hgcs) / gc_Scale) +
((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale); if (ma != mb) {
f_nsLO1 = n * (f_LO1 / gc_Scale);
f_nsLO2 = ma * (f_LO2 / gc_Scale);
f_Spur =
(gc_Scale * (f_nsLO1 - f_nsLO2)) +
n * (f_LO1 % gc_Scale) - ma * (f_LO2 % gc_Scale);
*fp = (((s32) d + f_Spur) / (ma - n)) + 1;
*fm = (-(f_Spur + (s32) c) / (ma - n)) + 1; return 1;
}
}
/* No spurs found */ return 0;
}
/* * MT_AvoidSpurs() - Main entry point to avoid spurs. * Checks for existing spurs in present LO1, LO2 freqs * and if present, chooses spur-free LO1, LO2 combination * that tunes the same input/output frequencies.
*/ static u32 MT2063_AvoidSpurs(struct MT2063_AvoidSpursData_t *pAS_Info)
{ int status = 0;
u32 fm, fp; /* restricted range on LO's */
pAS_Info->bSpurAvoided = 0;
pAS_Info->nSpursFound = 0;
dprintk(2, "\n");
if (pAS_Info->maxH1 == 0) return 0;
/* * Avoid LO Generated Spurs * * Make sure that have no LO-related spurs within the IF output * bandwidth. * * If there is an LO spur in this band, start at the current IF1 frequency * and work out until we find a spur-free frequency or run up against the * 1st IF SAW band edge. Use temporary copies of fLO1 and fLO2 so that they * will be unchanged if a spur-free setting is not found.
*/
pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp); if (pAS_Info->bSpurPresent) {
u32 zfIF1 = pAS_Info->f_LO1 - pAS_Info->f_in; /* current attempt at a 1st IF */
u32 zfLO1 = pAS_Info->f_LO1; /* current attempt at an LO1 freq */
u32 zfLO2 = pAS_Info->f_LO2; /* current attempt at an LO2 freq */
u32 delta_IF1;
u32 new_IF1;
/* ** Spur was found, attempt to find a spur-free 1st IF
*/ do {
pAS_Info->nSpursFound++;
pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp); /* * Continue while the new 1st IF is still within the 1st IF bandwidth * and there is a spur in the band (again)
*/
} while ((2 * delta_IF1 + pAS_Info->f_out_bw <= pAS_Info->f_if1_bw) && pAS_Info->bSpurPresent);
/* * Use the LO-spur free values found. If the search went all * the way to the 1st IF band edge and always found spurs, just * leave the original choice. It's as "good" as any other.
*/ if (pAS_Info->bSpurPresent == 1) {
status |= MT2063_SPUR_PRESENT_ERR;
pAS_Info->f_LO1 = zfLO1;
pAS_Info->f_LO2 = zfLO2;
} else
pAS_Info->bSpurAvoided = 1;
}
status |=
((pAS_Info->
nSpursFound << MT2063_SPUR_SHIFT) & MT2063_SPUR_CNT_MASK);
return status;
}
/* * Constants used by the tuning algorithm
*/ #define MT2063_REF_FREQ (16000000UL) /* Reference oscillator Frequency (in Hz) */ #define MT2063_IF1_BW (22000000UL) /* The IF1 filter bandwidth (in Hz) */ #define MT2063_TUNE_STEP_SIZE (50000UL) /* Tune in steps of 50 kHz */ #define MT2063_SPUR_STEP_HZ (250000UL) /* Step size (in Hz) to move IF1 when avoiding spurs */ #define MT2063_ZIF_BW (2000000UL) /* Zero-IF spur-free bandwidth (in Hz) */ #define MT2063_MAX_HARMONICS_1 (15UL) /* Highest intra-tuner LO Spur Harmonic to be avoided */ #define MT2063_MAX_HARMONICS_2 (5UL) /* Highest inter-tuner LO Spur Harmonic to be avoided */ #define MT2063_MIN_LO_SEP (1000000UL) /* Minimum inter-tuner LO frequency separation */ #define MT2063_LO1_FRACN_AVOID (0UL) /* LO1 FracN numerator avoid region (in Hz) */ #define MT2063_LO2_FRACN_AVOID (199999UL) /* LO2 FracN numerator avoid region (in Hz) */ #define MT2063_MIN_FIN_FREQ (44000000UL) /* Minimum input frequency (in Hz) */ #define MT2063_MAX_FIN_FREQ (1100000000UL) /* Maximum input frequency (in Hz) */ #define MT2063_MIN_FOUT_FREQ (36000000UL) /* Minimum output frequency (in Hz) */ #define MT2063_MAX_FOUT_FREQ (57000000UL) /* Maximum output frequency (in Hz) */ #define MT2063_MIN_DNC_FREQ (1293000000UL) /* Minimum LO2 frequency (in Hz) */ #define MT2063_MAX_DNC_FREQ (1614000000UL) /* Maximum LO2 frequency (in Hz) */ #define MT2063_MIN_UPC_FREQ (1396000000UL) /* Minimum LO1 frequency (in Hz) */ #define MT2063_MAX_UPC_FREQ (2750000000UL) /* Maximum LO1 frequency (in Hz) */
/* * Define the supported Part/Rev codes for the MT2063
*/ #define MT2063_B0 (0x9B) #define MT2063_B1 (0x9C) #define MT2063_B2 (0x9D) #define MT2063_B3 (0x9E)
/** * mt2063_lockStatus - Checks to see if LO1 and LO2 are locked * * @state: struct mt2063_state pointer * * This function returns 0, if no lock, 1 if locked and a value < 1 if error
*/ staticint mt2063_lockStatus(struct mt2063_state *state)
{ const u32 nMaxWait = 100; /* wait a maximum of 100 msec */ const u32 nPollRate = 2; /* poll status bits every 2 ms */ const u32 nMaxLoops = nMaxWait / nPollRate; const u8 LO1LK = 0x80;
u8 LO2LK = 0x08; int status;
u32 nDelays = 0;
dprintk(2, "\n");
/* LO2 Lock bit was in a different place for B0 version */ if (state->tuner_id == MT2063_B0)
LO2LK = 0x40;
do {
status = mt2063_read(state, MT2063_REG_LO_STATUS,
&state->reg[MT2063_REG_LO_STATUS], 1);
if (status < 0) return status;
if ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) ==
(LO1LK | LO2LK)) { return TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO;
}
msleep(nPollRate); /* Wait between retries */
} while (++nDelays < nMaxLoops);
if ((state->reg[MT2063_REG_DNC_GAIN] & 0x03) == 0x03) { /* if DNC1 is off */ if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */
*pValue = MT2063_DNC_NONE; else
*pValue = MT2063_DNC_2;
} else { /* DNC1 is on */ if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */
*pValue = MT2063_DNC_1; else
*pValue = MT2063_DNC_BOTH;
} return 0;
}
/* * mt2063_set_dnc_output_enable()
*/ static u32 mt2063_set_dnc_output_enable(struct mt2063_state *state, enum MT2063_DNC_Output_Enable nValue)
{ int status = 0; /* Status to be returned */
u8 val = 0;
dprintk(2, "\n");
/* selects, which DNC output is used */ switch (nValue) { case MT2063_DNC_NONE:
val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */ if (state->reg[MT2063_REG_DNC_GAIN] !=
val)
status |=
mt2063_setreg(state,
MT2063_REG_DNC_GAIN,
val);
val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */ if (state->reg[MT2063_REG_VGA_GAIN] !=
val)
status |=
mt2063_setreg(state,
MT2063_REG_VGA_GAIN,
val);
val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */ if (state->reg[MT2063_REG_RSVD_20] !=
val)
status |=
mt2063_setreg(state,
MT2063_REG_RSVD_20,
val);
break; case MT2063_DNC_1:
val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */ if (state->reg[MT2063_REG_DNC_GAIN] !=
val)
status |=
mt2063_setreg(state,
MT2063_REG_DNC_GAIN,
val);
val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */ if (state->reg[MT2063_REG_VGA_GAIN] !=
val)
status |=
mt2063_setreg(state,
MT2063_REG_VGA_GAIN,
val);
val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */ if (state->reg[MT2063_REG_RSVD_20] !=
val)
status |=
mt2063_setreg(state,
MT2063_REG_RSVD_20,
val);
break; case MT2063_DNC_2:
val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */ if (state->reg[MT2063_REG_DNC_GAIN] !=
val)
status |=
mt2063_setreg(state,
MT2063_REG_DNC_GAIN,
val);
val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */ if (state->reg[MT2063_REG_VGA_GAIN] !=
val)
status |=
mt2063_setreg(state,
MT2063_REG_VGA_GAIN,
val);
val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */ if (state->reg[MT2063_REG_RSVD_20] !=
val)
status |=
mt2063_setreg(state,
MT2063_REG_RSVD_20,
val);
break; case MT2063_DNC_BOTH:
val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */ if (state->reg[MT2063_REG_DNC_GAIN] !=
val)
status |=
mt2063_setreg(state,
MT2063_REG_DNC_GAIN,
val);
val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */ if (state->reg[MT2063_REG_VGA_GAIN] !=
val)
status |=
mt2063_setreg(state,
MT2063_REG_VGA_GAIN,
val);
val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */ if (state->reg[MT2063_REG_RSVD_20] !=
val)
status |=
mt2063_setreg(state,
MT2063_REG_RSVD_20,
val);
break; default: break;
}
return status;
}
/* * MT2063_SetReceiverMode() - Set the MT2063 receiver mode, according with * the selected enum mt2063_delivery_sys type. * * (DNC1GC & DNC2GC are the values, which are used, when the specific * DNC Output is selected, the other is always off) * * @state: ptr to mt2063_state structure * @Mode: desired receiver delivery system * * Note: Register cache must be valid for it to work
*/
static u32 MT2063_SetReceiverMode(struct mt2063_state *state, enum mt2063_delivery_sys Mode)
{ int status = 0; /* Status to be returned */
u8 val;
u32 longval;
dprintk(2, "\n");
if (Mode >= MT2063_NUM_RCVR_MODES)
status = -ERANGE;
/* RFAGCen */ if (status >= 0) {
val =
(state->
reg[MT2063_REG_PD1_TGT] & ~0x40) | (RFAGCEN[Mode]
? 0x40 :
0x00); if (state->reg[MT2063_REG_PD1_TGT] != val)
status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
}
/* LNARin */ if (status >= 0) {
u8 val = (state->reg[MT2063_REG_CTRL_2C] & ~0x03) |
(LNARIN[Mode] & 0x03); if (state->reg[MT2063_REG_CTRL_2C] != val)
status |= mt2063_setreg(state, MT2063_REG_CTRL_2C, val);
}
/* FIFFQEN and FIFFQ */ if (status >= 0) {
val =
(state->
reg[MT2063_REG_FIFF_CTRL2] & ~0xF0) |
(FIFFQEN[Mode] << 7) | (FIFFQ[Mode] << 4); if (state->reg[MT2063_REG_FIFF_CTRL2] != val) {
status |=
mt2063_setreg(state, MT2063_REG_FIFF_CTRL2, val); /* trigger FIFF calibration, needed after changing FIFFQ */
val =
(state->reg[MT2063_REG_FIFF_CTRL] | 0x01);
status |=
mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val);
val =
(state->
reg[MT2063_REG_FIFF_CTRL] & ~0x01);
status |=
mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val);
}
}
/* DNC1GC & DNC2GC */
status |= mt2063_get_dnc_output_enable(state, &longval);
status |= mt2063_set_dnc_output_enable(state, longval);
/* acLNAmax */ if (status >= 0) {
u8 val = (state->reg[MT2063_REG_LNA_OV] & ~0x1F) |
(ACLNAMAX[Mode] & 0x1F); if (state->reg[MT2063_REG_LNA_OV] != val)
status |= mt2063_setreg(state, MT2063_REG_LNA_OV, val);
}
/* LNATGT */ if (status >= 0) {
u8 val = (state->reg[MT2063_REG_LNA_TGT] & ~0x3F) |
(LNATGT[Mode] & 0x3F); if (state->reg[MT2063_REG_LNA_TGT] != val)
status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val);
}
/* ACRF */ if (status >= 0) {
u8 val = (state->reg[MT2063_REG_RF_OV] & ~0x1F) |
(ACRFMAX[Mode] & 0x1F); if (state->reg[MT2063_REG_RF_OV] != val)
status |= mt2063_setreg(state, MT2063_REG_RF_OV, val);
}
/* PD1TGT */ if (status >= 0) {
u8 val = (state->reg[MT2063_REG_PD1_TGT] & ~0x3F) |
(PD1TGT[Mode] & 0x3F); if (state->reg[MT2063_REG_PD1_TGT] != val)
status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
}
/* FIFATN */ if (status >= 0) {
u8 val = ACFIFMAX[Mode]; if (state->reg[MT2063_REG_PART_REV] != MT2063_B3 && val > 5)
val = 5;
val = (state->reg[MT2063_REG_FIF_OV] & ~0x1F) |
(val & 0x1F); if (state->reg[MT2063_REG_FIF_OV] != val)
status |= mt2063_setreg(state, MT2063_REG_FIF_OV, val);
}
/* PD2TGT */ if (status >= 0) {
u8 val = (state->reg[MT2063_REG_PD2_TGT] & ~0x3F) |
(PD2TGT[Mode] & 0x3F); if (state->reg[MT2063_REG_PD2_TGT] != val)
status |= mt2063_setreg(state, MT2063_REG_PD2_TGT, val);
}
/* Ignore ATN Overload */ if (status >= 0) {
val = (state->reg[MT2063_REG_LNA_TGT] & ~0x80) |
(RFOVDIS[Mode] ? 0x80 : 0x00); if (state->reg[MT2063_REG_LNA_TGT] != val)
status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val);
}
/* Ignore FIF Overload */ if (status >= 0) {
val = (state->reg[MT2063_REG_PD1_TGT] & ~0x80) |
(FIFOVDIS[Mode] ? 0x80 : 0x00); if (state->reg[MT2063_REG_PD1_TGT] != val)
status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val);
}
if (status >= 0) {
state->rcvr_mode = Mode;
dprintk(1, "mt2063 mode changed to %s\n",
mt2063_mode_name[state->rcvr_mode]);
}
return status;
}
/* * MT2063_ClearPowerMaskBits () - Clears the power-down mask bits for various * sections of the MT2063 * * @Bits: Mask bits to be cleared. * * See definition of MT2063_Mask_Bits type for description * of each of the power bits.
*/ static u32 MT2063_ClearPowerMaskBits(struct mt2063_state *state, enum MT2063_Mask_Bits Bits)
{ int status = 0;
dprintk(2, "\n");
Bits = (enum MT2063_Mask_Bits)(Bits & MT2063_ALL_SD); /* Only valid bits for this tuner */ if ((Bits & 0xFF00) != 0) {
state->reg[MT2063_REG_PWR_2] &= ~(u8) (Bits >> 8);
status |=
mt2063_write(state,
MT2063_REG_PWR_2,
&state->reg[MT2063_REG_PWR_2], 1);
} if ((Bits & 0xFF) != 0) {
state->reg[MT2063_REG_PWR_1] &= ~(u8) (Bits & 0xFF);
status |=
mt2063_write(state,
MT2063_REG_PWR_1,
&state->reg[MT2063_REG_PWR_1], 1);
}
return status;
}
/* * MT2063_SoftwareShutdown() - Enables or disables software shutdown function. * When Shutdown is 1, any section whose power * mask is set will be shutdown.
*/ static u32 MT2063_SoftwareShutdown(struct mt2063_state *state, u8 Shutdown)
{ int status;
/** * MT2063_fLO_FractionalTerm - Calculates the portion contributed by FracN / denom. * This function preserves maximum precision without * risk of overflow. It accurately calculates * f_ref * num / denom to within 1 HZ with fixed math. * * @f_ref: SRO frequency. * @num: Fractional portion of the multiplier * @denom: denominator portion of the ratio * * This calculation handles f_ref as two separate 14-bit fields. * Therefore, a maximum value of 2^28-1 may safely be used for f_ref. * This is the genesis of the magic number "14" and the magic mask value of * 0x03FFF. * * This routine successfully handles denom values up to and including 2^18. * Returns: f_ref * num / denom
*/ static u32 MT2063_fLO_FractionalTerm(u32 f_ref, u32 num, u32 denom)
{
u32 t1 = (f_ref >> 14) * num;
u32 term1 = t1 / denom;
u32 loss = t1 % denom;
u32 term2 =
(((f_ref & 0x00003FFF) * num + (loss << 14)) + (denom / 2)) / denom; return (term1 << 14) + term2;
}
/* * MT2063_CalcLO1Mult - Calculates Integer divider value and the numerator * value for a FracN PLL. * * This function assumes that the f_LO and f_Ref are * evenly divisible by f_LO_Step. * * @Div: OUTPUT: Whole number portion of the multiplier * @FracN: OUTPUT: Fractional portion of the multiplier * @f_LO: desired LO frequency. * @f_LO_Step: Minimum step size for the LO (in Hz). * @f_Ref: SRO frequency. * @f_Avoid: Range of PLL frequencies to avoid near integer multiples * of f_Ref (in Hz). * * Returns: Recalculated LO frequency.
*/ static u32 MT2063_CalcLO1Mult(u32 *Div,
u32 *FracN,
u32 f_LO,
u32 f_LO_Step, u32 f_Ref)
{ /* Calculate the whole number portion of the divider */
*Div = f_LO / f_Ref;
/** * MT2063_CalcLO2Mult - Calculates Integer divider value and the numerator * value for a FracN PLL. * * This function assumes that the f_LO and f_Ref are * evenly divisible by f_LO_Step. * * @Div: OUTPUT: Whole number portion of the multiplier * @FracN: OUTPUT: Fractional portion of the multiplier * @f_LO: desired LO frequency. * @f_LO_Step: Minimum step size for the LO (in Hz). * @f_Ref: SRO frequency. * * Returns: Recalculated LO frequency.
*/ static u32 MT2063_CalcLO2Mult(u32 *Div,
u32 *FracN,
u32 f_LO,
u32 f_LO_Step, u32 f_Ref)
{ /* Calculate the whole number portion of the divider */
*Div = f_LO / f_Ref;
/* * FindClearTuneFilter() - Calculate the correct ClearTune filter to be * used for a given input frequency. * * @state: ptr to tuner data structure * @f_in: RF input center frequency (in Hz). * * Returns: ClearTune filter number (0-31)
*/ static u32 FindClearTuneFilter(struct mt2063_state *state, u32 f_in)
{
u32 RFBand;
u32 idx; /* index loop */
/* ** Find RF Band setting
*/
RFBand = 31; /* def when f_in > all */ for (idx = 0; idx < 31; ++idx) { if (state->CTFiltMax[idx] >= f_in) {
RFBand = idx; break;
}
} return RFBand;
}
/* * MT2063_Tune() - Change the tuner's tuned frequency to RFin.
*/ static u32 MT2063_Tune(struct mt2063_state *state, u32 f_in)
{ /* RF input center frequency */
int status = 0;
u32 LO1; /* 1st LO register value */
u32 Num1; /* Numerator for LO1 reg. value */
u32 f_IF1; /* 1st IF requested */
u32 LO2; /* 2nd LO register value */
u32 Num2; /* Numerator for LO2 reg. value */
u32 ofLO1, ofLO2; /* last time's LO frequencies */
u8 fiffc = 0x80; /* FIFF center freq from tuner */
u32 fiffof; /* Offset from FIFF center freq */ const u8 LO1LK = 0x80; /* Mask for LO1 Lock bit */
u8 LO2LK = 0x08; /* Mask for LO2 Lock bit */
u8 val;
u32 RFBand;
dprintk(2, "\n"); /* Check the input and output frequency ranges */ if ((f_in < MT2063_MIN_FIN_FREQ) || (f_in > MT2063_MAX_FIN_FREQ)) return -EINVAL;
if ((state->AS_Data.f_out < MT2063_MIN_FOUT_FREQ)
|| (state->AS_Data.f_out > MT2063_MAX_FOUT_FREQ)) return -EINVAL;
/* * Save original LO1 and LO2 register values
*/
ofLO1 = state->AS_Data.f_LO1;
ofLO2 = state->AS_Data.f_LO2;
/* * Find and set RF Band setting
*/ if (state->ctfilt_sw == 1) {
val = (state->reg[MT2063_REG_CTUNE_CTRL] | 0x08); if (state->reg[MT2063_REG_CTUNE_CTRL] != val) {
status |=
mt2063_setreg(state, MT2063_REG_CTUNE_CTRL, val);
}
val = state->reg[MT2063_REG_CTUNE_OV];
RFBand = FindClearTuneFilter(state, f_in);
state->reg[MT2063_REG_CTUNE_OV] =
(u8) ((state->reg[MT2063_REG_CTUNE_OV] & ~0x1F)
| RFBand); if (state->reg[MT2063_REG_CTUNE_OV] != val) {
status |=
mt2063_setreg(state, MT2063_REG_CTUNE_OV, val);
}
}
/* * Read the FIFF Center Frequency from the tuner
*/ if (status >= 0) {
status |=
mt2063_read(state,
MT2063_REG_FIFFC,
&state->reg[MT2063_REG_FIFFC], 1);
fiffc = state->reg[MT2063_REG_FIFFC];
} /* * Assign in the requested values
*/
state->AS_Data.f_in = f_in; /* Request a 1st IF such that LO1 is on a step size */
state->AS_Data.f_if1_Request =
MT2063_Round_fLO(state->AS_Data.f_if1_Request + f_in,
state->AS_Data.f_LO1_Step,
state->AS_Data.f_ref) - f_in;
/* * Calculate frequency settings. f_IF1_FREQ + f_in is the * desired LO1 frequency
*/
MT2063_ResetExclZones(&state->AS_Data);
/* * Check for any LO spurs in the output bandwidth and adjust * the LO settings to avoid them if needed
*/
status |= MT2063_AvoidSpurs(&state->AS_Data); /* * MT_AvoidSpurs spurs may have changed the LO1 & LO2 values. * Recalculate the LO frequencies and the values to be placed * in the tuning registers.
*/
state->AS_Data.f_LO1 =
MT2063_CalcLO1Mult(&LO1, &Num1, state->AS_Data.f_LO1,
state->AS_Data.f_LO1_Step, state->AS_Data.f_ref);
state->AS_Data.f_LO2 =
MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in,
state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
state->AS_Data.f_LO2 =
MT2063_CalcLO2Mult(&LO2, &Num2, state->AS_Data.f_LO2,
state->AS_Data.f_LO2_Step, state->AS_Data.f_ref);
/* * Check the upconverter and downconverter frequency ranges
*/ if ((state->AS_Data.f_LO1 < MT2063_MIN_UPC_FREQ)
|| (state->AS_Data.f_LO1 > MT2063_MAX_UPC_FREQ))
status |= MT2063_UPC_RANGE; if ((state->AS_Data.f_LO2 < MT2063_MIN_DNC_FREQ)
|| (state->AS_Data.f_LO2 > MT2063_MAX_DNC_FREQ))
status |= MT2063_DNC_RANGE; /* LO2 Lock bit was in a different place for B0 version */ if (state->tuner_id == MT2063_B0)
LO2LK = 0x40;
/* * If we have the same LO frequencies and we're already locked, * then skip re-programming the LO registers.
*/ if ((ofLO1 != state->AS_Data.f_LO1)
|| (ofLO2 != state->AS_Data.f_LO2)
|| ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) !=
(LO1LK | LO2LK))) { /* * Calculate the FIFFOF register value * * IF1_Actual * FIFFOF = ------------ - 8 * FIFFC - 4992 * f_ref/64
*/
fiffof =
(state->AS_Data.f_LO1 -
f_in) / (state->AS_Data.f_ref / 64) - 8 * (u32) fiffc -
4992; if (fiffof > 0xFF)
fiffof = 0xFF;
/* * Now write out the computed register values * IMPORTANT: There is a required order for writing * (0x05 must follow all the others).
*/
status |= mt2063_write(state, MT2063_REG_LO1CQ_1, &state->reg[MT2063_REG_LO1CQ_1], 5); /* 0x01 - 0x05 */ if (state->tuner_id == MT2063_B0) { /* Re-write the one-shot bits to trigger the tune operation */
status |= mt2063_write(state, MT2063_REG_LO2CQ_3, &state->reg[MT2063_REG_LO2CQ_3], 1); /* 0x05 */
} /* Write out the FIFF offset only if it's changing */ if (state->reg[MT2063_REG_FIFF_OFFSET] !=
(u8) fiffof) {
state->reg[MT2063_REG_FIFF_OFFSET] =
(u8) fiffof;
status |=
mt2063_write(state,
MT2063_REG_FIFF_OFFSET,
&state->
reg[MT2063_REG_FIFF_OFFSET],
1);
}
}
/* * Check for LO's locking
*/
if (status < 0) return status;
status = mt2063_lockStatus(state); if (status < 0) return status; if (!status) return -EINVAL; /* Couldn't lock */
/* * If we locked OK, assign calculated data to mt2063_state structure
*/
state->f_IF1_actual = state->AS_Data.f_LO1 - f_in;
}
return status;
}
staticconst u8 MT2063B0_defaults[] = { /* Reg, Value */
0x19, 0x05,
0x1B, 0x1D,
0x1C, 0x1F,
0x1D, 0x0F,
0x1E, 0x3F,
0x1F, 0x0F,
0x20, 0x3F,
0x22, 0x21,
0x23, 0x3F,
0x24, 0x20,
0x25, 0x3F,
0x27, 0xEE,
0x2C, 0x27, /* bit at 0x20 is cleared below */
0x30, 0x03,
0x2C, 0x07, /* bit at 0x20 is cleared here */
0x2D, 0x87,
0x2E, 0xAA,
0x28, 0xE1, /* Set the FIFCrst bit here */
0x28, 0xE0, /* Clear the FIFCrst bit here */
0x00
};
/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */ staticconst u8 MT2063B1_defaults[] = { /* Reg, Value */
0x05, 0xF0,
0x11, 0x10, /* New Enable AFCsd */
0x19, 0x05,
0x1A, 0x6C,
0x1B, 0x24,
0x1C, 0x28,
0x1D, 0x8F,
0x1E, 0x14,
0x1F, 0x8F,
0x20, 0x57,
0x22, 0x21, /* New - ver 1.03 */
0x23, 0x3C, /* New - ver 1.10 */
0x24, 0x20, /* New - ver 1.03 */
0x2C, 0x24, /* bit at 0x20 is cleared below */
0x2D, 0x87, /* FIFFQ=0 */
0x2F, 0xF3,
0x30, 0x0C, /* New - ver 1.11 */
0x31, 0x1B, /* New - ver 1.11 */
0x2C, 0x04, /* bit at 0x20 is cleared here */
0x28, 0xE1, /* Set the FIFCrst bit here */
0x28, 0xE0, /* Clear the FIFCrst bit here */
0x00
};
/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */ staticconst u8 MT2063B3_defaults[] = { /* Reg, Value */
0x05, 0xF0,
0x19, 0x3D,
0x2C, 0x24, /* bit at 0x20 is cleared below */
0x2C, 0x04, /* bit at 0x20 is cleared here */
0x28, 0xE1, /* Set the FIFCrst bit here */
0x28, 0xE0, /* Clear the FIFCrst bit here */
0x00
};
/* Read the Part/Rev code from the tuner */
status = mt2063_read(state, MT2063_REG_PART_REV,
&state->reg[MT2063_REG_PART_REV], 1); if (status < 0) {
printk(KERN_ERR "Can't read mt2063 part ID\n"); return status;
}
/* Check the part/rev code */ switch (state->reg[MT2063_REG_PART_REV]) { case MT2063_B0:
step = "B0"; break; case MT2063_B1:
step = "B1"; break; case MT2063_B2:
step = "B2"; break; case MT2063_B3:
step = "B3"; break; default:
printk(KERN_ERR "mt2063: Unknown mt2063 device ID (0x%02x)\n",
state->reg[MT2063_REG_PART_REV]); return -ENODEV; /* Wrong tuner Part/Rev code */
}
/* Check the 2nd byte of the Part/Rev code from the tuner */
status = mt2063_read(state, MT2063_REG_RSVD_3B,
&state->reg[MT2063_REG_RSVD_3B], 1);
/* b7 != 0 ==> NOT MT2063 */ if (status < 0 || ((state->reg[MT2063_REG_RSVD_3B] & 0x80) != 0x00)) {
printk(KERN_ERR "mt2063: Unknown part ID (0x%02x%02x)\n",
state->reg[MT2063_REG_PART_REV],
state->reg[MT2063_REG_RSVD_3B]); return -ENODEV; /* Wrong tuner Part/Rev code */
}
printk(KERN_INFO "mt2063: detected a mt2063 %s\n", step);
/* Reset the tuner */
status = mt2063_write(state, MT2063_REG_LO2CQ_3, &all_resets, 1); if (status < 0) return status;
/* change all of the default values that vary from the HW reset values */ /* def = (state->reg[PART_REV] == MT2063_B0) ? MT2063B0_defaults : MT2063B1_defaults; */ switch (state->reg[MT2063_REG_PART_REV]) { case MT2063_B3:
def = MT2063B3_defaults; break;
case MT2063_B1:
def = MT2063B1_defaults; break;
case MT2063_B0:
def = MT2063B0_defaults; break;
default: return -ENODEV;
}
while (status >= 0 && *def) {
u8 reg = *def++;
u8 val = *def++;
status = mt2063_write(state, reg, &val, 1);
} if (status < 0) return status;
status = mt2063_read(state,
MT2063_REG_FIFFC,
&state->reg[MT2063_REG_FIFFC], 1); if (status < 0) return status;
/* Read back all the registers from the tuner */
status = mt2063_read(state,
MT2063_REG_PART_REV,
state->reg, MT2063_REG_END_REGS); if (status < 0) return status;
/* ** Fetch the FCU osc value and use it and the fRef value to ** scale all of the Band Max values
*/
state->reg[MT2063_REG_CTUNE_CTRL] = 0x0A;
status = mt2063_write(state, MT2063_REG_CTUNE_CTRL,
&state->reg[MT2063_REG_CTUNE_CTRL], 1); if (status < 0) return status;
/* Read the ClearTune filter calibration value */
status = mt2063_read(state, MT2063_REG_FIFFC,
&state->reg[MT2063_REG_FIFFC], 1); if (status < 0) return status;
fcu_osc = state->reg[MT2063_REG_FIFFC];
state->reg[MT2063_REG_CTUNE_CTRL] = 0x00;
status = mt2063_write(state, MT2063_REG_CTUNE_CTRL,
&state->reg[MT2063_REG_CTUNE_CTRL], 1); if (status < 0) return status;
/* Adjust each of the values in the ClearTune filter cross-over table */ for (i = 0; i < 31; i++)
state->CTFiltMax[i] = (state->CTFiltMax[i] / 768) * (fcu_osc + 640);
status = MT2063_SoftwareShutdown(state, 1); if (status < 0) return status;
status = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD); if (status < 0) return status;
status = MT2063_Tune(state, (params->frequency + (pict2chanb_vsb + (ch_bw / 2)))); if (status < 0) return status;
state->frequency = params->frequency; return 0;
}
/* * As defined on EN 300 429, the DVB-C roll-off factor is 0.15. * So, the amount of the needed bandwidth is given by: * Bw = Symbol_rate * (1 + 0.15) * As such, the maximum symbol rate supported by 6 MHz is given by: * max_symbol_rate = 6 MHz / 1.15 = 5217391 Bauds
*/ #define MAX_SYMBOL_RATE_6MHz 5217391
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.