cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tea575x.c (14458B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *   ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips
      4 *
      5 *	Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
      6 */
      7
      8#include <linux/delay.h>
      9#include <linux/module.h>
     10#include <linux/init.h>
     11#include <linux/slab.h>
     12#include <linux/sched.h>
     13#include <asm/io.h>
     14#include <media/v4l2-device.h>
     15#include <media/v4l2-dev.h>
     16#include <media/v4l2-fh.h>
     17#include <media/v4l2-ioctl.h>
     18#include <media/v4l2-event.h>
     19#include <media/drv-intf/tea575x.h>
     20
     21MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
     22MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips");
     23MODULE_LICENSE("GPL");
     24
     25/*
     26 * definitions
     27 */
     28
     29#define TEA575X_BIT_SEARCH	(1<<24)		/* 1 = search action, 0 = tuned */
     30#define TEA575X_BIT_UPDOWN	(1<<23)		/* 0 = search down, 1 = search up */
     31#define TEA575X_BIT_MONO	(1<<22)		/* 0 = stereo, 1 = mono */
     32#define TEA575X_BIT_BAND_MASK	(3<<20)
     33#define TEA575X_BIT_BAND_FM	(0<<20)
     34#define TEA575X_BIT_BAND_MW	(1<<20)
     35#define TEA575X_BIT_BAND_LW	(2<<20)
     36#define TEA575X_BIT_BAND_SW	(3<<20)
     37#define TEA575X_BIT_PORT_0	(1<<19)		/* user bit */
     38#define TEA575X_BIT_PORT_1	(1<<18)		/* user bit */
     39#define TEA575X_BIT_SEARCH_MASK	(3<<16)		/* search level */
     40#define TEA575X_BIT_SEARCH_5_28	     (0<<16)	/* FM >5uV, AM >28uV */
     41#define TEA575X_BIT_SEARCH_10_40     (1<<16)	/* FM >10uV, AM > 40uV */
     42#define TEA575X_BIT_SEARCH_30_63     (2<<16)	/* FM >30uV, AM > 63uV */
     43#define TEA575X_BIT_SEARCH_150_1000  (3<<16)	/* FM > 150uV, AM > 1000uV */
     44#define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */
     45#define TEA575X_BIT_FREQ_MASK	0x7fff
     46
     47enum { BAND_FM, BAND_FM_JAPAN, BAND_AM };
     48
     49static const struct v4l2_frequency_band bands[] = {
     50	{
     51		.type = V4L2_TUNER_RADIO,
     52		.index = 0,
     53		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
     54			      V4L2_TUNER_CAP_FREQ_BANDS,
     55		.rangelow   =  87500 * 16,
     56		.rangehigh  = 108000 * 16,
     57		.modulation = V4L2_BAND_MODULATION_FM,
     58	},
     59	{
     60		.type = V4L2_TUNER_RADIO,
     61		.index = 0,
     62		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
     63			      V4L2_TUNER_CAP_FREQ_BANDS,
     64		.rangelow   = 76000 * 16,
     65		.rangehigh  = 91000 * 16,
     66		.modulation = V4L2_BAND_MODULATION_FM,
     67	},
     68	{
     69		.type = V4L2_TUNER_RADIO,
     70		.index = 1,
     71		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
     72		.rangelow   =  530 * 16,
     73		.rangehigh  = 1710 * 16,
     74		.modulation = V4L2_BAND_MODULATION_AM,
     75	},
     76};
     77
     78/*
     79 * lowlevel part
     80 */
     81
     82static void snd_tea575x_write(struct snd_tea575x *tea, unsigned int val)
     83{
     84	u16 l;
     85	u8 data;
     86
     87	if (tea->ops->write_val)
     88		return tea->ops->write_val(tea, val);
     89
     90	tea->ops->set_direction(tea, 1);
     91	udelay(16);
     92
     93	for (l = 25; l > 0; l--) {
     94		data = (val >> 24) & TEA575X_DATA;
     95		val <<= 1;			/* shift data */
     96		tea->ops->set_pins(tea, data | TEA575X_WREN);
     97		udelay(2);
     98		tea->ops->set_pins(tea, data | TEA575X_WREN | TEA575X_CLK);
     99		udelay(2);
    100		tea->ops->set_pins(tea, data | TEA575X_WREN);
    101		udelay(2);
    102	}
    103
    104	if (!tea->mute)
    105		tea->ops->set_pins(tea, 0);
    106}
    107
    108static u32 snd_tea575x_read(struct snd_tea575x *tea)
    109{
    110	u16 l, rdata;
    111	u32 data = 0;
    112
    113	if (tea->ops->read_val)
    114		return tea->ops->read_val(tea);
    115
    116	tea->ops->set_direction(tea, 0);
    117	tea->ops->set_pins(tea, 0);
    118	udelay(16);
    119
    120	for (l = 24; l--;) {
    121		tea->ops->set_pins(tea, TEA575X_CLK);
    122		udelay(2);
    123		if (!l)
    124			tea->tuned = tea->ops->get_pins(tea) & TEA575X_MOST ? 0 : 1;
    125		tea->ops->set_pins(tea, 0);
    126		udelay(2);
    127		data <<= 1;			/* shift data */
    128		rdata = tea->ops->get_pins(tea);
    129		if (!l)
    130			tea->stereo = (rdata & TEA575X_MOST) ?  0 : 1;
    131		if (rdata & TEA575X_DATA)
    132			data++;
    133		udelay(2);
    134	}
    135
    136	if (tea->mute)
    137		tea->ops->set_pins(tea, TEA575X_WREN);
    138
    139	return data;
    140}
    141
    142static u32 snd_tea575x_val_to_freq(struct snd_tea575x *tea, u32 val)
    143{
    144	u32 freq = val & TEA575X_BIT_FREQ_MASK;
    145
    146	if (freq == 0)
    147		return freq;
    148
    149	switch (tea->band) {
    150	case BAND_FM:
    151		/* freq *= 12.5 */
    152		freq *= 125;
    153		freq /= 10;
    154		/* crystal fixup */
    155		freq -= TEA575X_FMIF;
    156		break;
    157	case BAND_FM_JAPAN:
    158		/* freq *= 12.5 */
    159		freq *= 125;
    160		freq /= 10;
    161		/* crystal fixup */
    162		freq += TEA575X_FMIF;
    163		break;
    164	case BAND_AM:
    165		/* crystal fixup */
    166		freq -= TEA575X_AMIF;
    167		break;
    168	}
    169
    170	return clamp(freq * 16, bands[tea->band].rangelow,
    171				bands[tea->band].rangehigh); /* from kHz */
    172}
    173
    174static u32 snd_tea575x_get_freq(struct snd_tea575x *tea)
    175{
    176	return snd_tea575x_val_to_freq(tea, snd_tea575x_read(tea));
    177}
    178
    179void snd_tea575x_set_freq(struct snd_tea575x *tea)
    180{
    181	u32 freq = tea->freq / 16;	/* to kHz */
    182	u32 band = 0;
    183
    184	switch (tea->band) {
    185	case BAND_FM:
    186		band = TEA575X_BIT_BAND_FM;
    187		/* crystal fixup */
    188		freq += TEA575X_FMIF;
    189		/* freq /= 12.5 */
    190		freq *= 10;
    191		freq /= 125;
    192		break;
    193	case BAND_FM_JAPAN:
    194		band = TEA575X_BIT_BAND_FM;
    195		/* crystal fixup */
    196		freq -= TEA575X_FMIF;
    197		/* freq /= 12.5 */
    198		freq *= 10;
    199		freq /= 125;
    200		break;
    201	case BAND_AM:
    202		band = TEA575X_BIT_BAND_MW;
    203		/* crystal fixup */
    204		freq += TEA575X_AMIF;
    205		break;
    206	}
    207
    208	tea->val &= ~(TEA575X_BIT_FREQ_MASK | TEA575X_BIT_BAND_MASK);
    209	tea->val |= band;
    210	tea->val |= freq & TEA575X_BIT_FREQ_MASK;
    211	snd_tea575x_write(tea, tea->val);
    212	tea->freq = snd_tea575x_val_to_freq(tea, tea->val);
    213}
    214EXPORT_SYMBOL(snd_tea575x_set_freq);
    215
    216/*
    217 * Linux Video interface
    218 */
    219
    220static int vidioc_querycap(struct file *file, void  *priv,
    221					struct v4l2_capability *v)
    222{
    223	struct snd_tea575x *tea = video_drvdata(file);
    224
    225	strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
    226	strscpy(v->card, tea->card, sizeof(v->card));
    227	strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card));
    228	strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
    229	return 0;
    230}
    231
    232int snd_tea575x_enum_freq_bands(struct snd_tea575x *tea,
    233					struct v4l2_frequency_band *band)
    234{
    235	int index;
    236
    237	if (band->tuner != 0)
    238		return -EINVAL;
    239
    240	switch (band->index) {
    241	case 0:
    242		if (tea->tea5759)
    243			index = BAND_FM_JAPAN;
    244		else
    245			index = BAND_FM;
    246		break;
    247	case 1:
    248		if (tea->has_am) {
    249			index = BAND_AM;
    250			break;
    251		}
    252		fallthrough;
    253	default:
    254		return -EINVAL;
    255	}
    256
    257	*band = bands[index];
    258	if (!tea->cannot_read_data)
    259		band->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED;
    260
    261	return 0;
    262}
    263EXPORT_SYMBOL(snd_tea575x_enum_freq_bands);
    264
    265static int vidioc_enum_freq_bands(struct file *file, void *priv,
    266					 struct v4l2_frequency_band *band)
    267{
    268	struct snd_tea575x *tea = video_drvdata(file);
    269
    270	return snd_tea575x_enum_freq_bands(tea, band);
    271}
    272
    273int snd_tea575x_g_tuner(struct snd_tea575x *tea, struct v4l2_tuner *v)
    274{
    275	struct v4l2_frequency_band band_fm = { 0, };
    276
    277	if (v->index > 0)
    278		return -EINVAL;
    279
    280	snd_tea575x_read(tea);
    281	snd_tea575x_enum_freq_bands(tea, &band_fm);
    282
    283	memset(v, 0, sizeof(*v));
    284	strscpy(v->name, tea->has_am ? "FM/AM" : "FM", sizeof(v->name));
    285	v->type = V4L2_TUNER_RADIO;
    286	v->capability = band_fm.capability;
    287	v->rangelow = tea->has_am ? bands[BAND_AM].rangelow : band_fm.rangelow;
    288	v->rangehigh = band_fm.rangehigh;
    289	v->rxsubchans = tea->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
    290	v->audmode = (tea->val & TEA575X_BIT_MONO) ?
    291		V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO;
    292	v->signal = tea->tuned ? 0xffff : 0;
    293	return 0;
    294}
    295EXPORT_SYMBOL(snd_tea575x_g_tuner);
    296
    297static int vidioc_g_tuner(struct file *file, void *priv,
    298					struct v4l2_tuner *v)
    299{
    300	struct snd_tea575x *tea = video_drvdata(file);
    301
    302	return snd_tea575x_g_tuner(tea, v);
    303}
    304
    305static int vidioc_s_tuner(struct file *file, void *priv,
    306					const struct v4l2_tuner *v)
    307{
    308	struct snd_tea575x *tea = video_drvdata(file);
    309	u32 orig_val = tea->val;
    310
    311	if (v->index)
    312		return -EINVAL;
    313	tea->val &= ~TEA575X_BIT_MONO;
    314	if (v->audmode == V4L2_TUNER_MODE_MONO)
    315		tea->val |= TEA575X_BIT_MONO;
    316	/* Only apply changes if currently tuning FM */
    317	if (tea->band != BAND_AM && tea->val != orig_val)
    318		snd_tea575x_set_freq(tea);
    319
    320	return 0;
    321}
    322
    323static int vidioc_g_frequency(struct file *file, void *priv,
    324					struct v4l2_frequency *f)
    325{
    326	struct snd_tea575x *tea = video_drvdata(file);
    327
    328	if (f->tuner != 0)
    329		return -EINVAL;
    330	f->type = V4L2_TUNER_RADIO;
    331	f->frequency = tea->freq;
    332	return 0;
    333}
    334
    335static int vidioc_s_frequency(struct file *file, void *priv,
    336					const struct v4l2_frequency *f)
    337{
    338	struct snd_tea575x *tea = video_drvdata(file);
    339
    340	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
    341		return -EINVAL;
    342
    343	if (tea->has_am && f->frequency < (20000 * 16))
    344		tea->band = BAND_AM;
    345	else if (tea->tea5759)
    346		tea->band = BAND_FM_JAPAN;
    347	else
    348		tea->band = BAND_FM;
    349
    350	tea->freq = clamp_t(u32, f->frequency, bands[tea->band].rangelow,
    351					bands[tea->band].rangehigh);
    352	snd_tea575x_set_freq(tea);
    353	return 0;
    354}
    355
    356int snd_tea575x_s_hw_freq_seek(struct file *file, struct snd_tea575x *tea,
    357				const struct v4l2_hw_freq_seek *a)
    358{
    359	unsigned long timeout;
    360	int i, spacing;
    361
    362	if (tea->cannot_read_data)
    363		return -ENOTTY;
    364	if (a->tuner || a->wrap_around)
    365		return -EINVAL;
    366
    367	if (file->f_flags & O_NONBLOCK)
    368		return -EWOULDBLOCK;
    369
    370	if (a->rangelow || a->rangehigh) {
    371		for (i = 0; i < ARRAY_SIZE(bands); i++) {
    372			if ((i == BAND_FM && tea->tea5759) ||
    373			    (i == BAND_FM_JAPAN && !tea->tea5759) ||
    374			    (i == BAND_AM && !tea->has_am))
    375				continue;
    376			if (bands[i].rangelow  == a->rangelow &&
    377			    bands[i].rangehigh == a->rangehigh)
    378				break;
    379		}
    380		if (i == ARRAY_SIZE(bands))
    381			return -EINVAL; /* No matching band found */
    382		if (i != tea->band) {
    383			tea->band = i;
    384			tea->freq = clamp(tea->freq, bands[i].rangelow,
    385						     bands[i].rangehigh);
    386			snd_tea575x_set_freq(tea);
    387		}
    388	}
    389
    390	spacing = (tea->band == BAND_AM) ? 5 : 50; /* kHz */
    391
    392	/* clear the frequency, HW will fill it in */
    393	tea->val &= ~TEA575X_BIT_FREQ_MASK;
    394	tea->val |= TEA575X_BIT_SEARCH;
    395	if (a->seek_upward)
    396		tea->val |= TEA575X_BIT_UPDOWN;
    397	else
    398		tea->val &= ~TEA575X_BIT_UPDOWN;
    399	snd_tea575x_write(tea, tea->val);
    400	timeout = jiffies + msecs_to_jiffies(10000);
    401	for (;;) {
    402		if (time_after(jiffies, timeout))
    403			break;
    404		if (schedule_timeout_interruptible(msecs_to_jiffies(10))) {
    405			/* some signal arrived, stop search */
    406			tea->val &= ~TEA575X_BIT_SEARCH;
    407			snd_tea575x_set_freq(tea);
    408			return -ERESTARTSYS;
    409		}
    410		if (!(snd_tea575x_read(tea) & TEA575X_BIT_SEARCH)) {
    411			u32 freq;
    412
    413			/* Found a frequency, wait until it can be read */
    414			for (i = 0; i < 100; i++) {
    415				msleep(10);
    416				freq = snd_tea575x_get_freq(tea);
    417				if (freq) /* available */
    418					break;
    419			}
    420			if (freq == 0) /* shouldn't happen */
    421				break;
    422			/*
    423			 * if we moved by less than the spacing, or in the
    424			 * wrong direction, continue seeking
    425			 */
    426			if (abs(tea->freq - freq) < 16 * spacing ||
    427					(a->seek_upward && freq < tea->freq) ||
    428					(!a->seek_upward && freq > tea->freq)) {
    429				snd_tea575x_write(tea, tea->val);
    430				continue;
    431			}
    432			tea->freq = freq;
    433			tea->val &= ~TEA575X_BIT_SEARCH;
    434			return 0;
    435		}
    436	}
    437	tea->val &= ~TEA575X_BIT_SEARCH;
    438	snd_tea575x_set_freq(tea);
    439	return -ENODATA;
    440}
    441EXPORT_SYMBOL(snd_tea575x_s_hw_freq_seek);
    442
    443static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
    444					const struct v4l2_hw_freq_seek *a)
    445{
    446	struct snd_tea575x *tea = video_drvdata(file);
    447
    448	return snd_tea575x_s_hw_freq_seek(file, tea, a);
    449}
    450
    451static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
    452{
    453	struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
    454
    455	switch (ctrl->id) {
    456	case V4L2_CID_AUDIO_MUTE:
    457		tea->mute = ctrl->val;
    458		snd_tea575x_set_freq(tea);
    459		return 0;
    460	}
    461
    462	return -EINVAL;
    463}
    464
    465static const struct v4l2_file_operations tea575x_fops = {
    466	.unlocked_ioctl	= video_ioctl2,
    467	.open           = v4l2_fh_open,
    468	.release        = v4l2_fh_release,
    469	.poll           = v4l2_ctrl_poll,
    470};
    471
    472static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
    473	.vidioc_querycap    = vidioc_querycap,
    474	.vidioc_g_tuner     = vidioc_g_tuner,
    475	.vidioc_s_tuner     = vidioc_s_tuner,
    476	.vidioc_g_frequency = vidioc_g_frequency,
    477	.vidioc_s_frequency = vidioc_s_frequency,
    478	.vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek,
    479	.vidioc_enum_freq_bands = vidioc_enum_freq_bands,
    480	.vidioc_log_status  = v4l2_ctrl_log_status,
    481	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
    482	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
    483};
    484
    485static const struct video_device tea575x_radio = {
    486	.ioctl_ops	= &tea575x_ioctl_ops,
    487	.release        = video_device_release_empty,
    488};
    489
    490static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
    491	.s_ctrl = tea575x_s_ctrl,
    492};
    493
    494
    495int snd_tea575x_hw_init(struct snd_tea575x *tea)
    496{
    497	tea->mute = true;
    498
    499	/* Not all devices can or know how to read the data back.
    500	   Such devices can set cannot_read_data to true. */
    501	if (!tea->cannot_read_data) {
    502		snd_tea575x_write(tea, 0x55AA);
    503		if (snd_tea575x_read(tea) != 0x55AA)
    504			return -ENODEV;
    505	}
    506
    507	tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_5_28;
    508	tea->freq = 90500 * 16;		/* 90.5Mhz default */
    509	snd_tea575x_set_freq(tea);
    510
    511	return 0;
    512}
    513EXPORT_SYMBOL(snd_tea575x_hw_init);
    514
    515int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner)
    516{
    517	int retval = snd_tea575x_hw_init(tea);
    518
    519	if (retval)
    520		return retval;
    521
    522	tea->vd = tea575x_radio;
    523	video_set_drvdata(&tea->vd, tea);
    524	mutex_init(&tea->mutex);
    525	strscpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name));
    526	tea->vd.lock = &tea->mutex;
    527	tea->vd.v4l2_dev = tea->v4l2_dev;
    528	tea->vd.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
    529	if (!tea->cannot_read_data)
    530		tea->vd.device_caps |= V4L2_CAP_HW_FREQ_SEEK;
    531	tea->fops = tea575x_fops;
    532	tea->fops.owner = owner;
    533	tea->vd.fops = &tea->fops;
    534	/* disable hw_freq_seek if we can't use it */
    535	if (tea->cannot_read_data)
    536		v4l2_disable_ioctl(&tea->vd, VIDIOC_S_HW_FREQ_SEEK);
    537
    538	if (!tea->cannot_mute) {
    539		tea->vd.ctrl_handler = &tea->ctrl_handler;
    540		v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
    541		v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops,
    542				  V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
    543		retval = tea->ctrl_handler.error;
    544		if (retval) {
    545			v4l2_err(tea->v4l2_dev, "can't initialize controls\n");
    546			v4l2_ctrl_handler_free(&tea->ctrl_handler);
    547			return retval;
    548		}
    549
    550		if (tea->ext_init) {
    551			retval = tea->ext_init(tea);
    552			if (retval) {
    553				v4l2_ctrl_handler_free(&tea->ctrl_handler);
    554				return retval;
    555			}
    556		}
    557
    558		v4l2_ctrl_handler_setup(&tea->ctrl_handler);
    559	}
    560
    561	retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->radio_nr);
    562	if (retval) {
    563		v4l2_err(tea->v4l2_dev, "can't register video device!\n");
    564		v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
    565		return retval;
    566	}
    567
    568	return 0;
    569}
    570EXPORT_SYMBOL(snd_tea575x_init);
    571
    572void snd_tea575x_exit(struct snd_tea575x *tea)
    573{
    574	video_unregister_device(&tea->vd);
    575	v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
    576}
    577EXPORT_SYMBOL(snd_tea575x_exit);