Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6652c716 authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab
Browse files

[media] radio-cadet: implement frequency band enumeration

parent cc0d3266
Loading
Loading
Loading
Loading
+74 −57
Original line number Original line Diff line number Diff line
@@ -66,7 +66,8 @@ struct cadet {
	struct video_device vdev;
	struct video_device vdev;
	struct v4l2_ctrl_handler ctrl_handler;
	struct v4l2_ctrl_handler ctrl_handler;
	int io;
	int io;
	int curtuner;
	bool is_fm_band;
	u32 curfreq;
	int tunestat;
	int tunestat;
	int sigstrength;
	int sigstrength;
	wait_queue_head_t read_queue;
	wait_queue_head_t read_queue;
@@ -84,9 +85,9 @@ static struct cadet cadet_card;
 * The V4L API spec does not define any particular unit for the signal
 * The V4L API spec does not define any particular unit for the signal
 * strength value.  These values are in microvolts of RF at the tuner's input.
 * strength value.  These values are in microvolts of RF at the tuner's input.
 */
 */
static __u16 sigtable[2][4] = {
static u16 sigtable[2][4] = {
	{ 1835, 2621,  4128, 65535 },
	{ 2185, 4369, 13107, 65535 },
	{ 2185, 4369, 13107, 65535 },
	{ 1835, 2621,  4128, 65535 }
};
};




@@ -94,7 +95,7 @@ static int cadet_getstereo(struct cadet *dev)
{
{
	int ret = V4L2_TUNER_SUB_MONO;
	int ret = V4L2_TUNER_SUB_MONO;


	if (dev->curtuner != 0)	/* Only FM has stereo capability! */
	if (!dev->is_fm_band)	/* Only FM has stereo capability! */
		return V4L2_TUNER_SUB_MONO;
		return V4L2_TUNER_SUB_MONO;


	outb(7, dev->io);          /* Select tuner control */
	outb(7, dev->io);          /* Select tuner control */
@@ -149,7 +150,9 @@ static unsigned cadet_getfreq(struct cadet *dev)
	/*
	/*
	 * Convert to actual frequency
	 * Convert to actual frequency
	 */
	 */
	if (dev->curtuner == 0) {    /* FM */
	if (!dev->is_fm_band)    /* AM */
		return ((fifo & 0x7fff) - 450) * 16;

	test = 12500;
	test = 12500;
	for (i = 0; i < 14; i++) {
	for (i = 0; i < 14; i++) {
		if ((fifo & 0x01) != 0)
		if ((fifo & 0x01) != 0)
@@ -159,10 +162,6 @@ static unsigned cadet_getfreq(struct cadet *dev)
	}
	}
	freq -= 10700000;           /* IF frequency is 10.7 MHz */
	freq -= 10700000;           /* IF frequency is 10.7 MHz */
	freq = (freq * 16) / 1000;   /* Make it 1/16 kHz */
	freq = (freq * 16) / 1000;   /* Make it 1/16 kHz */
	}
	if (dev->curtuner == 1)    /* AM */
		freq = ((fifo & 0x7fff) - 2010) * 16;

	return freq;
	return freq;
}
}


@@ -197,11 +196,12 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
	int i, j, test;
	int i, j, test;
	int curvol;
	int curvol;


	dev->curfreq = freq;
	/*
	/*
	 * Formulate a fifo command
	 * Formulate a fifo command
	 */
	 */
	fifo = 0;
	fifo = 0;
	if (dev->curtuner == 0) {    /* FM */
	if (dev->is_fm_band) {    /* FM */
		test = 102400;
		test = 102400;
		freq = freq / 16;       /* Make it kHz */
		freq = freq / 16;       /* Make it kHz */
		freq += 10700;               /* IF is 10700 kHz */
		freq += 10700;               /* IF is 10700 kHz */
@@ -213,9 +213,8 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
			}
			}
			test = test >> 1;
			test = test >> 1;
		}
		}
	}
	} else {	/* AM */
	if (dev->curtuner == 1) {    /* AM */
		fifo = (freq / 16) + 450;	/* Make it kHz */
		fifo = (freq / 16) + 2010;            /* Make it kHz */
		fifo |= 0x100000;		/* Select AM Band */
		fifo |= 0x100000;		/* Select AM Band */
	}
	}


@@ -239,7 +238,7 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)


		cadet_gettune(dev);
		cadet_gettune(dev);
		if ((dev->tunestat & 0x40) == 0) {   /* Tuned */
		if ((dev->tunestat & 0x40) == 0) {   /* Tuned */
			dev->sigstrength = sigtable[dev->curtuner][j];
			dev->sigstrength = sigtable[dev->is_fm_band][j];
			goto reset_rds;
			goto reset_rds;
		}
		}
	}
	}
@@ -338,39 +337,52 @@ static int vidioc_querycap(struct file *file, void *priv,
	return 0;
	return 0;
}
}


static const struct v4l2_frequency_band bands[] = {
	{
		.index = 0,
		.type = V4L2_TUNER_RADIO,
		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
		.rangelow = 8320,      /* 520 kHz */
		.rangehigh = 26400,    /* 1650 kHz */
		.modulation = V4L2_BAND_MODULATION_AM,
	}, {
		.index = 1,
		.type = V4L2_TUNER_RADIO,
		.capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
			V4L2_TUNER_CAP_RDS_BLOCK_IO | V4L2_TUNER_CAP_LOW |
			V4L2_TUNER_CAP_FREQ_BANDS,
		.rangelow = 1400000,   /* 87.5 MHz */
		.rangehigh = 1728000,  /* 108.0 MHz */
		.modulation = V4L2_BAND_MODULATION_FM,
	},
};

static int vidioc_g_tuner(struct file *file, void *priv,
static int vidioc_g_tuner(struct file *file, void *priv,
				struct v4l2_tuner *v)
				struct v4l2_tuner *v)
{
{
	struct cadet *dev = video_drvdata(file);
	struct cadet *dev = video_drvdata(file);


	if (v->index)
		return -EINVAL;
	v->type = V4L2_TUNER_RADIO;
	v->type = V4L2_TUNER_RADIO;
	switch (v->index) {
	strlcpy(v->name, "Radio", sizeof(v->name));
	case 0:
	v->capability = bands[0].capability | bands[1].capability;
		strlcpy(v->name, "FM", sizeof(v->name));
	v->rangelow = bands[0].rangelow;	   /* 520 kHz (start of AM band) */
		v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
	v->rangehigh = bands[1].rangehigh;    /* 108.0 MHz (end of FM band) */
			V4L2_TUNER_CAP_RDS_BLOCK_IO | V4L2_TUNER_CAP_LOW;
	if (dev->is_fm_band) {
		v->rangelow = 1400000;     /* 87.5 MHz */
		v->rangehigh = 1728000;    /* 108.0 MHz */
		v->rxsubchans = cadet_getstereo(dev);
		v->rxsubchans = cadet_getstereo(dev);
		v->audmode = V4L2_TUNER_MODE_STEREO;
		outb(3, dev->io);
		outb(3, dev->io);
		outb(inb(dev->io + 1) & 0x7f, dev->io + 1);
		outb(inb(dev->io + 1) & 0x7f, dev->io + 1);
		mdelay(100);
		mdelay(100);
		outb(3, dev->io);
		outb(3, dev->io);
		if (inb(dev->io + 1) & 0x80)
		if (inb(dev->io + 1) & 0x80)
			v->rxsubchans |= V4L2_TUNER_SUB_RDS;
			v->rxsubchans |= V4L2_TUNER_SUB_RDS;
		break;
	} else {
	case 1:
		strlcpy(v->name, "AM", sizeof(v->name));
		v->capability = V4L2_TUNER_CAP_LOW;
		v->rangelow = 8320;      /* 520 kHz */
		v->rangelow = 8320;      /* 520 kHz */
		v->rangehigh = 26400;    /* 1650 kHz */
		v->rangehigh = 26400;    /* 1650 kHz */
		v->rxsubchans = V4L2_TUNER_SUB_MONO;
		v->rxsubchans = V4L2_TUNER_SUB_MONO;
		v->audmode = V4L2_TUNER_MODE_MONO;
		break;
	default:
		return -EINVAL;
	}
	}
	v->audmode = V4L2_TUNER_MODE_STEREO;
	v->signal = dev->sigstrength; /* We might need to modify scaling of this */
	v->signal = dev->sigstrength; /* We might need to modify scaling of this */
	return 0;
	return 0;
}
}
@@ -378,8 +390,17 @@ static int vidioc_g_tuner(struct file *file, void *priv,
static int vidioc_s_tuner(struct file *file, void *priv,
static int vidioc_s_tuner(struct file *file, void *priv,
				struct v4l2_tuner *v)
				struct v4l2_tuner *v)
{
{
	if (v->index != 0 && v->index != 1)
	return v->index ? -EINVAL : 0;
}

static int vidioc_enum_freq_bands(struct file *file, void *priv,
				struct v4l2_frequency_band *band)
{
	if (band->tuner)
		return -EINVAL;
		return -EINVAL;
	if (band->index >= ARRAY_SIZE(bands))
		return -EINVAL;
	*band = bands[band->index];
	return 0;
	return 0;
}
}


@@ -388,10 +409,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
{
	struct cadet *dev = video_drvdata(file);
	struct cadet *dev = video_drvdata(file);


	if (f->tuner > 1)
	if (f->tuner)
		return -EINVAL;
		return -EINVAL;
	f->type = V4L2_TUNER_RADIO;
	f->type = V4L2_TUNER_RADIO;
	f->frequency = cadet_getfreq(dev);
	f->frequency = dev->curfreq;
	return 0;
	return 0;
}
}


@@ -401,20 +422,12 @@ static int vidioc_s_frequency(struct file *file, void *priv,
{
{
	struct cadet *dev = video_drvdata(file);
	struct cadet *dev = video_drvdata(file);


	if (f->type != V4L2_TUNER_RADIO)
	if (f->tuner)
		return -EINVAL;
	if (f->tuner == 0) {
		if (f->frequency < 1400000)
			f->frequency = 1400000;
		else if (f->frequency > 1728000)
			f->frequency = 1728000;
	} else if (f->tuner == 1) {
		if (f->frequency < 8320)
			f->frequency = 8320;
		else if (f->frequency > 26400)
			f->frequency = 26400;
	} else
		return -EINVAL;
		return -EINVAL;
	dev->is_fm_band =
		f->frequency >= (bands[0].rangehigh + bands[1].rangelow) / 2;
	clamp(f->frequency, bands[dev->is_fm_band].rangelow,
			    bands[dev->is_fm_band].rangehigh);
	cadet_setfreq(dev, f->frequency);
	cadet_setfreq(dev, f->frequency);
	return 0;
	return 0;
}
}
@@ -499,6 +512,7 @@ static const struct v4l2_ioctl_ops cadet_ioctl_ops = {
	.vidioc_s_tuner     = vidioc_s_tuner,
	.vidioc_s_tuner     = vidioc_s_tuner,
	.vidioc_g_frequency = vidioc_g_frequency,
	.vidioc_g_frequency = vidioc_g_frequency,
	.vidioc_s_frequency = vidioc_s_frequency,
	.vidioc_s_frequency = vidioc_s_frequency,
	.vidioc_enum_freq_bands = vidioc_enum_freq_bands,
	.vidioc_log_status  = v4l2_ctrl_log_status,
	.vidioc_log_status  = v4l2_ctrl_log_status,
	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
@@ -555,8 +569,8 @@ static void cadet_probe(struct cadet *dev)
	for (i = 0; i < 8; i++) {
	for (i = 0; i < 8; i++) {
		dev->io = iovals[i];
		dev->io = iovals[i];
		if (request_region(dev->io, 2, "cadet-probe")) {
		if (request_region(dev->io, 2, "cadet-probe")) {
			cadet_setfreq(dev, 1410);
			cadet_setfreq(dev, bands[1].rangelow);
			if (cadet_getfreq(dev) == 1410) {
			if (cadet_getfreq(dev) == bands[1].rangelow) {
				release_region(dev->io, 2);
				release_region(dev->io, 2);
				return;
				return;
			}
			}
@@ -619,6 +633,9 @@ static int __init cadet_init(void)
		goto err_hdl;
		goto err_hdl;
	}
	}


	dev->is_fm_band = true;
	dev->curfreq = bands[dev->is_fm_band].rangelow;
	cadet_setfreq(dev, dev->curfreq);
	strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
	strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
	dev->vdev.v4l2_dev = v4l2_dev;
	dev->vdev.v4l2_dev = v4l2_dev;
	dev->vdev.fops = &cadet_fops;
	dev->vdev.fops = &cadet_fops;