1/* 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> 22#include <linux/string.h> 23#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} 167