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