diff options
Diffstat (limited to 'sound/i2c')
-rw-r--r-- | sound/i2c/other/tea575x-tuner.c | 205 |
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>"); | |||
37 | MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips"); | 37 | MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips"); |
38 | MODULE_LICENSE("GPL"); | 38 | MODULE_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 | ||
62 | enum { BAND_FM, BAND_FM_JAPAN, BAND_AM }; | ||
63 | |||
64 | static 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 | ||
148 | static u32 snd_tea575x_get_freq(struct snd_tea575x *tea) | 189 | static 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 | ||
153 | static void snd_tea575x_set_freq(struct snd_tea575x *tea) | 194 | void 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 | ||
250 | static 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 | |||
193 | static int vidioc_g_tuner(struct file *file, void *priv, | 283 | static 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 | ||
257 | static int vidioc_s_hw_freq_seek(struct file *file, void *fh, | 359 | static 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 | ||
447 | EXPORT_SYMBOL(snd_tea575x_init); | 575 | EXPORT_SYMBOL(snd_tea575x_init); |
448 | EXPORT_SYMBOL(snd_tea575x_exit); | 576 | EXPORT_SYMBOL(snd_tea575x_exit); |
577 | EXPORT_SYMBOL(snd_tea575x_set_freq); | ||