summaryrefslogtreecommitdiff
path: root/drivers/media/radio/stfm1000/stfm1000-rds.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/radio/stfm1000/stfm1000-rds.c')
-rw-r--r--drivers/media/radio/stfm1000/stfm1000-rds.c1529
1 files changed, 1529 insertions, 0 deletions
diff --git a/drivers/media/radio/stfm1000/stfm1000-rds.c b/drivers/media/radio/stfm1000/stfm1000-rds.c
new file mode 100644
index 000000000000..5f63052d00c2
--- /dev/null
+++ b/drivers/media/radio/stfm1000/stfm1000-rds.c
@@ -0,0 +1,1529 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/init.h>
+
+#include "stfm1000.h"
+
+#include "stfm1000-rds.h"
+
+#define bitstream_to_rds_state(b) \
+ container_of(b, struct stfm1000_rds_state, bitstream)
+#define demod_to_rds_state(d) \
+ container_of(d, struct stfm1000_rds_state, demod)
+#define pkt_to_rds_state(p) \
+ container_of(p, struct stfm1000_rds_state, pkt)
+#define text_to_rds_state(t) \
+ container_of(t, struct stfm1000_rds_state, text)
+#define rds_state_to_stfm1000(r) \
+ container_of(r, struct stfm1000, rds_state)
+
+#define TADJSH 8 /*Shifts used in bitslice loop filter */
+
+/* Reverse of Matlab's Fquant (see MatchedFilterDecomposition.m), so that */
+/* mixandsum code is easy; Used by rds_bitstream_stfmdemod.arm */
+const s16 u16_rds_basis[2*RDS_BASISLENGTH+8] = {
+ 14, 24, 34, 43, 50, 56, 60, 62, 62,
+ 60, 55, 49, 41, 32, 22, 11, 14, 24,
+ 34, 43, 50, 56, 60, 62, 62, 60, 55,
+ 49, 41, 32, 22, 11, 14, 24, 34, 43,
+ 50, 56, 60, 62
+};
+
+static int bits_free(struct stfm1000_rds_bitstream *rdsb)
+{
+ /* Do not show the last one word free. */
+ int FreeSpace = rdsb->TailBitCount - rdsb->HeadBitCount - 32;
+
+ if (FreeSpace < 0)
+ FreeSpace = (RDS_BITBUFSIZE * 32) + FreeSpace;
+ return FreeSpace;
+}
+
+static void put1bit(struct stfm1000_rds_bitstream *rdsb, int bit)
+{
+ int index = (rdsb->HeadBitCount >> 5);
+ u32 CurBit = (rdsb->HeadBitCount & 0x1f);
+ u32 CurWord = rdsb->buf[index];
+
+ if (CurBit == 0)
+ CurWord = 0;
+
+ CurWord = CurWord | (((u32)bit & 1) << CurBit);
+ rdsb->buf[index] = CurWord;
+ rdsb->HeadBitCount++;
+ if (rdsb->HeadBitCount >= RDS_BITBUFSIZE * 32)
+ rdsb->HeadBitCount = 0;
+}
+
+static int get1bit(struct stfm1000_rds_bitstream *rdsb)
+{
+ int Bit = 0;
+ int index = (rdsb->TailBitCount >> 5);
+ int CurBit = (rdsb->TailBitCount & 0x1f);
+ u32 CurWord = rdsb->buf[index];
+
+ Bit = (CurWord >> CurBit) & 1;
+ rdsb->TailBitCount++;
+ if (rdsb->TailBitCount == RDS_BITBUFSIZE*32)
+ rdsb->TailBitCount = 0;
+
+ return Bit;
+}
+
+static int bits_filled(struct stfm1000_rds_bitstream *rdsb)
+{
+ int FilledSpace = rdsb->HeadBitCount - rdsb->TailBitCount;
+
+ if (FilledSpace < 0)
+ FilledSpace = (RDS_BITBUFSIZE * 32) + FilledSpace;
+ return FilledSpace;
+}
+
+static void rds_mix_msg(struct stfm1000_rds_demod *rdsd, u8 MixSetting)
+{
+ if (rdsd->mix_msg_pending)
+ rdsd->mix_msg_overrun++;
+ rdsd->mix_msg = MixSetting;
+ rdsd->mix_msg_pending = 1;
+
+ /* signal monitor thread */
+ stfm1000_monitor_signal(
+ rds_state_to_stfm1000(demod_to_rds_state(rdsd)),
+ EVENT_RDS_MIXFILT);
+}
+
+/* call with interrupts disabled please */
+int stfm1000_rds_mix_msg_get(struct stfm1000_rds_state *rds)
+{
+ struct stfm1000_rds_demod *rdsd = &rds->demod;
+
+ if (!rdsd->mix_msg_pending)
+ return -1;
+
+ return rdsd->mix_msg;
+}
+
+/* call with interrupts disabled please */
+int stfm1000_rds_mix_msg_processed(struct stfm1000_rds_state *rds, int mix_msg)
+{
+ struct stfm1000_rds_demod *rdsd = &rds->demod;
+
+ if (!rdsd->mix_msg_pending)
+ return -1;
+
+ rdsd->mix_msg_pending = 0;
+
+ /* update the completion indication bit */
+ if ((mix_msg & 0x8) == 0)
+ rdsd->MixPopDone = 1;
+
+ /* this is reflected off the hardware register */
+ rdsd->rds_mix_offset = mix_msg & 1;
+
+ if (rdsd->mix_msg != mix_msg) {
+ rdsd->mix_msg_processed_changed++;
+ return -1;
+ }
+ return 0;
+}
+
+static void rds_sdnominal_msg(struct stfm1000_rds_demod *rdsd, int sdnominal)
+{
+ if (rdsd->sdnominal_msg_pending)
+ rdsd->sdnominal_msg_overrun++;
+ rdsd->sdnominal_msg = sdnominal;
+ rdsd->sdnominal_msg_pending = 1;
+
+ /* signal monitor thread */
+ stfm1000_monitor_signal(
+ rds_state_to_stfm1000(demod_to_rds_state(rdsd)),
+ EVENT_RDS_SDNOMINAL);
+}
+
+/* call with interrupts disabled please */
+int stfm1000_rds_sdnominal_msg_get(struct stfm1000_rds_state *rds)
+{
+ struct stfm1000_rds_demod *rdsd = &rds->demod;
+
+ if (!rdsd->sdnominal_msg_pending)
+ return 0;
+
+ return rdsd->sdnominal_msg;
+}
+
+/* call with interrupts disabled please */
+int stfm1000_rds_sdnominal_msg_processed(struct stfm1000_rds_state *rds,
+ int sdnominal_msg)
+{
+ struct stfm1000_rds_demod *rdsd = &rds->demod;
+
+ if (!rdsd->sdnominal_msg_pending)
+ return -1;
+
+ rdsd->sdnominal_msg_pending = 0;
+ return 0;
+}
+
+void demod_loop(struct stfm1000_rds_bitstream *rdsb,
+ struct stfm1000_rds_demod *rdsd)
+{
+ s32 filter_out;
+ u32 freeSpace;
+ s32 decomp_hist_pp;
+ u8 phase;
+
+ /* Check if we're at a half-basis point */
+ if ((rdsd->i & (RDS_BASISLENGTH/2 - 1)) != 0)
+ return; /* Nope, return */
+
+ /* Yes, time to do our work */
+ /* Rotate the length 3 history buffer */
+ decomp_hist_pp = rdsd->decomp_hist_p;
+ rdsd->decomp_hist_p = rdsd->decomp_hist;
+ if ((rdsd->i & (RDS_BASISLENGTH-1)) == 0) {
+ rdsd->decomp_hist = rdsd->mixandsum1>>9; /* Grab output of
+ * mixandsum1/512 */
+ rdsd->mixandsum1 = 0; /* Reset mixandsum #1 */
+ } else {
+ rdsd->decomp_hist = rdsd->mixandsum2>>9; /*Grab output of
+ * mixandsum2/512 */
+ rdsd->mixandsum2 = 0; /* Reset mixandsum #2 */
+ }
+
+ /* Form correlator/decimator output by convolving with the
+ * decomposition coefficients, DecompQuant from Matlab work. */
+ filter_out = (-58*rdsd->decomp_hist + 59*decomp_hist_pp)>>7;
+
+ /*Figure out which half-basis we are in (out of a bit-length cycle) */
+ phase = rdsd->i*2/RDS_BASISLENGTH;
+ /*Now what we do depends on the phase variable */
+ /*Phase 0: Bitslice and do timing alignment */
+ /*others (1-3): Keep value for timing alignment */
+
+ if (phase == 0) { /*Main processing (bitslice) */
+ u32 Ph;
+ u8 OldBit = rdsd->sliced_data; /* Save the previous value */
+
+ rdsd->return_num = 1;
+ if (filter_out >= 0) { /*This bit is "1" */
+ /*return value is XOR of previous bit (still in
+ * sliced_data) w/ this */
+ /* bit (1), which equals (NOT of the previous bit) */
+ rdsd->return_rdsdemod = !OldBit;
+ rdsd->sliced_data = 1; /*Newest bit value is 1 */
+ } else { /*This bit is "0" */
+ /*return value is XOR of previous bit (still in
+ * sliced_data) w/ this */
+ /* bit (0), which equals the previous bit */
+ rdsd->return_rdsdemod = OldBit;
+ rdsd->sliced_data = 0; /*Newest bit value is 0 */
+ }
+
+ freeSpace = bits_free(rdsb);
+
+ if (freeSpace > 0)
+ put1bit(rdsb, rdsd->return_rdsdemod);
+ else
+ rdsd->RdsDemodSkippedBitCnt++;
+
+ /*Increment bits received counter */
+ rdsd->BitAlignmentCounter++;
+ /*If mixer phase determination hasn't been done, start it */
+ if ((rdsd->MixPhaseState == 0) && (!rdsd->MixPhaseDetInProg)) {
+ rdsd->MixPhaseDetInProg = 1;
+ /*Go to first mixer setting (0) */
+ rds_mix_msg(rdsd, 0);
+ }
+
+ /* Do bit-slicing time adaption after the mixer phase
+ * determination */
+ if (!(rdsd->MixPhaseDetInProg) && !(rdsd->Synchronous)) {
+
+ /* Bitslice Timing Adjust Code (runs after
+ * MixPhaseDetInProg and if RDS is not synchronous to
+ * the FM pilot. */
+
+ u8 BigPh2; /* Expecting a large value in
+ * PhaseValue[2] */
+ u32 MaxRMS = 0; /*Largest phase RMS */
+ s8 MaxPh = 0; /*Index of largest phase RMS */
+ s32 zerocross;
+
+ /* Locate the largest phase RMS
+ * (should be at phase zero) */
+ for (Ph = 0; Ph < 4; Ph++)
+ if (rdsd->Ph_RMS[Ph] > MaxRMS) {
+ MaxRMS = rdsd->Ph_RMS[Ph];
+ MaxPh = Ph;
+ }
+
+ /* During each bit time we expect the four phases to
+ * take one of the following patterns, where 1
+ * corresponds to maximum modulation:
+ * 1, 0, -1, 0 Case I
+ * -1, 0, 1, 0 Case II
+ * 1, 1/2, 0, -1/2 Case III
+ * -1, -1/2, 0, 1/2 Case IV
+ * We need to distinguish between cases in order to do
+ * the timing adjustment. Below we compare the
+ * correlation of the samples with Case I and Case III
+ * to see which has a bigger abs(correlation). Thus
+ * BigPh2, if set, means that we decided on Case I or
+ * Case II; if BigPh2 clear, we decided Case III or IV.
+ */
+ BigPh2 = abs(rdsd->PhaseValue[0]-rdsd->PhaseValue[2]) >
+ abs(rdsd->PhaseValue[0] +
+ ((rdsd->PhaseValue[1]-
+ rdsd->PhaseValue[3])>>1));
+ /* If BigPh2, use the difference between phase 1 value
+ * (downgoing for Case I, upgoing for Case II) and
+ * phase 3 value (upgoing for Case I, downgoing for
+ * Case II, thus the subtraction) to indicate timing
+ * error. If not BigPh2, use the sum of the phase 1
+ * value (downgoing for Case III, upgoing for Case IV)
+ * and phase 3 value (downgoing for Case III, upgoing
+ * for Case IV, thus the addition) to indicate timing
+ * error. If BigPh2, the slopes at phase 1 & phase 3
+ * are approximately double that if not BigPh2.
+ * Since we are trying to measure timing, scale
+ * by 1/2 in the BigPh2 case. */
+ if (BigPh2)
+ zerocross = (rdsd->PhaseValue[1]-
+ rdsd->PhaseValue[3])>>1;
+ else
+ zerocross = rdsd->PhaseValue[1]+
+ rdsd->PhaseValue[3];
+ /* Now if the prev bit was a "1", then the first zero
+ * crossing (phase 1 if BigPh2, phase 2 if !BigPh2)
+ * was a falling one, and if we were late then
+ * zerocross should be negative. If the prev bit was a
+ * "0", then the first zero crossing was a rising one,
+ * and if we were late then zerocross would be
+ * positive. If we are "late" it means that we need to
+ * do a shorter cycle of, say, 15 samples instead of
+ * 16, to "catch up" so that in the future we will be
+ * sampling earlier. We shorten the cycle by adding
+ * to i, so "late" is going to mean "increment i".
+ * Therefore "late" should be positive, which is done
+ * here by inverting zerocross if the previous bit was
+ * 1. You could say that this step reflects cases I
+ * and III into II and IV, respectively. */
+ if (OldBit)
+ zerocross = -zerocross;
+ if (!rdsd->DisablePushing) {
+ /*The algorithm so far has a stable operating
+ * point 17 phases away from the correct one.
+ * The following code is experimental and may
+ * be deleterious in low SNR conditions, but is
+ * an attempt to move off of the incorrect
+ * operating point. */
+
+ if (MaxPh != 0) {
+ /* If it isn't the same MaxPh as the
+ * last non-zero one, clear the counter
+ */
+ if (MaxPh != rdsd->PushLastMaxPh) {
+ /*Reset the counter */
+ rdsd->PushCounter = 0;
+ /*Record which phase we're now
+ * counting */
+ rdsd->PushLastMaxPh = MaxPh;
+ }
+ /* If the Max RMS is on the same
+ * non-zero phase, count up */
+ rdsd->PushCounter++;
+ }
+ /* Once every 128 bits, check and then reset
+ * PushCounter */
+ if (!(rdsd->BitAlignmentCounter & 0x0FF)) {
+ /*If 90% of the time the max phase has
+ * been from the same non-zero phase,
+ * decide that we are latched onto a 0
+ * lock point. Do a large push of the
+ * timing. */
+ if (rdsd->PushCounter > 230) {
+ s32 pshiph;
+ /*Convert from phase number to
+ * the number of filter
+ * output samples that we need
+ * to shift */
+ if (rdsd->PushLastMaxPh >= 2)
+ pshiph =
+ 4 - (s8)rdsd->
+ PushLastMaxPh;
+ else
+ pshiph =
+ -(s8)rdsd->
+ PushLastMaxPh;
+ /* Scale by the number of i-
+ * phases per output sample */
+ pshiph <<=
+ RDS_BASISSHIFTS-1;
+ /* Perform big pop to get near
+ * correct timing */
+ rdsd->i += (RDS_BASISLENGTH<<1)
+ + pshiph;
+ /* Set status indicating big
+ * pop was needed. Reset all
+ * leaky-bucket and summation
+ * variables because the big
+ * timing shift has invalidated
+ * them. Ph_RMS values don't
+ * need to be reset because
+ * they will shift over to
+ * reasonable values again
+ * before their erroneous
+ * values could have effect. */
+ rdsd->rds_big_timeshift = 1;
+ /*rdsd->Ph_RMS[0] = 0; */
+ /*rdsd->Ph_RMS[1] = 0; */
+ /*rdsd->Ph_RMS[2] = 0; */
+ /*rdsd->Ph_RMS[3] = 0; */
+ rdsd->mixandsum1 = 0;
+ rdsd->mixandsum2 = 0;
+ rdsd->SkipsAccum +=
+ pshiph;
+
+ /* Make adjustments in other
+ * values because of the push
+ * (they wouldn't otherwise be
+ * able to use the information
+ * that a push was needed in
+ * their future control
+ * decisions). */
+ if (rdsd->PushLastMaxPh != 2) {
+ /* If we weren't
+ * pushing from phase
+ * two, accumulate (for
+ * use in adapting
+ * SDNOMINAL) the
+ * phases moved by
+ * pushing. Phase two
+ * pushes are not used;
+ * the push direction
+ * is arbitrary since
+ * Phase 2 is 180
+ * degrees out. Also,
+ * phase 2 pushes don't
+ * result from
+ * reasonable slippage.
+ * */
+
+ if (rdsd->sdnom_adapt)
+ rdsd->SdnomSk
+ += pshiph;
+
+ /* Modify timing_adj to
+ * account for half of
+ * the DC response that
+ * would have occurred
+ * in timing_adj if
+ * that control loop
+ * had seen the push
+ * happen. (Why half?
+ * Because the loop has
+ * already seen a
+ * history of zerocross
+ * values that heads it
+ * in the same
+ * direction as this
+ * adjustment, but may
+ * have seen as few as
+ * half of what it
+ * should have.) */
+ rdsd->timing_adj +=
+ pshiph <<
+ (TADJSH+1);
+ }
+ /*Set countdown timer that will
+ * prevent any mixer popping
+ * until the Ph_RMS variables
+ * have had enough time to
+ * stabilize */
+
+ /* 2.5 time constants */
+ rdsd->PushSafetyZone = 5;
+ }
+ /*Reset the push counter */
+ rdsd->PushCounter = 0;
+ } /*end once every 128 bits */
+ } /*end if !DisablePushing */
+
+ /* Further possible additions:
+ *
+ * 1. Pushes modify timing_adj to decrease convergence
+ * time.
+ * 2. Separate timing_adj into pilottracking and non-pt
+ * cases (avoids convergence time after stereo/mono
+ * transitions)
+ *
+ * Old loop filter was a leaky bucket integrator, and
+ * it always lagged behind if the FM station had RDS
+ * asynchronous to the pilot, because the control loop
+ * needs another integrator to converge on a frequency
+ * error.
+ * New loop filter = 1/(1-1/z) * (a-1/z) * k,
+ * where a = 1+1/256 and k = 1/1024.
+ * You can narrow the loop bandwidth by making "a"
+ * twice as close to 1 and halving k, e.g. a = 1+1/512
+ * and k = 1/2048.
+ * (The way implemented, that narrowing loop BW by
+ * a factor of 2 can be done by incrementing TADJSH.)
+ *
+ * TGR 8/31/2007 */
+
+ /*Integrator, 1/(1-1/z) */
+ rdsd->timing_adj += zerocross;
+ /*Limit to 1 phase every 8 samples */
+ if (rdsd->SkipSafetyZone) {
+ rdsd->SkipSafetyZone--;
+ rdsd->sampskip = 0;
+ } else {
+ /*sampskip of non-zero is allowed,
+ * calculate what it really is */
+
+ /*Saturate timing_adj to 2's comp
+ * (2*TADJSH+4)-bit range. */
+ if (rdsd->timing_adj > (1<<(2*TADJSH+3))-1)
+ rdsd->timing_adj = (1<<(2*TADJSH+3))-1;
+ if (rdsd->timing_adj < -(1<<(2*TADJSH+3)))
+ rdsd->timing_adj = -(1<<(2*TADJSH+3));
+
+ /* Zero, implemented after the integrator
+ * output.
+ * (a-1/z) = (1+1/256) - 1/z = (1-1/z) + 1/256.
+ * But (1 - 1/z) is timing_adj-
+ * prev_timing_adj = zerocross. */
+ rdsd->sampskip = zerocross /* 1 - 1/z */
+ /* 1/256 (with rounding) */
+ + ((rdsd->timing_adj
+ + (1<<(TADJSH-1)))>>TADJSH);
+ /*Round and apply k */
+ rdsd->sampskip += (1<<(TADJSH+1));
+ rdsd->sampskip >>= (TADJSH+2);
+ /*Limit to [-1,+1] inclusive */
+ if (rdsd->sampskip > 1)
+ rdsd->sampskip = 1;
+ if (rdsd->sampskip < -1)
+ rdsd->sampskip = -1;
+ /* If non-zero, start the skip safety zone,
+ * which excludes more sample skipping for a
+ * while. Note that the safety zone only
+ * applies to the skips -- pushes can still
+ * happen inside a SkipSafetyZone. */
+ if (rdsd->sampskip)
+ rdsd->SkipSafetyZone = 8-1;
+ }
+ /**********************************************
+ * End Timing Adjust Code
+ **********************************************/
+
+ /**********************************************
+ * Begin Phase Popper Code
+ **********************************************/
+ /* If Phase Popping is enabled and 1/2 of a
+ * time constant has gone by... */
+ if (rdsd->PhasePoppingEnabled &&
+ !(rdsd->BitAlignmentCounter &
+ ((1<<(RMSALPHASHIFTS-1))-1))) {
+
+ u8 ForcePop = 0; /* Used to force a pop */
+
+ /*Record the maximum of the envelope */
+ if (MaxRMS > rdsd->PhasePopMaxRMS)
+ rdsd->PhasePopMaxRMS = MaxRMS;
+ /* Also track MaxRMS into MixPhase0/1Mag, so
+ * that we can see what the largest RMS on each
+ * of those phases is. On synchronous stations
+ * (meaning the RDS carrier and bit rate are
+ * synchronized with the pilot), the right mix
+ * phase will always be big and the wrong phase
+ * small. On asynchronous stations (and
+ * stations without RDS), both phases will at
+ * some time or other have about the
+ * same amplitude on each of the phases. */
+ if (rdsd->rds_mix_offset) {
+ if (MaxRMS > rdsd->MixPhase1Mag)
+ rdsd->MixPhase1Mag = MaxRMS;
+ } else {
+ if (MaxRMS > rdsd->MixPhase0Mag)
+ rdsd->MixPhase0Mag = MaxRMS;
+ }
+ /* Update PopSafetyZone and PushSafetyZone
+ * counters. With RMSALPHASHIFTS = 5, each
+ * tick is 16/1187.5 =~ 13.5 ms. */
+ if (rdsd->PopSafetyZone) {
+ rdsd->PopSafetyZone--;
+ /* If safety zone just ended and this
+ * mix phase is giving smaller RMS than
+ * before the pop, then the pop was a
+ * mistake. Go back to previous mixer
+ * phase */
+ if (!(rdsd->PopSafetyZone)
+ && (rdsd->PhasePopMaxRMS <
+ rdsd->PrePopRMS))
+ ForcePop = 1;
+ }
+ /* If there is no recent push, and Phase 0 has
+ * the maximum RMS, and at least 1/7th of a
+ * second has passed since the last phase pop,
+ * and ((the RMS is less than 1/2 of
+ * PhasePopMaxRMS) or (the RMS is less than
+ * 100)), then try a phase pop. */
+ if (/* (rdsd->Ph_RMS[0] == MaxRMS) &&
+ * Phase 0 has maximum RMS */
+ !(rdsd->PopSafetyZone)) {
+ /* and Long enough since last
+ * phase pop */
+
+ /* Eligible for a pop, see if one of
+ * the pop conditions is met */
+ if ((MaxRMS<<1) <
+ rdsd->PhasePopMaxRMS) {
+ /*RMS decline from its peak */
+ ForcePop = 1;
+ } else if ((MaxRMS>>RMSALPHASHIFTS)
+ < 50) {
+ /*RMS too small to receive,
+ * either there's no RDS or
+ * this is the wrong phase */
+ ForcePop = 1;
+ }
+ }
+ if (ForcePop) {
+
+ /*Pop to opposite setting */
+ rds_mix_msg(rdsd, 0x8 |
+ !rdsd->rds_mix_offset);
+
+ /*Save the pre-pop RMS so that later we
+ * can see if the pop was actually
+ * effective */
+ rdsd->PrePopRMS = MaxRMS;
+ /*Reset the PhasePopMaxRMS. We rely on
+ * the PopSafetyZone to give time to
+ * get a new valid max RMS before we're
+ * eligible for the next phase pop. If
+ * there were no reset we'd be forever
+ * incrementing PhasePopMaxRMS due
+ * to just happenstance large-noise
+ * samples and it might eventually get
+ * some freakish large value causing
+ * frequent erroneous pops. */
+ rdsd->PhasePopMaxRMS = 0;
+ /* Pop Safety zone length is decided by
+ * how much of an asynchronous
+ * frequency can be supported. Allowing
+ * 50 ppm of transmitter error (error
+ * between their own pilot, that we
+ * should be locked to, and their RDS
+ * carrier (which by RDS spec should be
+ * locked to their pilot, but we've
+ * recently found frequently isn't).
+ * 50ppm * 57kHz = 2.85Hz.
+ * (2.85 cycles/sec)(4 pops/cycle)
+ * = 11.4 pops/second.
+ * Safety zone = (1/11.4) seconds =~ 104
+ * bits, round down to 96 bits which
+ * yields 6 ticks if RMSALPHASHIFTS = 5.
+ * */
+ rdsd->PopSafetyZone = 96>>
+ (RMSALPHASHIFTS-1);
+ }
+ }
+ /******************************************************
+ * End Phase Popper Code
+ ******************************************************/
+
+ /* SDNOMINAL adaption */
+ if (rdsd->sdnom_adapt) {
+ rdsd->SdnomSk += rdsd->sampskip;
+ if (rdsd->pCoefForcedMono &&
+ (rdsd->BitAlignmentCounter & 0xFFF) ==
+ 0x800) {
+
+ rds_sdnominal_msg(rdsd,
+ -(rdsd->SdnomSk<<9));
+
+ /*Reset skips counter */
+ rdsd->SdnomSk = 0;
+ }
+ }
+
+ rdsd->SkipsAccum += rdsd->sampskip;
+ /* Once per 3.45 seconds, print out signal strength,
+ * skips and pops. Then reset the variables totalling
+ * those occurrences */
+ if (!(rdsd->BitAlignmentCounter & 0xFFF)) {
+ /* During very noisy input (or if no RDS, or no
+ * station present), timing_adj can go crazy,
+ * since it is the integral of noise. Although
+ * it is a saturated value (earlier, in the
+ * timing adjust code), the level at which we
+ * can saturate still leaves room for
+ * timing_adj to get too big. A large value of
+ * timing_adj is a persistent pathology because
+ * the phase is shifting so quickly that the
+ * push detector (which relies on stable
+ * phase-RMS values) never triggers, thus there
+ * is no implemented rescue besides this
+ * clearing that restores proper function. */
+ if (abs(rdsd->SkipsAccum) > 300)
+ rdsd->timing_adj = 0;
+ /*Reset the accumulations. */
+ rdsd->SkipsAccum = 0;
+ }
+ } /*End of bit timing adaption */
+
+ /* If mixer phase determination in progress,
+ * perform actions at certain times */
+ if (rdsd->MixPhaseDetInProg) {
+ /*~10ms settling time after mixer phase change */
+ #define MIXPHASE_STARTMEAS 12
+ /*~20ms measurement window */
+ #define MIXPHASE_ENDMEAS (MIXPHASE_STARTMEAS+24)
+ if (rdsd->BitAlignmentCounter == MIXPHASE_STARTMEAS) {
+ /*Reset the RMS variables */
+ rdsd->Ph_RMS[0] = 0;
+ rdsd->Ph_RMS[1] = 0;
+ rdsd->Ph_RMS[2] = 0;
+ rdsd->Ph_RMS[3] = 0;
+ /* Don't reset mixandsum values because at
+ * least they have filtered continuously. All
+ * we really need for the mixer phase decision
+ * is a constant measurement window. */
+ } else if (rdsd->BitAlignmentCounter ==
+ MIXPHASE_ENDMEAS) {
+ /*Measurement = mean of RMS values */
+ u32 Ndx, MeasVal = 0;
+ for (Ndx = 0; Ndx < 4;
+ MeasVal += rdsd->Ph_RMS[Ndx++]>>2);
+ /*Store measurement in correct place */
+ if (rdsd->MixPhaseState == 1) {
+ rdsd->MixPhase0Mag = MeasVal;
+ /*Go to next mixer setting */
+ rds_mix_msg(rdsd, 1);
+ } else if (rdsd->MixPhaseState == 2) {
+ u8 NextMixSetting;
+ rdsd->MixPhase1Mag = MeasVal;
+ /* Both measurements done now, see what
+ * mixer setting we need to use.
+ * 0 if MixPhase0Mag > MixPhase1Mag,
+ * 1 otherwise. */
+ NextMixSetting = (rdsd->MixPhase0Mag
+ <= rdsd->MixPhase1Mag);
+ /* If the mixer setting needed is 1,
+ * that is already the current setting.
+ * Terminate mixer phase determination.
+ * Otherwise send message to switch the
+ * mixer phase setting. */
+ if (NextMixSetting) {
+ rdsd->MixPhaseState = 3;
+ rdsd->MixPhaseDetInProg = 0;
+ } else
+ rds_mix_msg(rdsd, 0);
+ }
+ }
+ /* Reset BitAlignmentCounter if the Mixer just popped
+ * Change state, if required. States are:
+ * 0: Initial state, send msg causing RDS_MIXOFFSET=>0
+ * 1: Measure with RDS_MIXOFFSET = 0.
+ * Lasts just over 30 ms.
+ * 2: Measure with RDS_MIXOFFSET = 1.
+ * Lasts just over 30 ms.
+ * 3: At final RDS_MIXOFFSET value.
+ * Lasts as long as RDS continues. */
+ if (rdsd->MixPopDone) {
+ rdsd->MixPopDone = 0;
+ rdsd->BitAlignmentCounter = 0;
+ rdsd->MixPhaseState++; /*Go to next state */
+ /* If we got to state 3, turn off mixer phase
+ * determination code */
+ if (rdsd->MixPhaseState == 3)
+ rdsd->MixPhaseDetInProg = 0;
+ }
+ }
+
+ /* Update status variables */
+ rdsd->RDS_BIT_AMP_STAT_REG9 = rdsd->Ph_RMS[0]>>RMSALPHASHIFTS;
+ /*Saturate */
+ if (rdsd->RDS_BIT_AMP_STAT_REG9 > 511)
+ rdsd->RDS_BIT_AMP_STAT_REG9 = 511;
+ } /*End phase 0 code */
+
+ /***************************************************
+ * Actions common to all phases
+ ***************************************************/
+
+ /* Save the output of each phase for possible
+ * calculations during phase 0 */
+ rdsd->PhaseValue[phase] = filter_out;
+
+ /*So that we can measure signal amplitude and/or determine what (if */
+ /* any) big jump is needed, maintain the RMS of each phase. Phase */
+ /* 0 RMS is already in Ph_RMS[0] (see bitslicing code, earlier). */
+ rdsd->Ph_RMS[phase] += abs(filter_out) -
+ (rdsd->Ph_RMS[phase]>>RMSALPHASHIFTS);
+}
+
+#if defined(CONFIG_ARM)
+
+/* assembly version for ARM */
+#define RDS_MAC(_acc, _x, _y) \
+ __asm__ __volatile__ ( \
+ "smlabb %0, %1, %2, %0\n" \
+ : "=&r" (_acc) \
+ : "r" (_x), "r" (_y) \
+ : "cc")
+
+#else
+
+/* all others, use standard C */
+#define RDS_MAC(_acc, _x, _y) \
+ do { \
+ (_acc) += (s16)(_x) * (s16)(_y); \
+ } while (0)
+
+#endif
+
+static void rds_demod(const u16 *data, struct stfm1000_rds_demod *rdsd,
+ struct stfm1000_rds_bitstream *rbit, int total)
+{
+ register const s16 *basis0;
+ register const s16 *basis1;
+ register s16 val;
+ register int i;
+ register int sampskip;
+ register s32 acc1;
+ register s32 acc2;
+
+ /* point to the table */
+ basis0 = u16_rds_basis;
+ basis1 = basis0 + 8;
+
+ rdsd->return_num = 0;
+
+ /* restore state */
+ i = rdsd->i;
+ acc1 = rdsd->mixandsum1;
+ acc2 = rdsd->mixandsum2; /* 64 bit */
+ sampskip = rdsd->sampskip;
+
+ while (total-- > 0) {
+
+ val = data[3]; /* load RDS data */
+ data += 4;
+ if (val == 0x7fff) /* illegal RDS sample */
+ continue;
+
+ RDS_MAC(acc1, val, basis0[i]);
+ RDS_MAC(acc2, val, basis1[i]);
+
+ if (i == 4) {
+ i += sampskip;
+ sampskip = 0;
+ }
+
+ if ((i & (RDS_BASISLENGTH / 2 - 1)) == 0) {
+
+ /* save state */
+ rdsd->mixandsum1 = acc1;
+ rdsd->mixandsum2 = acc2;
+ rdsd->i = i;
+ rdsd->sampskip = sampskip;
+
+ demod_loop(rbit, rdsd);
+
+ /* restore state */
+ acc1 = rdsd->mixandsum1;
+ acc2 = rdsd->mixandsum2;
+ i = rdsd->i;
+ sampskip = rdsd->sampskip;
+ }
+ i = (i + 1) & 31;
+ }
+
+ /* save state */
+ rdsd->mixandsum1 = acc1;
+ rdsd->mixandsum2 = acc2;
+ rdsd->i = i;
+ rdsd->sampskip = sampskip;
+}
+
+void stfm1000_rds_demod(struct stfm1000_rds_state *rds, const u16 *dri_data,
+ int total)
+{
+ rds_demod(dri_data, &rds->demod, &rds->bitstream, total);
+
+ /* signal only when we have enough */
+ if (bits_filled(&rds->bitstream) > 128)
+ stfm1000_monitor_signal(rds_state_to_stfm1000(rds),
+ EVENT_RDS_BITS);
+}
+
+static void bitstream_reset(struct stfm1000_rds_bitstream *rdsb)
+{
+ memset(rdsb, 0, sizeof(*rdsb));
+}
+
+static void demod_reset(struct stfm1000_rds_demod *rdsd)
+{
+ memset(rdsd, 0, sizeof(*rdsd));
+ rdsd->sdnom_adapt = 0; /* XXX this doesn't really work right */
+ /* it causes underruns at ALSA */
+ rdsd->PhasePoppingEnabled = 1; /* does this? */
+}
+
+static void packet_reset(struct stfm1000_rds_pkt *rdsp)
+{
+ memset(rdsp, 0, sizeof(*rdsp));
+ rdsp->state = SYNC_OFFSET_A;
+}
+
+static void text_reset(struct stfm1000_rds_text *rdst)
+{
+ memset(rdst, 0, sizeof(*rdst));
+}
+
+void stfm1000_rds_reset(struct stfm1000_rds_state *rds)
+{
+ bitstream_reset(&rds->bitstream);
+ demod_reset(&rds->demod);
+ packet_reset(&rds->pkt);
+ text_reset(&rds->text);
+ rds->reset_req = 0;
+}
+
+int stfm1000_rds_bits_available(struct stfm1000_rds_state *rds)
+{
+ return bits_filled(&rds->bitstream);
+}
+
+int stmf1000_rds_get_bit(struct stfm1000_rds_state *rds)
+{
+ if (bits_filled(&rds->bitstream) == 0)
+ return -1;
+ return get1bit(&rds->bitstream);
+}
+
+int stmf1000_rds_avail_bits(struct stfm1000_rds_state *rds)
+{
+ return bits_filled(&rds->bitstream);
+}
+
+static const u32 rds_ParityCheck[] = {
+ 0x31B, 0x38F, 0x2A7, 0x0F7, 0x1EE,
+ 0x3DC, 0x201, 0x1BB, 0x376, 0x355,
+ 0x313, 0x39F, 0x287, 0x0B7, 0x16E,
+ 0x2DC, 0x001, 0x002, 0x004, 0x008,
+ 0x010, 0x020, 0x040, 0x080, 0x100,
+ 0x200
+};
+
+static int calc_syndrome(u32 rdscrc)
+{
+ int i;
+ u32 syndrome = 0;
+ int word = 0x1;
+
+ for (i = 0; i < 26; i++) {
+ if (rdscrc & word)
+ syndrome ^= rds_ParityCheck[i];
+ word <<= 1;
+ }
+ return syndrome;
+}
+
+static u32 ecc_table[1024];
+static int ecc_table_generated;
+
+static void generate_ecc_table(void)
+{
+ int i, j, size;
+ u32 syndrome, word;
+
+ for (i = 0; i < ECC_TBL_SIZE; i++)
+ ecc_table[i] = 0xFFFFFFFF;
+ ecc_table[0] = 0x0;
+
+ for (j = 0; j < 5; j++) {
+ word = (1 << (j + 1)) - 1; /* 0x01 0x03 0x07 0x0f 0x1f */
+ size = 26 - j; /* 26, 25, 24, 23, 22 */
+ syndrome = 0;
+ for (i = 0; i < size; i++) {
+ syndrome = calc_syndrome(word);
+ ecc_table[syndrome] = word;
+ word <<= 1;
+ }
+ }
+}
+
+static u32 ecc_correct(u32 rdsBits, int *recovered)
+{
+ u32 syndrome;
+ u32 errorBits;
+
+ if (recovered)
+ *recovered = 0;
+
+ /* Calculate Syndrome on Received Packet */
+ syndrome = calc_syndrome(rdsBits);
+
+ if (syndrome == 0)
+ return rdsBits; /* block is clean */
+
+ /* generate table first time we get here */
+ if (!ecc_table_generated) {
+ generate_ecc_table();
+ ecc_table_generated = 1;
+ }
+
+ /* Attempt to recover block */
+ errorBits = ecc_table[syndrome];
+ if (errorBits == UNRECOVERABLE_RDS_BLOCK)
+ return UNRECOVERABLE_RDS_BLOCK; /* Block can not be recovered.
+ * it is bad packet */
+
+ rdsBits = rdsBits ^ errorBits;
+ if (recovered)
+ (*recovered)++;
+ return rdsBits; /* ECC correct */
+}
+
+/* The following table lists the RDS and RBDS Program Type codes
+ * and their meanings:
+ * PTY code RDS Program type RBDS Program type */
+static const struct stfm1000_rds_pty stc_tss_pty_tab[] = {
+ { 0, "No program type", "No program type"},
+ { 1, "News", "News"},
+ { 2, "Current affairs", "Information"},
+ { 3, "Information", "Sports"},
+ { 4, "Sports", "Talk"},
+ { 5, "Education", "Rock"},
+ { 6, "Drama", "Classic Rock"},
+ { 7, "Culture", "Adult Hits"},
+ { 8, "Science", "Soft Rock"},
+ { 9, "Varied", "Top 40"},
+ { 10, "Pop", "Music Country"},
+ { 11, "Rock", "Music Oldies"},
+ { 12, "M.O.R.", "Music Soft"},
+ { 13, "Light classical", "Nostalgia"},
+ { 14, "Serious", "Classical Jazz"},
+ { 15, "Other Music", "Classical"},
+ { 16, "Weather", "Rhythm and Blues"},
+ { 17, "Finance", "Soft Rhythm and Blues"},
+ { 18, "Children's programs", "Language"},
+ { 19, "Social Affairs", "Religious Music"},
+ { 20, "Religion", "Religious Talk"},
+ { 21, "Phone In", "Personality"},
+ { 22, "Travel", "Public"},
+ { 23, "Leisure", "College"},
+ { 24, "Jazz Music", "Unassigned"},
+ { 25, "Country Music", "Unassigned"},
+ { 26, "National Music", "Unassigned"},
+ { 27, "Oldies Music", "Unassigned"},
+ { 28, "Folk Music", "Unassigned"},
+ { 29, "Documentary", "Weather"},
+ { 30, "Alarm Test", "Emergency Test"},
+ { 31, "Alarm", "Emergency"},
+};
+
+#if 0
+static const char *rds_group_txt[] = {
+ [RDS_GROUP_TYPE_0A] = "Basic tuning and switching information (0A)",
+ [RDS_GROUP_TYPE_0B] = "Basic tuning and switching information (0B)",
+ [RDS_GROUP_TYPE_1A] = "Program item number and slow labeling codes",
+ [RDS_GROUP_TYPE_1B] = "Program item number",
+ [RDS_GROUP_TYPE_2A] = "Radio Text (2A)",
+ [RDS_GROUP_TYPE_2B] = "Radio Text (2B)",
+ [RDS_GROUP_TYPE_3A] = "Application identification for ODA only",
+ [RDS_GROUP_TYPE_3B] = "Open data applications",
+ [RDS_GROUP_TYPE_4A] = "Clock-time and date",
+ [RDS_GROUP_TYPE_4B] = "Open data applications",
+ [RDS_GROUP_TYPE_5A] = "Transparent Data Channels (32 ch.) or ODA (5A)",
+ [RDS_GROUP_TYPE_5B] = "Transparent Data Channels (32 ch.) or ODA (5B)",
+ [RDS_GROUP_TYPE_6A] = "In House Applications or ODA (6A)",
+ [RDS_GROUP_TYPE_6B] = "In House Applications or ODA (6B)",
+ [RDS_GROUP_TYPE_7A] = "Radio Paging or ODA",
+ [RDS_GROUP_TYPE_7B] = "Open Data Applications",
+ [RDS_GROUP_TYPE_8A] = "Traffic Message Channel or ODA",
+ [RDS_GROUP_TYPE_8B] = "Open Data Applications",
+ [RDS_GROUP_TYPE_9A] = "Emergency warning system or ODA",
+ [RDS_GROUP_TYPE_9B] = "Open Data Applications",
+ [RDS_GROUP_TYPE_10A] = "Program Type Name",
+ [RDS_GROUP_TYPE_10B] = "Open Data Applications (10B)",
+ [RDS_GROUP_TYPE_11A] = "Open Data Applications (11A)",
+ [RDS_GROUP_TYPE_11B] = "Open Data Applications (11B)",
+ [RDS_GROUP_TYPE_12A] = "Open Data Applications (12A)",
+ [RDS_GROUP_TYPE_12B] = "Open Data Applications (12B)",
+ [RDS_GROUP_TYPE_13A] = "Enhanced Radio Paging or ODA",
+ [RDS_GROUP_TYPE_13B] = "Open Data Applications",
+ [RDS_GROUP_TYPE_14A] = "Enhanced Other Networks information (14A)",
+ [RDS_GROUP_TYPE_14B] = "Enhanced Other Networks information (14B)",
+ [RDS_GROUP_TYPE_15A] = "Defined in RBDS",
+ [RDS_GROUP_TYPE_15B] = "Fast switching information",
+};
+#endif
+
+static void dump_rds_packet(u8 *buf)
+{
+ u16 pi, offb;
+
+ pi = (u16)(buf[0] << 8) | buf[1];
+ offb = (u16)(buf[1] << 8) | buf[2];
+
+ printk(KERN_INFO "GRP: "
+ "PI=0x%04x "
+ "GT=%2d VER=%d TP=%d PTY=%2d "
+ "PS_SEG=%2d RT_AB=%2d RT_SEG=%2d\n", pi,
+ RDS_GROUP_TYPE(offb), RDS_VERSION(offb), RDS_TP(offb),
+ RDS_PTY(offb),
+ RDS_PS_SEG(offb), RDS_RT_AB(offb), RDS_RT_SEG(offb));
+}
+
+void stfm1000_rds_process_packet(struct stfm1000_rds_state *rds, u8 *buffer)
+{
+ struct stfm1000_rds_text *rdst = &rds->text;
+ /* char tempCallLetters[5] = {0}; */
+ struct rds_group_data grp;
+ int grpno;
+ u32 offset;
+ char tps[9];
+ int i, seg, idx;
+
+ grp.piCode = ((u16)buffer[0] << 8) | buffer[1];
+ grp.offsetB = ((u16)buffer[2] << 8) | buffer[3];
+ grp.offsetC = ((u16)buffer[4] << 8) | buffer[5];
+ grp.offsetD = ((u16)buffer[6] << 8) | buffer[7];
+
+ grpno = (grp.offsetB >> (8 + 3)) & 0x1f;
+
+ if (rds_state_to_stfm1000(rds)->rds_info)
+ dump_rds_packet(buffer);
+
+ /* Is this the first time through? */
+ if (!rdst->bRds_detected) {
+ rdst->pi = grp.piCode;
+ rdst->tp = RDS_TP(grp.offsetB);
+ rdst->version = RDS_VERSION(grp.offsetB);
+ rdst->pty.id = RDS_PTY(grp.offsetB);
+ rdst->pty.pRds = stc_tss_pty_tab[rdst->pty.id].pRds;
+ rdst->pty.pRdbs = stc_tss_pty_tab[rdst->pty.id].pRdbs;
+ rdst->bRds_detected = 1;
+ }
+
+ /* Have we process too many PI errors? */
+ if (grp.piCode != rdst->pi) {
+ if (rdst->mismatch++ > 10) {
+
+ /* requested reset of RDS */
+ rds->reset_req = 1;
+
+ /* signal monitor thread */
+ stfm1000_monitor_signal(rds_state_to_stfm1000(rds),
+ EVENT_RDS_RESET);
+
+ if (rds_state_to_stfm1000(rds)->rds_info)
+ printk(KERN_INFO "RDS: RESET!!!\n");
+
+ text_reset(rdst);
+ }
+ rdst->consecutiveGood = 0;
+ return;
+ }
+
+ if (rdst->consecutiveGood++ > 10)
+ rdst->mismatch = 0; /* reset bad count */
+
+ if (rdst->consecutiveGood > rdst->consecutiveGoodMax)
+ rdst->consecutiveGoodMax = rdst->consecutiveGood;
+
+ switch (grpno) {
+ case RDS_GROUP_TYPE_0A:
+ case RDS_GROUP_TYPE_0B:
+ /* Extract Service Name information */
+ offset = RDS_PS_SEG(grp.offsetB) * 2;
+ rdst->wk_ps[offset] = buffer[6]; /* better */
+ rdst->wk_ps[offset + 1] = buffer[7];
+ rdst->wk_ps_mask |= 1 << RDS_PS_SEG(grp.offsetB);
+
+ if (rds_state_to_stfm1000(rds)->rds_info) {
+ for (i = 0; i < 8; i++) {
+ if (rdst->wk_ps_mask & (1 << i)) {
+ tps[i * 2] =
+ rdst->wk_ps[i * 2];
+ tps[i * 2 + 1] =
+ rdst->wk_ps[i * 2 + 1];
+ } else {
+ tps[i * 2] = '_';
+ tps[i * 2 + 1] = '_';
+ }
+ }
+ tps[ARRAY_SIZE(tps) - 1] = '\0';
+ if (rds_state_to_stfm1000(rds)->rds_info)
+ printk(KERN_INFO "RDS-PS (curr): %s\n", tps);
+ }
+
+ if (rdst->wk_ps_mask != ALL_SEGMENT_BITS)
+ break;
+
+ if (rdst->ps_valid) {
+ if (memcmp(rdst->ps, rdst->wk_ps, 8) != 0) {
+ memset(rdst->cp_ps, 0, 8);
+ memset(rdst->wk_ps, 0, 8);
+ rdst->wk_ps_mask = 0;
+ }
+
+ memset(rdst->ps, 0, 8);
+ rdst->ps_valid = 0;
+ break;
+ }
+
+ /* does working buffer == compare buffer */
+ if (memcmp(rdst->cp_ps, rdst->wk_ps, 8) != 0) {
+ /* just copy from working to compare buffer */
+ memcpy(rdst->cp_ps, rdst->wk_ps, 8);
+ rdst->wk_ps_mask = 0;
+ break;
+ }
+
+ /* working buffer matches compare buffer, send to UI */
+ memcpy(rdst->ps, rdst->cp_ps, 8);
+ rdst->ps_valid = 1;
+
+ if (rds_state_to_stfm1000(rds)->rds_info)
+ printk(KERN_INFO "RDS: PS '%s'\n", rdst->ps);
+
+ /* clear working mask-only */
+ rdst->wk_ps_mask = 0;
+ break;
+
+ case RDS_GROUP_TYPE_2A:
+
+ /* Clear buffer */
+ if (rdst->textAB_flag != RDS_RT_AB(grp.offsetB)) {
+ memset(rdst->wk_text, 0, 64);
+ rdst->wk_text_mask = 0;
+ rdst->textAB_flag = RDS_RT_AB(grp.offsetB);
+ }
+
+ /* Extract Text */
+ seg = RDS_RT_SEG(grp.offsetB);
+ idx = seg * 4;
+
+ #define CNVT_EOT(x) ((x) != RDS_EOT ? (x) : 0)
+ rdst->wk_text[idx++] = CNVT_EOT(buffer[4]);
+ rdst->wk_text[idx++] = CNVT_EOT(buffer[5]);
+ rdst->wk_text[idx++] = CNVT_EOT(buffer[6]);
+ rdst->wk_text[idx++] = CNVT_EOT(buffer[7]);
+
+ rdst->wk_text_mask |= 1 << seg;
+ /* scan msg data for EOT. If found set all higher
+ * mask bits */
+ for (idx = 0; idx < 4; idx++) {
+ if (rdst->text[idx] == RDS_EOT)
+ break;
+ }
+ if (idx < 4) {
+ /* set current and all higher bits */
+ for (idx = RDS_RT_SEG(grp.offsetB); idx < 16;
+ idx++)
+ rdst->wk_text_mask |= 1 << idx;
+ }
+
+ /* Process buffer when filled */
+ if (rdst->wk_text_mask != ALL_TEXT_BITS)
+ break;
+
+ if (!rdst->text_valid)
+ rdst->text_valid = 1;
+ else if (memcmp(rdst->text, rdst->wk_text, 64) == 0)
+ break;
+
+ memcpy(rdst->text, rdst->wk_text, 64);
+
+ if (rds_state_to_stfm1000(rds)->rds_info)
+ printk(KERN_INFO "RDS: TEXT '%s'\n", rdst->text);
+
+ memset(rdst->wk_text, 0, 64);
+ rdst->wk_text_mask = 0;
+ break;
+
+ default:
+ break;
+ }
+}
+
+int stfm1000_rds_packet_dequeue(struct stfm1000_rds_state *rds, u8 *buf)
+{
+ struct stfm1000_rds_pkt *pkt = &rds->pkt;
+
+ if (pkt->buf_cnt == 0)
+ return -1;
+
+ memcpy(buf, &pkt->buf_queue[pkt->buf_tail][0], 8);
+ if (++pkt->buf_tail >= RDS_PKT_QUEUE)
+ pkt->buf_tail = 0;
+ pkt->buf_cnt--;
+
+ return 0;
+}
+
+void stfm1000_rds_packet_bit(struct stfm1000_rds_state *rds, int bit)
+{
+ struct stfm1000_rds_pkt *pkt = &rds->pkt;
+ u32 rdsdata, rdscrc, rdscrc_c, rdscrc_cp;
+ int correct, correct2, recovered, recovered2;
+ int RetVal;
+
+ /* Stick into shift register */
+ pkt->rdsstream = ((pkt->rdsstream << 1) | bit) & 0x03ffffff;
+ pkt->bitsinfifo++;
+ pkt->bitcount++;
+
+ /* wait for 26 bits of block */
+ if (pkt->bitsinfifo < 26)
+ return;
+
+ rdsdata = pkt->rdsstream & 0x03fffc00; /* 16 bits of Info. word */
+ rdscrc = pkt->rdsstream & 0x3ff; /* 10 bits of Checkword */
+
+ switch (pkt->state) {
+ case SYNC_OFFSET_A:
+
+ RetVal = calc_syndrome(pkt->rdsstream);
+
+ switch (RetVal) {
+ case RDS_SYNDROME_OFFSETA:
+ pkt->state = OFFSET_B;
+ break;
+ case RDS_SYNDROME_OFFSETB:
+ pkt->state = OFFSET_C_CP;
+ break;
+ case RDS_SYNDROME_OFFSETC:
+ pkt->state = OFFSET_D;
+ break;
+ case RDS_SYNDROME_OFFSETCP:
+ pkt->state = OFFSET_D;
+ break;
+ case RDS_SYNDROME_OFFSETD:
+ pkt->state = OFFSET_A;
+ break;
+ default:
+ pkt->state = SYNC_OFFSET_A;
+ break;
+ }
+ if (pkt->state == SYNC_OFFSET_A) {
+ pkt->sync_lost_packets++;
+ /* XXX send info? */
+ break;
+ }
+
+ pkt->good_packets++;
+
+ rdsdata = pkt->rdsstream & 0x03fffc00;
+
+ /* Save type A packet in buffer */
+ rdsdata >>= 10;
+ pkt->buffer[0] = (rdsdata >> 8);
+ pkt->buffer[1] = (rdsdata & 0xff);
+ pkt->bitsinfifo = 0;
+
+ /* We found a block with zero errors, but it is not at the
+ * start of the group. */
+ if (pkt->state == OFFSET_B)
+ pkt->discardpacket = 0;
+ else
+ pkt->discardpacket = 1;
+ break;
+
+ case OFFSET_A: /* Type A: we are in sync now */
+ rdscrc ^= RDS_OFFSETA;
+ correct = ecc_correct(rdsdata | rdscrc, &recovered);
+ if (correct == UNRECOVERABLE_RDS_BLOCK) {
+ pkt->bad_packets++;
+ pkt->discardpacket++;
+ pkt->state++;
+ pkt->bitsinfifo = 0;
+ break;
+ }
+
+ if (recovered)
+ pkt->recovered_packets++;
+ pkt->good_packets++;
+
+ /* Attempt to see, if we can get the entire group.
+ * Don't discard. */
+ pkt->discardpacket = 0;
+ rdsdata = correct & 0x03fffc00;
+
+ /* Save type A packet in buffer */
+ rdsdata >>= 10;
+ pkt->buffer[0] = (rdsdata >> 8);
+ pkt->buffer[1] = (rdsdata & 0xff);
+ pkt->bitsinfifo = 0;
+ pkt->state++;
+ break;
+
+ case OFFSET_B: /* Waiting for type B */
+ rdscrc ^= RDS_OFFSETB;
+ correct = ecc_correct(rdsdata | rdscrc, &recovered);
+ if (correct == UNRECOVERABLE_RDS_BLOCK) {
+ pkt->bad_packets++;
+ pkt->discardpacket++;
+ pkt->state++;
+ pkt->bitsinfifo = 0;
+ break;
+ }
+ if (recovered)
+ pkt->recovered_packets++;
+ pkt->good_packets++;
+
+ rdsdata = correct & 0x03fffc00;
+
+ /* Save type B packet in buffer */
+ rdsdata >>= 10;
+ pkt->buffer[2] = (rdsdata >> 8);
+ pkt->buffer[3] = (rdsdata & 0xff);
+ pkt->bitsinfifo = 0;
+ pkt->state++;
+ break;
+
+ case OFFSET_C_CP: /* Waiting for type C or C' */
+ rdscrc_c = rdscrc ^ RDS_OFFSETC;
+ rdscrc_cp = rdscrc ^ RDS_OFFSETCP;
+ correct = ecc_correct(rdsdata | rdscrc_c, &recovered);
+ correct2 = ecc_correct(rdsdata | rdscrc_cp, &recovered2);
+ if (correct == UNRECOVERABLE_RDS_BLOCK
+ && correct2 == UNRECOVERABLE_RDS_BLOCK) {
+ pkt->bad_packets++;
+ pkt->discardpacket++;
+ pkt->state++;
+ pkt->bitsinfifo = 0;
+ break;
+ }
+
+ if (recovered || recovered2)
+ pkt->recovered_packets++;
+ pkt->good_packets++;
+
+ if (correct == UNRECOVERABLE_RDS_BLOCK)
+ correct = correct2;
+
+ rdsdata = correct & 0x03fffc00;
+
+ /* Save type C packet in buffer */
+ rdsdata >>= 10;
+ pkt->buffer[4] = (rdsdata >> 8);
+ pkt->buffer[5] = (rdsdata & 0xff);
+ pkt->bitsinfifo = 0;
+ pkt->state++;
+ break;
+
+ case OFFSET_D: /* Waiting for type D */
+ rdscrc ^= RDS_OFFSETD;
+ correct = ecc_correct(rdsdata | rdscrc, &recovered);
+ if (correct == UNRECOVERABLE_RDS_BLOCK) {
+ pkt->bad_packets++;
+ pkt->discardpacket++;
+ pkt->state = OFFSET_A;
+ pkt->bitsinfifo = 0;
+ break;
+ }
+
+ if (recovered)
+ pkt->recovered_packets++;
+ pkt->good_packets++;
+
+ rdsdata = correct & 0x03fffc00;
+
+ /* Save type D packet in buffer */
+ rdsdata >>= 10;
+ pkt->buffer[6] = (rdsdata >> 8);
+ pkt->buffer[7] = (rdsdata & 0xff);
+
+ /* buffer it if all segments were ok */
+ if (pkt->discardpacket) {
+ /* We're still in sync, so back to state 1 */
+ pkt->state = OFFSET_A;
+ pkt->bitsinfifo = 0;
+ pkt->discardpacket = 0;
+ break;
+ }
+
+ pkt->state++;
+ /* fall-through */
+
+ case PACKET_OUT:
+ pkt->GroupDropOnce = 1;
+
+ /* queue packet */
+ if (pkt->buf_cnt < RDS_PKT_QUEUE) {
+ memcpy(&pkt->buf_queue[pkt->buf_head][0],
+ pkt->buffer, 8);
+ if (++pkt->buf_head >= RDS_PKT_QUEUE)
+ pkt->buf_head = 0;
+ pkt->buf_cnt++;
+ } else
+ pkt->buf_overruns++;
+
+ /* We're still in sync, so back to state 1 */
+ pkt->state = OFFSET_A;
+ pkt->bitsinfifo = 0;
+ pkt->discardpacket = 0;
+ break;
+
+ }
+
+ /* Lots of errors? If so, go back to resync mode */
+ if (pkt->discardpacket >= 10) {
+ pkt->state = SYNC_OFFSET_A; /* reset sync state */
+ pkt->bitsinfifo = 26; /* resync a bit faster */
+ }
+}
+
+/* GROUP_TYPE 0A-0B (buffer must have enough space for 9 bytes) */
+int stfm1000_rds_get_ps(struct stfm1000_rds_state *rds, u8 *buffer,
+ int bufsize)
+{
+ struct stfm1000_rds_text *rdst = &rds->text;
+
+ if (bufsize < 9)
+ return -1;
+
+ if (!rdst->ps_valid)
+ return -1;
+
+ memcpy(buffer, rdst->ps, 8);
+ buffer[8] = '\0';
+
+ return 8;
+}
+
+/* GROUP_TYPE 2A (buffer must have enough space for 65 bytes) */
+int stfm1000_rds_get_text(struct stfm1000_rds_state *rds, u8 *buffer,
+ int bufsize)
+{
+ struct stfm1000_rds_text *rdst = &rds->text;
+
+ if (bufsize < 9)
+ return -1;
+
+ if (!rdst->text_valid)
+ return -1;
+
+ memcpy(buffer, rdst->text, 64);
+ buffer[64] = '\0';
+
+ return 64;
+}