linux/drivers/media/platform/vivid/vivid-vbi-gen.c
<<
>>
Prefs
   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
  16static void wss_insert(u8 *wss, u32 val, unsigned size)
  17{
  18        while (size--)
  19                *wss++ = (val & (1 << size)) ? 0xc0 : 0x10;
  20}
  21
  22static 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
  48static 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
  69static 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
  83static 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
 109void 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
 140static 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
 151static 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
 166static 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
 176static 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
 211static const u8 hamming[16] = {
 212        0x15, 0x02, 0x49, 0x5e, 0x64, 0x73, 0x38, 0x2f,
 213        0xd0, 0xc7, 0x8c, 0x9b, 0xa1, 0xb6, 0xfd, 0xea
 214};
 215
 216static 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
 246void 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}
 312