Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Hans de Goede and committed by
Mauro Carvalho Chehab
fc488517 355a4d01

+165 -36
+1
drivers/media/radio/radio-shark.c
··· 333 333 shark->tea.radio_nr = -1; 334 334 shark->tea.ops = &shark_tea_ops; 335 335 shark->tea.cannot_mute = true; 336 + shark->tea.has_am = true; 336 337 strlcpy(shark->tea.card, "Griffin radioSHARK", 337 338 sizeof(shark->tea.card)); 338 339 usb_make_path(shark->usbdev, shark->tea.bus_info,
+3
include/sound/tea575x-tuner.h
··· 28 28 #include <media/v4l2-device.h> 29 29 30 30 #define TEA575X_FMIF 10700 31 + #define TEA575X_AMIF 450 31 32 32 33 #define TEA575X_DATA (1 << 0) 33 34 #define TEA575X_CLK (1 << 1) ··· 53 52 struct video_device vd; /* video device */ 54 53 int radio_nr; /* radio_nr */ 55 54 bool tea5759; /* 5759 chip is present */ 55 + bool has_am; /* Device can tune to AM freqs */ 56 56 bool cannot_read_data; /* Device cannot read the data pin */ 57 57 bool cannot_mute; /* Device cannot mute */ 58 58 bool mute; /* Device is muted? */ 59 59 bool stereo; /* receiving stereo */ 60 60 bool tuned; /* tuned to a station */ 61 61 unsigned int val; /* hw value */ 62 + u32 band; /* 0: FM, 1: FM-Japan, 2: AM */ 62 63 u32 freq; /* frequency */ 63 64 struct mutex mutex; 64 65 struct snd_tea575x_ops *ops;
+161 -36
sound/i2c/other/tea575x-tuner.c
··· 37 37 MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips"); 38 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 41 * definitions 45 42 */ ··· 47 50 #define TEA575X_BIT_BAND_MASK (3<<20) 48 51 #define TEA575X_BIT_BAND_FM (0<<20) 49 52 #define TEA575X_BIT_BAND_MW (1<<20) 50 - #define TEA575X_BIT_BAND_LW (1<<21) 51 - #define TEA575X_BIT_BAND_SW (1<<22) 53 + #define TEA575X_BIT_BAND_LW (2<<20) 54 + #define TEA575X_BIT_BAND_SW (3<<20) 52 55 #define TEA575X_BIT_PORT_0 (1<<19) /* user bit */ 53 56 #define TEA575X_BIT_PORT_1 (1<<18) /* user bit */ 54 57 #define TEA575X_BIT_SEARCH_MASK (3<<16) /* search level */ ··· 58 61 #define TEA575X_BIT_SEARCH_150_1000 (3<<16) /* FM > 150uV, AM > 1000uV */ 59 62 #define TEA575X_BIT_DUMMY (1<<15) /* buffer */ 60 63 #define TEA575X_BIT_FREQ_MASK 0x7fff 64 + 65 + enum { BAND_FM, BAND_FM_JAPAN, BAND_AM }; 66 + 67 + static const struct v4l2_frequency_band bands[] = { 68 + { 69 + .type = V4L2_TUNER_RADIO, 70 + .index = 0, 71 + .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | 72 + V4L2_TUNER_CAP_FREQ_BANDS, 73 + .rangelow = 87500 * 16, 74 + .rangehigh = 108000 * 16, 75 + .modulation = V4L2_BAND_MODULATION_FM, 76 + }, 77 + { 78 + .type = V4L2_TUNER_RADIO, 79 + .index = 0, 80 + .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | 81 + V4L2_TUNER_CAP_FREQ_BANDS, 82 + .rangelow = 76000 * 16, 83 + .rangehigh = 91000 * 16, 84 + .modulation = V4L2_BAND_MODULATION_FM, 85 + }, 86 + { 87 + .type = V4L2_TUNER_RADIO, 88 + .index = 1, 89 + .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS, 90 + .rangelow = 530 * 16, 91 + .rangehigh = 1710 * 16, 92 + .modulation = V4L2_BAND_MODULATION_AM, 93 + }, 94 + }; 61 95 62 96 /* 63 97 * lowlevel part ··· 161 133 if (freq == 0) 162 134 return freq; 163 135 164 - /* freq *= 12.5 */ 165 - freq *= 125; 166 - freq /= 10; 167 - /* crystal fixup */ 168 - if (tea->tea5759) 169 - freq += TEA575X_FMIF; 170 - else 136 + switch (tea->band) { 137 + case BAND_FM: 138 + /* freq *= 12.5 */ 139 + freq *= 125; 140 + freq /= 10; 141 + /* crystal fixup */ 171 142 freq -= TEA575X_FMIF; 143 + break; 144 + case BAND_FM_JAPAN: 145 + /* freq *= 12.5 */ 146 + freq *= 125; 147 + freq /= 10; 148 + /* crystal fixup */ 149 + freq += TEA575X_FMIF; 150 + break; 151 + case BAND_AM: 152 + /* crystal fixup */ 153 + freq -= TEA575X_AMIF; 154 + break; 155 + } 172 156 173 - return clamp(freq * 16, FREQ_LO, FREQ_HI); /* from kHz */ 157 + return clamp(freq * 16, bands[tea->band].rangelow, 158 + bands[tea->band].rangehigh); /* from kHz */ 174 159 } 175 160 176 161 static u32 snd_tea575x_get_freq(struct snd_tea575x *tea) ··· 193 152 194 153 static void snd_tea575x_set_freq(struct snd_tea575x *tea) 195 154 { 196 - u32 freq = tea->freq; 155 + u32 freq = tea->freq / 16; /* to kHz */ 156 + u32 band = 0; 197 157 198 - freq /= 16; /* to kHz */ 199 - /* crystal fixup */ 200 - if (tea->tea5759) 201 - freq -= TEA575X_FMIF; 202 - else 158 + switch (tea->band) { 159 + case BAND_FM: 160 + band = TEA575X_BIT_BAND_FM; 161 + /* crystal fixup */ 203 162 freq += TEA575X_FMIF; 204 - /* freq /= 12.5 */ 205 - freq *= 10; 206 - freq /= 125; 163 + /* freq /= 12.5 */ 164 + freq *= 10; 165 + freq /= 125; 166 + break; 167 + case BAND_FM_JAPAN: 168 + band = TEA575X_BIT_BAND_FM; 169 + /* crystal fixup */ 170 + freq -= TEA575X_FMIF; 171 + /* freq /= 12.5 */ 172 + freq *= 10; 173 + freq /= 125; 174 + break; 175 + case BAND_AM: 176 + band = TEA575X_BIT_BAND_MW; 177 + /* crystal fixup */ 178 + freq += TEA575X_AMIF; 179 + break; 180 + } 207 181 208 - tea->val &= ~TEA575X_BIT_FREQ_MASK; 182 + tea->val &= ~(TEA575X_BIT_FREQ_MASK | TEA575X_BIT_BAND_MASK); 183 + tea->val |= band; 209 184 tea->val |= freq & TEA575X_BIT_FREQ_MASK; 210 185 snd_tea575x_write(tea, tea->val); 211 186 tea->freq = snd_tea575x_val_to_freq(tea, tea->val); ··· 247 190 return 0; 248 191 } 249 192 193 + static int vidioc_enum_freq_bands(struct file *file, void *priv, 194 + struct v4l2_frequency_band *band) 195 + { 196 + struct snd_tea575x *tea = video_drvdata(file); 197 + int index; 198 + 199 + if (band->tuner != 0) 200 + return -EINVAL; 201 + 202 + switch (band->index) { 203 + case 0: 204 + if (tea->tea5759) 205 + index = BAND_FM_JAPAN; 206 + else 207 + index = BAND_FM; 208 + break; 209 + case 1: 210 + if (tea->has_am) { 211 + index = BAND_AM; 212 + break; 213 + } 214 + /* Fall through */ 215 + default: 216 + return -EINVAL; 217 + } 218 + 219 + *band = bands[index]; 220 + if (!tea->cannot_read_data) 221 + band->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED; 222 + 223 + return 0; 224 + } 225 + 250 226 static int vidioc_g_tuner(struct file *file, void *priv, 251 227 struct v4l2_tuner *v) 252 228 { 253 229 struct snd_tea575x *tea = video_drvdata(file); 230 + struct v4l2_frequency_band band_fm = { 0, }; 254 231 255 232 if (v->index > 0) 256 233 return -EINVAL; 257 234 258 235 snd_tea575x_read(tea); 236 + vidioc_enum_freq_bands(file, priv, &band_fm); 259 237 260 - strcpy(v->name, "FM"); 238 + memset(v, 0, sizeof(*v)); 239 + strlcpy(v->name, tea->has_am ? "FM/AM" : "FM", sizeof(v->name)); 261 240 v->type = V4L2_TUNER_RADIO; 262 - v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; 263 - if (!tea->cannot_read_data) 264 - v->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED; 265 - v->rangelow = FREQ_LO; 266 - v->rangehigh = FREQ_HI; 241 + v->capability = band_fm.capability; 242 + v->rangelow = tea->has_am ? bands[BAND_AM].rangelow : band_fm.rangelow; 243 + v->rangehigh = band_fm.rangehigh; 267 244 v->rxsubchans = tea->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; 268 245 v->audmode = (tea->val & TEA575X_BIT_MONO) ? 269 246 V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO; ··· 309 218 struct v4l2_tuner *v) 310 219 { 311 220 struct snd_tea575x *tea = video_drvdata(file); 221 + u32 orig_val = tea->val; 312 222 313 223 if (v->index) 314 224 return -EINVAL; 315 225 tea->val &= ~TEA575X_BIT_MONO; 316 226 if (v->audmode == V4L2_TUNER_MODE_MONO) 317 227 tea->val |= TEA575X_BIT_MONO; 318 - snd_tea575x_write(tea, tea->val); 228 + /* Only apply changes if currently tuning FM */ 229 + if (tea->band != BAND_AM && tea->val != orig_val) 230 + snd_tea575x_set_freq(tea); 231 + 319 232 return 0; 320 233 } 321 234 ··· 343 248 if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) 344 249 return -EINVAL; 345 250 346 - tea->val &= ~TEA575X_BIT_SEARCH; 347 - tea->freq = clamp(f->frequency, FREQ_LO, FREQ_HI); 251 + if (tea->has_am && f->frequency < (20000 * 16)) 252 + tea->band = BAND_AM; 253 + else if (tea->tea5759) 254 + tea->band = BAND_FM_JAPAN; 255 + else 256 + tea->band = BAND_FM; 257 + 258 + tea->freq = clamp(f->frequency, bands[tea->band].rangelow, 259 + bands[tea->band].rangehigh); 348 260 snd_tea575x_set_freq(tea); 349 261 return 0; 350 262 } ··· 361 259 { 362 260 struct snd_tea575x *tea = video_drvdata(file); 363 261 unsigned long timeout; 364 - int i; 262 + int i, spacing; 365 263 366 264 if (tea->cannot_read_data) 367 265 return -ENOTTY; 368 266 if (a->tuner || a->wrap_around) 369 267 return -EINVAL; 268 + 269 + if (a->rangelow || a->rangehigh) { 270 + for (i = 0; i < ARRAY_SIZE(bands); i++) { 271 + if ((i == BAND_FM && tea->tea5759) || 272 + (i == BAND_FM_JAPAN && !tea->tea5759) || 273 + (i == BAND_AM && !tea->has_am)) 274 + continue; 275 + if (bands[i].rangelow == a->rangelow && 276 + bands[i].rangehigh == a->rangehigh) 277 + break; 278 + } 279 + if (i == ARRAY_SIZE(bands)) 280 + return -EINVAL; /* No matching band found */ 281 + if (i != tea->band) { 282 + tea->band = i; 283 + tea->freq = clamp(tea->freq, bands[i].rangelow, 284 + bands[i].rangehigh); 285 + snd_tea575x_set_freq(tea); 286 + } 287 + } 288 + 289 + spacing = (tea->band == BAND_AM) ? 5 : 50; /* kHz */ 370 290 371 291 /* clear the frequency, HW will fill it in */ 372 292 tea->val &= ~TEA575X_BIT_FREQ_MASK; ··· 421 297 if (freq == 0) /* shouldn't happen */ 422 298 break; 423 299 /* 424 - * if we moved by less than 50 kHz, or in the wrong 425 - * direction, continue seeking 300 + * if we moved by less than the spacing, or in the 301 + * wrong direction, continue seeking 426 302 */ 427 - if (abs(tea->freq - freq) < 16 * 50 || 303 + if (abs(tea->freq - freq) < 16 * spacing || 428 304 (a->seek_upward && freq < tea->freq) || 429 305 (!a->seek_upward && freq > tea->freq)) { 430 306 snd_tea575x_write(tea, tea->val); ··· 468 344 .vidioc_g_frequency = vidioc_g_frequency, 469 345 .vidioc_s_frequency = vidioc_s_frequency, 470 346 .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek, 347 + .vidioc_enum_freq_bands = vidioc_enum_freq_bands, 471 348 .vidioc_log_status = v4l2_ctrl_log_status, 472 349 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 473 350 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,