Commit | Line | Data |
---|---|---|
eb1143a3 YK |
1 | |
2 | #include "fm_low_struc.h" | |
3 | #include "radio-s610.h" | |
4 | #include "fm_rds.h" | |
5 | extern struct s610_radio *gradio; | |
6 | ||
7 | #ifdef USE_RINGBUFF_API | |
8 | void ringbuf_reset(struct ringbuf_t *rb) | |
9 | { | |
10 | rb->head = rb->tail = rb->buf; | |
11 | } | |
12 | ||
13 | int ringbuf_buffer_size(const struct ringbuf_t *rb) | |
14 | { | |
15 | return rb->size; | |
16 | } | |
17 | ||
18 | int ringbuf_capacity(const struct ringbuf_t *rb) | |
19 | { | |
20 | return ringbuf_buffer_size(rb) - 1; | |
21 | } | |
22 | ||
23 | static const u8 *ringbuf_end(const struct ringbuf_t *rb) | |
24 | { | |
25 | return rb->buf + ringbuf_buffer_size(rb); | |
26 | } | |
27 | ||
28 | int ringbuf_bytes_free(const struct ringbuf_t *rb) | |
29 | { | |
30 | if (rb->head >= rb->tail) | |
31 | return ringbuf_capacity(rb) - (rb->head - rb->tail); | |
32 | else | |
33 | return rb->tail - rb->head - 1; | |
34 | } | |
35 | ||
36 | int ringbuf_bytes_used(const struct ringbuf_t *rb) | |
37 | { | |
38 | return ringbuf_capacity(rb) - ringbuf_bytes_free(rb); | |
39 | } | |
40 | ||
41 | int ringbuf_is_full(const struct ringbuf_t *rb) | |
42 | { | |
43 | return ringbuf_bytes_free(rb) == 0; | |
44 | } | |
45 | ||
46 | int ringbuf_is_empty(const struct ringbuf_t *rb) | |
47 | { | |
48 | return ringbuf_bytes_free(rb) == ringbuf_capacity(rb); | |
49 | } | |
50 | ||
51 | const void *ringbuf_tail(const struct ringbuf_t *rb) | |
52 | { | |
53 | return rb->tail; | |
54 | } | |
55 | ||
56 | const void *ringbuf_head(const struct ringbuf_t *rb) | |
57 | { | |
58 | return rb->head; | |
59 | } | |
60 | ||
61 | static u8 *ringbuf_nextp(struct ringbuf_t *rb, const u8 *p) | |
62 | { | |
63 | /* | |
64 | * The assert guarantees the expression (++p - rb->buf) is | |
65 | * non-negative; therefore, the modulus operation is safe and | |
66 | * portable. | |
67 | */ | |
68 | return rb->buf + ((++p - rb->buf) % ringbuf_buffer_size(rb)); | |
69 | } | |
70 | ||
71 | void *ringbuf_memcpy_into(struct ringbuf_t *dst, const void *src, int count) | |
72 | { | |
73 | const u8 *u8src = src; | |
74 | const u8 *bufend = ringbuf_end(dst); | |
75 | int overflow = count > ringbuf_bytes_free(dst); | |
76 | int nread = 0; | |
77 | int n = 0; | |
78 | ||
79 | while (nread != count) { | |
80 | n = MIN(bufend - dst->head, count - nread); | |
81 | memcpy(dst->head, u8src + nread, n); | |
82 | dst->head += n; | |
83 | nread += n; | |
84 | ||
85 | /* wrap? */ | |
86 | if (dst->head == bufend) | |
87 | dst->head = dst->buf; | |
88 | } | |
89 | ||
90 | if (overflow) | |
91 | dst->tail = ringbuf_nextp(dst, dst->head); | |
92 | ||
93 | return dst->head; | |
94 | } | |
95 | ||
96 | void *ringbuf_memcpy_from(void *dst, struct ringbuf_t *src, int count) | |
97 | { | |
98 | int n = 0; | |
99 | int bytes_used = ringbuf_bytes_used(src); | |
100 | u8 *u8dst = dst; | |
101 | const u8 *bufend = ringbuf_end(src); | |
102 | int nwritten = 0; | |
103 | ||
104 | if (count > bytes_used) | |
105 | return 0; | |
106 | ||
107 | while (nwritten != count) { | |
108 | n = MIN(bufend - src->tail, count - nwritten); | |
109 | memcpy(u8dst + nwritten, src->tail, n); | |
110 | src->tail += n; | |
111 | nwritten += n; | |
112 | ||
113 | /* wrap ? */ | |
114 | if (src->tail == bufend) | |
115 | src->tail = src->buf; | |
116 | } | |
117 | ||
118 | return src->tail; | |
119 | } | |
120 | ||
121 | void * ringbuf_memcpy_remove(struct ringbuf_t *dst, int count) | |
122 | { | |
123 | int n = 0; | |
124 | int bytes_used = ringbuf_bytes_used(dst); | |
125 | const u8 *bufend = ringbuf_end(dst); | |
126 | int nwritten = 0; | |
127 | unsigned char *cls_start; | |
128 | ||
129 | if (count > bytes_used) | |
130 | return 0; | |
131 | ||
132 | while (nwritten != count) { | |
133 | n = MIN(bufend - dst->head, count - nwritten); | |
134 | cls_start = dst->head - n; | |
135 | memset(cls_start, 0, n); | |
136 | dst->head -= n; | |
137 | nwritten += n; | |
138 | ||
139 | /* wrap ? */ | |
140 | if (dst->head == bufend) | |
141 | dst->head = dst->buf; | |
142 | } | |
143 | ||
144 | return dst->head; | |
145 | } | |
146 | #endif /* USE_RINGBUFF_API */ | |
147 | ||
148 | #ifdef USE_RINGBUFF_API | |
149 | void fm_rds_write_data(struct s610_radio *radio, | |
150 | u16 rds_data, fm_rds_block_type_enum blk_type, | |
151 | fm_host_rds_errors_enum errors) | |
152 | { | |
153 | u8 buf_ptr[HOST_RDS_BLOCK_SIZE]; | |
154 | u16 usage; | |
155 | ||
156 | if (ringbuf_is_full(&radio->rds_rb)) { | |
e16618ee | 157 | RDSEBUG(radio, "%s():>>>RB full! H[%ld]T[%ld]", |
eb1143a3 YK |
158 | __func__, |
159 | (unsigned long) (radio->rds_rb.head - radio->rds_rb.buf), | |
160 | (unsigned long) (radio->rds_rb.tail - radio->rds_rb.buf)); | |
161 | ||
162 | goto skip_into_buf; | |
163 | } | |
164 | ||
165 | buf_ptr[HOST_RDS_BLOCK_FMT_LSB] = (u8)(rds_data & 0xff); | |
166 | buf_ptr[HOST_RDS_BLOCK_FMT_MSB] = (u8)(rds_data >> 8); | |
167 | buf_ptr[HOST_RDS_BLOCK_FMT_STATUS] = | |
168 | (blk_type << HOST_RDS_DATA_BLKTYPE_POSI) | |
169 | | (errors << HOST_RDS_DATA_ERRORS_POSI) | |
170 | | HOST_RDS_DATA_AVAIL_MASK; | |
171 | ||
172 | if (!ringbuf_memcpy_into(&radio->rds_rb, buf_ptr, HOST_RDS_BLOCK_SIZE)) { | |
173 | usage = ringbuf_bytes_used(&radio->rds_rb); | |
e16618ee | 174 | RDSEBUG(radio, |
eb1143a3 YK |
175 | "%s():>>>RB memcpy into fail! usage:%04d", |
176 | __func__, ringbuf_bytes_used(&radio->rds_rb)); | |
177 | if (!usage) | |
178 | return; | |
179 | } | |
180 | ||
181 | skip_into_buf: | |
182 | usage = ringbuf_bytes_used(&radio->rds_rb); | |
183 | ||
184 | if (usage >= HOST_RDS_BLOCK_SIZE) | |
185 | radio->low->fm_state.status |= STATUS_MASK_RDS_AVA; | |
186 | ||
187 | if (radio->low->fm_state.rds_mem_thresh != 0) { | |
188 | if (usage >= (radio->low->fm_state.rds_mem_thresh+(HOST_RDS_BLOCK_SIZE*4))) { | |
189 | if (atomic_read(&radio->is_rds_new)) | |
190 | return; | |
191 | ||
192 | fm_set_flag_bits(radio, FLAG_BUF_FUL); | |
193 | atomic_set(&radio->is_rds_new, 1); | |
194 | wake_up_interruptible(&radio->core->rds_read_queue); | |
195 | radio->rds_n_count++; | |
196 | ||
197 | if (!(radio->rds_n_count%200)) { | |
198 | fm_update_rssi_work(radio); | |
e16618ee | 199 | RDSEBUG(radio, |
eb1143a3 YK |
200 | ">>>[FM] RSSI[%03d] NCOUNT[%08d] FIFO_ERR[%08d] USAGE[%04d] SYNCLOSS[%08d]", |
201 | radio->low->fm_state.rssi, radio->rds_n_count, radio->rds_fifo_err_cnt, | |
202 | usage, radio->rds_sync_loss_cnt); | |
203 | } | |
204 | } | |
205 | } | |
206 | } | |
207 | #else /* USE_RINGBUFF_API */ | |
208 | void fm_rds_write_data(struct s610_radio *radio, u16 rds_data, | |
209 | fm_rds_block_type_enum blk_type, fm_host_rds_errors_enum errors) | |
210 | { | |
211 | u8 *buf_ptr; | |
212 | u16 usage; | |
213 | u16 capa; | |
214 | ||
215 | capa = radio->low->rds_buffer->size; | |
216 | if (radio->low->rds_buffer->outdex | |
217 | > radio->low->rds_buffer->index) | |
218 | usage = radio->low->rds_buffer->size | |
219 | - radio->low->rds_buffer->outdex | |
220 | + radio->low->rds_buffer->index; | |
221 | else | |
222 | usage = radio->low->rds_buffer->index | |
223 | - radio->low->rds_buffer->outdex; | |
224 | ||
225 | if ((capa - usage) >= (HOST_RDS_BLOCK_SIZE * 4)) { | |
226 | buf_ptr = radio->low->rds_buffer->base | |
227 | + radio->low->rds_buffer->index; | |
228 | ||
229 | buf_ptr[HOST_RDS_BLOCK_FMT_LSB] = (u8)(rds_data & 0xff); | |
230 | buf_ptr[HOST_RDS_BLOCK_FMT_MSB] = (u8) (rds_data >> 8); | |
231 | buf_ptr[HOST_RDS_BLOCK_FMT_STATUS] = (blk_type | |
232 | << HOST_RDS_DATA_BLKTYPE_POSI) | |
233 | | (errors << HOST_RDS_DATA_ERRORS_POSI) | |
234 | | HOST_RDS_DATA_AVAIL_MASK; | |
235 | ||
236 | /* Advances the buffer's index */ | |
237 | radio->low->rds_buffer->index | |
238 | += HOST_RDS_BLOCK_SIZE; | |
239 | ||
240 | /* Check if the buffer's index wraps */ | |
241 | if (radio->low->rds_buffer->index >= | |
242 | radio->low->rds_buffer->size) { | |
243 | radio->low->rds_buffer->index -= | |
244 | radio->low->rds_buffer->size; | |
245 | } | |
246 | ||
247 | if (usage >= HOST_RDS_BLOCK_SIZE) | |
248 | radio->low->fm_state.status |= STATUS_MASK_RDS_AVA; | |
249 | ||
250 | } | |
251 | ||
252 | if (radio->low->fm_state.rds_mem_thresh != 0) { | |
253 | if (usage >= (radio->low->fm_state.rds_mem_thresh+(HOST_RDS_BLOCK_SIZE*4) | |
254 | /** HOST_RDS_BLOCK_SIZE*/)) { | |
255 | if (atomic_read(&radio->is_rds_new)) | |
256 | return; | |
257 | ||
258 | fm_set_flag_bits(radio, FLAG_BUF_FUL); | |
259 | atomic_set(&radio->is_rds_new, 1); | |
260 | wake_up_interruptible(&radio->core->rds_read_queue); | |
261 | radio->rds_n_count++; | |
262 | ||
263 | if (!(radio->rds_n_count%200)) { | |
264 | fm_update_rssi_work(radio); | |
265 | dev_info(radio->dev, | |
266 | ">>>[FM] RSSI[%03d] NCOUNT[%08d] FIFO_ERR[%08d] USAGE[%04d] SYNCLOSS[%08d]", | |
267 | radio->low->fm_state.rssi, radio->rds_n_count, radio->rds_fifo_err_cnt, | |
268 | usage, radio->rds_sync_loss_cnt); | |
269 | } | |
270 | } | |
271 | } | |
272 | } | |
273 | #endif /* USE_RINGBUFF_API */ | |
274 | ||
275 | #ifdef USE_RDS_BLOCK_SEQ_CORRECT | |
276 | #ifdef USE_RINGBUFF_API | |
277 | void fm_rds_write_data_remove(struct s610_radio *radio, | |
278 | fm_rds_rm_align_enum removeblock) | |
279 | { | |
280 | unsigned long pre_head, cur_head; | |
281 | pre_head = (unsigned long) radio->rds_rb.head; | |
282 | ringbuf_memcpy_remove(&radio->rds_rb, | |
283 | (int)removeblock*HOST_RDS_BLOCK_SIZE); | |
284 | cur_head = (unsigned long) radio->rds_rb.head; | |
e16618ee | 285 | RDSEBUG(radio, ">>> pre-head :%08lX, cur-head :%08lX\n", |
eb1143a3 YK |
286 | pre_head, cur_head); |
287 | } | |
288 | #else | |
289 | void fm_rds_write_data_remove(struct s610_radio *radio, | |
290 | fm_rds_rm_align_enum removeblock) | |
291 | { | |
292 | int i; | |
293 | u8 *buf_ptr; | |
294 | ||
295 | for (i = 0; i < removeblock*HOST_RDS_BLOCK_SIZE; i++) { | |
296 | buf_ptr = radio->low->rds_buffer->base | |
297 | + radio->low->rds_buffer->index; | |
298 | buf_ptr[0] = 0; | |
299 | if (radio->low->rds_buffer->index == 0) | |
300 | radio->low->rds_buffer->index = radio->low->rds_buffer->size; | |
301 | radio->low->rds_buffer->index--; | |
302 | } | |
303 | RDSEBUG(radio, "%s():<<<WR-RM:%d index[%04d]", | |
304 | __func__, removeblock, | |
305 | radio->low->rds_buffer->index); | |
306 | ||
307 | } | |
308 | #endif /*USE_RINGBUFF_API*/ | |
309 | #endif /* USE_RDS_BLOCK_SEQ_CORRECT */ | |
310 | ||
311 | #ifdef USE_RINGBUFF_API | |
312 | int fm_read_rds_data(struct s610_radio *radio, u8 *buffer, int size, | |
313 | u16 *blocks) | |
314 | { | |
315 | u16 rsize = size; | |
316 | ||
317 | if (ringbuf_is_empty(&radio->rds_rb)) { | |
e16618ee | 318 | RDSEBUG(radio, |
eb1143a3 YK |
319 | "%s():>>>RB empty!! H[%04ld]T[%04ld]", |
320 | __func__, | |
321 | (unsigned long) (radio->rds_rb.head - radio->rds_rb.buf), | |
322 | (unsigned long) (radio->rds_rb.tail - radio->rds_rb.buf)); | |
323 | ||
324 | return 0; | |
325 | } | |
326 | radio->rb_used = ringbuf_bytes_used(&radio->rds_rb); | |
327 | if (!ringbuf_memcpy_from(buffer, &radio->rds_rb, rsize)) { | |
e16618ee | 328 | RDSEBUG(radio, |
eb1143a3 YK |
329 | "%s():>>>RB memcpy from fail! H[%04ld]T[%04ld]", |
330 | __func__, | |
331 | (unsigned long) (radio->rds_rb.head - radio->rds_rb.buf), | |
332 | (unsigned long) (radio->rds_rb.tail - radio->rds_rb.buf)); | |
333 | ||
334 | /* ringbuff reset */ | |
335 | ringbuf_reset(&radio->rds_rb); | |
336 | if (blocks) | |
337 | blocks = 0; | |
338 | return 0; | |
339 | } | |
340 | ||
341 | if (blocks) | |
342 | *blocks = rsize / HOST_RDS_BLOCK_SIZE; | |
343 | ||
344 | /* Update RDS flags */ | |
345 | if ((rsize / HOST_RDS_BLOCK_SIZE) < radio->low->fm_state.rds_mem_thresh) | |
346 | fm_clear_flag_bits(radio, FLAG_BUF_FUL); | |
347 | ||
348 | RDSEBUG(radio, | |
349 | "%s():>>>RB1 H[%04ld]T[%04ld]", | |
350 | __func__, | |
351 | (unsigned long) (radio->rds_rb.head - radio->rds_rb.buf), | |
352 | (unsigned long) (radio->rds_rb.tail - radio->rds_rb.buf)); | |
353 | ||
354 | return rsize; | |
355 | } | |
356 | #else /* USE_RINGBUFF_API */ | |
357 | u16 fm_rds_get_avail_bytes(struct s610_radio *radio) | |
358 | { | |
359 | u16 avail_bytes; | |
360 | ||
361 | if (radio->low->rds_buffer->outdex > | |
362 | radio->low->rds_buffer->index) | |
363 | avail_bytes = (radio->low->rds_buffer->size | |
364 | - radio->low->rds_buffer->outdex | |
365 | + radio->low->rds_buffer->index); | |
366 | else | |
367 | avail_bytes = (radio->low->rds_buffer->index | |
368 | - radio->low->rds_buffer->outdex); | |
369 | ||
370 | return avail_bytes; | |
371 | } | |
372 | ||
373 | ||
374 | int fm_read_rds_data(struct s610_radio *radio, u8 *buffer, int size, | |
375 | u16 *blocks) | |
376 | { | |
377 | u16 avail_bytes; | |
378 | s16 avail_blocks; | |
379 | s16 orig_avail; | |
380 | u8 *buf_ptr; | |
381 | ||
382 | if (radio->low->rds_buffer == NULL) { | |
383 | size = 0; | |
384 | if (blocks) | |
385 | *blocks = 0; | |
386 | return FALSE; | |
387 | } | |
388 | ||
389 | orig_avail = avail_bytes = fm_rds_get_avail_bytes(radio); | |
390 | ||
391 | if (avail_bytes > size) | |
392 | avail_bytes = size; | |
393 | ||
394 | avail_blocks = avail_bytes / HOST_RDS_BLOCK_SIZE; | |
395 | avail_bytes = avail_blocks * HOST_RDS_BLOCK_SIZE; | |
396 | ||
397 | if (avail_bytes == 0) { | |
398 | size = 0; | |
399 | if (blocks) | |
400 | *blocks = 0; | |
401 | return FALSE; | |
402 | } | |
403 | ||
404 | buf_ptr = radio->low->rds_buffer->base | |
405 | + radio->low->rds_buffer->outdex; | |
406 | (void) memcpy(buffer, buf_ptr, avail_bytes); | |
407 | ||
408 | /* advances the buffer's outdex */ | |
409 | radio->low->rds_buffer->outdex += avail_bytes; | |
410 | ||
411 | /* Check if the buffer's outdex wraps */ | |
412 | if (radio->low->rds_buffer->outdex >= radio->low->rds_buffer->size) | |
413 | radio->low->rds_buffer->outdex -= radio->low->rds_buffer->size; | |
414 | ||
415 | if (orig_avail == avail_bytes) { | |
416 | buffer[(avail_blocks - 1) * HOST_RDS_BLOCK_SIZE | |
417 | + HOST_RDS_BLOCK_FMT_STATUS] &= | |
418 | ~HOST_RDS_DATA_AVAIL_MASK; | |
419 | radio->low->fm_state.status &= ~STATUS_MASK_RDS_AVA; | |
420 | } | |
421 | ||
422 | size = avail_bytes; /* number of bytes read */ | |
423 | ||
424 | if (blocks) | |
425 | *blocks = avail_bytes / HOST_RDS_BLOCK_SIZE; | |
426 | ||
427 | /* Update RDS flags */ | |
428 | if ((avail_bytes / HOST_RDS_BLOCK_SIZE) | |
429 | < radio->low->fm_state.rds_mem_thresh) | |
430 | fm_clear_flag_bits(radio, FLAG_BUF_FUL); | |
431 | ||
432 | return size; | |
433 | } | |
434 | #endif /* USE_RINGBUFF_API */ | |
435 | ||
436 | #ifdef USE_RDS_HW_DECODER | |
437 | void fm_rds_change_state(struct s610_radio *radio, | |
438 | fm_rds_state_enum new_state) | |
439 | { | |
440 | fm_rds_state_enum old_state = | |
441 | (fm_rds_state_enum) radio->low->fm_rds_state.current_state; | |
442 | ||
443 | radio->low->fm_rds_state.current_state = new_state; | |
444 | ||
445 | if ((old_state == RDS_STATE_FULL_SYNC) | |
446 | && (new_state == RDS_STATE_HAVE_DATA)) { | |
447 | fm_update_rds_sync_status(radio, FALSE); /* unsynced */ | |
448 | } else if ((old_state != RDS_STATE_FULL_SYNC) | |
449 | && (new_state == RDS_STATE_FULL_SYNC)) { | |
450 | fm_update_rds_sync_status(radio, TRUE); /* synced */ | |
451 | } | |
452 | } | |
453 | #else /*USE_RDS_HW_DECODER*/ | |
454 | void fm_rds_change_state(struct s610_radio *radio, | |
455 | fm_rds_state_enum new_state) | |
456 | { | |
457 | radio->low->fm_rds_state.current_state = new_state; | |
458 | } | |
459 | ||
460 | fm_rds_state_enum fm_rds_get_state(struct s610_radio *radio) | |
461 | { | |
462 | return radio->low->fm_rds_state.current_state; | |
463 | } | |
464 | #endif /*USE_RDS_HW_DECODER*/ | |
465 | ||
466 | void fm_rds_update_error_status(struct s610_radio *radio, u16 errors) | |
467 | { | |
468 | if (errors == 0) { | |
469 | radio->low->fm_rds_state.error_bits = 0; | |
470 | radio->low->fm_rds_state.error_blks = 0; | |
471 | } else { | |
472 | radio->low->fm_rds_state.error_bits += errors; | |
473 | radio->low->fm_rds_state.error_blks++; | |
474 | } | |
475 | ||
476 | if (radio->low->fm_rds_state.error_blks | |
477 | >= radio->low->fm_state.rds_unsync_blk_cnt) { | |
478 | if (radio->low->fm_rds_state.error_bits | |
479 | >= radio->low->fm_state.rds_unsync_bit_cnt) { | |
480 | /* sync-loss */ | |
481 | fm_rds_change_state(radio, RDS_STATE_HAVE_DATA); | |
482 | RDSEBUG(radio, "%s() >>>>> RDS sync-loss[%08d]!!!!!", | |
483 | __func__, radio->rds_sync_loss_cnt); | |
484 | ||
485 | #ifdef USE_RDS_HW_DECODER | |
486 | fm_rds_change_state(radio, RDS_STATE_HAVE_DATA); | |
487 | #else | |
488 | if (!radio->rds_sync_loss_cnt) { | |
489 | #ifdef USE_RINGBUFF_API | |
490 | ringbuf_reset(&radio->rds_rb); | |
491 | #else | |
492 | radio->low->rds_buffer->index = radio->low->rds_buffer->outdex = 0; | |
493 | #endif | |
494 | } else { | |
495 | /*remove data*/ | |
e6869d40 | 496 | fm_rds_write_data_remove(radio, (fm_rds_rm_align_enum)fm_rds_get_state(radio)); |
eb1143a3 YK |
497 | } |
498 | fm_rds_change_state(radio, RDS_STATE_INIT); | |
499 | #endif /*USE_RDS_HW_DECODER*/ | |
500 | } | |
501 | radio->low->fm_rds_state.error_bits = 0; | |
502 | radio->low->fm_rds_state.error_blks = 0; | |
503 | radio->rds_sync_loss_cnt++; | |
504 | } | |
505 | } | |
506 | ||
507 | static fm_host_rds_errors_enum fm_rds_process_block( | |
508 | struct s610_radio *radio, | |
509 | u16 data, fm_rds_block_type_enum block_type, | |
510 | u16 err_count) | |
511 | { | |
512 | fm_host_rds_errors_enum error_type; | |
513 | struct fm_rds_parser_info *pi; | |
514 | ||
515 | if (radio->rds_parser_enable) | |
516 | pi = &(radio->pi); | |
517 | ||
518 | if (err_count == 0) { | |
519 | error_type = HOST_RDS_ERRS_NONE; | |
520 | } else if ((err_count <= 2) | |
521 | && (err_count | |
522 | <= radio->low->fm_config.rds_error_limit)) { | |
523 | error_type = HOST_RDS_ERRS_2CORR; | |
524 | } else if ((err_count <= 5) | |
525 | && (err_count | |
526 | <= radio->low->fm_config.rds_error_limit)) { | |
527 | error_type = HOST_RDS_ERRS_5CORR; | |
528 | } else { | |
529 | error_type = HOST_RDS_ERRS_UNCORR; | |
530 | } | |
531 | ||
532 | /* Write the data into the buffer */ | |
533 | if ((block_type != RDS_BLKTYPE_E) | |
534 | || (radio->low->fm_state.save_eblks)) { | |
535 | if (radio->rds_parser_enable) { | |
536 | fm_rds_parser(pi, data, block_type, error_type); | |
537 | fm_rds_write_data_pi(radio, pi); | |
538 | } else { | |
e16618ee YK |
539 | fm_rds_write_data(radio, data, block_type, error_type); |
540 | } | |
eb1143a3 YK |
541 | } |
542 | ||
543 | return error_type; | |
544 | } | |
545 | ||
546 | #ifdef USE_RDS_BLOCK_SEQ_CORRECT | |
547 | fm_rds_rm_align_enum fm_check_block_seq(fm_rds_block_type_enum pre_block_type, | |
548 | fm_rds_block_type_enum curr_block_type) | |
549 | { | |
550 | fm_rds_rm_align_enum ret = RDS_RM_ALIGN_NONE; | |
551 | ||
552 | if ((pre_block_type == RDS_BLKTYPE_A) && (curr_block_type != RDS_BLKTYPE_B)) { | |
553 | ret = RDS_RM_ALIGN_1; | |
554 | } | |
555 | else if ((pre_block_type == RDS_BLKTYPE_B) && (curr_block_type != RDS_BLKTYPE_C)) { | |
556 | ret = RDS_RM_ALIGN_2; | |
557 | } | |
558 | else if ((pre_block_type == RDS_BLKTYPE_C) && (curr_block_type != RDS_BLKTYPE_D)) { | |
559 | ret = RDS_RM_ALIGN_3; | |
560 | } | |
561 | else if ((pre_block_type == RDS_BLKTYPE_D) && (curr_block_type != RDS_BLKTYPE_A)) { | |
562 | ret = RDS_RM_ALIGN_0; | |
563 | } | |
564 | return ret; | |
565 | } | |
566 | #endif /* USE_RDS_BLOCK_SEQ_CORRECT */ | |
567 | ||
568 | #ifdef USE_RDS_HW_DECODER | |
569 | void fm_process_rds_data(struct s610_radio *radio) | |
570 | { | |
571 | u32 fifo_data; | |
572 | u16 i; | |
573 | u16 avail_blocks; | |
574 | u16 data; | |
575 | u8 status; | |
576 | u16 err_count; | |
577 | fm_rds_block_type_enum block_type; | |
578 | #ifdef USE_RDS_BLOCK_SEQ_CORRECT | |
579 | fm_rds_rm_align_enum rm_blk = RDS_RM_ALIGN_NONE; | |
580 | #endif /* USE_RDS_BLOCK_SEQ_CORRECT */ | |
581 | ||
582 | API_ENTRY(radio); | |
583 | ||
584 | if (!radio->low->fm_state.rds_rx_enabled) | |
585 | return; | |
586 | ||
587 | avail_blocks = RDS_MEM_MAX_THRESH/4; | |
588 | ||
589 | for (i = 0; i < avail_blocks; i++) { | |
590 | /* Fetch the RDS word data. */ | |
591 | atomic_set(&radio->is_rds_doing, 1); | |
592 | fifo_data = fmspeedy_get_reg_work(0xFFF3C0); | |
593 | radio->rds_fifo_rd_cnt++; | |
594 | data = (u16)((fifo_data >> 16) & 0xFFFF); | |
595 | status = (u8)((fifo_data >> 8) & 0xFF); | |
596 | block_type = | |
597 | (fm_rds_block_type_enum) ((status & RDS_BLK_TYPE_MASK) | |
598 | >> RDS_BLK_TYPE_SHIFT); | |
599 | err_count = (status & RDS_ERR_CNT_MASK); | |
600 | atomic_set(&radio->is_rds_doing, 0); | |
601 | ||
602 | switch (radio->low->fm_rds_state.current_state) { | |
603 | case RDS_STATE_INIT: | |
604 | APIEBUG(radio, "RDS_STATE_INIT"); | |
605 | fm_rds_change_state(radio, RDS_STATE_HAVE_DATA); | |
606 | case RDS_STATE_HAVE_DATA: | |
607 | APIEBUG(radio, "RDS_STATE_HAVE_DATA"); | |
608 | if ((block_type == RDS_BLKTYPE_A) | |
609 | && (err_count == 0)) { | |
610 | #ifdef USE_RDS_BLOCK_SEQ_CORRECT | |
611 | radio->block_seq = RDS_BLKTYPE_A; | |
612 | #endif /*USE_RDS_BLOCK_SEQ_CORRECT */ | |
613 | /* Move to full sync */ | |
614 | fm_rds_change_state(radio, | |
615 | RDS_STATE_FULL_SYNC); | |
616 | fm_rds_process_block(radio, | |
617 | data, block_type, err_count); | |
618 | } | |
619 | break; | |
620 | case RDS_STATE_PRE_SYNC: | |
621 | break; | |
622 | case RDS_STATE_FULL_SYNC: | |
623 | APIEBUG(radio, "RDS_STATE_FULL_SYNC"); | |
624 | #ifdef USE_RDS_BLOCK_SEQ_CORRECT | |
625 | rm_blk = fm_check_block_seq(radio->block_seq, block_type); | |
626 | if (rm_blk < RDS_RM_ALIGN_NONE) { | |
627 | RDSEBUG(radio, | |
628 | "pre block[%02d],curr block[%02d],err count[%08d]", | |
629 | radio->block_seq, block_type, radio->rds_fifo_err_cnt); | |
630 | if (rm_blk != RDS_RM_ALIGN_0) { | |
631 | fm_rds_write_data_remove(radio, rm_blk); | |
632 | radio->block_seq = RDS_BLKTYPE_D; | |
633 | } | |
634 | fm_rds_change_state(radio, RDS_STATE_HAVE_DATA); | |
635 | radio->rds_fifo_err_cnt++; | |
636 | break; | |
637 | } | |
638 | radio->block_seq = block_type; | |
639 | #endif /* USE_RDS_BLOCK_SEQ_CORRECT */ | |
640 | if (fm_rds_process_block(radio, | |
641 | data, block_type, err_count) | |
642 | == HOST_RDS_ERRS_UNCORR) { | |
643 | fm_rds_update_error_status(radio, | |
644 | radio->low->fm_state.rds_unsync_uncorr_weight); | |
645 | } else { | |
646 | fm_rds_update_error_status(radio, err_count); | |
647 | } | |
648 | break; | |
649 | } | |
650 | } | |
651 | ||
652 | API_EXIT(radio); | |
653 | } | |
654 | #endif /* USE_RDS_HW_DECODER */ | |
655 | void find_pi_data(struct fm_rds_parser_info *pi, u16 info) | |
656 | { | |
657 | ||
658 | pi->pi_buf[pi->pi_idx % 2] = info; | |
659 | ||
660 | if (!(++pi->pi_idx % 2)) { | |
661 | if (pi->pi_buf[0] == pi->pi_buf[1]) { | |
662 | pi->rds_event |= RDS_EVENT_PI_MASK; | |
663 | RDSEBUG(gradio, "[RDS] PI : 0x%x\n", pi->pi_buf[0]); | |
664 | } | |
665 | } | |
666 | } | |
667 | ||
668 | void find_ecc_data(struct fm_rds_parser_info *pi, u16 info) | |
669 | { | |
670 | ||
671 | pi->ecc_buf[pi->ecc_idx % 2] = info & 0xFF; | |
672 | ||
673 | if (!(++pi->ecc_idx % 2)) { | |
674 | if (pi->ecc_buf[0] == pi->ecc_buf[1]) { | |
675 | pi->rds_event |= RDS_EVENT_ECC_MASK; | |
676 | RDSEBUG(gradio, "[RDS] ECC : %d\n", pi->ecc_buf[0]); | |
677 | } | |
678 | } | |
679 | } | |
680 | ||
681 | void store_ps_data(struct fm_rds_parser_info *pi, | |
682 | u16 info, u8 err_cnt) | |
683 | { | |
684 | char a, b; | |
685 | u32 i = pi->ps_idx % 3; | |
686 | u8 seg = pi->ps_segment; | |
687 | ||
688 | if (pi->drop_blk) | |
689 | return; | |
690 | ||
691 | a = (info >> 8) & 0xff; | |
692 | b = info & 0xff; | |
693 | ||
694 | pi->ps_buf[i][seg * 2] = a; | |
695 | pi->ps_buf[i][seg * 2 + 1] = b; | |
696 | pi->ps_err[i][seg] = err_cnt; | |
697 | /*RDSEBUG(gradio, | |
698 | "[RDS] PS: [%c][%c] [%d][%d], modulo idx=%d, seg=%d\n", | |
699 | a, b, (int)a, (int)b, i, seg);*/ | |
700 | } | |
701 | ||
702 | void validate_ps_data(struct fm_rds_parser_info *pi) | |
703 | { | |
704 | u32 i; | |
705 | bool match = true; | |
706 | ||
707 | for (i = 0; i < pi->ps_len / 2; i++) { | |
708 | if (pi->ps_err[pi->ps_idx % 3][i] > 0) | |
709 | break; | |
710 | } | |
711 | ||
712 | if (i == pi->ps_len / 2) { | |
713 | memcpy(pi->ps_candidate, | |
714 | pi->ps_buf[pi->ps_idx % 3], | |
715 | pi->ps_len); | |
716 | memset(pi->ps_err[pi->ps_idx % 3], | |
717 | 0xFF, MAX_PS / 2); | |
718 | pi->ps_candidate[pi->ps_len] = 0; | |
719 | pi->rds_event |= RDS_EVENT_PS_MASK; | |
720 | pi->ps_idx++; | |
721 | RDSEBUG(gradio, | |
722 | "[RDS] ### PS candidate[i]: %s\n", | |
723 | pi->ps_candidate); | |
724 | } else { | |
725 | if (++pi->ps_idx >= 3) { | |
726 | for (i = 0; i < pi->ps_len; i++) { | |
727 | if (pi->ps_buf[0][i] == pi->ps_buf[1][i]) | |
728 | pi->ps_candidate[i] = pi->ps_buf[0][i]; | |
729 | else if (pi->ps_buf[0][i] == pi->ps_buf[2][i]) | |
730 | pi->ps_candidate[i] = pi->ps_buf[0][i]; | |
731 | else if (pi->ps_buf[1][i] == pi->ps_buf[2][i]) | |
732 | pi->ps_candidate[i] = pi->ps_buf[1][i]; | |
733 | else | |
734 | match = false; | |
735 | } | |
736 | ||
737 | if (match) { | |
738 | pi->ps_candidate[pi->ps_len] = 0; | |
739 | pi->rds_event |= RDS_EVENT_PS_MASK; | |
740 | RDSEBUG(gradio, | |
741 | "[RDS] ### PS candidate[m]: %s\n", | |
742 | pi->ps_candidate); | |
743 | } | |
744 | } | |
745 | } | |
746 | ||
747 | i = pi->ps_idx - 1; | |
748 | pi->ps_buf[i % 3][pi->ps_len] = 0; | |
749 | ||
750 | for (i = 0; i < 3; i++) | |
751 | RDSEBUG(gradio, | |
752 | "[RDS] ### PS received[%d]: %s\n", | |
753 | i, pi->ps_buf[i]); | |
754 | } | |
755 | ||
756 | void store_rt_data(struct fm_rds_parser_info *pi, u16 info, u8 blk_type, u8 err_cnt) | |
757 | { | |
758 | char a, b; | |
759 | u32 i = pi->rt_idx % 3; | |
760 | u8 seg = pi->rt_segment; | |
761 | ||
762 | if (pi->drop_blk) | |
763 | return; | |
764 | ||
765 | a = (info >> 8) & 0xff; | |
766 | b = info & 0xff; | |
767 | ||
768 | switch (blk_type) { | |
769 | case RDS_BLKTYPE_C: | |
770 | pi->rt_buf[i][seg * 4] = a; | |
771 | pi->rt_buf[i][seg * 4 + 1] = b; | |
772 | pi->rt_err[i][seg * 2] = err_cnt; | |
773 | /*RDSEBUG(gradio, | |
774 | "[RDS] RT_A(C): [%c][%c] [%d][%d], modulo idx=%d, seg=%d\n", | |
775 | a, b, (int)a, (int)b, i, seg);*/ | |
776 | break; | |
777 | ||
778 | case RDS_BLKTYPE_D: | |
779 | if (pi->grp == RDS_GRPTYPE_2A) { | |
780 | pi->rt_buf[i][seg * 4 + 2] = a; | |
781 | pi->rt_buf[i][seg * 4 + 3] = b; | |
782 | pi->rt_err[i][seg * 2 + 1] = err_cnt; | |
783 | /*RDSEBUG(gradio, | |
784 | "[RDS] RT_A(D): [%c][%c] [%d][%d], modulo idx=%d, seg=%d\n", | |
785 | a, b, (int)a, (int)b, i, seg);*/ | |
786 | } else if (pi->grp == RDS_GRPTYPE_2B) { | |
787 | pi->rt_buf[i][seg * 2] = a; | |
788 | pi->rt_buf[i][seg * 2 + 1] = b; | |
789 | pi->rt_err[i][seg] = err_cnt; | |
790 | /*RDSEBUG(gradio, | |
791 | "[RDS] RT_B(C): [%c][%c] [%d][%d], modulo idx=%d, seg=%d\n", | |
792 | a, b, (int)a, (int)b, i, seg);*/ | |
793 | } | |
794 | default: | |
795 | break; | |
796 | } | |
797 | } | |
798 | ||
799 | void validate_rt_data(struct fm_rds_parser_info *pi) | |
800 | { | |
801 | u32 i; | |
802 | bool match = true; | |
803 | ||
804 | for (i = 0; i < pi->rt_len / 2; i++) { | |
805 | if (pi->rt_err[pi->rt_idx % 3][i] > 0) | |
806 | break; | |
807 | } | |
808 | ||
809 | if (i == pi->rt_len / 2) { | |
810 | memcpy(pi->rt_candidate, pi->rt_buf[pi->rt_idx % 3], pi->rt_len); | |
811 | memset(pi->rt_err[pi->rt_idx % 3], 0xFF, MAX_RT / 2); | |
812 | pi->rt_candidate[pi->rt_len] = 0; | |
813 | ||
c2fb5b4c | 814 | if (strlen(pi->rt_candidate) >= (pi->rt_len - 2)) { |
eb1143a3 YK |
815 | pi->rds_event |= RDS_EVENT_RT_MASK; |
816 | pi->rt_validated = 1; | |
817 | pi->rt_idx++; | |
818 | RDSEBUG(gradio, | |
819 | "[RDS] ### RT candidate[i]: %s, %d, %d\n", | |
820 | pi->rt_candidate, (int)strlen(pi->rt_candidate), pi->rt_len); | |
821 | } | |
822 | } else { | |
823 | if (++pi->rt_idx >= 3) { | |
824 | for (i = 0; i < pi->rt_len; i++) { | |
825 | if (pi->rt_buf[0][i] == pi->rt_buf[1][i]) | |
826 | pi->rt_candidate[i] = pi->rt_buf[0][i]; | |
827 | else if (pi->rt_buf[0][i] == pi->rt_buf[2][i]) | |
828 | pi->rt_candidate[i] = pi->rt_buf[0][i]; | |
829 | else if (pi->rt_buf[1][i] == pi->rt_buf[2][i]) | |
830 | pi->rt_candidate[i] = pi->rt_buf[1][i]; | |
831 | else | |
832 | match = false; | |
833 | } | |
834 | ||
835 | if (match) { | |
836 | pi->rt_candidate[pi->rt_len] = 0; | |
837 | ||
c2fb5b4c | 838 | if (strlen(pi->rt_candidate) >= (pi->rt_len - 2)) { |
eb1143a3 YK |
839 | pi->rds_event |= RDS_EVENT_RT_MASK; |
840 | pi->rt_validated = 1; | |
841 | RDSEBUG(gradio, | |
842 | "[RDS] ### RT candidate[m]: %s, %d, %d\n", | |
843 | pi->rt_candidate, (int)strlen(pi->rt_candidate), pi->rt_len); | |
844 | } | |
845 | } | |
846 | } | |
847 | } | |
848 | ||
849 | i = pi->rt_idx - 1; | |
850 | pi->rt_buf[i % 3][pi->rt_len] = 0; | |
851 | for (i = 0; i < 3; i++) | |
852 | RDSEBUG(gradio, | |
853 | "[RDS] ### RT received[%d]: %s\n", | |
854 | i, pi->rt_buf[i]); | |
855 | } | |
856 | ||
857 | void reset_rtp_data(struct fm_rds_parser_info *pi) | |
858 | { | |
859 | memset(&(pi->rtp_data), 0x0, sizeof(struct rtp_info)); | |
860 | memset(pi->rtp_raw_data, 0x0, sizeof(u16) * 3); | |
861 | } | |
862 | ||
863 | void validate_rtp_data(struct fm_rds_parser_info *pi) | |
864 | { | |
865 | u8 i; | |
866 | static u16 rtp_validated; | |
867 | struct rtp_tag_info tag[2]; | |
868 | ||
869 | if (!pi->rtp_data.running) { | |
870 | RDSEBUG(gradio, "[RDS] RTP running is stopped\n"); | |
871 | return; | |
872 | } | |
873 | ||
874 | tag[0].content_type = ((pi->rtp_raw_data[RDS_BLKTYPE_B - 1] & 0x0007) << 3) | | |
875 | ((pi->rtp_raw_data[RDS_BLKTYPE_C - 1] & 0xE000) >> 13); | |
876 | tag[0].start_pos = (pi->rtp_raw_data[RDS_BLKTYPE_C - 1] & 0x1F80) >> 7; | |
877 | tag[0].len = (pi->rtp_raw_data[RDS_BLKTYPE_C - 1] & 0x007E) >> 1; | |
878 | ||
879 | tag[1].content_type = ((pi->rtp_raw_data[RDS_BLKTYPE_C - 1] & 0x0001) << 5) | | |
880 | ((pi->rtp_raw_data[RDS_BLKTYPE_D - 1] & 0xF800) >> 11); | |
881 | tag[1].start_pos = (pi->rtp_raw_data[RDS_BLKTYPE_D - 1] & 0x07E0) >> 5; | |
882 | tag[1].len = pi->rtp_raw_data[RDS_BLKTYPE_D - 1] & 0x001F; | |
883 | ||
884 | RDSEBUG(gradio, "[RDS] RTP tag[0]:[%d,%d,%d]\n", tag[0].content_type, tag[0].start_pos, tag[0].len); | |
885 | RDSEBUG(gradio, "[RDS] RTP tag[1]:[%d,%d,%d]\n", tag[1].content_type, tag[1].start_pos, tag[1].len); | |
886 | ||
887 | /* Check overlap */ | |
888 | if (((tag[1].content_type != 0) && (tag[0].start_pos < tag[1].start_pos) && ((tag[0].start_pos + tag[0].len) >= tag[1].start_pos)) || | |
889 | ((tag[0].content_type != 0) && (tag[1].start_pos < tag[0].start_pos) && ((tag[1].start_pos + tag[1].len) >= tag[0].start_pos))) { | |
890 | RDSEBUG(gradio, "[RDS] RTP tag[0] & tag[1] are overlapped.\n"); | |
891 | return; | |
892 | } | |
893 | ||
894 | for (i = 0; i < MAX_RTP_TAG; i++) { | |
895 | if (tag[i].content_type == pi->rtp_data.tag[i].content_type && | |
896 | tag[i].start_pos == pi->rtp_data.tag[i].start_pos && | |
897 | tag[i].len == pi->rtp_data.tag[i].len) { | |
898 | rtp_validated++; | |
899 | RDSEBUG(gradio, "[RDS] RTP tag validation check count:0x%x\n", (int)rtp_validated); | |
900 | } else { | |
901 | pi->rtp_data.tag[i].content_type = tag[i].content_type; | |
902 | pi->rtp_data.tag[i].start_pos = tag[i].start_pos; | |
903 | pi->rtp_data.tag[i].len = tag[i].len; | |
904 | rtp_validated = 0; | |
905 | } | |
906 | } | |
907 | ||
908 | /* RT is ready to be displayed along with RTP data */ | |
909 | if (pi->rt_validated && rtp_validated > 2) | |
910 | pi->rds_event |= RDS_EVENT_RTP_MASK; | |
911 | } | |
912 | ||
913 | void store_rtp_data(struct fm_rds_parser_info *pi, u16 info, u8 blk_type) | |
914 | { | |
915 | u8 toggle; | |
916 | ||
917 | if (pi->drop_blk) { | |
918 | return; | |
919 | } | |
920 | ||
921 | if (pi->grp != pi->rtp_code_group) { | |
922 | RDSEBUG(gradio, "[RDS] Received unexpected code group(0x%x), expected=0x%x\n", | |
923 | (int)pi->grp, (int)pi->rtp_code_group); | |
924 | return; | |
925 | } | |
926 | ||
927 | switch (blk_type) { | |
928 | case RDS_BLKTYPE_B: | |
929 | toggle = (info & 0x010) >> 4; | |
930 | if (toggle != pi->rtp_data.toggle) { | |
931 | reset_rtp_data(pi); | |
932 | pi->rtp_data.toggle = toggle; | |
933 | } | |
934 | pi->rtp_data.running = (info & 0x0008) >> 3; | |
935 | pi->rtp_raw_data[blk_type-1] = info; | |
936 | RDSEBUG(gradio, "[RDS] Received RTP B block/0x%x Group\n", (int)pi->grp); | |
937 | break; | |
938 | case RDS_BLKTYPE_C: | |
939 | pi->rtp_raw_data[blk_type-1] = info; | |
940 | RDSEBUG(gradio, "[RDS] Received RTP C block/0x%x Group\n", (int)pi->grp); | |
941 | break; | |
942 | case RDS_BLKTYPE_D: | |
943 | pi->rtp_raw_data[blk_type-1] = info; | |
944 | RDSEBUG(gradio, "[RDS] Received RTP D block/0x%x Group\n", (int)pi->grp); | |
945 | validate_rtp_data(pi); | |
946 | break; | |
947 | default: | |
948 | break; | |
949 | } | |
950 | } | |
951 | ||
952 | void find_rtp_data(struct fm_rds_parser_info *pi, u16 info, u8 blk_type) | |
953 | { | |
954 | if (pi->drop_blk) | |
955 | return; | |
956 | ||
957 | switch (blk_type) { | |
958 | case RDS_BLKTYPE_B: | |
959 | pi->rtp_code_group = info & 0x1f; | |
960 | RDSEBUG(gradio, "[RDS] RTP code group:0x%x\n", (int)pi->rtp_code_group); | |
961 | break; | |
962 | case RDS_BLKTYPE_C: | |
963 | /* Not support SCB/RTP template */ | |
964 | RDSEBUG(gradio, "[RDS] RTP not support SCB/RTP template\n"); | |
965 | break; | |
966 | case RDS_BLKTYPE_D: | |
967 | if (info != RDS_RTP_AID) { | |
968 | RDSEBUG(gradio, "[RDS] Invalid RTP aid=0x%x\n", (int)info); | |
969 | pi->rtp_code_group = 0; | |
970 | } | |
971 | break; | |
972 | default: | |
973 | break; | |
974 | } | |
975 | } | |
976 | ||
977 | void find_group_data(struct fm_rds_parser_info *pi, u16 info) | |
978 | { | |
979 | u8 segment, rt_change; | |
980 | ||
981 | pi->grp = (info >> 11) & 0x1f; | |
982 | ||
4e871175 YK |
983 | pi->pty = (info >> 5) & 0x1f; |
984 | pi->rds_event |= RDS_EVENT_PTY_MASK; | |
985 | ||
eb1143a3 YK |
986 | switch (pi->grp) { |
987 | case RDS_GRPTYPE_0A: | |
988 | case RDS_GRPTYPE_0B: | |
989 | segment = info & 0x3; | |
990 | ||
991 | if (!segment && pi->ps_segment != 0xFF) | |
992 | validate_ps_data(pi); | |
993 | ||
994 | pi->ps_segment = segment; | |
995 | ||
996 | if (pi->ps_len < (segment + 1) * 2) | |
997 | pi->ps_len = (segment + 1) * 2; | |
998 | ||
999 | /*RDSEBUG(gradio, | |
1000 | "[RDS] PS: seg=%d, len=%d\n", | |
1001 | segment, pi->ps_len);*/ | |
1002 | break; | |
1003 | ||
1004 | case RDS_GRPTYPE_2A: | |
1005 | case RDS_GRPTYPE_2B: | |
1006 | segment = info & 0xF; | |
1007 | rt_change = (info & 0x10) >> 4; | |
1008 | ||
1009 | RDSEBUG(gradio, | |
1010 | "[RDS] segment=%d, pi->rt_segment=%d, pi->rt_change=%d, rt_change=%d\n", | |
1011 | segment, pi->rt_segment, pi->rt_change, rt_change); | |
1012 | ||
1013 | if ((!segment && pi->rt_segment != 0xFF) | |
1014 | || (pi->rt_change != 0xFF && pi->rt_change != rt_change)) { | |
1015 | validate_rt_data(pi); | |
1016 | ||
1017 | if (pi->rt_change != 0xFF && pi->rt_change != rt_change) { | |
1018 | pi->rt_len = 0; | |
1019 | pi->rt_idx = 0; | |
1020 | memset(pi->rt_buf, 0, 3 * (MAX_RT + 1)); | |
1021 | memset(pi->rt_err, 0xFF, 3 * MAX_RT / 2); | |
1022 | pi->rt_validated = 0; | |
1023 | } | |
1024 | } | |
1025 | ||
1026 | pi->rt_segment = segment; | |
1027 | pi->rt_change = rt_change; | |
1028 | ||
1029 | if (pi->grp == RDS_GRPTYPE_2A) { | |
1030 | if (pi->rt_len < (segment + 1) * 4) | |
1031 | pi->rt_len = (segment + 1) * 4; | |
1032 | } else { | |
1033 | if (pi->rt_len < (segment + 1) * 2) | |
1034 | pi->rt_len = (segment + 1) * 2; | |
1035 | } | |
1036 | ||
1037 | RDSEBUG(gradio, | |
1038 | "[RDS] RT: seg=%d, tc=%d, len=%d\n", | |
1039 | segment, rt_change, pi->rt_len); | |
1040 | break; | |
1041 | ||
1042 | case RDS_GRPTYPE_3A: | |
1043 | find_rtp_data(pi, info, RDS_BLKTYPE_B); | |
1044 | break; | |
1045 | ||
1046 | case RDS_GRPTYPE_5A: | |
1047 | case RDS_GRPTYPE_6A: | |
1048 | case RDS_GRPTYPE_7A: | |
1049 | case RDS_GRPTYPE_8A: | |
1050 | case RDS_GRPTYPE_9A: | |
1051 | case RDS_GRPTYPE_11A: | |
1052 | case RDS_GRPTYPE_12A: | |
1053 | case RDS_GRPTYPE_13A: | |
1054 | store_rtp_data(pi, info, RDS_BLKTYPE_B); | |
1055 | break; | |
1056 | ||
1057 | default: | |
1058 | break; | |
1059 | } | |
1060 | } | |
1061 | ||
1062 | void find_af_data(struct fm_rds_parser_info *pi, u16 info) | |
1063 | { | |
1064 | ||
1065 | pi->af_buf[pi->af_idx % 2] = info; | |
1066 | ||
1067 | if (!(++pi->af_idx % 2)) { | |
1068 | if (pi->af_buf[0] == pi->af_buf[1]) | |
1069 | pi->rds_event |= RDS_EVENT_AF_MASK; | |
1070 | } | |
1071 | } | |
1072 | ||
1073 | void fm_rds_parser_reset(struct fm_rds_parser_info *pi) | |
1074 | { | |
1075 | /* reset rds parser data structure */ | |
1076 | memset(pi, 0x0, sizeof(struct fm_rds_parser_info)); | |
1077 | ||
1078 | pi->ps_segment = 0xFF; | |
1079 | pi->rt_segment = 0xFF; | |
1080 | pi->rt_change = 0xFF; | |
1081 | pi->rtp_data.toggle = 0xFF; | |
1082 | memset(pi->rt_err, 0xFF, 3 * MAX_RT / 2); | |
1083 | memset(pi->ps_err, 0xFF, 3 * MAX_PS / 2); | |
1084 | } | |
1085 | ||
1086 | void fm_rds_parser(struct fm_rds_parser_info *pi, u16 info, u8 blk_type, u8 err_cnt) | |
1087 | { | |
1088 | ||
1089 | switch (blk_type) { | |
1090 | case RDS_BLKTYPE_A: | |
1091 | find_pi_data(pi, info); | |
1092 | break; | |
1093 | ||
1094 | case RDS_BLKTYPE_B: | |
1095 | if (err_cnt > 2) { | |
1096 | pi->grp = RDS_GRPTYPE_NONE; | |
1097 | pi->drop_blk = true; | |
1098 | break; | |
1099 | } else { | |
1100 | pi->drop_blk = false; | |
1101 | } | |
1102 | ||
1103 | find_group_data(pi, info); | |
1104 | break; | |
1105 | ||
1106 | case RDS_BLKTYPE_C: | |
1107 | if (err_cnt > 5) | |
1108 | return; | |
1109 | ||
1110 | switch (pi->grp) { | |
1111 | case RDS_GRPTYPE_0A: | |
1112 | find_af_data(pi, info); | |
1113 | break; | |
1114 | ||
1115 | case RDS_GRPTYPE_0B: | |
1116 | find_pi_data(pi, info); | |
1117 | break; | |
1118 | ||
1119 | case RDS_GRPTYPE_1A: | |
1120 | find_ecc_data(pi, info); | |
1121 | break; | |
1122 | ||
1123 | case RDS_GRPTYPE_2A: | |
1124 | store_rt_data(pi, info, blk_type, err_cnt); | |
1125 | break; | |
1126 | ||
1127 | case RDS_GRPTYPE_2B: | |
1128 | find_pi_data(pi, info); | |
1129 | break; | |
1130 | ||
1131 | case RDS_GRPTYPE_3A: | |
1132 | find_rtp_data(pi, info, blk_type); | |
1133 | break; | |
1134 | ||
1135 | case RDS_GRPTYPE_5A: | |
1136 | case RDS_GRPTYPE_6A: | |
1137 | case RDS_GRPTYPE_7A: | |
1138 | case RDS_GRPTYPE_8A: | |
1139 | case RDS_GRPTYPE_9A: | |
1140 | case RDS_GRPTYPE_11A: | |
1141 | case RDS_GRPTYPE_12A: | |
1142 | case RDS_GRPTYPE_13A: | |
1143 | store_rtp_data(pi, info, blk_type); | |
1144 | break; | |
1145 | ||
1146 | default: | |
1147 | break; | |
1148 | } | |
1149 | break; | |
1150 | ||
1151 | case RDS_BLKTYPE_D: | |
1152 | if (err_cnt > 5) | |
1153 | return; | |
1154 | ||
a9d2f3c7 | 1155 | switch (pi->grp) { |
eb1143a3 YK |
1156 | case RDS_GRPTYPE_0A: |
1157 | case RDS_GRPTYPE_0B: | |
1158 | store_ps_data(pi, info, err_cnt); | |
1159 | break; | |
1160 | ||
1161 | case RDS_GRPTYPE_2A: | |
1162 | case RDS_GRPTYPE_2B: | |
1163 | store_rt_data(pi, info, blk_type, err_cnt); | |
1164 | break; | |
1165 | ||
1166 | case RDS_GRPTYPE_3A: | |
1167 | find_rtp_data(pi, info, blk_type); | |
1168 | break; | |
1169 | ||
1170 | case RDS_GRPTYPE_5A: | |
1171 | case RDS_GRPTYPE_6A: | |
1172 | case RDS_GRPTYPE_7A: | |
1173 | case RDS_GRPTYPE_8A: | |
1174 | case RDS_GRPTYPE_9A: | |
1175 | case RDS_GRPTYPE_11A: | |
1176 | case RDS_GRPTYPE_12A: | |
1177 | case RDS_GRPTYPE_13A: | |
1178 | store_rtp_data(pi, info, blk_type); | |
1179 | break; | |
1180 | ||
1181 | default: | |
1182 | break; | |
a9d2f3c7 S |
1183 | } |
1184 | break; | |
eb1143a3 YK |
1185 | } |
1186 | } | |
1187 | ||
1188 | void fm_rds_write_data_pi(struct s610_radio *radio, | |
1189 | struct fm_rds_parser_info *pi) | |
1190 | { | |
1191 | u8 buf_ptr[RDS_MEM_MAX_THRESH_PARSER]; | |
1192 | u8 i, str_len; | |
1193 | int total_size = 0; | |
1194 | u16 usage; | |
1195 | ||
1196 | #ifdef USE_RINGBUFF_API | |
1197 | if (ringbuf_bytes_free(&radio->rds_rb) < RDS_MEM_MAX_THRESH_PARSER) { | |
e16618ee | 1198 | RDSEBUG(radio, ">>> ringbuff not free, wait.."); |
eb1143a3 YK |
1199 | /*ringbuf_reset(&radio->rds_rb);*/ |
1200 | return; | |
1201 | } | |
1202 | #else | |
1203 | dev_info(radio->dev, ">>> ringbuff API not used, fail.."); | |
1204 | return; | |
1205 | #endif /* USE_RINGBUFF_API */ | |
1206 | ||
1207 | memset(buf_ptr, 0, RDS_MEM_MAX_THRESH_PARSER); | |
1208 | ||
1209 | while (pi->rds_event) { | |
1210 | RDSEBUG(radio, | |
1211 | "%s() rds_event[%04X]\n", __func__, pi->rds_event); | |
1212 | ||
1213 | if (pi->rds_event & RDS_EVENT_PI_MASK) { | |
1214 | pi->rds_event &= ~RDS_EVENT_PI_MASK; | |
1215 | buf_ptr[total_size] = RDS_EVENT_PI; | |
1216 | buf_ptr[total_size+1] = 2; | |
1217 | buf_ptr[total_size+2] = (u8)(pi->pi_buf[0] & 0xFF); | |
1218 | buf_ptr[total_size+3] = (u8)(pi->pi_buf[0] >> 8); | |
1219 | RDSEBUG(radio, | |
1220 | "[RDS] Enqueue PI data[%02X:%02X:%02X:%02X]. total=%d\n", | |
1221 | buf_ptr[total_size],buf_ptr[total_size+1],buf_ptr[total_size+2],buf_ptr[total_size+3], | |
1222 | total_size+4); | |
1223 | ||
1224 | total_size += 4; | |
1225 | ||
1226 | } else if (pi->rds_event & RDS_EVENT_AF_MASK) { | |
1227 | pi->rds_event &= ~RDS_EVENT_AF_MASK; | |
1228 | buf_ptr[total_size] = RDS_EVENT_AF; | |
1229 | buf_ptr[total_size+1] = 2; | |
1230 | buf_ptr[total_size+2] = (u8)(pi->af_buf[0] & 0xFF); | |
1231 | buf_ptr[total_size+3] = (u8)(pi->af_buf[0] >> 8); | |
1232 | RDSEBUG(radio, | |
1233 | "[RDS] Enqueue AF data[%02X:%02X:%02X:%02X]. total=%d\n", | |
1234 | buf_ptr[total_size],buf_ptr[total_size+1],buf_ptr[total_size+2],buf_ptr[total_size+3], | |
1235 | total_size+4); | |
1236 | ||
1237 | total_size += 4; | |
1238 | ||
1239 | } else if (pi->rds_event & RDS_EVENT_PS_MASK) { | |
1240 | pi->rds_event &= ~RDS_EVENT_PS_MASK; | |
1241 | str_len = strlen(pi->ps_candidate); | |
1242 | buf_ptr[total_size] = RDS_EVENT_PS; | |
1243 | buf_ptr[total_size+1] = str_len; | |
1244 | ||
1245 | for (i = 0; i < str_len; i++) { | |
1246 | buf_ptr[total_size + i + 2] = pi->ps_candidate[i]; | |
1247 | } | |
1248 | ||
1249 | total_size += str_len + 2; | |
1250 | RDSEBUG(radio, | |
1251 | "[RDS] Enqueue PS data. total=%d,%d,%d\n", total_size, pi->ps_len + 2, str_len); | |
1252 | } else if (pi->rds_event & RDS_EVENT_RT_MASK) { | |
1253 | pi->rds_event &= ~RDS_EVENT_RT_MASK; | |
1254 | str_len = strlen(pi->rt_candidate); | |
1255 | buf_ptr[total_size] = RDS_EVENT_RT; | |
1256 | buf_ptr[total_size+1] = str_len; | |
1257 | ||
1258 | for (i = 0; i < str_len; i++) { | |
1259 | buf_ptr[total_size + i + 2] = pi->rt_candidate[i]; | |
1260 | } | |
1261 | ||
1262 | total_size += str_len + 2; | |
1263 | RDSEBUG(radio, | |
1264 | "[RDS] Enqueue RT data. total=%d,%d,%d\n", total_size, pi->rt_len + 2, str_len + 2); | |
1265 | } else if (pi->rds_event & RDS_EVENT_ECC_MASK) { | |
1266 | pi->rds_event &= ~RDS_EVENT_ECC_MASK; | |
1267 | ||
1268 | buf_ptr[total_size] = RDS_EVENT_ECC; | |
1269 | buf_ptr[total_size+1] = 1; | |
1270 | buf_ptr[total_size+2] = (u8)pi->ecc_buf[0]; | |
1271 | RDSEBUG(radio, | |
1272 | "[RDS] Enqueue ECC data[%02d:%02d:%02d]. total=%d\n", | |
1273 | buf_ptr[total_size],buf_ptr[total_size+1],buf_ptr[total_size+2], | |
1274 | total_size+3); | |
1275 | ||
1276 | total_size += 3; | |
1277 | } else if (pi->rds_event & RDS_EVENT_RTP_MASK) { | |
1278 | pi->rds_event &= ~RDS_EVENT_RTP_MASK; | |
1279 | ||
1280 | buf_ptr[total_size] = RDS_EVENT_RTP; | |
1281 | buf_ptr[total_size+1] = sizeof(struct rtp_info); | |
1282 | memcpy(buf_ptr+total_size+2, &(pi->rtp_data), sizeof(struct rtp_info)); | |
1283 | RDSEBUG(radio, "[RDS] Enqueue RTPlus data\n"); | |
1284 | RDSEBUG(radio, "[RDS] Toggle:%d, Running:%d, Validated;%d\n", pi->rtp_data.toggle, pi->rtp_data.running, pi->rtp_data.validated); | |
1285 | RDSEBUG(radio, "[%d,%d,%d]\n", pi->rtp_data.tag[0].content_type, pi->rtp_data.tag[0].start_pos, pi->rtp_data.tag[0].len); | |
1286 | RDSEBUG(radio, "[%d,%d,%d]\n", pi->rtp_data.tag[1].content_type, pi->rtp_data.tag[1].start_pos, pi->rtp_data.tag[1].len); | |
1287 | ||
1288 | total_size += sizeof(struct rtp_info) + 2; | |
4e871175 YK |
1289 | } else if (pi->rds_event & RDS_EVENT_PTY_MASK) { |
1290 | pi->rds_event &= ~RDS_EVENT_PTY_MASK; | |
1291 | ||
1292 | buf_ptr[total_size] = RDS_EVENT_PTY; | |
1293 | buf_ptr[total_size+1] = 1; | |
1294 | buf_ptr[total_size+2] = pi->pty; | |
1295 | RDSEBUG(radio, | |
1296 | "[RDS] Enqueue PTY data[%02X:%02X:%02X]. total=%d\n", | |
1297 | buf_ptr[total_size],buf_ptr[total_size+1],buf_ptr[total_size+2], | |
1298 | total_size+3); | |
1299 | ||
1300 | total_size += 3; | |
eb1143a3 YK |
1301 | } |
1302 | ||
1303 | if (!pi->rds_event && total_size) { | |
1304 | #ifdef USE_RINGBUFF_API | |
1305 | if (!ringbuf_memcpy_into(&radio->rds_rb, buf_ptr, total_size)) { | |
1306 | usage = ringbuf_bytes_used(&radio->rds_rb); | |
e16618ee | 1307 | RDSEBUG(radio, |
eb1143a3 YK |
1308 | "%s():>>>RB memcpy into fail! usage:%04d", |
1309 | __func__, ringbuf_bytes_used(&radio->rds_rb)); | |
1310 | if (!usage) | |
1311 | return; | |
1312 | } | |
1313 | ||
1314 | usage = ringbuf_bytes_used(&radio->rds_rb); | |
1315 | #else | |
e16618ee | 1316 | RDSEBUG(radio, ">>> ringbuff API not used, fail.."); |
eb1143a3 YK |
1317 | #endif /* USE_RINGBUFF_API */ |
1318 | ||
1319 | if (atomic_read(&radio->is_rds_new)) | |
1320 | return; | |
1321 | ||
1322 | fm_set_flag_bits(radio, FLAG_BUF_FUL); | |
1323 | atomic_set(&radio->is_rds_new, 1); | |
1324 | wake_up_interruptible(&radio->core->rds_read_queue); | |
1325 | radio->rds_n_count++; | |
1326 | ||
1327 | if (!(radio->rds_n_count%200)) { | |
1328 | fm_update_rssi_work(radio); | |
e16618ee | 1329 | RDSEBUG(radio, |
eb1143a3 YK |
1330 | ">>>[FM] RSSI[%03d] NCOUNT[%08d] FIFO_ERR[%08d] USAGE[%04d] SYNCLOSS[%08d]", |
1331 | radio->low->fm_state.rssi, radio->rds_n_count, radio->rds_fifo_err_cnt, | |
1332 | usage, radio->rds_sync_loss_cnt); | |
1333 | } | |
1334 | } | |
1335 | } | |
1336 | } | |
1337 | ||
1338 | #ifndef USE_RDS_HW_DECODER | |
1339 | struct fm_decoded_data { | |
1340 | u16 info; | |
1341 | fm_rds_block_type_enum blk_type; | |
1342 | u8 err_cnt; | |
1343 | }; | |
1344 | ||
1345 | void put_bit_to_byte(u8 *fifo, u32 data) | |
1346 | { | |
1347 | s8 i, j; | |
1348 | u32 mask = 0x80000000; | |
1349 | ||
1350 | for (i = BUF_LEN - 1, j = 0; i >= 0; i--, j++) { | |
1351 | *(fifo + j) = (mask & data) ? 1 : 0; | |
1352 | mask = mask >> 1; | |
1353 | } | |
1354 | } | |
1355 | ||
1356 | u32 get_block_data(u8 *fifo, u32 sp) | |
1357 | { | |
1358 | u8 i, j; | |
1359 | u32 data = 0; | |
1360 | ||
1361 | data |= *(fifo + (sp++ % 160)); | |
1362 | ||
1363 | for (i = BLK_LEN-1, j = 0; i > 0; i--, j++) { | |
1364 | data <<= 1; | |
1365 | data |= *(fifo + (sp++ % 160)); | |
1366 | } | |
1367 | ||
1368 | return data; | |
1369 | } | |
1370 | ||
1371 | u8 find_code_word(u32 *data, fm_rds_block_type_enum b_type, bool seq_lock) | |
1372 | { | |
1373 | u16 first13; | |
1374 | u16 second13; | |
1375 | u16 syndrome; | |
1376 | ||
1377 | first13 = *data >> 13; | |
1378 | second13 = (*data & 0x1FFF) ^ OFFSET_WORD[b_type]; | |
1379 | ||
1380 | syndrome = CRC_LUT[(CRC_LUT[first13] << 3) ^ second13]; | |
1381 | ||
1382 | if (syndrome) { | |
1383 | u32 corrected; | |
1384 | u8 i, j; | |
1385 | u8 maxerr = (b_type == RDS_BLKTYPE_A) ? 2 : sizeof(burstErrorPattern) / sizeof(u8); | |
1386 | ||
1387 | for (i = 0; i < maxerr; i++) { | |
1388 | for (j = 0; j <= BLK_LEN - burstErrorLen[i]; j++) { | |
1389 | corrected = *data ^ (burstErrorPattern[i] << j); | |
1390 | ||
1391 | first13 = corrected >> 13; | |
1392 | second13 = (corrected & 0x1FFF) ^ OFFSET_WORD[b_type]; | |
1393 | ||
1394 | syndrome = CRC_LUT[(CRC_LUT[first13] << 3) ^ second13]; | |
1395 | ||
1396 | if (!syndrome) { | |
1397 | *data = corrected; | |
1398 | return burstErrorLen[i]; | |
1399 | } | |
1400 | } | |
1401 | } | |
1402 | } else { | |
1403 | return 0; | |
1404 | } | |
1405 | return 6; | |
1406 | } | |
1407 | ||
1408 | void fm_process_rds_data(struct s610_radio *radio) | |
1409 | { | |
1410 | u16 i, j, k, l; | |
1411 | u16 avail_blocks; | |
1412 | u32 fifo_data; | |
1413 | u32 data; | |
1414 | fm_host_rds_errors_enum err_cnt; | |
1415 | u8 min_pos = 0; | |
1416 | u8 min_blk = 0; | |
1417 | u8 min_blk_tmp = 0; | |
1418 | u8 min_pos_sum, min_blk_sum; | |
1419 | ||
1420 | static u32 idx; | |
1421 | static u8 fifo[160]; | |
1422 | static u32 start_pos; | |
1423 | static u32 end_pos; | |
1424 | static bool fetch_data; | |
1425 | static bool seq_lock; | |
1426 | static bool remains; | |
1427 | static struct fm_decoded_data decoded[BLK_LEN][RDS_NUM_BLOCK_TYPES - 1][RDS_NUM_BLOCK_TYPES - 1]; | |
1428 | u32 fifo_status; | |
1429 | ||
1430 | fifo_status = fmspeedy_get_reg_work(0xFFF398); | |
1431 | avail_blocks = (((fifo_status >> 8) & 0x3F)); | |
1432 | RDSEBUG(radio,"%s(): avail_blocks:%d fifo_status[%08X]",__func__, avail_blocks, fifo_status); | |
1433 | ||
1434 | while (avail_blocks) { | |
1435 | /* Fetch the RDS raw data. */ | |
1436 | if (fetch_data) { | |
1437 | fifo_data = fmspeedy_get_reg_work(0xFFF3C0); | |
1438 | put_bit_to_byte(fifo + 32 * ((idx++) % 5), fifo_data); | |
1439 | avail_blocks--; | |
1440 | } | |
1441 | ||
1442 | switch (fm_rds_get_state(radio)) { | |
1443 | case RDS_STATE_INIT: | |
1444 | fm_rds_change_state(radio, RDS_STATE_HAVE_DATA); | |
1445 | fm_update_rds_sync_status(radio, false); | |
1446 | radio->low->fm_rds_state.error_bits = 0; | |
1447 | radio->low->fm_rds_state.error_blks = 0; | |
1448 | ||
1449 | idx = 0; | |
1450 | start_pos = 0; | |
1451 | end_pos = BLK_LEN - 1; | |
1452 | fetch_data = false; | |
1453 | seq_lock = false; | |
1454 | memset(fifo, 0, sizeof(fifo) / sizeof(u8)); | |
1455 | ||
1456 | case RDS_STATE_HAVE_DATA: | |
1457 | if (idx < 5) { | |
1458 | fifo_data = fmspeedy_get_reg_work(0xFFF3C0); | |
1459 | put_bit_to_byte(fifo + 32 * ((idx++) % 5), fifo_data); | |
1460 | avail_blocks--; | |
1461 | ||
1462 | if (idx == 5) { | |
1463 | for (i = 0; i < BLK_LEN; i++) { | |
1464 | for (j = 0; j < RDS_NUM_BLOCK_TYPES - 1; j++) { | |
1465 | start_pos = i; | |
1466 | ||
1467 | for (k = 0, l = j; k < RDS_NUM_BLOCK_TYPES - 1; k++) { | |
1468 | data = get_block_data(fifo, start_pos); | |
1469 | err_cnt = find_code_word(&data, l, seq_lock); | |
1470 | ||
1471 | decoded[i][j][k].info = data >> 10; | |
1472 | decoded[i][j][k].blk_type = l; | |
1473 | decoded[i][j][k].err_cnt = err_cnt; | |
1474 | start_pos += BLK_LEN; | |
1475 | l = (l + 1) % (RDS_NUM_BLOCK_TYPES - 1); | |
1476 | } | |
1477 | } | |
1478 | } | |
1479 | ||
1480 | for (i = 0, min_pos_sum = 0xFF; i < BLK_LEN; i++) { | |
1481 | for (j = 0, min_blk_sum = 0xFF; j < RDS_NUM_BLOCK_TYPES - 1; j++) { | |
1482 | for (k = 0, err_cnt = 0; k < RDS_NUM_BLOCK_TYPES - 1; k++) { | |
1483 | err_cnt += decoded[i][j][k].err_cnt; | |
1484 | } | |
1485 | ||
1486 | if (min_blk_sum > err_cnt) { | |
1487 | min_blk_sum = err_cnt; | |
1488 | min_blk_tmp = j; | |
1489 | } | |
1490 | } | |
1491 | ||
1492 | if (min_pos_sum > min_blk_sum) { | |
1493 | min_pos_sum = min_blk_sum; | |
1494 | min_pos = i; | |
1495 | min_blk = min_blk_tmp; | |
1496 | } | |
1497 | ||
1498 | } | |
1499 | ||
1500 | fm_rds_change_state(radio, RDS_STATE_PRE_SYNC); | |
1501 | } else | |
1502 | break; | |
1503 | } | |
1504 | ||
1505 | case RDS_STATE_PRE_SYNC: | |
1506 | for (i = 0; i < RDS_NUM_BLOCK_TYPES - 1; i++) { | |
1507 | if (decoded[min_pos][min_blk][i].blk_type == RDS_BLKTYPE_A) { | |
1508 | fm_update_rds_sync_status(radio, TRUE); | |
1509 | } | |
1510 | ||
1511 | if (fm_get_rds_sync_status(radio)) { | |
1512 | ||
1513 | if (fm_rds_process_block(radio, decoded[min_pos][min_blk][i].info, | |
1514 | decoded[min_pos][min_blk][i].blk_type, decoded[min_pos][min_blk][i].err_cnt) | |
1515 | == HOST_RDS_ERRS_UNCORR) { | |
1516 | fm_rds_update_error_status(radio, | |
1517 | radio->low->fm_state.rds_unsync_uncorr_weight); | |
1518 | } else { | |
1519 | fm_rds_update_error_status(radio, decoded[min_pos][min_blk][i].err_cnt); | |
1520 | } | |
1521 | } | |
1522 | } | |
1523 | ||
1524 | start_pos = min_pos + BLK_LEN * 3; | |
1525 | end_pos = start_pos + BLK_LEN - 1; | |
1526 | ||
1527 | fm_rds_change_state(radio, (min_blk + 3) % (RDS_NUM_BLOCK_TYPES - 1)); | |
1528 | seq_lock = false; | |
1529 | remains = true; | |
1530 | ||
1531 | case RDS_STATE_FOUND_BL_A: | |
1532 | case RDS_STATE_FOUND_BL_B: | |
1533 | case RDS_STATE_FOUND_BL_C: | |
1534 | case RDS_STATE_FOUND_BL_D: | |
1535 | if ((end_pos / BUF_LEN != (end_pos + BLK_LEN) / BUF_LEN) && !fetch_data && !remains) { | |
1536 | fetch_data = true; | |
1537 | } else { | |
1538 | if (end_pos + BLK_LEN >= 160 && remains) { | |
1539 | remains = false; | |
1540 | fetch_data = true; | |
1541 | break; | |
1542 | } | |
1543 | ||
1544 | start_pos += BLK_LEN; | |
1545 | end_pos += BLK_LEN; | |
1546 | ||
1547 | data = get_block_data(fifo, start_pos); | |
1548 | fetch_data = false; | |
1549 | ||
1550 | i = (fm_rds_get_state(radio) + 1) % (RDS_NUM_BLOCK_TYPES - 1); | |
1551 | fm_rds_change_state(radio, i); | |
1552 | err_cnt = find_code_word(&data, i, seq_lock); | |
1553 | ||
1554 | RDSEBUG(radio,"%s(): data:0x%x, errcnt:%d, gcount:%d", __func__, data, err_cnt, radio->rds_gcnt++); | |
1555 | if (fm_rds_process_block(radio, data >> 10, i, err_cnt) == HOST_RDS_ERRS_UNCORR) { | |
1556 | fm_rds_update_error_status(radio, radio->low->fm_state.rds_unsync_uncorr_weight); | |
1557 | } else { | |
1558 | fm_rds_update_error_status(radio, err_cnt); | |
1559 | } | |
1560 | } | |
1561 | break; | |
1562 | ||
1563 | default: | |
1564 | break; | |
1565 | } | |
1566 | } | |
1567 | } | |
1568 | #endif /*USE_RDS_HW_DECODER*/ |