aboutsummaryrefslogtreecommitdiffstats
path: root/sound/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/i2c')
-rw-r--r--sound/i2c/other/tea575x-tuner.c205
1 files changed, 167 insertions, 38 deletions
diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c
index d14edb7d6484..3c6c1e3226f3 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>");
37MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips"); 37MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips");
38MODULE_LICENSE("GPL"); 38MODULE_LICENSE("GPL");
39 39
40#define FREQ_LO ((tea->tea5759 ? 760 : 875) * 1600U)
41#define FREQ_HI ((tea->tea5759 ? 910 : 1080) * 1600U)
42
43/* 40/*
44 * definitions 41 * definitions
45 */ 42 */
@@ -50,8 +47,8 @@ MODULE_LICENSE("GPL");
50#define TEA575X_BIT_BAND_MASK (3<<20) 47#define TEA575X_BIT_BAND_MASK (3<<20)
51#define TEA575X_BIT_BAND_FM (0<<20) 48#define TEA575X_BIT_BAND_FM (0<<20)
52#define TEA575X_BIT_BAND_MW (1<<20) 49#define TEA575X_BIT_BAND_MW (1<<20)
53#define TEA575X_BIT_BAND_LW (1<<21) 50#define TEA575X_BIT_BAND_LW (2<<20)
54#define TEA575X_BIT_BAND_SW (1<<22) 51#define TEA575X_BIT_BAND_SW (3<<20)
55#define TEA575X_BIT_PORT_0 (1<<19) /* user bit */ 52#define TEA575X_BIT_PORT_0 (1<<19) /* user bit */
56#define TEA575X_BIT_PORT_1 (1<<18) /* user bit */ 53#define TEA575X_BIT_PORT_1 (1<<18) /* user bit */
57#define TEA575X_BIT_SEARCH_MASK (3<<16) /* search level */ 54#define TEA575X_BIT_SEARCH_MASK (3<<16) /* search level */
@@ -62,6 +59,37 @@ MODULE_LICENSE("GPL");
62#define TEA575X_BIT_DUMMY (1<<15) /* buffer */ 59#define TEA575X_BIT_DUMMY (1<<15) /* buffer */
63#define TEA575X_BIT_FREQ_MASK 0x7fff 60#define TEA575X_BIT_FREQ_MASK 0x7fff
64 61
62enum { BAND_FM, BAND_FM_JAPAN, BAND_AM };
63
64static const struct v4l2_frequency_band bands[] = {
65 {
66 .type = V4L2_TUNER_RADIO,
67 .index = 0,
68 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
69 V4L2_TUNER_CAP_FREQ_BANDS,
70 .rangelow = 87500 * 16,
71 .rangehigh = 108000 * 16,
72 .modulation = V4L2_BAND_MODULATION_FM,
73 },
74 {
75 .type = V4L2_TUNER_RADIO,
76 .index = 0,
77 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
78 V4L2_TUNER_CAP_FREQ_BANDS,
79 .rangelow = 76000 * 16,
80 .rangehigh = 91000 * 16,
81 .modulation = V4L2_BAND_MODULATION_FM,
82 },
83 {
84 .type = V4L2_TUNER_RADIO,
85 .index = 1,
86 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
87 .rangelow = 530 * 16,
88 .rangehigh = 1710 * 16,
89 .modulation = V4L2_BAND_MODULATION_AM,
90 },
91};
92
65/* 93/*
66 * lowlevel part 94 * lowlevel part
67 */ 95 */
@@ -133,16 +161,29 @@ static u32 snd_tea575x_val_to_freq(struct snd_tea575x *tea, u32 val)
133 if (freq == 0) 161 if (freq == 0)
134 return freq; 162 return freq;
135 163
136 /* freq *= 12.5 */ 164 switch (tea->band) {
137 freq *= 125; 165 case BAND_FM:
138 freq /= 10; 166 /* freq *= 12.5 */
139 /* crystal fixup */ 167 freq *= 125;
140 if (tea->tea5759) 168 freq /= 10;
141 freq += TEA575X_FMIF; 169 /* crystal fixup */
142 else
143 freq -= TEA575X_FMIF; 170 freq -= TEA575X_FMIF;
171 break;
172 case BAND_FM_JAPAN:
173 /* freq *= 12.5 */
174 freq *= 125;
175 freq /= 10;
176 /* crystal fixup */
177 freq += TEA575X_FMIF;
178 break;
179 case BAND_AM:
180 /* crystal fixup */
181 freq -= TEA575X_AMIF;
182 break;
183 }
144 184
145 return clamp(freq * 16, FREQ_LO, FREQ_HI); /* from kHz */ 185 return clamp(freq * 16, bands[tea->band].rangelow,
186 bands[tea->band].rangehigh); /* from kHz */
146} 187}
147 188
148static u32 snd_tea575x_get_freq(struct snd_tea575x *tea) 189static u32 snd_tea575x_get_freq(struct snd_tea575x *tea)
@@ -150,21 +191,37 @@ static u32 snd_tea575x_get_freq(struct snd_tea575x *tea)
150 return snd_tea575x_val_to_freq(tea, snd_tea575x_read(tea)); 191 return snd_tea575x_val_to_freq(tea, snd_tea575x_read(tea));
151} 192}
152 193
153static void snd_tea575x_set_freq(struct snd_tea575x *tea) 194void snd_tea575x_set_freq(struct snd_tea575x *tea)
154{ 195{
155 u32 freq = tea->freq; 196 u32 freq = tea->freq / 16; /* to kHz */
197 u32 band = 0;
156 198
157 freq /= 16; /* to kHz */ 199 switch (tea->band) {
158 /* crystal fixup */ 200 case BAND_FM:
159 if (tea->tea5759) 201 band = TEA575X_BIT_BAND_FM;
160 freq -= TEA575X_FMIF; 202 /* crystal fixup */
161 else
162 freq += TEA575X_FMIF; 203 freq += TEA575X_FMIF;
163 /* freq /= 12.5 */ 204 /* freq /= 12.5 */
164 freq *= 10; 205 freq *= 10;
165 freq /= 125; 206 freq /= 125;
207 break;
208 case BAND_FM_JAPAN:
209 band = TEA575X_BIT_BAND_FM;
210 /* crystal fixup */
211 freq -= TEA575X_FMIF;
212 /* freq /= 12.5 */
213 freq *= 10;
214 freq /= 125;
215 break;
216 case BAND_AM:
217 band = TEA575X_BIT_BAND_MW;
218 /* crystal fixup */
219 freq += TEA575X_AMIF;
220 break;
221 }
166 222
167 tea->val &= ~TEA575X_BIT_FREQ_MASK; 223 tea->val &= ~(TEA575X_BIT_FREQ_MASK | TEA575X_BIT_BAND_MASK);
224 tea->val |= band;
168 tea->val |= freq & TEA575X_BIT_FREQ_MASK; 225 tea->val |= freq & TEA575X_BIT_FREQ_MASK;
169 snd_tea575x_write(tea, tea->val); 226 snd_tea575x_write(tea, tea->val);
170 tea->freq = snd_tea575x_val_to_freq(tea, tea->val); 227 tea->freq = snd_tea575x_val_to_freq(tea, tea->val);
@@ -190,23 +247,57 @@ static int vidioc_querycap(struct file *file, void *priv,
190 return 0; 247 return 0;
191} 248}
192 249
250static int vidioc_enum_freq_bands(struct file *file, void *priv,
251 struct v4l2_frequency_band *band)
252{
253 struct snd_tea575x *tea = video_drvdata(file);
254 int index;
255
256 if (band->tuner != 0)
257 return -EINVAL;
258
259 switch (band->index) {
260 case 0:
261 if (tea->tea5759)
262 index = BAND_FM_JAPAN;
263 else
264 index = BAND_FM;
265 break;
266 case 1:
267 if (tea->has_am) {
268 index = BAND_AM;
269 break;
270 }
271 /* Fall through */
272 default:
273 return -EINVAL;
274 }
275
276 *band = bands[index];
277 if (!tea->cannot_read_data)
278 band->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED;
279
280 return 0;
281}
282
193static int vidioc_g_tuner(struct file *file, void *priv, 283static int vidioc_g_tuner(struct file *file, void *priv,
194 struct v4l2_tuner *v) 284 struct v4l2_tuner *v)
195{ 285{
196 struct snd_tea575x *tea = video_drvdata(file); 286 struct snd_tea575x *tea = video_drvdata(file);
287 struct v4l2_frequency_band band_fm = { 0, };
197 288
198 if (v->index > 0) 289 if (v->index > 0)
199 return -EINVAL; 290 return -EINVAL;
200 291
201 snd_tea575x_read(tea); 292 snd_tea575x_read(tea);
293 vidioc_enum_freq_bands(file, priv, &band_fm);
202 294
203 strcpy(v->name, "FM"); 295 memset(v, 0, sizeof(*v));
296 strlcpy(v->name, tea->has_am ? "FM/AM" : "FM", sizeof(v->name));
204 v->type = V4L2_TUNER_RADIO; 297 v->type = V4L2_TUNER_RADIO;
205 v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; 298 v->capability = band_fm.capability;
206 if (!tea->cannot_read_data) 299 v->rangelow = tea->has_am ? bands[BAND_AM].rangelow : band_fm.rangelow;
207 v->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED; 300 v->rangehigh = band_fm.rangehigh;
208 v->rangelow = FREQ_LO;
209 v->rangehigh = FREQ_HI;
210 v->rxsubchans = tea->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; 301 v->rxsubchans = tea->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
211 v->audmode = (tea->val & TEA575X_BIT_MONO) ? 302 v->audmode = (tea->val & TEA575X_BIT_MONO) ?
212 V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO; 303 V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO;
@@ -218,13 +309,17 @@ static int vidioc_s_tuner(struct file *file, void *priv,
218 struct v4l2_tuner *v) 309 struct v4l2_tuner *v)
219{ 310{
220 struct snd_tea575x *tea = video_drvdata(file); 311 struct snd_tea575x *tea = video_drvdata(file);
312 u32 orig_val = tea->val;
221 313
222 if (v->index) 314 if (v->index)
223 return -EINVAL; 315 return -EINVAL;
224 tea->val &= ~TEA575X_BIT_MONO; 316 tea->val &= ~TEA575X_BIT_MONO;
225 if (v->audmode == V4L2_TUNER_MODE_MONO) 317 if (v->audmode == V4L2_TUNER_MODE_MONO)
226 tea->val |= TEA575X_BIT_MONO; 318 tea->val |= TEA575X_BIT_MONO;
227 snd_tea575x_write(tea, tea->val); 319 /* Only apply changes if currently tuning FM */
320 if (tea->band != BAND_AM && tea->val != orig_val)
321 snd_tea575x_set_freq(tea);
322
228 return 0; 323 return 0;
229} 324}
230 325
@@ -248,24 +343,56 @@ static int vidioc_s_frequency(struct file *file, void *priv,
248 if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) 343 if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
249 return -EINVAL; 344 return -EINVAL;
250 345
251 tea->val &= ~TEA575X_BIT_SEARCH; 346 if (tea->has_am && f->frequency < (20000 * 16))
252 tea->freq = clamp(f->frequency, FREQ_LO, FREQ_HI); 347 tea->band = BAND_AM;
348 else if (tea->tea5759)
349 tea->band = BAND_FM_JAPAN;
350 else
351 tea->band = BAND_FM;
352
353 tea->freq = clamp(f->frequency, bands[tea->band].rangelow,
354 bands[tea->band].rangehigh);
253 snd_tea575x_set_freq(tea); 355 snd_tea575x_set_freq(tea);
254 return 0; 356 return 0;
255} 357}
256 358
257static int vidioc_s_hw_freq_seek(struct file *file, void *fh, 359static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
258 struct v4l2_hw_freq_seek *a) 360 const struct v4l2_hw_freq_seek *a)
259{ 361{
260 struct snd_tea575x *tea = video_drvdata(file); 362 struct snd_tea575x *tea = video_drvdata(file);
261 unsigned long timeout; 363 unsigned long timeout;
262 int i; 364 int i, spacing;
263 365
264 if (tea->cannot_read_data) 366 if (tea->cannot_read_data)
265 return -ENOTTY; 367 return -ENOTTY;
266 if (a->tuner || a->wrap_around) 368 if (a->tuner || a->wrap_around)
267 return -EINVAL; 369 return -EINVAL;
268 370
371 if (file->f_flags & O_NONBLOCK)
372 return -EWOULDBLOCK;
373
374 if (a->rangelow || a->rangehigh) {
375 for (i = 0; i < ARRAY_SIZE(bands); i++) {
376 if ((i == BAND_FM && tea->tea5759) ||
377 (i == BAND_FM_JAPAN && !tea->tea5759) ||
378 (i == BAND_AM && !tea->has_am))
379 continue;
380 if (bands[i].rangelow == a->rangelow &&
381 bands[i].rangehigh == a->rangehigh)
382 break;
383 }
384 if (i == ARRAY_SIZE(bands))
385 return -EINVAL; /* No matching band found */
386 if (i != tea->band) {
387 tea->band = i;
388 tea->freq = clamp(tea->freq, bands[i].rangelow,
389 bands[i].rangehigh);
390 snd_tea575x_set_freq(tea);
391 }
392 }
393
394 spacing = (tea->band == BAND_AM) ? 5 : 50; /* kHz */
395
269 /* clear the frequency, HW will fill it in */ 396 /* clear the frequency, HW will fill it in */
270 tea->val &= ~TEA575X_BIT_FREQ_MASK; 397 tea->val &= ~TEA575X_BIT_FREQ_MASK;
271 tea->val |= TEA575X_BIT_SEARCH; 398 tea->val |= TEA575X_BIT_SEARCH;
@@ -297,10 +424,10 @@ static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
297 if (freq == 0) /* shouldn't happen */ 424 if (freq == 0) /* shouldn't happen */
298 break; 425 break;
299 /* 426 /*
300 * if we moved by less than 50 kHz, or in the wrong 427 * if we moved by less than the spacing, or in the
301 * direction, continue seeking 428 * wrong direction, continue seeking
302 */ 429 */
303 if (abs(tea->freq - freq) < 16 * 50 || 430 if (abs(tea->freq - freq) < 16 * spacing ||
304 (a->seek_upward && freq < tea->freq) || 431 (a->seek_upward && freq < tea->freq) ||
305 (!a->seek_upward && freq > tea->freq)) { 432 (!a->seek_upward && freq > tea->freq)) {
306 snd_tea575x_write(tea, tea->val); 433 snd_tea575x_write(tea, tea->val);
@@ -344,6 +471,7 @@ static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
344 .vidioc_g_frequency = vidioc_g_frequency, 471 .vidioc_g_frequency = vidioc_g_frequency,
345 .vidioc_s_frequency = vidioc_s_frequency, 472 .vidioc_s_frequency = vidioc_s_frequency,
346 .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek, 473 .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek,
474 .vidioc_enum_freq_bands = vidioc_enum_freq_bands,
347 .vidioc_log_status = v4l2_ctrl_log_status, 475 .vidioc_log_status = v4l2_ctrl_log_status,
348 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 476 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
349 .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 477 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
@@ -446,3 +574,4 @@ module_exit(alsa_tea575x_module_exit)
446 574
447EXPORT_SYMBOL(snd_tea575x_init); 575EXPORT_SYMBOL(snd_tea575x_init);
448EXPORT_SYMBOL(snd_tea575x_exit); 576EXPORT_SYMBOL(snd_tea575x_exit);
577EXPORT_SYMBOL(snd_tea575x_set_freq);