blob: c382343fdb66935eeb6bcb53e8e4898ce3c30721 [file] [log] [blame]
Hans Verkuil55d58e92014-08-25 08:02:56 -03001/*
2 * vivid-rds-gen.c - rds (radio data system) generator support functions.
3 *
4 * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
5 *
6 * This program is free software; you may redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
11 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
13 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
14 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
15 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17 * SOFTWARE.
18 */
19
20#include <linux/kernel.h>
21#include <linux/ktime.h>
Hans Verkuil5754d0d2014-09-03 04:29:00 -030022#include <linux/string.h>
Hans Verkuil55d58e92014-08-25 08:02:56 -030023#include <linux/videodev2.h>
24
25#include "vivid-rds-gen.h"
26
27static u8 vivid_get_di(const struct vivid_rds_gen *rds, unsigned grp)
28{
29 switch (grp) {
30 case 0:
31 return (rds->dyn_pty << 2) | (grp & 3);
32 case 1:
33 return (rds->compressed << 2) | (grp & 3);
34 case 2:
35 return (rds->art_head << 2) | (grp & 3);
36 case 3:
37 return (rds->mono_stereo << 2) | (grp & 3);
38 }
39 return 0;
40}
41
42/*
43 * This RDS generator creates 57 RDS groups (one group == four RDS blocks).
44 * Groups 0-3, 22-25 and 44-47 (spaced 22 groups apart) are filled with a
45 * standard 0B group containing the PI code and PS name.
46 *
47 * Groups 4-19 and 26-41 use group 2A for the radio text.
48 *
49 * Group 56 contains the time (group 4A).
50 *
51 * All remaining groups use a filler group 15B block that just repeats
52 * the PI and PTY codes.
53 */
54void vivid_rds_generate(struct vivid_rds_gen *rds)
55{
56 struct v4l2_rds_data *data = rds->data;
57 unsigned grp;
58 struct tm tm;
59 unsigned date;
60 unsigned time;
61 int l;
62
63 for (grp = 0; grp < VIVID_RDS_GEN_GROUPS; grp++, data += VIVID_RDS_GEN_BLKS_PER_GRP) {
64 data[0].lsb = rds->picode & 0xff;
65 data[0].msb = rds->picode >> 8;
66 data[0].block = V4L2_RDS_BLOCK_A | (V4L2_RDS_BLOCK_A << 3);
67 data[1].lsb = rds->pty << 5;
68 data[1].msb = (rds->pty >> 3) | (rds->tp << 2);
69 data[1].block = V4L2_RDS_BLOCK_B | (V4L2_RDS_BLOCK_B << 3);
70 data[3].block = V4L2_RDS_BLOCK_D | (V4L2_RDS_BLOCK_D << 3);
71
72 switch (grp) {
73 case 0 ... 3:
74 case 22 ... 25:
75 case 44 ... 47: /* Group 0B */
76 data[1].lsb |= (rds->ta << 4) | (rds->ms << 3);
77 data[1].lsb |= vivid_get_di(rds, grp % 22);
78 data[1].msb |= 1 << 3;
79 data[2].lsb = rds->picode & 0xff;
80 data[2].msb = rds->picode >> 8;
81 data[2].block = V4L2_RDS_BLOCK_C_ALT | (V4L2_RDS_BLOCK_C_ALT << 3);
82 data[3].lsb = rds->psname[2 * (grp % 22) + 1];
83 data[3].msb = rds->psname[2 * (grp % 22)];
84 break;
85 case 4 ... 19:
86 case 26 ... 41: /* Group 2A */
87 data[1].lsb |= (grp - 4) % 22;
88 data[1].msb |= 4 << 3;
89 data[2].msb = rds->radiotext[4 * ((grp - 4) % 22)];
90 data[2].lsb = rds->radiotext[4 * ((grp - 4) % 22) + 1];
91 data[2].block = V4L2_RDS_BLOCK_C | (V4L2_RDS_BLOCK_C << 3);
92 data[3].msb = rds->radiotext[4 * ((grp - 4) % 22) + 2];
93 data[3].lsb = rds->radiotext[4 * ((grp - 4) % 22) + 3];
94 break;
95 case 56:
96 /*
97 * Group 4A
98 *
99 * Uses the algorithm from Annex G of the RDS standard
100 * EN 50067:1998 to convert a UTC date to an RDS Modified
101 * Julian Day.
102 */
103 time_to_tm(get_seconds(), 0, &tm);
104 l = tm.tm_mon <= 1;
105 date = 14956 + tm.tm_mday + ((tm.tm_year - l) * 1461) / 4 +
106 ((tm.tm_mon + 2 + l * 12) * 306001) / 10000;
107 time = (tm.tm_hour << 12) |
108 (tm.tm_min << 6) |
109 (sys_tz.tz_minuteswest >= 0 ? 0x20 : 0) |
110 (abs(sys_tz.tz_minuteswest) / 30);
111 data[1].lsb &= ~3;
112 data[1].lsb |= date >> 15;
113 data[1].msb |= 8 << 3;
114 data[2].lsb = (date << 1) & 0xfe;
115 data[2].lsb |= (time >> 16) & 1;
116 data[2].msb = (date >> 7) & 0xff;
117 data[2].block = V4L2_RDS_BLOCK_C | (V4L2_RDS_BLOCK_C << 3);
118 data[3].lsb = time & 0xff;
119 data[3].msb = (time >> 8) & 0xff;
120 break;
121 default: /* Group 15B */
122 data[1].lsb |= (rds->ta << 4) | (rds->ms << 3);
123 data[1].lsb |= vivid_get_di(rds, grp % 22);
124 data[1].msb |= 0x1f << 3;
125 data[2].lsb = rds->picode & 0xff;
126 data[2].msb = rds->picode >> 8;
127 data[2].block = V4L2_RDS_BLOCK_C_ALT | (V4L2_RDS_BLOCK_C_ALT << 3);
128 data[3].lsb = rds->pty << 5;
129 data[3].lsb |= (rds->ta << 4) | (rds->ms << 3);
130 data[3].lsb |= vivid_get_di(rds, grp % 22);
131 data[3].msb |= rds->pty >> 3;
132 data[3].msb |= 0x1f << 3;
133 break;
134 }
135 }
136}
137
138void vivid_rds_gen_fill(struct vivid_rds_gen *rds, unsigned freq,
139 bool alt)
140{
141 /* Alternate PTY between Info and Weather */
142 if (rds->use_rbds) {
143 rds->picode = 0x2e75; /* 'KLNX' call sign */
144 rds->pty = alt ? 29 : 2;
145 } else {
146 rds->picode = 0x8088;
147 rds->pty = alt ? 16 : 3;
148 }
149 rds->mono_stereo = true;
150 rds->art_head = false;
151 rds->compressed = false;
152 rds->dyn_pty = false;
153 rds->tp = true;
154 rds->ta = alt;
155 rds->ms = true;
156 snprintf(rds->psname, sizeof(rds->psname), "%6d.%1d",
157 freq / 16, ((freq & 0xf) * 10) / 16);
158 if (alt)
159 strlcpy(rds->radiotext,
160 " The Radio Data System can switch between different Radio Texts ",
161 sizeof(rds->radiotext));
162 else
163 strlcpy(rds->radiotext,
164 "An example of Radio Text as transmitted by the Radio Data System",
165 sizeof(rds->radiotext));
166}