Merge branch 'nfs-for-2.6.40' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6
[firefly-linux-kernel-4.4.55.git] / sound / i2c / other / tea575x-tuner.c
index ee538f1ae8462f0de200d66fa861009fae537a43..4831800239d3b786081de5b17b06bff2e2d8caa9 100644 (file)
@@ -37,8 +37,8 @@ static int radio_nr = -1;
 module_param(radio_nr, int, 0);
 
 #define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-#define FREQ_LO                 (87 * 16000)
-#define FREQ_HI                (108 * 16000)
+#define FREQ_LO                 (50UL * 16000)
+#define FREQ_HI                (150UL * 16000)
 
 /*
  * definitions
@@ -77,27 +77,95 @@ static struct v4l2_queryctrl radio_qctrl[] = {
  * lowlevel part
  */
 
+static void snd_tea575x_write(struct snd_tea575x *tea, unsigned int val)
+{
+       u16 l;
+       u8 data;
+
+       tea->ops->set_direction(tea, 1);
+       udelay(16);
+
+       for (l = 25; l > 0; l--) {
+               data = (val >> 24) & TEA575X_DATA;
+               val <<= 1;                      /* shift data */
+               tea->ops->set_pins(tea, data | TEA575X_WREN);
+               udelay(2);
+               tea->ops->set_pins(tea, data | TEA575X_WREN | TEA575X_CLK);
+               udelay(2);
+               tea->ops->set_pins(tea, data | TEA575X_WREN);
+               udelay(2);
+       }
+
+       if (!tea->mute)
+               tea->ops->set_pins(tea, 0);
+}
+
+static unsigned int snd_tea575x_read(struct snd_tea575x *tea)
+{
+       u16 l, rdata;
+       u32 data = 0;
+
+       tea->ops->set_direction(tea, 0);
+       tea->ops->set_pins(tea, 0);
+       udelay(16);
+
+       for (l = 24; l--;) {
+               tea->ops->set_pins(tea, TEA575X_CLK);
+               udelay(2);
+               if (!l)
+                       tea->tuned = tea->ops->get_pins(tea) & TEA575X_MOST ? 0 : 1;
+               tea->ops->set_pins(tea, 0);
+               udelay(2);
+               data <<= 1;                     /* shift data */
+               rdata = tea->ops->get_pins(tea);
+               if (!l)
+                       tea->stereo = (rdata & TEA575X_MOST) ?  0 : 1;
+               if (rdata & TEA575X_DATA)
+                       data++;
+               udelay(2);
+       }
+
+       if (tea->mute)
+               tea->ops->set_pins(tea, TEA575X_WREN);
+
+       return data;
+}
+
+static void snd_tea575x_get_freq(struct snd_tea575x *tea)
+{
+       unsigned long freq;
+
+       freq = snd_tea575x_read(tea) & TEA575X_BIT_FREQ_MASK;
+       /* freq *= 12.5 */
+       freq *= 125;
+       freq /= 10;
+       /* crystal fixup */
+       if (tea->tea5759)
+               freq += TEA575X_FMIF;
+       else
+               freq -= TEA575X_FMIF;
+
+       tea->freq = freq * 16;          /* from kHz */
+}
+
 static void snd_tea575x_set_freq(struct snd_tea575x *tea)
 {
        unsigned long freq;
 
-       freq = tea->freq / 16;          /* to kHz */
-       if (freq > 108000)
-               freq = 108000;
-       if (freq < 87000)
-               freq = 87000;
+       freq = clamp(tea->freq, FREQ_LO, FREQ_HI);
+       freq /= 16;             /* to kHz */
        /* crystal fixup */
        if (tea->tea5759)
-               freq -= tea->freq_fixup;
+               freq -= TEA575X_FMIF;
        else
-               freq += tea->freq_fixup;
+               freq += TEA575X_FMIF;
        /* freq /= 12.5 */
        freq *= 10;
        freq /= 125;
 
        tea->val &= ~TEA575X_BIT_FREQ_MASK;
        tea->val |= freq & TEA575X_BIT_FREQ_MASK;
-       tea->ops->write(tea, tea->val);
+       snd_tea575x_write(tea, tea->val);
 }
 
 /*
@@ -109,29 +177,34 @@ static int vidioc_querycap(struct file *file, void  *priv,
 {
        struct snd_tea575x *tea = video_drvdata(file);
 
-       strcpy(v->card, tea->tea5759 ? "TEA5759" : "TEA5757");
        strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver));
-       strlcpy(v->card, "Maestro Radio", sizeof(v->card));
-       sprintf(v->bus_info, "PCI");
+       strlcpy(v->card, tea->card, sizeof(v->card));
+       strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card));
+       strlcpy(v->bus_info, tea->bus_info, sizeof(v->bus_info));
        v->version = RADIO_VERSION;
-       v->capabilities = V4L2_CAP_TUNER;
+       v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
        return 0;
 }
 
 static int vidioc_g_tuner(struct file *file, void *priv,
                                        struct v4l2_tuner *v)
 {
+       struct snd_tea575x *tea = video_drvdata(file);
+
        if (v->index > 0)
                return -EINVAL;
 
+       snd_tea575x_read(tea);
+
        strcpy(v->name, "FM");
        v->type = V4L2_TUNER_RADIO;
+       v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
        v->rangelow = FREQ_LO;
        v->rangehigh = FREQ_HI;
-       v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
-       v->capability = V4L2_TUNER_CAP_LOW;
-       v->audmode = V4L2_TUNER_MODE_MONO;
-       v->signal = 0xffff;
+       v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+       v->audmode = tea->stereo ? V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO;
+       v->signal = tea->tuned ? 0xffff : 0;
+
        return 0;
 }
 
@@ -148,7 +221,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
 {
        struct snd_tea575x *tea = video_drvdata(file);
 
+       if (f->tuner != 0)
+               return -EINVAL;
        f->type = V4L2_TUNER_RADIO;
+       snd_tea575x_get_freq(tea);
        f->frequency = tea->freq;
        return 0;
 }
@@ -158,6 +234,9 @@ static int vidioc_s_frequency(struct file *file, void *priv,
 {
        struct snd_tea575x *tea = video_drvdata(file);
 
+       if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+               return -EINVAL;
+
        if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
                return -EINVAL;
 
@@ -209,10 +288,8 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
 
        switch (ctrl->id) {
        case V4L2_CID_AUDIO_MUTE:
-               if (tea->ops->mute) {
-                       ctrl->value = tea->mute;
-                       return 0;
-               }
+               ctrl->value = tea->mute;
+               return 0;
        }
        return -EINVAL;
 }
@@ -224,11 +301,11 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
 
        switch (ctrl->id) {
        case V4L2_CID_AUDIO_MUTE:
-               if (tea->ops->mute) {
-                       tea->ops->mute(tea, ctrl->value);
+               if (tea->mute != ctrl->value) {
                        tea->mute = ctrl->value;
-                       return 0;
+                       snd_tea575x_set_freq(tea);
                }
+               return 0;
        }
        return -EINVAL;
 }
@@ -293,18 +370,16 @@ static struct video_device tea575x_radio = {
 /*
  * initialize all the tea575x chips
  */
-void snd_tea575x_init(struct snd_tea575x *tea)
+int snd_tea575x_init(struct snd_tea575x *tea)
 {
        int retval;
-       unsigned int val;
        struct video_device *tea575x_radio_inst;
 
-       val = tea->ops->read(tea);
-       if (val == 0x1ffffff || val == 0) {
-               snd_printk(KERN_ERR
-                          "tea575x-tuner: Cannot find TEA575x chip\n");
-               return;
-       }
+       tea->mute = 1;
+
+       snd_tea575x_write(tea, 0x55AA);
+       if (snd_tea575x_read(tea) != 0x55AA)
+               return -ENODEV;
 
        tea->in_use = 0;
        tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
@@ -313,7 +388,7 @@ void snd_tea575x_init(struct snd_tea575x *tea)
        tea575x_radio_inst = video_device_alloc();
        if (tea575x_radio_inst == NULL) {
                printk(KERN_ERR "tea575x-tuner: not enough memory\n");
-               return;
+               return -ENOMEM;
        }
 
        memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
@@ -328,17 +403,13 @@ void snd_tea575x_init(struct snd_tea575x *tea)
        if (retval) {
                printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
                kfree(tea575x_radio_inst);
-               return;
+               return retval;
        }
 
        snd_tea575x_set_freq(tea);
-
-       /* mute on init */
-       if (tea->ops->mute) {
-               tea->ops->mute(tea, 1);
-               tea->mute = 1;
-       }
        tea->vd = tea575x_radio_inst;
+
+       return 0;
 }
 
 void snd_tea575x_exit(struct snd_tea575x *tea)