0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/delay.h>
0009 #include <linux/module.h>
0010 #include <linux/init.h>
0011 #include <linux/slab.h>
0012 #include <linux/sched.h>
0013 #include <asm/io.h>
0014 #include <media/v4l2-device.h>
0015 #include <media/v4l2-dev.h>
0016 #include <media/v4l2-fh.h>
0017 #include <media/v4l2-ioctl.h>
0018 #include <media/v4l2-event.h>
0019 #include <media/drv-intf/tea575x.h>
0020
0021 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
0022 MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips");
0023 MODULE_LICENSE("GPL");
0024
0025
0026
0027
0028
0029 #define TEA575X_BIT_SEARCH (1<<24)
0030 #define TEA575X_BIT_UPDOWN (1<<23)
0031 #define TEA575X_BIT_MONO (1<<22)
0032 #define TEA575X_BIT_BAND_MASK (3<<20)
0033 #define TEA575X_BIT_BAND_FM (0<<20)
0034 #define TEA575X_BIT_BAND_MW (1<<20)
0035 #define TEA575X_BIT_BAND_LW (2<<20)
0036 #define TEA575X_BIT_BAND_SW (3<<20)
0037 #define TEA575X_BIT_PORT_0 (1<<19)
0038 #define TEA575X_BIT_PORT_1 (1<<18)
0039 #define TEA575X_BIT_SEARCH_MASK (3<<16)
0040 #define TEA575X_BIT_SEARCH_5_28 (0<<16)
0041 #define TEA575X_BIT_SEARCH_10_40 (1<<16)
0042 #define TEA575X_BIT_SEARCH_30_63 (2<<16)
0043 #define TEA575X_BIT_SEARCH_150_1000 (3<<16)
0044 #define TEA575X_BIT_DUMMY (1<<15)
0045 #define TEA575X_BIT_FREQ_MASK 0x7fff
0046
0047 enum { BAND_FM, BAND_FM_JAPAN, BAND_AM };
0048
0049 static const struct v4l2_frequency_band bands[] = {
0050 {
0051 .type = V4L2_TUNER_RADIO,
0052 .index = 0,
0053 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
0054 V4L2_TUNER_CAP_FREQ_BANDS,
0055 .rangelow = 87500 * 16,
0056 .rangehigh = 108000 * 16,
0057 .modulation = V4L2_BAND_MODULATION_FM,
0058 },
0059 {
0060 .type = V4L2_TUNER_RADIO,
0061 .index = 0,
0062 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
0063 V4L2_TUNER_CAP_FREQ_BANDS,
0064 .rangelow = 76000 * 16,
0065 .rangehigh = 91000 * 16,
0066 .modulation = V4L2_BAND_MODULATION_FM,
0067 },
0068 {
0069 .type = V4L2_TUNER_RADIO,
0070 .index = 1,
0071 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
0072 .rangelow = 530 * 16,
0073 .rangehigh = 1710 * 16,
0074 .modulation = V4L2_BAND_MODULATION_AM,
0075 },
0076 };
0077
0078
0079
0080
0081
0082 static void snd_tea575x_write(struct snd_tea575x *tea, unsigned int val)
0083 {
0084 u16 l;
0085 u8 data;
0086
0087 if (tea->ops->write_val)
0088 return tea->ops->write_val(tea, val);
0089
0090 tea->ops->set_direction(tea, 1);
0091 udelay(16);
0092
0093 for (l = 25; l > 0; l--) {
0094 data = (val >> 24) & TEA575X_DATA;
0095 val <<= 1;
0096 tea->ops->set_pins(tea, data | TEA575X_WREN);
0097 udelay(2);
0098 tea->ops->set_pins(tea, data | TEA575X_WREN | TEA575X_CLK);
0099 udelay(2);
0100 tea->ops->set_pins(tea, data | TEA575X_WREN);
0101 udelay(2);
0102 }
0103
0104 if (!tea->mute)
0105 tea->ops->set_pins(tea, 0);
0106 }
0107
0108 static u32 snd_tea575x_read(struct snd_tea575x *tea)
0109 {
0110 u16 l, rdata;
0111 u32 data = 0;
0112
0113 if (tea->ops->read_val)
0114 return tea->ops->read_val(tea);
0115
0116 tea->ops->set_direction(tea, 0);
0117 tea->ops->set_pins(tea, 0);
0118 udelay(16);
0119
0120 for (l = 24; l--;) {
0121 tea->ops->set_pins(tea, TEA575X_CLK);
0122 udelay(2);
0123 if (!l)
0124 tea->tuned = tea->ops->get_pins(tea) & TEA575X_MOST ? 0 : 1;
0125 tea->ops->set_pins(tea, 0);
0126 udelay(2);
0127 data <<= 1;
0128 rdata = tea->ops->get_pins(tea);
0129 if (!l)
0130 tea->stereo = (rdata & TEA575X_MOST) ? 0 : 1;
0131 if (rdata & TEA575X_DATA)
0132 data++;
0133 udelay(2);
0134 }
0135
0136 if (tea->mute)
0137 tea->ops->set_pins(tea, TEA575X_WREN);
0138
0139 return data;
0140 }
0141
0142 static u32 snd_tea575x_val_to_freq(struct snd_tea575x *tea, u32 val)
0143 {
0144 u32 freq = val & TEA575X_BIT_FREQ_MASK;
0145
0146 if (freq == 0)
0147 return freq;
0148
0149 switch (tea->band) {
0150 case BAND_FM:
0151
0152 freq *= 125;
0153 freq /= 10;
0154
0155 freq -= TEA575X_FMIF;
0156 break;
0157 case BAND_FM_JAPAN:
0158
0159 freq *= 125;
0160 freq /= 10;
0161
0162 freq += TEA575X_FMIF;
0163 break;
0164 case BAND_AM:
0165
0166 freq -= TEA575X_AMIF;
0167 break;
0168 }
0169
0170 return clamp(freq * 16, bands[tea->band].rangelow,
0171 bands[tea->band].rangehigh);
0172 }
0173
0174 static u32 snd_tea575x_get_freq(struct snd_tea575x *tea)
0175 {
0176 return snd_tea575x_val_to_freq(tea, snd_tea575x_read(tea));
0177 }
0178
0179 void snd_tea575x_set_freq(struct snd_tea575x *tea)
0180 {
0181 u32 freq = tea->freq / 16;
0182 u32 band = 0;
0183
0184 switch (tea->band) {
0185 case BAND_FM:
0186 band = TEA575X_BIT_BAND_FM;
0187
0188 freq += TEA575X_FMIF;
0189
0190 freq *= 10;
0191 freq /= 125;
0192 break;
0193 case BAND_FM_JAPAN:
0194 band = TEA575X_BIT_BAND_FM;
0195
0196 freq -= TEA575X_FMIF;
0197
0198 freq *= 10;
0199 freq /= 125;
0200 break;
0201 case BAND_AM:
0202 band = TEA575X_BIT_BAND_MW;
0203
0204 freq += TEA575X_AMIF;
0205 break;
0206 }
0207
0208 tea->val &= ~(TEA575X_BIT_FREQ_MASK | TEA575X_BIT_BAND_MASK);
0209 tea->val |= band;
0210 tea->val |= freq & TEA575X_BIT_FREQ_MASK;
0211 snd_tea575x_write(tea, tea->val);
0212 tea->freq = snd_tea575x_val_to_freq(tea, tea->val);
0213 }
0214 EXPORT_SYMBOL(snd_tea575x_set_freq);
0215
0216
0217
0218
0219
0220 static int vidioc_querycap(struct file *file, void *priv,
0221 struct v4l2_capability *v)
0222 {
0223 struct snd_tea575x *tea = video_drvdata(file);
0224
0225 strscpy(v->driver, tea->v4l2_dev->name, sizeof(v->driver));
0226 strscpy(v->card, tea->card, sizeof(v->card));
0227 strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card));
0228 strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
0229 return 0;
0230 }
0231
0232 int snd_tea575x_enum_freq_bands(struct snd_tea575x *tea,
0233 struct v4l2_frequency_band *band)
0234 {
0235 int index;
0236
0237 if (band->tuner != 0)
0238 return -EINVAL;
0239
0240 switch (band->index) {
0241 case 0:
0242 if (tea->tea5759)
0243 index = BAND_FM_JAPAN;
0244 else
0245 index = BAND_FM;
0246 break;
0247 case 1:
0248 if (tea->has_am) {
0249 index = BAND_AM;
0250 break;
0251 }
0252 fallthrough;
0253 default:
0254 return -EINVAL;
0255 }
0256
0257 *band = bands[index];
0258 if (!tea->cannot_read_data)
0259 band->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED;
0260
0261 return 0;
0262 }
0263 EXPORT_SYMBOL(snd_tea575x_enum_freq_bands);
0264
0265 static int vidioc_enum_freq_bands(struct file *file, void *priv,
0266 struct v4l2_frequency_band *band)
0267 {
0268 struct snd_tea575x *tea = video_drvdata(file);
0269
0270 return snd_tea575x_enum_freq_bands(tea, band);
0271 }
0272
0273 int snd_tea575x_g_tuner(struct snd_tea575x *tea, struct v4l2_tuner *v)
0274 {
0275 struct v4l2_frequency_band band_fm = { 0, };
0276
0277 if (v->index > 0)
0278 return -EINVAL;
0279
0280 snd_tea575x_read(tea);
0281 snd_tea575x_enum_freq_bands(tea, &band_fm);
0282
0283 memset(v, 0, sizeof(*v));
0284 strscpy(v->name, tea->has_am ? "FM/AM" : "FM", sizeof(v->name));
0285 v->type = V4L2_TUNER_RADIO;
0286 v->capability = band_fm.capability;
0287 v->rangelow = tea->has_am ? bands[BAND_AM].rangelow : band_fm.rangelow;
0288 v->rangehigh = band_fm.rangehigh;
0289 v->rxsubchans = tea->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
0290 v->audmode = (tea->val & TEA575X_BIT_MONO) ?
0291 V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO;
0292 v->signal = tea->tuned ? 0xffff : 0;
0293 return 0;
0294 }
0295 EXPORT_SYMBOL(snd_tea575x_g_tuner);
0296
0297 static int vidioc_g_tuner(struct file *file, void *priv,
0298 struct v4l2_tuner *v)
0299 {
0300 struct snd_tea575x *tea = video_drvdata(file);
0301
0302 return snd_tea575x_g_tuner(tea, v);
0303 }
0304
0305 static int vidioc_s_tuner(struct file *file, void *priv,
0306 const struct v4l2_tuner *v)
0307 {
0308 struct snd_tea575x *tea = video_drvdata(file);
0309 u32 orig_val = tea->val;
0310
0311 if (v->index)
0312 return -EINVAL;
0313 tea->val &= ~TEA575X_BIT_MONO;
0314 if (v->audmode == V4L2_TUNER_MODE_MONO)
0315 tea->val |= TEA575X_BIT_MONO;
0316
0317 if (tea->band != BAND_AM && tea->val != orig_val)
0318 snd_tea575x_set_freq(tea);
0319
0320 return 0;
0321 }
0322
0323 static int vidioc_g_frequency(struct file *file, void *priv,
0324 struct v4l2_frequency *f)
0325 {
0326 struct snd_tea575x *tea = video_drvdata(file);
0327
0328 if (f->tuner != 0)
0329 return -EINVAL;
0330 f->type = V4L2_TUNER_RADIO;
0331 f->frequency = tea->freq;
0332 return 0;
0333 }
0334
0335 static int vidioc_s_frequency(struct file *file, void *priv,
0336 const struct v4l2_frequency *f)
0337 {
0338 struct snd_tea575x *tea = video_drvdata(file);
0339
0340 if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
0341 return -EINVAL;
0342
0343 if (tea->has_am && f->frequency < (20000 * 16))
0344 tea->band = BAND_AM;
0345 else if (tea->tea5759)
0346 tea->band = BAND_FM_JAPAN;
0347 else
0348 tea->band = BAND_FM;
0349
0350 tea->freq = clamp_t(u32, f->frequency, bands[tea->band].rangelow,
0351 bands[tea->band].rangehigh);
0352 snd_tea575x_set_freq(tea);
0353 return 0;
0354 }
0355
0356 int snd_tea575x_s_hw_freq_seek(struct file *file, struct snd_tea575x *tea,
0357 const struct v4l2_hw_freq_seek *a)
0358 {
0359 unsigned long timeout;
0360 int i, spacing;
0361
0362 if (tea->cannot_read_data)
0363 return -ENOTTY;
0364 if (a->tuner || a->wrap_around)
0365 return -EINVAL;
0366
0367 if (file->f_flags & O_NONBLOCK)
0368 return -EWOULDBLOCK;
0369
0370 if (a->rangelow || a->rangehigh) {
0371 for (i = 0; i < ARRAY_SIZE(bands); i++) {
0372 if ((i == BAND_FM && tea->tea5759) ||
0373 (i == BAND_FM_JAPAN && !tea->tea5759) ||
0374 (i == BAND_AM && !tea->has_am))
0375 continue;
0376 if (bands[i].rangelow == a->rangelow &&
0377 bands[i].rangehigh == a->rangehigh)
0378 break;
0379 }
0380 if (i == ARRAY_SIZE(bands))
0381 return -EINVAL;
0382 if (i != tea->band) {
0383 tea->band = i;
0384 tea->freq = clamp(tea->freq, bands[i].rangelow,
0385 bands[i].rangehigh);
0386 snd_tea575x_set_freq(tea);
0387 }
0388 }
0389
0390 spacing = (tea->band == BAND_AM) ? 5 : 50;
0391
0392
0393 tea->val &= ~TEA575X_BIT_FREQ_MASK;
0394 tea->val |= TEA575X_BIT_SEARCH;
0395 if (a->seek_upward)
0396 tea->val |= TEA575X_BIT_UPDOWN;
0397 else
0398 tea->val &= ~TEA575X_BIT_UPDOWN;
0399 snd_tea575x_write(tea, tea->val);
0400 timeout = jiffies + msecs_to_jiffies(10000);
0401 for (;;) {
0402 if (time_after(jiffies, timeout))
0403 break;
0404 if (schedule_timeout_interruptible(msecs_to_jiffies(10))) {
0405
0406 tea->val &= ~TEA575X_BIT_SEARCH;
0407 snd_tea575x_set_freq(tea);
0408 return -ERESTARTSYS;
0409 }
0410 if (!(snd_tea575x_read(tea) & TEA575X_BIT_SEARCH)) {
0411 u32 freq;
0412
0413
0414 for (i = 0; i < 100; i++) {
0415 msleep(10);
0416 freq = snd_tea575x_get_freq(tea);
0417 if (freq)
0418 break;
0419 }
0420 if (freq == 0)
0421 break;
0422
0423
0424
0425
0426 if (abs(tea->freq - freq) < 16 * spacing ||
0427 (a->seek_upward && freq < tea->freq) ||
0428 (!a->seek_upward && freq > tea->freq)) {
0429 snd_tea575x_write(tea, tea->val);
0430 continue;
0431 }
0432 tea->freq = freq;
0433 tea->val &= ~TEA575X_BIT_SEARCH;
0434 return 0;
0435 }
0436 }
0437 tea->val &= ~TEA575X_BIT_SEARCH;
0438 snd_tea575x_set_freq(tea);
0439 return -ENODATA;
0440 }
0441 EXPORT_SYMBOL(snd_tea575x_s_hw_freq_seek);
0442
0443 static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
0444 const struct v4l2_hw_freq_seek *a)
0445 {
0446 struct snd_tea575x *tea = video_drvdata(file);
0447
0448 return snd_tea575x_s_hw_freq_seek(file, tea, a);
0449 }
0450
0451 static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
0452 {
0453 struct snd_tea575x *tea = container_of(ctrl->handler, struct snd_tea575x, ctrl_handler);
0454
0455 switch (ctrl->id) {
0456 case V4L2_CID_AUDIO_MUTE:
0457 tea->mute = ctrl->val;
0458 snd_tea575x_set_freq(tea);
0459 return 0;
0460 }
0461
0462 return -EINVAL;
0463 }
0464
0465 static const struct v4l2_file_operations tea575x_fops = {
0466 .unlocked_ioctl = video_ioctl2,
0467 .open = v4l2_fh_open,
0468 .release = v4l2_fh_release,
0469 .poll = v4l2_ctrl_poll,
0470 };
0471
0472 static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
0473 .vidioc_querycap = vidioc_querycap,
0474 .vidioc_g_tuner = vidioc_g_tuner,
0475 .vidioc_s_tuner = vidioc_s_tuner,
0476 .vidioc_g_frequency = vidioc_g_frequency,
0477 .vidioc_s_frequency = vidioc_s_frequency,
0478 .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek,
0479 .vidioc_enum_freq_bands = vidioc_enum_freq_bands,
0480 .vidioc_log_status = v4l2_ctrl_log_status,
0481 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
0482 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
0483 };
0484
0485 static const struct video_device tea575x_radio = {
0486 .ioctl_ops = &tea575x_ioctl_ops,
0487 .release = video_device_release_empty,
0488 };
0489
0490 static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
0491 .s_ctrl = tea575x_s_ctrl,
0492 };
0493
0494
0495 int snd_tea575x_hw_init(struct snd_tea575x *tea)
0496 {
0497 tea->mute = true;
0498
0499
0500
0501 if (!tea->cannot_read_data) {
0502 snd_tea575x_write(tea, 0x55AA);
0503 if (snd_tea575x_read(tea) != 0x55AA)
0504 return -ENODEV;
0505 }
0506
0507 tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_5_28;
0508 tea->freq = 90500 * 16;
0509 snd_tea575x_set_freq(tea);
0510
0511 return 0;
0512 }
0513 EXPORT_SYMBOL(snd_tea575x_hw_init);
0514
0515 int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner)
0516 {
0517 int retval = snd_tea575x_hw_init(tea);
0518
0519 if (retval)
0520 return retval;
0521
0522 tea->vd = tea575x_radio;
0523 video_set_drvdata(&tea->vd, tea);
0524 mutex_init(&tea->mutex);
0525 strscpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name));
0526 tea->vd.lock = &tea->mutex;
0527 tea->vd.v4l2_dev = tea->v4l2_dev;
0528 tea->vd.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
0529 if (!tea->cannot_read_data)
0530 tea->vd.device_caps |= V4L2_CAP_HW_FREQ_SEEK;
0531 tea->fops = tea575x_fops;
0532 tea->fops.owner = owner;
0533 tea->vd.fops = &tea->fops;
0534
0535 if (tea->cannot_read_data)
0536 v4l2_disable_ioctl(&tea->vd, VIDIOC_S_HW_FREQ_SEEK);
0537
0538 if (!tea->cannot_mute) {
0539 tea->vd.ctrl_handler = &tea->ctrl_handler;
0540 v4l2_ctrl_handler_init(&tea->ctrl_handler, 1);
0541 v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops,
0542 V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
0543 retval = tea->ctrl_handler.error;
0544 if (retval) {
0545 v4l2_err(tea->v4l2_dev, "can't initialize controls\n");
0546 v4l2_ctrl_handler_free(&tea->ctrl_handler);
0547 return retval;
0548 }
0549
0550 if (tea->ext_init) {
0551 retval = tea->ext_init(tea);
0552 if (retval) {
0553 v4l2_ctrl_handler_free(&tea->ctrl_handler);
0554 return retval;
0555 }
0556 }
0557
0558 v4l2_ctrl_handler_setup(&tea->ctrl_handler);
0559 }
0560
0561 retval = video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->radio_nr);
0562 if (retval) {
0563 v4l2_err(tea->v4l2_dev, "can't register video device!\n");
0564 v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
0565 return retval;
0566 }
0567
0568 return 0;
0569 }
0570 EXPORT_SYMBOL(snd_tea575x_init);
0571
0572 void snd_tea575x_exit(struct snd_tea575x *tea)
0573 {
0574 video_unregister_device(&tea->vd);
0575 v4l2_ctrl_handler_free(tea->vd.ctrl_handler);
0576 }
0577 EXPORT_SYMBOL(snd_tea575x_exit);