root/drivers/media/platform/vivid/vivid-rds-gen.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. vivid_get_di
  2. vivid_rds_generate
  3. vivid_rds_gen_fill

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

/* [<][>][^][v][top][bottom][index][help] */