2 * vivid-rds-gen.c - rds (radio data system) generator support functions.
4 * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
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.
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
20 #include <linux/kernel.h>
21 #include <linux/ktime.h>
22 #include <linux/string.h>
23 #include <linux/videodev2.h>
25 #include "vivid-rds-gen.h"
27 static u8
vivid_get_di(const struct vivid_rds_gen
*rds
, unsigned grp
)
31 return (rds
->dyn_pty
<< 2) | (grp
& 3);
33 return (rds
->compressed
<< 2) | (grp
& 3);
35 return (rds
->art_head
<< 2) | (grp
& 3);
37 return (rds
->mono_stereo
<< 2) | (grp
& 3);
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.
47 * Groups 4-19 and 26-41 use group 2A for the radio text.
49 * Group 56 contains the time (group 4A).
51 * All remaining groups use a filler group 15B block that just repeats
52 * the PI and PTY codes.
54 void vivid_rds_generate(struct vivid_rds_gen
*rds
)
56 struct v4l2_rds_data
*data
= rds
->data
;
64 for (grp
= 0; grp
< VIVID_RDS_GEN_GROUPS
; grp
++, data
+= VIVID_RDS_GEN_BLKS_PER_GRP
) {
65 data
[0].lsb
= rds
->picode
& 0xff;
66 data
[0].msb
= rds
->picode
>> 8;
67 data
[0].block
= V4L2_RDS_BLOCK_A
| (V4L2_RDS_BLOCK_A
<< 3);
68 data
[1].lsb
= rds
->pty
<< 5;
69 data
[1].msb
= (rds
->pty
>> 3) | (rds
->tp
<< 2);
70 data
[1].block
= V4L2_RDS_BLOCK_B
| (V4L2_RDS_BLOCK_B
<< 3);
71 data
[3].block
= V4L2_RDS_BLOCK_D
| (V4L2_RDS_BLOCK_D
<< 3);
76 case 44 ... 47: /* Group 0B */
78 data
[1].lsb
|= (rds
->ta
<< 4) | (rds
->ms
<< 3);
79 data
[1].lsb
|= vivid_get_di(rds
, idx
);
80 data
[1].msb
|= 1 << 3;
81 data
[2].lsb
= rds
->picode
& 0xff;
82 data
[2].msb
= rds
->picode
>> 8;
83 data
[2].block
= V4L2_RDS_BLOCK_C_ALT
| (V4L2_RDS_BLOCK_C_ALT
<< 3);
84 data
[3].lsb
= rds
->psname
[2 * idx
+ 1];
85 data
[3].msb
= rds
->psname
[2 * idx
];
88 case 26 ... 41: /* Group 2A */
89 idx
= ((grp
- 4) % 22) % 16;
91 data
[1].msb
|= 4 << 3;
92 data
[2].msb
= rds
->radiotext
[4 * idx
];
93 data
[2].lsb
= rds
->radiotext
[4 * idx
+ 1];
94 data
[2].block
= V4L2_RDS_BLOCK_C
| (V4L2_RDS_BLOCK_C
<< 3);
95 data
[3].msb
= rds
->radiotext
[4 * idx
+ 2];
96 data
[3].lsb
= rds
->radiotext
[4 * idx
+ 3];
102 * Uses the algorithm from Annex G of the RDS standard
103 * EN 50067:1998 to convert a UTC date to an RDS Modified
106 time64_to_tm(ktime_get_real_seconds(), 0, &tm
);
108 date
= 14956 + tm
.tm_mday
+ ((tm
.tm_year
- l
) * 1461) / 4 +
109 ((tm
.tm_mon
+ 2 + l
* 12) * 306001) / 10000;
110 time
= (tm
.tm_hour
<< 12) |
112 (sys_tz
.tz_minuteswest
>= 0 ? 0x20 : 0) |
113 (abs(sys_tz
.tz_minuteswest
) / 30);
115 data
[1].lsb
|= date
>> 15;
116 data
[1].msb
|= 8 << 3;
117 data
[2].lsb
= (date
<< 1) & 0xfe;
118 data
[2].lsb
|= (time
>> 16) & 1;
119 data
[2].msb
= (date
>> 7) & 0xff;
120 data
[2].block
= V4L2_RDS_BLOCK_C
| (V4L2_RDS_BLOCK_C
<< 3);
121 data
[3].lsb
= time
& 0xff;
122 data
[3].msb
= (time
>> 8) & 0xff;
124 default: /* Group 15B */
125 data
[1].lsb
|= (rds
->ta
<< 4) | (rds
->ms
<< 3);
126 data
[1].lsb
|= vivid_get_di(rds
, grp
% 22);
127 data
[1].msb
|= 0x1f << 3;
128 data
[2].lsb
= rds
->picode
& 0xff;
129 data
[2].msb
= rds
->picode
>> 8;
130 data
[2].block
= V4L2_RDS_BLOCK_C_ALT
| (V4L2_RDS_BLOCK_C_ALT
<< 3);
131 data
[3].lsb
= rds
->pty
<< 5;
132 data
[3].lsb
|= (rds
->ta
<< 4) | (rds
->ms
<< 3);
133 data
[3].lsb
|= vivid_get_di(rds
, grp
% 22);
134 data
[3].msb
|= rds
->pty
>> 3;
135 data
[3].msb
|= 0x1f << 3;
141 void vivid_rds_gen_fill(struct vivid_rds_gen
*rds
, unsigned freq
,
144 /* Alternate PTY between Info and Weather */
146 rds
->picode
= 0x2e75; /* 'KLNX' call sign */
147 rds
->pty
= alt
? 29 : 2;
149 rds
->picode
= 0x8088;
150 rds
->pty
= alt
? 16 : 3;
152 rds
->mono_stereo
= true;
153 rds
->art_head
= false;
154 rds
->compressed
= false;
155 rds
->dyn_pty
= false;
159 snprintf(rds
->psname
, sizeof(rds
->psname
), "%6d.%1d",
160 freq
/ 16, ((freq
& 0xf) * 10) / 16);
162 strlcpy(rds
->radiotext
,
163 " The Radio Data System can switch between different Radio Texts ",
164 sizeof(rds
->radiotext
));
166 strlcpy(rds
->radiotext
,
167 "An example of Radio Text as transmitted by the Radio Data System",
168 sizeof(rds
->radiotext
));