11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 4c1017a4cdb68ae5368fbc9ee42c77f1f5dca8916Jaroslav Kysela * Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz> 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (at your option) any later version. 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful, 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details. 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 214b29631db33292d416dc395c56122ea865e7635cIgor M. Liplianin */ 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h> 24da155d5b40587815a4397e1a69382fe2366d940bPaul Gortmaker#include <linux/module.h> 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 265a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 27d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil#include <linux/sched.h> 28eab924d0e2bdfd53c902162b0b499b8464c1fb4aMauro Carvalho Chehab#include <asm/io.h> 29d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil#include <media/v4l2-device.h> 304522e825dbfc19537a08f65719dc3d69c46fe661Ondrej Zary#include <media/v4l2-dev.h> 31d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil#include <media/v4l2-fh.h> 324522e825dbfc19537a08f65719dc3d69c46fe661Ondrej Zary#include <media/v4l2-ioctl.h> 33d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil#include <media/v4l2-event.h> 3459b564599bc66f086856b09e480f89555d47b35cOndrej Zary#include <media/tea575x.h> 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 36c1017a4cdb68ae5368fbc9ee42c77f1f5dca8916Jaroslav KyselaMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips"); 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * definitions 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TEA575X_BIT_SEARCH (1<<24) /* 1 = search action, 0 = tuned */ 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TEA575X_BIT_UPDOWN (1<<23) /* 0 = search down, 1 = search up */ 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TEA575X_BIT_MONO (1<<22) /* 0 = stereo, 1 = mono */ 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TEA575X_BIT_BAND_MASK (3<<20) 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TEA575X_BIT_BAND_FM (0<<20) 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TEA575X_BIT_BAND_MW (1<<20) 50fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede#define TEA575X_BIT_BAND_LW (2<<20) 51fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede#define TEA575X_BIT_BAND_SW (3<<20) 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TEA575X_BIT_PORT_0 (1<<19) /* user bit */ 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TEA575X_BIT_PORT_1 (1<<18) /* user bit */ 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TEA575X_BIT_SEARCH_MASK (3<<16) /* search level */ 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TEA575X_BIT_SEARCH_5_28 (0<<16) /* FM >5uV, AM >28uV */ 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TEA575X_BIT_SEARCH_10_40 (1<<16) /* FM >10uV, AM > 40uV */ 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TEA575X_BIT_SEARCH_30_63 (2<<16) /* FM >30uV, AM > 63uV */ 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TEA575X_BIT_SEARCH_150_1000 (3<<16) /* FM > 150uV, AM > 1000uV */ 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TEA575X_BIT_DUMMY (1<<15) /* buffer */ 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TEA575X_BIT_FREQ_MASK 0x7fff 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 62fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goedeenum { BAND_FM, BAND_FM_JAPAN, BAND_AM }; 63fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede 64fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goedestatic const struct v4l2_frequency_band bands[] = { 65fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede { 66fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .type = V4L2_TUNER_RADIO, 67fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .index = 0, 68fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | 69fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede V4L2_TUNER_CAP_FREQ_BANDS, 70fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .rangelow = 87500 * 16, 71fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .rangehigh = 108000 * 16, 72fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .modulation = V4L2_BAND_MODULATION_FM, 73fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede }, 74fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede { 75fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .type = V4L2_TUNER_RADIO, 76fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .index = 0, 77fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | 78fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede V4L2_TUNER_CAP_FREQ_BANDS, 79fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .rangelow = 76000 * 16, 80fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .rangehigh = 91000 * 16, 81fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .modulation = V4L2_BAND_MODULATION_FM, 82fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede }, 83fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede { 84fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .type = V4L2_TUNER_RADIO, 85fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .index = 1, 86fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS, 87fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .rangelow = 530 * 16, 88fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .rangehigh = 1710 * 16, 89fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .modulation = V4L2_BAND_MODULATION_AM, 90fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede }, 91fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede}; 92fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * lowlevel part 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9714219d06592025541559027d0fd8f96ef75f313cOndrej Zarystatic void snd_tea575x_write(struct snd_tea575x *tea, unsigned int val) 9814219d06592025541559027d0fd8f96ef75f313cOndrej Zary{ 9914219d06592025541559027d0fd8f96ef75f313cOndrej Zary u16 l; 10014219d06592025541559027d0fd8f96ef75f313cOndrej Zary u8 data; 10114219d06592025541559027d0fd8f96ef75f313cOndrej Zary 10231a62d415726d94bae5caf5f2e50232aa06babf6Hans de Goede if (tea->ops->write_val) 10331a62d415726d94bae5caf5f2e50232aa06babf6Hans de Goede return tea->ops->write_val(tea, val); 10431a62d415726d94bae5caf5f2e50232aa06babf6Hans de Goede 10514219d06592025541559027d0fd8f96ef75f313cOndrej Zary tea->ops->set_direction(tea, 1); 10614219d06592025541559027d0fd8f96ef75f313cOndrej Zary udelay(16); 10714219d06592025541559027d0fd8f96ef75f313cOndrej Zary 10814219d06592025541559027d0fd8f96ef75f313cOndrej Zary for (l = 25; l > 0; l--) { 10914219d06592025541559027d0fd8f96ef75f313cOndrej Zary data = (val >> 24) & TEA575X_DATA; 11014219d06592025541559027d0fd8f96ef75f313cOndrej Zary val <<= 1; /* shift data */ 11114219d06592025541559027d0fd8f96ef75f313cOndrej Zary tea->ops->set_pins(tea, data | TEA575X_WREN); 11214219d06592025541559027d0fd8f96ef75f313cOndrej Zary udelay(2); 11314219d06592025541559027d0fd8f96ef75f313cOndrej Zary tea->ops->set_pins(tea, data | TEA575X_WREN | TEA575X_CLK); 11414219d06592025541559027d0fd8f96ef75f313cOndrej Zary udelay(2); 11514219d06592025541559027d0fd8f96ef75f313cOndrej Zary tea->ops->set_pins(tea, data | TEA575X_WREN); 11614219d06592025541559027d0fd8f96ef75f313cOndrej Zary udelay(2); 11714219d06592025541559027d0fd8f96ef75f313cOndrej Zary } 11814219d06592025541559027d0fd8f96ef75f313cOndrej Zary 11914219d06592025541559027d0fd8f96ef75f313cOndrej Zary if (!tea->mute) 12014219d06592025541559027d0fd8f96ef75f313cOndrej Zary tea->ops->set_pins(tea, 0); 12114219d06592025541559027d0fd8f96ef75f313cOndrej Zary} 12214219d06592025541559027d0fd8f96ef75f313cOndrej Zary 123bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuilstatic u32 snd_tea575x_read(struct snd_tea575x *tea) 12414219d06592025541559027d0fd8f96ef75f313cOndrej Zary{ 12514219d06592025541559027d0fd8f96ef75f313cOndrej Zary u16 l, rdata; 12614219d06592025541559027d0fd8f96ef75f313cOndrej Zary u32 data = 0; 12714219d06592025541559027d0fd8f96ef75f313cOndrej Zary 12831a62d415726d94bae5caf5f2e50232aa06babf6Hans de Goede if (tea->ops->read_val) 12931a62d415726d94bae5caf5f2e50232aa06babf6Hans de Goede return tea->ops->read_val(tea); 13031a62d415726d94bae5caf5f2e50232aa06babf6Hans de Goede 13114219d06592025541559027d0fd8f96ef75f313cOndrej Zary tea->ops->set_direction(tea, 0); 13214219d06592025541559027d0fd8f96ef75f313cOndrej Zary tea->ops->set_pins(tea, 0); 13314219d06592025541559027d0fd8f96ef75f313cOndrej Zary udelay(16); 13414219d06592025541559027d0fd8f96ef75f313cOndrej Zary 13514219d06592025541559027d0fd8f96ef75f313cOndrej Zary for (l = 24; l--;) { 13614219d06592025541559027d0fd8f96ef75f313cOndrej Zary tea->ops->set_pins(tea, TEA575X_CLK); 13714219d06592025541559027d0fd8f96ef75f313cOndrej Zary udelay(2); 13814219d06592025541559027d0fd8f96ef75f313cOndrej Zary if (!l) 13914219d06592025541559027d0fd8f96ef75f313cOndrej Zary tea->tuned = tea->ops->get_pins(tea) & TEA575X_MOST ? 0 : 1; 14014219d06592025541559027d0fd8f96ef75f313cOndrej Zary tea->ops->set_pins(tea, 0); 14114219d06592025541559027d0fd8f96ef75f313cOndrej Zary udelay(2); 14214219d06592025541559027d0fd8f96ef75f313cOndrej Zary data <<= 1; /* shift data */ 14314219d06592025541559027d0fd8f96ef75f313cOndrej Zary rdata = tea->ops->get_pins(tea); 14414219d06592025541559027d0fd8f96ef75f313cOndrej Zary if (!l) 14514219d06592025541559027d0fd8f96ef75f313cOndrej Zary tea->stereo = (rdata & TEA575X_MOST) ? 0 : 1; 14614219d06592025541559027d0fd8f96ef75f313cOndrej Zary if (rdata & TEA575X_DATA) 14714219d06592025541559027d0fd8f96ef75f313cOndrej Zary data++; 14814219d06592025541559027d0fd8f96ef75f313cOndrej Zary udelay(2); 14914219d06592025541559027d0fd8f96ef75f313cOndrej Zary } 15014219d06592025541559027d0fd8f96ef75f313cOndrej Zary 15114219d06592025541559027d0fd8f96ef75f313cOndrej Zary if (tea->mute) 15214219d06592025541559027d0fd8f96ef75f313cOndrej Zary tea->ops->set_pins(tea, TEA575X_WREN); 15314219d06592025541559027d0fd8f96ef75f313cOndrej Zary 15414219d06592025541559027d0fd8f96ef75f313cOndrej Zary return data; 15514219d06592025541559027d0fd8f96ef75f313cOndrej Zary} 15614219d06592025541559027d0fd8f96ef75f313cOndrej Zary 15740e006aea811afb15e56164383b914cff7a078c7Hans de Goedestatic u32 snd_tea575x_val_to_freq(struct snd_tea575x *tea, u32 val) 158bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil{ 15940e006aea811afb15e56164383b914cff7a078c7Hans de Goede u32 freq = val & TEA575X_BIT_FREQ_MASK; 160bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil 161bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil if (freq == 0) 162bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil return freq; 163bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil 164fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede switch (tea->band) { 165fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede case BAND_FM: 166fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede /* freq *= 12.5 */ 167fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede freq *= 125; 168fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede freq /= 10; 169fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede /* crystal fixup */ 170bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil freq -= TEA575X_FMIF; 171fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede break; 172fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede case BAND_FM_JAPAN: 173fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede /* freq *= 12.5 */ 174fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede freq *= 125; 175fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede freq /= 10; 176fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede /* crystal fixup */ 177fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede freq += TEA575X_FMIF; 178fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede break; 179fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede case BAND_AM: 180fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede /* crystal fixup */ 181fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede freq -= TEA575X_AMIF; 182fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede break; 183fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede } 184bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil 185fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede return clamp(freq * 16, bands[tea->band].rangelow, 186fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede bands[tea->band].rangehigh); /* from kHz */ 187bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil} 188bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil 18940e006aea811afb15e56164383b914cff7a078c7Hans de Goedestatic u32 snd_tea575x_get_freq(struct snd_tea575x *tea) 19040e006aea811afb15e56164383b914cff7a078c7Hans de Goede{ 19140e006aea811afb15e56164383b914cff7a078c7Hans de Goede return snd_tea575x_val_to_freq(tea, snd_tea575x_read(tea)); 19240e006aea811afb15e56164383b914cff7a078c7Hans de Goede} 19340e006aea811afb15e56164383b914cff7a078c7Hans de Goede 194559c2009003bb8092e4927a4bac99cbf75834979Hans de Goedevoid snd_tea575x_set_freq(struct snd_tea575x *tea) 1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 196fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede u32 freq = tea->freq / 16; /* to kHz */ 197fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede u32 band = 0; 1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 199fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede switch (tea->band) { 200fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede case BAND_FM: 201fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede band = TEA575X_BIT_BAND_FM; 202fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede /* crystal fixup */ 203ea27316e4cd13b25727715c0db8adb0b1661f5e7Ondrej Zary freq += TEA575X_FMIF; 204fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede /* freq /= 12.5 */ 205fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede freq *= 10; 206fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede freq /= 125; 207fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede break; 208fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede case BAND_FM_JAPAN: 209fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede band = TEA575X_BIT_BAND_FM; 210fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede /* crystal fixup */ 211fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede freq -= TEA575X_FMIF; 212fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede /* freq /= 12.5 */ 213fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede freq *= 10; 214fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede freq /= 125; 215fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede break; 216fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede case BAND_AM: 217fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede band = TEA575X_BIT_BAND_MW; 218fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede /* crystal fixup */ 219fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede freq += TEA575X_AMIF; 220fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede break; 221fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede } 2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 223fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede tea->val &= ~(TEA575X_BIT_FREQ_MASK | TEA575X_BIT_BAND_MASK); 224fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede tea->val |= band; 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tea->val |= freq & TEA575X_BIT_FREQ_MASK; 22614219d06592025541559027d0fd8f96ef75f313cOndrej Zary snd_tea575x_write(tea, tea->val); 22740e006aea811afb15e56164383b914cff7a078c7Hans de Goede tea->freq = snd_tea575x_val_to_freq(tea, tea->val); 2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Linux Video interface 2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2349b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehabstatic int vidioc_querycap(struct file *file, void *priv, 2359b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab struct v4l2_capability *v) 2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 237c170ecf434bceb0e188b14a6deb3bfa3ec9ef699Hans Verkuil struct snd_tea575x *tea = video_drvdata(file); 2389b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 239d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil strlcpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver)); 24010ca72014741554ad37c149ff0d9e93c1e3d5b7dOndrej Zary strlcpy(v->card, tea->card, sizeof(v->card)); 24110ca72014741554ad37c149ff0d9e93c1e3d5b7dOndrej Zary strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card)); 24210ca72014741554ad37c149ff0d9e93c1e3d5b7dOndrej Zary strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info)); 243d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; 244d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil if (!tea->cannot_read_data) 245d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil v->device_caps |= V4L2_CAP_HW_FREQ_SEEK; 246d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; 2479b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab return 0; 2489b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab} 2499b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 250fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goedestatic int vidioc_enum_freq_bands(struct file *file, void *priv, 251fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede struct v4l2_frequency_band *band) 252fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede{ 253fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede struct snd_tea575x *tea = video_drvdata(file); 254fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede int index; 255fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede 256fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede if (band->tuner != 0) 257fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede return -EINVAL; 258fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede 259fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede switch (band->index) { 260fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede case 0: 261fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede if (tea->tea5759) 262fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede index = BAND_FM_JAPAN; 263fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede else 264fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede index = BAND_FM; 265fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede break; 266fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede case 1: 267fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede if (tea->has_am) { 268fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede index = BAND_AM; 269fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede break; 270fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede } 271fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede /* Fall through */ 272fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede default: 273fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede return -EINVAL; 274fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede } 275fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede 276fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede *band = bands[index]; 277fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede if (!tea->cannot_read_data) 278fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede band->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED; 279fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede 280fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede return 0; 281fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede} 282fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede 2839b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehabstatic int vidioc_g_tuner(struct file *file, void *priv, 2849b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab struct v4l2_tuner *v) 2859b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab{ 286375d135818f32bbe7b3f071bd54d977c4ff8d84aOndrej Zary struct snd_tea575x *tea = video_drvdata(file); 287fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede struct v4l2_frequency_band band_fm = { 0, }; 288375d135818f32bbe7b3f071bd54d977c4ff8d84aOndrej Zary 2899b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab if (v->index > 0) 2909b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab return -EINVAL; 2919b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 29214219d06592025541559027d0fd8f96ef75f313cOndrej Zary snd_tea575x_read(tea); 293fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede vidioc_enum_freq_bands(file, priv, &band_fm); 294375d135818f32bbe7b3f071bd54d977c4ff8d84aOndrej Zary 295fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede memset(v, 0, sizeof(*v)); 296fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede strlcpy(v->name, tea->has_am ? "FM/AM" : "FM", sizeof(v->name)); 2979b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab v->type = V4L2_TUNER_RADIO; 298fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede v->capability = band_fm.capability; 299fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede v->rangelow = tea->has_am ? bands[BAND_AM].rangelow : band_fm.rangelow; 300fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede v->rangehigh = band_fm.rangehigh; 301d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil v->rxsubchans = tea->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; 302d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil v->audmode = (tea->val & TEA575X_BIT_MONO) ? 303d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO; 304375d135818f32bbe7b3f071bd54d977c4ff8d84aOndrej Zary v->signal = tea->tuned ? 0xffff : 0; 3059b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab return 0; 3069b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab} 3079b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 3089b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehabstatic int vidioc_s_tuner(struct file *file, void *priv, 3092f73c7c582a685b3198b974cd6d964d0338f8ab5Hans Verkuil const struct v4l2_tuner *v) 3109b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab{ 311d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil struct snd_tea575x *tea = video_drvdata(file); 312fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede u32 orig_val = tea->val; 313d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil 314d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil if (v->index) 3159b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab return -EINVAL; 316d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil tea->val &= ~TEA575X_BIT_MONO; 317d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil if (v->audmode == V4L2_TUNER_MODE_MONO) 318d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil tea->val |= TEA575X_BIT_MONO; 319fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede /* Only apply changes if currently tuning FM */ 320fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede if (tea->band != BAND_AM && tea->val != orig_val) 321fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede snd_tea575x_set_freq(tea); 322fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede 3239b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab return 0; 3249b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab} 3259b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 3269b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehabstatic int vidioc_g_frequency(struct file *file, void *priv, 3279b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab struct v4l2_frequency *f) 3289b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab{ 3299b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab struct snd_tea575x *tea = video_drvdata(file); 3309b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 331375d135818f32bbe7b3f071bd54d977c4ff8d84aOndrej Zary if (f->tuner != 0) 332375d135818f32bbe7b3f071bd54d977c4ff8d84aOndrej Zary return -EINVAL; 3339b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab f->type = V4L2_TUNER_RADIO; 3349b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab f->frequency = tea->freq; 3359b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab return 0; 3369b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab} 3379b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 3389b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehabstatic int vidioc_s_frequency(struct file *file, void *priv, 339b530a447bb588fdf43fdf4eb909e4ee1921d47acHans Verkuil const struct v4l2_frequency *f) 3409b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab{ 3419b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab struct snd_tea575x *tea = video_drvdata(file); 3429b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 343375d135818f32bbe7b3f071bd54d977c4ff8d84aOndrej Zary if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) 344375d135818f32bbe7b3f071bd54d977c4ff8d84aOndrej Zary return -EINVAL; 345375d135818f32bbe7b3f071bd54d977c4ff8d84aOndrej Zary 346fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede if (tea->has_am && f->frequency < (20000 * 16)) 347fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede tea->band = BAND_AM; 348fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede else if (tea->tea5759) 349fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede tea->band = BAND_FM_JAPAN; 350fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede else 351fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede tea->band = BAND_FM; 352fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede 353b530a447bb588fdf43fdf4eb909e4ee1921d47acHans Verkuil tea->freq = clamp_t(u32, f->frequency, bands[tea->band].rangelow, 354fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede bands[tea->band].rangehigh); 3559b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab snd_tea575x_set_freq(tea); 3569b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab return 0; 3579b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab} 3589b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 359d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuilstatic int vidioc_s_hw_freq_seek(struct file *file, void *fh, 360ec6f4328108f1c83d5ac907c0d978fa886ef9627Hans Verkuil const struct v4l2_hw_freq_seek *a) 3619b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab{ 362d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil struct snd_tea575x *tea = video_drvdata(file); 363bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil unsigned long timeout; 364fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede int i, spacing; 3659b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 366d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil if (tea->cannot_read_data) 367d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil return -ENOTTY; 368d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil if (a->tuner || a->wrap_around) 3699b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab return -EINVAL; 370bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil 371617ade61ea88a370c89960918eddfa10c41316f5Hans Verkuil if (file->f_flags & O_NONBLOCK) 372617ade61ea88a370c89960918eddfa10c41316f5Hans Verkuil return -EWOULDBLOCK; 373617ade61ea88a370c89960918eddfa10c41316f5Hans Verkuil 374fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede if (a->rangelow || a->rangehigh) { 375fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede for (i = 0; i < ARRAY_SIZE(bands); i++) { 376fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede if ((i == BAND_FM && tea->tea5759) || 377fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede (i == BAND_FM_JAPAN && !tea->tea5759) || 378fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede (i == BAND_AM && !tea->has_am)) 379fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede continue; 380fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede if (bands[i].rangelow == a->rangelow && 381fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede bands[i].rangehigh == a->rangehigh) 382fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede break; 383fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede } 384fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede if (i == ARRAY_SIZE(bands)) 385fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede return -EINVAL; /* No matching band found */ 386fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede if (i != tea->band) { 387fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede tea->band = i; 388fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede tea->freq = clamp(tea->freq, bands[i].rangelow, 389fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede bands[i].rangehigh); 390fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede snd_tea575x_set_freq(tea); 391fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede } 392fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede } 393fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede 394fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede spacing = (tea->band == BAND_AM) ? 5 : 50; /* kHz */ 395fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede 396bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil /* clear the frequency, HW will fill it in */ 397bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil tea->val &= ~TEA575X_BIT_FREQ_MASK; 398d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil tea->val |= TEA575X_BIT_SEARCH; 399d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil if (a->seek_upward) 400d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil tea->val |= TEA575X_BIT_UPDOWN; 401bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil else 402bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil tea->val &= ~TEA575X_BIT_UPDOWN; 403d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil snd_tea575x_write(tea, tea->val); 404bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil timeout = jiffies + msecs_to_jiffies(10000); 405d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil for (;;) { 406bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil if (time_after(jiffies, timeout)) 407bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil break; 408d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil if (schedule_timeout_interruptible(msecs_to_jiffies(10))) { 409d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil /* some signal arrived, stop search */ 410d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil tea->val &= ~TEA575X_BIT_SEARCH; 411bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil snd_tea575x_set_freq(tea); 412d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil return -ERESTARTSYS; 413d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil } 414bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil if (!(snd_tea575x_read(tea) & TEA575X_BIT_SEARCH)) { 415bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil u32 freq; 416bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil 417bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil /* Found a frequency, wait until it can be read */ 418bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil for (i = 0; i < 100; i++) { 419bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil msleep(10); 420bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil freq = snd_tea575x_get_freq(tea); 421bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil if (freq) /* available */ 422bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil break; 423bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil } 424bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil if (freq == 0) /* shouldn't happen */ 425bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil break; 426bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil /* 427fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede * if we moved by less than the spacing, or in the 428fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede * wrong direction, continue seeking 429bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil */ 430fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede if (abs(tea->freq - freq) < 16 * spacing || 431bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil (a->seek_upward && freq < tea->freq) || 432bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil (!a->seek_upward && freq > tea->freq)) { 433bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil snd_tea575x_write(tea, tea->val); 434bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil continue; 435bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil } 436bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil tea->freq = freq; 437bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil tea->val &= ~TEA575X_BIT_SEARCH; 438bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil return 0; 439bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil } 440d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil } 441bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil tea->val &= ~TEA575X_BIT_SEARCH; 442bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil snd_tea575x_set_freq(tea); 44354f6019b5860ec062d1149b3a97a5a63ad3e4da9Hans Verkuil return -ENODATA; 4449b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab} 4459b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 4464522e825dbfc19537a08f65719dc3d69c46fe661Ondrej Zarystatic int tea575x_s_ctrl(struct v4l2_ctrl *ctrl) 4479b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab{ 4484522e825dbfc19537a08f65719dc3d69c46fe661Ondrej Zary struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler); 4499b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 4509b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab switch (ctrl->id) { 4519b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab case V4L2_CID_AUDIO_MUTE: 4524522e825dbfc19537a08f65719dc3d69c46fe661Ondrej Zary tea->mute = ctrl->val; 4534522e825dbfc19537a08f65719dc3d69c46fe661Ondrej Zary snd_tea575x_set_freq(tea); 45414219d06592025541559027d0fd8f96ef75f313cOndrej Zary return 0; 4559b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab } 4569b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 4579b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab return -EINVAL; 4589b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab} 4599b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 4609b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehabstatic const struct v4l2_file_operations tea575x_fops = { 4616a529c1a4a87e0f5d143ad3bc0d37179332f210eOndrej Zary .unlocked_ioctl = video_ioctl2, 462d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil .open = v4l2_fh_open, 463d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil .release = v4l2_fh_release, 464d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil .poll = v4l2_ctrl_poll, 4659b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab}; 4669b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 4679b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehabstatic const struct v4l2_ioctl_ops tea575x_ioctl_ops = { 4689b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab .vidioc_querycap = vidioc_querycap, 4699b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab .vidioc_g_tuner = vidioc_g_tuner, 4709b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab .vidioc_s_tuner = vidioc_s_tuner, 4719b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab .vidioc_g_frequency = vidioc_g_frequency, 4729b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab .vidioc_s_frequency = vidioc_s_frequency, 473d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek, 474fc488517cc0d50bcc9e4ffa90fee5755f9c914fcHans de Goede .vidioc_enum_freq_bands = vidioc_enum_freq_bands, 475d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil .vidioc_log_status = v4l2_ctrl_log_status, 476d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 477d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 4789b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab}; 4799b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 480d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuilstatic const struct video_device tea575x_radio = { 4819b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab .ioctl_ops = &tea575x_ioctl_ops, 482d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil .release = video_device_release_empty, 4834522e825dbfc19537a08f65719dc3d69c46fe661Ondrej Zary}; 4844522e825dbfc19537a08f65719dc3d69c46fe661Ondrej Zary 4854522e825dbfc19537a08f65719dc3d69c46fe661Ondrej Zarystatic const struct v4l2_ctrl_ops tea575x_ctrl_ops = { 4864522e825dbfc19537a08f65719dc3d69c46fe661Ondrej Zary .s_ctrl = tea575x_s_ctrl, 4879b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab}; 4889b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4908fd79579c9ccc6ac98d02c2c97d5fb367cfc0e29Ondrej Zaryint snd_tea575x_hw_init(struct snd_tea575x *tea) 4918fd79579c9ccc6ac98d02c2c97d5fb367cfc0e29Ondrej Zary{ 492d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil tea->mute = true; 49314219d06592025541559027d0fd8f96ef75f313cOndrej Zary 494d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil /* Not all devices can or know how to read the data back. 495d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil Such devices can set cannot_read_data to true. */ 496d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil if (!tea->cannot_read_data) { 497d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil snd_tea575x_write(tea, 0x55AA); 498d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil if (snd_tea575x_read(tea) != 0x55AA) 499d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil return -ENODEV; 500d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil } 5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 502bc2b395c031430639e5ad5dcb2669cfcf1fa5fd2Hans Verkuil tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_5_28; 5039b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab tea->freq = 90500 * 16; /* 90.5Mhz default */ 5044522e825dbfc19537a08f65719dc3d69c46fe661Ondrej Zary snd_tea575x_set_freq(tea); 5059b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 5068fd79579c9ccc6ac98d02c2c97d5fb367cfc0e29Ondrej Zary return 0; 5078fd79579c9ccc6ac98d02c2c97d5fb367cfc0e29Ondrej Zary} 5088fd79579c9ccc6ac98d02c2c97d5fb367cfc0e29Ondrej ZaryEXPORT_SYMBOL(snd_tea575x_hw_init); 5098fd79579c9ccc6ac98d02c2c97d5fb367cfc0e29Ondrej Zary 5108fd79579c9ccc6ac98d02c2c97d5fb367cfc0e29Ondrej Zaryint snd_tea575x_init(struct snd_tea575x *tea, struct module *owner) 5118fd79579c9ccc6ac98d02c2c97d5fb367cfc0e29Ondrej Zary{ 5128fd79579c9ccc6ac98d02c2c97d5fb367cfc0e29Ondrej Zary int retval = snd_tea575x_hw_init(tea); 5138fd79579c9ccc6ac98d02c2c97d5fb367cfc0e29Ondrej Zary 5148fd79579c9ccc6ac98d02c2c97d5fb367cfc0e29Ondrej Zary if (retval) 5158fd79579c9ccc6ac98d02c2c97d5fb367cfc0e29Ondrej Zary return retval; 5168fd79579c9ccc6ac98d02c2c97d5fb367cfc0e29Ondrej Zary 5174522e825dbfc19537a08f65719dc3d69c46fe661Ondrej Zary tea->vd = tea575x_radio; 5184522e825dbfc19537a08f65719dc3d69c46fe661Ondrej Zary video_set_drvdata(&tea->vd, tea); 5196a529c1a4a87e0f5d143ad3bc0d37179332f210eOndrej Zary mutex_init(&tea->mutex); 520d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil strlcpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name)); 5216a529c1a4a87e0f5d143ad3bc0d37179332f210eOndrej Zary tea->vd.lock = &tea->mutex; 522d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil tea->vd.v4l2_dev = tea->v4l2_dev; 5235daf53a6eb5c54c618c9def388d81c2769fd11a0Hans de Goede tea->fops = tea575x_fops; 5245daf53a6eb5c54c618c9def388d81c2769fd11a0Hans de Goede tea->fops.owner = owner; 5255daf53a6eb5c54c618c9def388d81c2769fd11a0Hans de Goede tea->vd.fops = &tea->fops; 5266539799599f50e9ce36da784ee0f545540a9732cHans Verkuil /* disable hw_freq_seek if we can't use it */ 5276539799599f50e9ce36da784ee0f545540a9732cHans Verkuil if (tea->cannot_read_data) 528152a3a7320d1582009db85d8be365ce430d079afHans Verkuil v4l2_disable_ioctl(&tea->vd, VIDIOC_S_HW_FREQ_SEEK); 5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5303d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede if (!tea->cannot_mute) { 5313d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede tea->vd.ctrl_handler = &tea->ctrl_handler; 5323d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede v4l2_ctrl_handler_init(&tea->ctrl_handler, 1); 5333d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, 5343d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); 5353d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede retval = tea->ctrl_handler.error; 5364522e825dbfc19537a08f65719dc3d69c46fe661Ondrej Zary if (retval) { 5373d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede v4l2_err(tea->v4l2_dev, "can't initialize controls\n"); 5384522e825dbfc19537a08f65719dc3d69c46fe661Ondrej Zary v4l2_ctrl_handler_free(&tea->ctrl_handler); 5394522e825dbfc19537a08f65719dc3d69c46fe661Ondrej Zary return retval; 5404522e825dbfc19537a08f65719dc3d69c46fe661Ondrej Zary } 5419b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 5423d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede if (tea->ext_init) { 5433d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede retval = tea->ext_init(tea); 5443d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede if (retval) { 5453d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede v4l2_ctrl_handler_free(&tea->ctrl_handler); 5463d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede return retval; 5473d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede } 5483d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede } 5493d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede 5503d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede v4l2_ctrl_handler_setup(&tea->ctrl_handler); 5513d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede } 5529b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab 553d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->radio_nr); 5549b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab if (retval) { 555d4ecc83b79cc290eadf1ffb33a589c3c72bbc295Hans Verkuil v4l2_err(tea->v4l2_dev, "can't register video device!\n"); 5563d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede v4l2_ctrl_handler_free(tea->vd.ctrl_handler); 55714219d06592025541559027d0fd8f96ef75f313cOndrej Zary return retval; 5589b76ede411145d7456ae5e467b65003ca7990b06Mauro Carvalho Chehab } 5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 56014219d06592025541559027d0fd8f96ef75f313cOndrej Zary return 0; 5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 56397f02e05f246a2346275c1c93a3079e8933e74b2Takashi Iwaivoid snd_tea575x_exit(struct snd_tea575x *tea) 5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5654522e825dbfc19537a08f65719dc3d69c46fe661Ondrej Zary video_unregister_device(&tea->vd); 5663d0fe51cfa3d07751224c034f8226136a7dd05f2Hans de Goede v4l2_ctrl_handler_free(tea->vd.ctrl_handler); 5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init alsa_tea575x_module_init(void) 5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5734b29631db33292d416dc395c56122ea865e7635cIgor M. Liplianin 5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit alsa_tea575x_module_exit(void) 5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5774b29631db33292d416dc395c56122ea865e7635cIgor M. Liplianin 5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(alsa_tea575x_module_init) 5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(alsa_tea575x_module_exit) 5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(snd_tea575x_init); 5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(snd_tea575x_exit); 583559c2009003bb8092e4927a4bac99cbf75834979Hans de GoedeEXPORT_SYMBOL(snd_tea575x_set_freq); 584