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

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

DEFINITIONS

This source file includes following definitions.
  1. wss_insert
  2. vivid_vbi_gen_wss_raw
  3. vivid_vbi_gen_teletext_raw
  4. cc_insert
  5. vivid_vbi_gen_cc_raw
  6. vivid_vbi_gen_raw
  7. calc_parity
  8. vivid_vbi_gen_set_time_of_day
  9. vivid_vbi_gen_teletext
  10. vivid_vbi_gen_sliced

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * vivid-vbi-gen.c - vbi generator support functions.
   4  *
   5  * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
   6  */
   7 
   8 #include <linux/errno.h>
   9 #include <linux/kernel.h>
  10 #include <linux/ktime.h>
  11 #include <linux/string.h>
  12 #include <linux/videodev2.h>
  13 
  14 #include "vivid-vbi-gen.h"
  15 
  16 static void wss_insert(u8 *wss, u32 val, unsigned size)
  17 {
  18         while (size--)
  19                 *wss++ = (val & (1 << size)) ? 0xc0 : 0x10;
  20 }
  21 
  22 static void vivid_vbi_gen_wss_raw(const struct v4l2_sliced_vbi_data *data,
  23                 u8 *buf, unsigned sampling_rate)
  24 {
  25         const unsigned rate = 5000000;  /* WSS has a 5 MHz transmission rate */
  26         u8 wss[29 + 24 + 24 + 24 + 18 + 18] = { 0 };
  27         const unsigned zero = 0x07;
  28         const unsigned one = 0x38;
  29         unsigned bit = 0;
  30         u16 wss_data;
  31         int i;
  32 
  33         wss_insert(wss + bit, 0x1f1c71c7, 29); bit += 29;
  34         wss_insert(wss + bit, 0x1e3c1f, 24); bit += 24;
  35 
  36         wss_data = (data->data[1] << 8) | data->data[0];
  37         for (i = 0; i <= 13; i++, bit += 6)
  38                 wss_insert(wss + bit, (wss_data & (1 << i)) ? one : zero, 6);
  39 
  40         for (i = 0, bit = 0; bit < sizeof(wss); bit++) {
  41                 unsigned n = ((bit + 1) * sampling_rate) / rate;
  42 
  43                 while (i < n)
  44                         buf[i++] = wss[bit];
  45         }
  46 }
  47 
  48 static void vivid_vbi_gen_teletext_raw(const struct v4l2_sliced_vbi_data *data,
  49                 u8 *buf, unsigned sampling_rate)
  50 {
  51         const unsigned rate = 6937500 / 10;     /* Teletext has a 6.9375 MHz transmission rate */
  52         u8 teletext[45] = { 0x55, 0x55, 0x27 };
  53         unsigned bit = 0;
  54         int i;
  55 
  56         memcpy(teletext + 3, data->data, sizeof(teletext) - 3);
  57         /* prevents 32 bit overflow */
  58         sampling_rate /= 10;
  59 
  60         for (i = 0, bit = 0; bit < sizeof(teletext) * 8; bit++) {
  61                 unsigned n = ((bit + 1) * sampling_rate) / rate;
  62                 u8 val = (teletext[bit / 8] & (1 << (bit & 7))) ? 0xc0 : 0x10;
  63 
  64                 while (i < n)
  65                         buf[i++] = val;
  66         }
  67 }
  68 
  69 static void cc_insert(u8 *cc, u8 ch)
  70 {
  71         unsigned tot = 0;
  72         unsigned i;
  73 
  74         for (i = 0; i < 7; i++) {
  75                 cc[2 * i] = cc[2 * i + 1] = (ch & (1 << i)) ? 1 : 0;
  76                 tot += cc[2 * i];
  77         }
  78         cc[14] = cc[15] = !(tot & 1);
  79 }
  80 
  81 #define CC_PREAMBLE_BITS (14 + 4 + 2)
  82 
  83 static void vivid_vbi_gen_cc_raw(const struct v4l2_sliced_vbi_data *data,
  84                 u8 *buf, unsigned sampling_rate)
  85 {
  86         const unsigned rate = 1000000;  /* CC has a 1 MHz transmission rate */
  87 
  88         u8 cc[CC_PREAMBLE_BITS + 2 * 16] = {
  89                 /* Clock run-in: 7 cycles */
  90                 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
  91                 /* 2 cycles of 0 */
  92                 0, 0, 0, 0,
  93                 /* Start bit of 1 (each bit is two cycles) */
  94                 1, 1
  95         };
  96         unsigned bit, i;
  97 
  98         cc_insert(cc + CC_PREAMBLE_BITS, data->data[0]);
  99         cc_insert(cc + CC_PREAMBLE_BITS + 16, data->data[1]);
 100 
 101         for (i = 0, bit = 0; bit < sizeof(cc); bit++) {
 102                 unsigned n = ((bit + 1) * sampling_rate) / rate;
 103 
 104                 while (i < n)
 105                         buf[i++] = cc[bit] ? 0xc0 : 0x10;
 106         }
 107 }
 108 
 109 void vivid_vbi_gen_raw(const struct vivid_vbi_gen_data *vbi,
 110                 const struct v4l2_vbi_format *vbi_fmt, u8 *buf)
 111 {
 112         unsigned idx;
 113 
 114         for (idx = 0; idx < 25; idx++) {
 115                 const struct v4l2_sliced_vbi_data *data = vbi->data + idx;
 116                 unsigned start_2nd_field;
 117                 unsigned line = data->line;
 118                 u8 *linebuf = buf;
 119 
 120                 start_2nd_field = (data->id & V4L2_SLICED_VBI_525) ? 263 : 313;
 121                 if (data->field)
 122                         line += start_2nd_field;
 123                 line -= vbi_fmt->start[data->field];
 124 
 125                 if (vbi_fmt->flags & V4L2_VBI_INTERLACED)
 126                         linebuf += (line * 2 + data->field) *
 127                                 vbi_fmt->samples_per_line;
 128                 else
 129                         linebuf += (line + data->field * vbi_fmt->count[0]) *
 130                                 vbi_fmt->samples_per_line;
 131                 if (data->id == V4L2_SLICED_CAPTION_525)
 132                         vivid_vbi_gen_cc_raw(data, linebuf, vbi_fmt->sampling_rate);
 133                 else if (data->id == V4L2_SLICED_WSS_625)
 134                         vivid_vbi_gen_wss_raw(data, linebuf, vbi_fmt->sampling_rate);
 135                 else if (data->id == V4L2_SLICED_TELETEXT_B)
 136                         vivid_vbi_gen_teletext_raw(data, linebuf, vbi_fmt->sampling_rate);
 137         }
 138 }
 139 
 140 static const u8 vivid_cc_sequence1[30] = {
 141         0x14, 0x20,     /* Resume Caption Loading */
 142         'H',  'e',
 143         'l',  'l',
 144         'o',  ' ',
 145         'w',  'o',
 146         'r',  'l',
 147         'd',  '!',
 148         0x14, 0x2f,     /* End of Caption */
 149 };
 150 
 151 static const u8 vivid_cc_sequence2[30] = {
 152         0x14, 0x20,     /* Resume Caption Loading */
 153         'C',  'l',
 154         'o',  's',
 155         'e',  'd',
 156         ' ',  'c',
 157         'a',  'p',
 158         't',  'i',
 159         'o',  'n',
 160         's',  ' ',
 161         't',  'e',
 162         's',  't',
 163         0x14, 0x2f,     /* End of Caption */
 164 };
 165 
 166 static u8 calc_parity(u8 val)
 167 {
 168         unsigned i;
 169         unsigned tot = 0;
 170 
 171         for (i = 0; i < 7; i++)
 172                 tot += (val & (1 << i)) ? 1 : 0;
 173         return val | ((tot & 1) ? 0 : 0x80);
 174 }
 175 
 176 static void vivid_vbi_gen_set_time_of_day(u8 *packet)
 177 {
 178         struct tm tm;
 179         u8 checksum, i;
 180 
 181         time64_to_tm(ktime_get_real_seconds(), 0, &tm);
 182         packet[0] = calc_parity(0x07);
 183         packet[1] = calc_parity(0x01);
 184         packet[2] = calc_parity(0x40 | tm.tm_min);
 185         packet[3] = calc_parity(0x40 | tm.tm_hour);
 186         packet[4] = calc_parity(0x40 | tm.tm_mday);
 187         if (tm.tm_mday == 1 && tm.tm_mon == 2 &&
 188             sys_tz.tz_minuteswest > tm.tm_min + tm.tm_hour * 60)
 189                 packet[4] = calc_parity(0x60 | tm.tm_mday);
 190         packet[5] = calc_parity(0x40 | (1 + tm.tm_mon));
 191         packet[6] = calc_parity(0x40 | (1 + tm.tm_wday));
 192         packet[7] = calc_parity(0x40 | ((tm.tm_year - 90) & 0x3f));
 193         packet[8] = calc_parity(0x0f);
 194         for (checksum = i = 0; i <= 8; i++)
 195                 checksum += packet[i] & 0x7f;
 196         packet[9] = calc_parity(0x100 - checksum);
 197         checksum = 0;
 198         packet[10] = calc_parity(0x07);
 199         packet[11] = calc_parity(0x04);
 200         if (sys_tz.tz_minuteswest >= 0)
 201                 packet[12] = calc_parity(0x40 | ((sys_tz.tz_minuteswest / 60) & 0x1f));
 202         else
 203                 packet[12] = calc_parity(0x40 | ((24 + sys_tz.tz_minuteswest / 60) & 0x1f));
 204         packet[13] = calc_parity(0);
 205         packet[14] = calc_parity(0x0f);
 206         for (checksum = 0, i = 10; i <= 14; i++)
 207                 checksum += packet[i] & 0x7f;
 208         packet[15] = calc_parity(0x100 - checksum);
 209 }
 210 
 211 static const u8 hamming[16] = {
 212         0x15, 0x02, 0x49, 0x5e, 0x64, 0x73, 0x38, 0x2f,
 213         0xd0, 0xc7, 0x8c, 0x9b, 0xa1, 0xb6, 0xfd, 0xea
 214 };
 215 
 216 static void vivid_vbi_gen_teletext(u8 *packet, unsigned line, unsigned frame)
 217 {
 218         unsigned offset = 2;
 219         unsigned i;
 220 
 221         packet[0] = hamming[1 + ((line & 1) << 3)];
 222         packet[1] = hamming[line >> 1];
 223         memset(packet + 2, 0x20, 40);
 224         if (line == 0) {
 225                 /* subcode */
 226                 packet[2] = hamming[frame % 10];
 227                 packet[3] = hamming[frame / 10];
 228                 packet[4] = hamming[0];
 229                 packet[5] = hamming[0];
 230                 packet[6] = hamming[0];
 231                 packet[7] = hamming[0];
 232                 packet[8] = hamming[0];
 233                 packet[9] = hamming[1];
 234                 offset = 10;
 235         }
 236         packet += offset;
 237         memcpy(packet, "Page: 100 Row: 10", 17);
 238         packet[7] = '0' + frame / 10;
 239         packet[8] = '0' + frame % 10;
 240         packet[15] = '0' + line / 10;
 241         packet[16] = '0' + line % 10;
 242         for (i = 0; i < 42 - offset; i++)
 243                 packet[i] = calc_parity(packet[i]);
 244 }
 245 
 246 void vivid_vbi_gen_sliced(struct vivid_vbi_gen_data *vbi,
 247                 bool is_60hz, unsigned seqnr)
 248 {
 249         struct v4l2_sliced_vbi_data *data0 = vbi->data;
 250         struct v4l2_sliced_vbi_data *data1 = vbi->data + 1;
 251         unsigned frame = seqnr % 60;
 252 
 253         memset(vbi->data, 0, sizeof(vbi->data));
 254 
 255         if (!is_60hz) {
 256                 unsigned i;
 257 
 258                 for (i = 0; i <= 11; i++) {
 259                         data0->id = V4L2_SLICED_TELETEXT_B;
 260                         data0->line = 7 + i;
 261                         vivid_vbi_gen_teletext(data0->data, i, frame);
 262                         data0++;
 263                 }
 264                 data0->id = V4L2_SLICED_WSS_625;
 265                 data0->line = 23;
 266                 /* 4x3 video aspect ratio */
 267                 data0->data[0] = 0x08;
 268                 data0++;
 269                 for (i = 0; i <= 11; i++) {
 270                         data0->id = V4L2_SLICED_TELETEXT_B;
 271                         data0->field = 1;
 272                         data0->line = 7 + i;
 273                         vivid_vbi_gen_teletext(data0->data, 12 + i, frame);
 274                         data0++;
 275                 }
 276                 return;
 277         }
 278 
 279         data0->id = V4L2_SLICED_CAPTION_525;
 280         data0->line = 21;
 281         data1->id = V4L2_SLICED_CAPTION_525;
 282         data1->field = 1;
 283         data1->line = 21;
 284 
 285         if (frame < 15) {
 286                 data0->data[0] = calc_parity(vivid_cc_sequence1[2 * frame]);
 287                 data0->data[1] = calc_parity(vivid_cc_sequence1[2 * frame + 1]);
 288         } else if (frame >= 30 && frame < 45) {
 289                 frame -= 30;
 290                 data0->data[0] = calc_parity(vivid_cc_sequence2[2 * frame]);
 291                 data0->data[1] = calc_parity(vivid_cc_sequence2[2 * frame + 1]);
 292         } else {
 293                 data0->data[0] = calc_parity(0);
 294                 data0->data[1] = calc_parity(0);
 295         }
 296 
 297         frame = seqnr % (30 * 60);
 298         switch (frame) {
 299         case 0:
 300                 vivid_vbi_gen_set_time_of_day(vbi->time_of_day_packet);
 301                 /* fall through */
 302         case 1 ... 7:
 303                 data1->data[0] = vbi->time_of_day_packet[frame * 2];
 304                 data1->data[1] = vbi->time_of_day_packet[frame * 2 + 1];
 305                 break;
 306         default:
 307                 data1->data[0] = calc_parity(0);
 308                 data1->data[1] = calc_parity(0);
 309                 break;
 310         }
 311 }

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