diff options
| author | Hans de Goede <hdegoede@redhat.com> | 2012-07-12 17:39:18 -0300 | 
|---|---|---|
| committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-09-13 17:42:09 -0300 | 
| commit | fc488517cc0d50bcc9e4ffa90fee5755f9c914fc (patch) | |
| tree | e32c210cbe62c44bcb7291ddd18821370bef5c20 | |
| parent | 355a4d01bd74bdd7b9dd9adeec683b2e3dd9549b (diff) | |
| download | olio-linux-3.10-fc488517cc0d50bcc9e4ffa90fee5755f9c914fc.tar.xz olio-linux-3.10-fc488517cc0d50bcc9e4ffa90fee5755f9c914fc.zip  | |
[media] snd_tea575x: Add support for tuning AM
Add support for tuning AM (on devices with the necessary additional
hardware components), and advertise the available bands using the new
VIDIOC_ENUM_FREQ_BANDS ioctl.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
CC: Ondrej Zary <linux@rainbow-software.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
| -rw-r--r-- | drivers/media/radio/radio-shark.c | 1 | ||||
| -rw-r--r-- | include/sound/tea575x-tuner.h | 3 | ||||
| -rw-r--r-- | sound/i2c/other/tea575x-tuner.c | 197 | 
3 files changed, 165 insertions, 36 deletions
diff --git a/drivers/media/radio/radio-shark.c b/drivers/media/radio/radio-shark.c index 72ded29728b..e1970bf031a 100644 --- a/drivers/media/radio/radio-shark.c +++ b/drivers/media/radio/radio-shark.c @@ -333,6 +333,7 @@ static int usb_shark_probe(struct usb_interface *intf,  	shark->tea.radio_nr = -1;  	shark->tea.ops = &shark_tea_ops;  	shark->tea.cannot_mute = true; +	shark->tea.has_am = true;  	strlcpy(shark->tea.card, "Griffin radioSHARK",  		sizeof(shark->tea.card));  	usb_make_path(shark->usbdev, shark->tea.bus_info, diff --git a/include/sound/tea575x-tuner.h b/include/sound/tea575x-tuner.h index fe8590cac5c..2a695356855 100644 --- a/include/sound/tea575x-tuner.h +++ b/include/sound/tea575x-tuner.h @@ -28,6 +28,7 @@  #include <media/v4l2-device.h>  #define TEA575X_FMIF	10700 +#define TEA575X_AMIF	  450  #define TEA575X_DATA	(1 << 0)  #define TEA575X_CLK	(1 << 1) @@ -52,12 +53,14 @@ struct snd_tea575x {  	struct video_device vd;		/* video device */  	int radio_nr;			/* radio_nr */  	bool tea5759;			/* 5759 chip is present */ +	bool has_am;			/* Device can tune to AM freqs */  	bool cannot_read_data;		/* Device cannot read the data pin */  	bool cannot_mute;		/* Device cannot mute */  	bool mute;			/* Device is muted? */  	bool stereo;			/* receiving stereo */  	bool tuned;			/* tuned to a station */  	unsigned int val;		/* hw value */ +	u32 band;			/* 0: FM, 1: FM-Japan, 2: AM */  	u32 freq;			/* frequency */  	struct mutex mutex;  	struct snd_tea575x_ops *ops; diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index d14edb7d648..88bbd88c066 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -37,9 +37,6 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");  MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips");  MODULE_LICENSE("GPL"); -#define FREQ_LO		((tea->tea5759 ? 760 :  875) * 1600U) -#define FREQ_HI		((tea->tea5759 ? 910 : 1080) * 1600U) -  /*   * definitions   */ @@ -50,8 +47,8 @@ MODULE_LICENSE("GPL");  #define TEA575X_BIT_BAND_MASK	(3<<20)  #define TEA575X_BIT_BAND_FM	(0<<20)  #define TEA575X_BIT_BAND_MW	(1<<20) -#define TEA575X_BIT_BAND_LW	(1<<21) -#define TEA575X_BIT_BAND_SW	(1<<22) +#define TEA575X_BIT_BAND_LW	(2<<20) +#define TEA575X_BIT_BAND_SW	(3<<20)  #define TEA575X_BIT_PORT_0	(1<<19)		/* user bit */  #define TEA575X_BIT_PORT_1	(1<<18)		/* user bit */  #define TEA575X_BIT_SEARCH_MASK	(3<<16)		/* search level */ @@ -62,6 +59,37 @@ MODULE_LICENSE("GPL");  #define TEA575X_BIT_DUMMY	(1<<15)		/* buffer */  #define TEA575X_BIT_FREQ_MASK	0x7fff +enum { BAND_FM, BAND_FM_JAPAN, BAND_AM }; + +static const struct v4l2_frequency_band bands[] = { +	{ +		.type = V4L2_TUNER_RADIO, +		.index = 0, +		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | +			      V4L2_TUNER_CAP_FREQ_BANDS, +		.rangelow   =  87500 * 16, +		.rangehigh  = 108000 * 16, +		.modulation = V4L2_BAND_MODULATION_FM, +	}, +	{ +		.type = V4L2_TUNER_RADIO, +		.index = 0, +		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | +			      V4L2_TUNER_CAP_FREQ_BANDS, +		.rangelow   = 76000 * 16, +		.rangehigh  = 91000 * 16, +		.modulation = V4L2_BAND_MODULATION_FM, +	}, +	{ +		.type = V4L2_TUNER_RADIO, +		.index = 1, +		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS, +		.rangelow   =  530 * 16, +		.rangehigh  = 1710 * 16, +		.modulation = V4L2_BAND_MODULATION_AM, +	}, +}; +  /*   * lowlevel part   */ @@ -133,16 +161,29 @@ static u32 snd_tea575x_val_to_freq(struct snd_tea575x *tea, u32 val)  	if (freq == 0)  		return freq; -	/* freq *= 12.5 */ -	freq *= 125; -	freq /= 10; -	/* crystal fixup */ -	if (tea->tea5759) -		freq += TEA575X_FMIF; -	else +	switch (tea->band) { +	case BAND_FM: +		/* freq *= 12.5 */ +		freq *= 125; +		freq /= 10; +		/* crystal fixup */  		freq -= TEA575X_FMIF; +		break; +	case BAND_FM_JAPAN: +		/* freq *= 12.5 */ +		freq *= 125; +		freq /= 10; +		/* crystal fixup */ +		freq += TEA575X_FMIF; +		break; +	case BAND_AM: +		/* crystal fixup */ +		freq -= TEA575X_AMIF; +		break; +	} -	return clamp(freq * 16, FREQ_LO, FREQ_HI); /* from kHz */ +	return clamp(freq * 16, bands[tea->band].rangelow, +				bands[tea->band].rangehigh); /* from kHz */  }  static u32 snd_tea575x_get_freq(struct snd_tea575x *tea) @@ -152,19 +193,35 @@ static u32 snd_tea575x_get_freq(struct snd_tea575x *tea)  static void snd_tea575x_set_freq(struct snd_tea575x *tea)  { -	u32 freq = tea->freq; +	u32 freq = tea->freq / 16;	/* to kHz */ +	u32 band = 0; -	freq /= 16;		/* to kHz */ -	/* crystal fixup */ -	if (tea->tea5759) -		freq -= TEA575X_FMIF; -	else +	switch (tea->band) { +	case BAND_FM: +		band = TEA575X_BIT_BAND_FM; +		/* crystal fixup */  		freq += TEA575X_FMIF; -	/* freq /= 12.5 */ -	freq *= 10; -	freq /= 125; +		/* freq /= 12.5 */ +		freq *= 10; +		freq /= 125; +		break; +	case BAND_FM_JAPAN: +		band = TEA575X_BIT_BAND_FM; +		/* crystal fixup */ +		freq -= TEA575X_FMIF; +		/* freq /= 12.5 */ +		freq *= 10; +		freq /= 125; +		break; +	case BAND_AM: +		band = TEA575X_BIT_BAND_MW; +		/* crystal fixup */ +		freq += TEA575X_AMIF; +		break; +	} -	tea->val &= ~TEA575X_BIT_FREQ_MASK; +	tea->val &= ~(TEA575X_BIT_FREQ_MASK | TEA575X_BIT_BAND_MASK); +	tea->val |= band;  	tea->val |= freq & TEA575X_BIT_FREQ_MASK;  	snd_tea575x_write(tea, tea->val);  	tea->freq = snd_tea575x_val_to_freq(tea, tea->val); @@ -190,23 +247,57 @@ static int vidioc_querycap(struct file *file, void  *priv,  	return 0;  } +static int vidioc_enum_freq_bands(struct file *file, void *priv, +					 struct v4l2_frequency_band *band) +{ +	struct snd_tea575x *tea = video_drvdata(file); +	int index; + +	if (band->tuner != 0) +		return -EINVAL; + +	switch (band->index) { +	case 0: +		if (tea->tea5759) +			index = BAND_FM_JAPAN; +		else +			index = BAND_FM; +		break; +	case 1: +		if (tea->has_am) { +			index = BAND_AM; +			break; +		} +		/* Fall through */ +	default: +		return -EINVAL; +	} + +	*band = bands[index]; +	if (!tea->cannot_read_data) +		band->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED; + +	return 0; +} +  static int vidioc_g_tuner(struct file *file, void *priv,  					struct v4l2_tuner *v)  {  	struct snd_tea575x *tea = video_drvdata(file); +	struct v4l2_frequency_band band_fm = { 0, };  	if (v->index > 0)  		return -EINVAL;  	snd_tea575x_read(tea); +	vidioc_enum_freq_bands(file, priv, &band_fm); -	strcpy(v->name, "FM"); +	memset(v, 0, sizeof(*v)); +	strlcpy(v->name, tea->has_am ? "FM/AM" : "FM", sizeof(v->name));  	v->type = V4L2_TUNER_RADIO; -	v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; -	if (!tea->cannot_read_data) -		v->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED; -	v->rangelow = FREQ_LO; -	v->rangehigh = FREQ_HI; +	v->capability = band_fm.capability; +	v->rangelow = tea->has_am ? bands[BAND_AM].rangelow : band_fm.rangelow; +	v->rangehigh = band_fm.rangehigh;  	v->rxsubchans = tea->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;  	v->audmode = (tea->val & TEA575X_BIT_MONO) ?  		V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO; @@ -218,13 +309,17 @@ static int vidioc_s_tuner(struct file *file, void *priv,  					struct v4l2_tuner *v)  {  	struct snd_tea575x *tea = video_drvdata(file); +	u32 orig_val = tea->val;  	if (v->index)  		return -EINVAL;  	tea->val &= ~TEA575X_BIT_MONO;  	if (v->audmode == V4L2_TUNER_MODE_MONO)  		tea->val |= TEA575X_BIT_MONO; -	snd_tea575x_write(tea, tea->val); +	/* Only apply changes if currently tuning FM */ +	if (tea->band != BAND_AM && tea->val != orig_val) +		snd_tea575x_set_freq(tea); +  	return 0;  } @@ -248,8 +343,15 @@ static int vidioc_s_frequency(struct file *file, void *priv,  	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)  		return -EINVAL; -	tea->val &= ~TEA575X_BIT_SEARCH; -	tea->freq = clamp(f->frequency, FREQ_LO, FREQ_HI); +	if (tea->has_am && f->frequency < (20000 * 16)) +		tea->band = BAND_AM; +	else if (tea->tea5759) +		tea->band = BAND_FM_JAPAN; +	else +		tea->band = BAND_FM; + +	tea->freq = clamp(f->frequency, bands[tea->band].rangelow, +					bands[tea->band].rangehigh);  	snd_tea575x_set_freq(tea);  	return 0;  } @@ -259,13 +361,35 @@ static int vidioc_s_hw_freq_seek(struct file *file, void *fh,  {  	struct snd_tea575x *tea = video_drvdata(file);  	unsigned long timeout; -	int i; +	int i, spacing;  	if (tea->cannot_read_data)  		return -ENOTTY;  	if (a->tuner || a->wrap_around)  		return -EINVAL; +	if (a->rangelow || a->rangehigh) { +		for (i = 0; i < ARRAY_SIZE(bands); i++) { +			if ((i == BAND_FM && tea->tea5759) || +			    (i == BAND_FM_JAPAN && !tea->tea5759) || +			    (i == BAND_AM && !tea->has_am)) +				continue; +			if (bands[i].rangelow  == a->rangelow && +			    bands[i].rangehigh == a->rangehigh) +				break; +		} +		if (i == ARRAY_SIZE(bands)) +			return -EINVAL; /* No matching band found */ +		if (i != tea->band) { +			tea->band = i; +			tea->freq = clamp(tea->freq, bands[i].rangelow, +						     bands[i].rangehigh); +			snd_tea575x_set_freq(tea); +		} +	} + +	spacing = (tea->band == BAND_AM) ? 5 : 50; /* kHz */ +  	/* clear the frequency, HW will fill it in */  	tea->val &= ~TEA575X_BIT_FREQ_MASK;  	tea->val |= TEA575X_BIT_SEARCH; @@ -297,10 +421,10 @@ static int vidioc_s_hw_freq_seek(struct file *file, void *fh,  			if (freq == 0) /* shouldn't happen */  				break;  			/* -			 * if we moved by less than 50 kHz, or in the wrong -			 * direction, continue seeking +			 * if we moved by less than the spacing, or in the +			 * wrong direction, continue seeking  			 */ -			if (abs(tea->freq - freq) < 16 * 50 || +			if (abs(tea->freq - freq) < 16 * spacing ||  					(a->seek_upward && freq < tea->freq) ||  					(!a->seek_upward && freq > tea->freq)) {  				snd_tea575x_write(tea, tea->val); @@ -344,6 +468,7 @@ static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {  	.vidioc_g_frequency = vidioc_g_frequency,  	.vidioc_s_frequency = vidioc_s_frequency,  	.vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek, +	.vidioc_enum_freq_bands = vidioc_enum_freq_bands,  	.vidioc_log_status  = v4l2_ctrl_log_status,  	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,  	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,  |