brintos

brintos / linux-shallow public Read only

0
0
Text · 4.6 KiB · c577711 Raw
158 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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 15static 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 a33 * 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 repeats40 * the PI and PTY codes.41 */42void 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 4A89			 *90			 * Uses the algorithm from Annex G of the RDS standard91			 * EN 50067:1998 to convert a UTC date to an RDS Modified92			 * 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 129void 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) % 1000000, (((freq & 0xf) * 10) / 16) % 10);149	if (alt)150		strscpy(rds->radiotext,151			" The Radio Data System can switch between different Radio Texts ",152			sizeof(rds->radiotext));153	else154		strscpy(rds->radiotext,155			"An example of Radio Text as transmitted by the Radio Data System",156			sizeof(rds->radiotext));157}158