Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *   ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips
0004  *
0005  *  Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
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  * definitions
0027  */
0028 
0029 #define TEA575X_BIT_SEARCH  (1<<24)     /* 1 = search action, 0 = tuned */
0030 #define TEA575X_BIT_UPDOWN  (1<<23)     /* 0 = search down, 1 = search up */
0031 #define TEA575X_BIT_MONO    (1<<22)     /* 0 = stereo, 1 = mono */
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)     /* user bit */
0038 #define TEA575X_BIT_PORT_1  (1<<18)     /* user bit */
0039 #define TEA575X_BIT_SEARCH_MASK (3<<16)     /* search level */
0040 #define TEA575X_BIT_SEARCH_5_28      (0<<16)    /* FM >5uV, AM >28uV */
0041 #define TEA575X_BIT_SEARCH_10_40     (1<<16)    /* FM >10uV, AM > 40uV */
0042 #define TEA575X_BIT_SEARCH_30_63     (2<<16)    /* FM >30uV, AM > 63uV */
0043 #define TEA575X_BIT_SEARCH_150_1000  (3<<16)    /* FM > 150uV, AM > 1000uV */
0044 #define TEA575X_BIT_DUMMY   (1<<15)     /* buffer */
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  * lowlevel part
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;          /* shift data */
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;         /* shift data */
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         /* freq *= 12.5 */
0152         freq *= 125;
0153         freq /= 10;
0154         /* crystal fixup */
0155         freq -= TEA575X_FMIF;
0156         break;
0157     case BAND_FM_JAPAN:
0158         /* freq *= 12.5 */
0159         freq *= 125;
0160         freq /= 10;
0161         /* crystal fixup */
0162         freq += TEA575X_FMIF;
0163         break;
0164     case BAND_AM:
0165         /* crystal fixup */
0166         freq -= TEA575X_AMIF;
0167         break;
0168     }
0169 
0170     return clamp(freq * 16, bands[tea->band].rangelow,
0171                 bands[tea->band].rangehigh); /* from kHz */
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;  /* to kHz */
0182     u32 band = 0;
0183 
0184     switch (tea->band) {
0185     case BAND_FM:
0186         band = TEA575X_BIT_BAND_FM;
0187         /* crystal fixup */
0188         freq += TEA575X_FMIF;
0189         /* freq /= 12.5 */
0190         freq *= 10;
0191         freq /= 125;
0192         break;
0193     case BAND_FM_JAPAN:
0194         band = TEA575X_BIT_BAND_FM;
0195         /* crystal fixup */
0196         freq -= TEA575X_FMIF;
0197         /* freq /= 12.5 */
0198         freq *= 10;
0199         freq /= 125;
0200         break;
0201     case BAND_AM:
0202         band = TEA575X_BIT_BAND_MW;
0203         /* crystal fixup */
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  * Linux Video interface
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     /* Only apply changes if currently tuning FM */
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; /* No matching band found */
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; /* kHz */
0391 
0392     /* clear the frequency, HW will fill it in */
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             /* some signal arrived, stop search */
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             /* Found a frequency, wait until it can be read */
0414             for (i = 0; i < 100; i++) {
0415                 msleep(10);
0416                 freq = snd_tea575x_get_freq(tea);
0417                 if (freq) /* available */
0418                     break;
0419             }
0420             if (freq == 0) /* shouldn't happen */
0421                 break;
0422             /*
0423              * if we moved by less than the spacing, or in the
0424              * wrong direction, continue seeking
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     /* Not all devices can or know how to read the data back.
0500        Such devices can set cannot_read_data to true. */
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;     /* 90.5Mhz default */
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     /* disable hw_freq_seek if we can't use it */
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);