vivid-rds-gen.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * vivid-rds-gen.c - rds (radio data system) generator support functions.
  4. *
  5. * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  6. */
  7. #include <linux/kernel.h>
  8. #include <linux/ktime.h>
  9. #include <linux/string.h>
  10. #include <linux/videodev2.h>
  11. #include "vivid-rds-gen.h"
  12. static u8 vivid_get_di(const struct vivid_rds_gen *rds, unsigned grp)
  13. {
  14. switch (grp) {
  15. case 0:
  16. return (rds->dyn_pty << 2) | (grp & 3);
  17. case 1:
  18. return (rds->compressed << 2) | (grp & 3);
  19. case 2:
  20. return (rds->art_head << 2) | (grp & 3);
  21. case 3:
  22. return (rds->mono_stereo << 2) | (grp & 3);
  23. }
  24. return 0;
  25. }
  26. /*
  27. * This RDS generator creates 57 RDS groups (one group == four RDS blocks).
  28. * Groups 0-3, 22-25 and 44-47 (spaced 22 groups apart) are filled with a
  29. * standard 0B group containing the PI code and PS name.
  30. *
  31. * Groups 4-19 and 26-41 use group 2A for the radio text.
  32. *
  33. * Group 56 contains the time (group 4A).
  34. *
  35. * All remaining groups use a filler group 15B block that just repeats
  36. * the PI and PTY codes.
  37. */
  38. void vivid_rds_generate(struct vivid_rds_gen *rds)
  39. {
  40. struct v4l2_rds_data *data = rds->data;
  41. unsigned grp;
  42. unsigned idx;
  43. struct tm tm;
  44. unsigned date;
  45. unsigned time;
  46. int l;
  47. for (grp = 0; grp < VIVID_RDS_GEN_GROUPS; grp++, data += VIVID_RDS_GEN_BLKS_PER_GRP) {
  48. data[0].lsb = rds->picode & 0xff;
  49. data[0].msb = rds->picode >> 8;
  50. data[0].block = V4L2_RDS_BLOCK_A | (V4L2_RDS_BLOCK_A << 3);
  51. data[1].lsb = rds->pty << 5;
  52. data[1].msb = (rds->pty >> 3) | (rds->tp << 2);
  53. data[1].block = V4L2_RDS_BLOCK_B | (V4L2_RDS_BLOCK_B << 3);
  54. data[3].block = V4L2_RDS_BLOCK_D | (V4L2_RDS_BLOCK_D << 3);
  55. switch (grp) {
  56. case 0 ... 3:
  57. case 22 ... 25:
  58. case 44 ... 47: /* Group 0B */
  59. idx = (grp % 22) % 4;
  60. data[1].lsb |= (rds->ta << 4) | (rds->ms << 3);
  61. data[1].lsb |= vivid_get_di(rds, idx);
  62. data[1].msb |= 1 << 3;
  63. data[2].lsb = rds->picode & 0xff;
  64. data[2].msb = rds->picode >> 8;
  65. data[2].block = V4L2_RDS_BLOCK_C_ALT | (V4L2_RDS_BLOCK_C_ALT << 3);
  66. data[3].lsb = rds->psname[2 * idx + 1];
  67. data[3].msb = rds->psname[2 * idx];
  68. break;
  69. case 4 ... 19:
  70. case 26 ... 41: /* Group 2A */
  71. idx = ((grp - 4) % 22) % 16;
  72. data[1].lsb |= idx;
  73. data[1].msb |= 4 << 3;
  74. data[2].msb = rds->radiotext[4 * idx];
  75. data[2].lsb = rds->radiotext[4 * idx + 1];
  76. data[2].block = V4L2_RDS_BLOCK_C | (V4L2_RDS_BLOCK_C << 3);
  77. data[3].msb = rds->radiotext[4 * idx + 2];
  78. data[3].lsb = rds->radiotext[4 * idx + 3];
  79. break;
  80. case 56:
  81. /*
  82. * Group 4A
  83. *
  84. * Uses the algorithm from Annex G of the RDS standard
  85. * EN 50067:1998 to convert a UTC date to an RDS Modified
  86. * Julian Day.
  87. */
  88. time64_to_tm(ktime_get_real_seconds(), 0, &tm);
  89. l = tm.tm_mon <= 1;
  90. date = 14956 + tm.tm_mday + ((tm.tm_year - l) * 1461) / 4 +
  91. ((tm.tm_mon + 2 + l * 12) * 306001) / 10000;
  92. time = (tm.tm_hour << 12) |
  93. (tm.tm_min << 6) |
  94. (sys_tz.tz_minuteswest >= 0 ? 0x20 : 0) |
  95. (abs(sys_tz.tz_minuteswest) / 30);
  96. data[1].lsb &= ~3;
  97. data[1].lsb |= date >> 15;
  98. data[1].msb |= 8 << 3;
  99. data[2].lsb = (date << 1) & 0xfe;
  100. data[2].lsb |= (time >> 16) & 1;
  101. data[2].msb = (date >> 7) & 0xff;
  102. data[2].block = V4L2_RDS_BLOCK_C | (V4L2_RDS_BLOCK_C << 3);
  103. data[3].lsb = time & 0xff;
  104. data[3].msb = (time >> 8) & 0xff;
  105. break;
  106. default: /* Group 15B */
  107. data[1].lsb |= (rds->ta << 4) | (rds->ms << 3);
  108. data[1].lsb |= vivid_get_di(rds, grp % 22);
  109. data[1].msb |= 0x1f << 3;
  110. data[2].lsb = rds->picode & 0xff;
  111. data[2].msb = rds->picode >> 8;
  112. data[2].block = V4L2_RDS_BLOCK_C_ALT | (V4L2_RDS_BLOCK_C_ALT << 3);
  113. data[3].lsb = rds->pty << 5;
  114. data[3].lsb |= (rds->ta << 4) | (rds->ms << 3);
  115. data[3].lsb |= vivid_get_di(rds, grp % 22);
  116. data[3].msb |= rds->pty >> 3;
  117. data[3].msb |= 0x1f << 3;
  118. break;
  119. }
  120. }
  121. }
  122. void vivid_rds_gen_fill(struct vivid_rds_gen *rds, unsigned freq,
  123. bool alt)
  124. {
  125. /* Alternate PTY between Info and Weather */
  126. if (rds->use_rbds) {
  127. rds->picode = 0x2e75; /* 'KLNX' call sign */
  128. rds->pty = alt ? 29 : 2;
  129. } else {
  130. rds->picode = 0x8088;
  131. rds->pty = alt ? 16 : 3;
  132. }
  133. rds->mono_stereo = true;
  134. rds->art_head = false;
  135. rds->compressed = false;
  136. rds->dyn_pty = false;
  137. rds->tp = true;
  138. rds->ta = alt;
  139. rds->ms = true;
  140. snprintf(rds->psname, sizeof(rds->psname), "%6d.%1d",
  141. freq / 16, ((freq & 0xf) * 10) / 16);
  142. if (alt)
  143. strlcpy(rds->radiotext,
  144. " The Radio Data System can switch between different Radio Texts ",
  145. sizeof(rds->radiotext));
  146. else
  147. strlcpy(rds->radiotext,
  148. "An example of Radio Text as transmitted by the Radio Data System",
  149. sizeof(rds->radiotext));
  150. }