/* * si470x_set_chan - set the channel */ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) { int retval; unsigned long timeout; bool timed_out = 0; /* start tuning */ radio->registers[CHANNEL] &= ~CHANNEL_CHAN; radio->registers[CHANNEL] |= CHANNEL_TUNE | chan; retval = si470x_set_register(radio, CHANNEL); if (retval < 0) return retval; /* wait till seek operation has completed */ timeout = jiffies + msecs_to_jiffies(tune_timeout); do { retval = si470x_get_register(radio, STATUSRSSI); if (retval < 0) return retval; timed_out = time_after(jiffies, timeout); } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && (!timed_out)); if (timed_out) printk(KERN_WARNING DRIVER_NAME ": seek does not finish after %u ms\n", tune_timeout); /* stop tuning */ radio->registers[CHANNEL] &= ~CHANNEL_TUNE; return si470x_set_register(radio, CHANNEL); }
/* * si470x_vidioc_g_tuner - get tuner attributes */ static int si470x_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { struct si470x_device *radio = video_drvdata(file); int retval = 0; if (tuner->index != 0) return -EINVAL; if (!radio->status_rssi_auto_update) { retval = si470x_get_register(radio, STATUSRSSI); if (retval < 0) return retval; } /* driver constants */ strcpy(tuner->name, "FM"); tuner->type = V4L2_TUNER_RADIO; tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO | V4L2_TUNER_CAP_HWSEEK_BOUNDED | V4L2_TUNER_CAP_HWSEEK_WRAP; tuner->rangelow = 76 * FREQ_MUL; tuner->rangehigh = 108 * FREQ_MUL; /* stereo indicator == stereo (instead of mono) */ if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0) tuner->rxsubchans = V4L2_TUNER_SUB_MONO; else tuner->rxsubchans = V4L2_TUNER_SUB_STEREO; /* If there is a reliable method of detecting an RDS channel, then this code should check for that before setting this RDS subchannel. */ tuner->rxsubchans |= V4L2_TUNER_SUB_RDS; /* mono/stereo selector */ if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0) tuner->audmode = V4L2_TUNER_MODE_STEREO; else tuner->audmode = V4L2_TUNER_MODE_MONO; /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */ /* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */ tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI); /* the ideal factor is 0xffff/75 = 873,8 */ tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10); if (tuner->signal > 0xffff) tuner->signal = 0xffff; /* automatic frequency control: -1: freq to low, 1 freq to high */ /* AFCRL does only indicate that freq. differs, not if too low/high */ tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0; return retval; }
/* * si470x_vidioc_g_tuner - get tuner attributes */ static int si470x_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); int retval; if (radio->disconnected) return -EIO; if (tuner->index > 0) return -EINVAL; /* read status rssi */ retval = si470x_get_register(radio, STATUSRSSI); if (retval < 0) return retval; strcpy(tuner->name, "FM"); tuner->type = V4L2_TUNER_RADIO; switch (band) { /* 0: 87.5 - 108 MHz (USA, Europe, default) */ default: tuner->rangelow = 87.5 * FREQ_MUL; tuner->rangehigh = 108 * FREQ_MUL; break; /* 1: 76 - 108 MHz (Japan wide band) */ case 1 : tuner->rangelow = 76 * FREQ_MUL; tuner->rangehigh = 108 * FREQ_MUL; break; /* 2: 76 - 90 MHz (Japan) */ case 2 : tuner->rangelow = 76 * FREQ_MUL; tuner->rangehigh = 90 * FREQ_MUL; break; }; tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; tuner->capability = V4L2_TUNER_CAP_LOW; /* Stereo indicator == Stereo (instead of Mono) */ if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 1) tuner->audmode = V4L2_TUNER_MODE_STEREO; else tuner->audmode = V4L2_TUNER_MODE_MONO; /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */ tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI) * 0x0101; /* automatic frequency control: -1: freq to low, 1 freq to high */ tuner->afc = 0; return 0; }
/* * si470x_get_freq - get the frequency */ static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq) { int chan, retval; /* read channel */ retval = si470x_get_register(radio, READCHAN); chan = radio->registers[READCHAN] & READCHAN_READCHAN; /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */ *freq = chan * si470x_get_step(radio) + bands[radio->band].rangelow; return retval; }
/* * si470x_set_seek - set seek */ static int si470x_set_seek(struct si470x_device *radio, unsigned int wrap_around, unsigned int seek_upward) { int retval = 0; unsigned long timeout; bool timed_out = 0; /* start seeking */ radio->registers[POWERCFG] |= POWERCFG_SEEK; if (wrap_around == 1) radio->registers[POWERCFG] &= ~POWERCFG_SKMODE; else radio->registers[POWERCFG] |= POWERCFG_SKMODE; if (seek_upward == 1) radio->registers[POWERCFG] |= POWERCFG_SEEKUP; else radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP; retval = si470x_set_register(radio, POWERCFG); if (retval < 0) goto done; /* wait till seek operation has completed */ timeout = jiffies + msecs_to_jiffies(seek_timeout); do { retval = si470x_get_register(radio, STATUSRSSI); if (retval < 0) goto stop; timed_out = time_after(jiffies, timeout); } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && (!timed_out)); if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) printk(KERN_WARNING DRIVER_NAME ": seek does not complete\n"); if (radio->registers[STATUSRSSI] & STATUSRSSI_SF) printk(KERN_WARNING DRIVER_NAME ": seek failed / band limit reached\n"); if (timed_out) printk(KERN_WARNING DRIVER_NAME ": seek timed out after %u ms\n", seek_timeout); stop: /* stop seeking */ radio->registers[POWERCFG] &= ~POWERCFG_SEEK; retval = si470x_set_register(radio, POWERCFG); done: /* try again, if timed out */ if ((retval == 0) && timed_out) retval = -EAGAIN; return retval; }
/* * si470x_set_chan - set the channel */ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) { int retval; unsigned long timeout; bool timed_out = 0; /* start tuning */ radio->registers[CHANNEL] &= ~CHANNEL_CHAN; radio->registers[CHANNEL] |= CHANNEL_TUNE | chan; retval = si470x_set_register(radio, CHANNEL); if (retval < 0) goto done; /* currently I2C driver only uses interrupt way to tune */ if (radio->stci_enabled) { INIT_COMPLETION(radio->completion); /* wait till tune operation has completed */ retval = wait_for_completion_timeout(&radio->completion, msecs_to_jiffies(tune_timeout)); if (!retval) timed_out = true; } else { /* wait till tune operation has completed */ timeout = jiffies + msecs_to_jiffies(tune_timeout); do { retval = si470x_get_register(radio, STATUSRSSI); if (retval < 0) goto stop; timed_out = time_after(jiffies, timeout); } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && (!timed_out)); } if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) dev_warn(&radio->videodev->dev, "tune does not complete\n"); if (timed_out) dev_warn(&radio->videodev->dev, "tune timed out after %u ms\n", tune_timeout); stop: /* stop tuning */ radio->registers[CHANNEL] &= ~CHANNEL_TUNE; retval = si470x_set_register(radio, CHANNEL); done: return retval; }
/* * si470x_get_freq - get the frequency */ static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq) { unsigned int spacing, band_bottom; unsigned short chan; int retval; /* Spacing (kHz) */ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) { /* 0: 200 kHz (USA, Australia) */ case 0: spacing = 0.200 * FREQ_MUL; break; /* 1: 100 kHz (Europe, Japan) */ case 1: spacing = 0.100 * FREQ_MUL; break; /* 2: 50 kHz */ default: spacing = 0.050 * FREQ_MUL; break; }; /* Bottom of Band (MHz) */ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { /* 0: 87.5 - 108 MHz (USA, Europe) */ case 0: band_bottom = 87.5 * FREQ_MUL; break; /* 1: 76 - 108 MHz (Japan wide band) */ default: band_bottom = 76 * FREQ_MUL; break; /* 2: 76 - 90 MHz (Japan) */ case 2: band_bottom = 76 * FREQ_MUL; break; }; /* read channel */ retval = si470x_get_register(radio, READCHAN); chan = radio->registers[READCHAN] & READCHAN_READCHAN; /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */ *freq = chan * spacing + band_bottom; return retval; }
/* * si470x_set_chan - set the channel */ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) { int retval; unsigned long timeout; bool timed_out = 0; /* start tuning */ radio->registers[CHANNEL] &= ~CHANNEL_CHAN; radio->registers[CHANNEL] |= CHANNEL_TUNE | chan; retval = si470x_set_register(radio, CHANNEL); if (retval < 0) goto done; /* wait till tune operation has completed */ timeout = jiffies + msecs_to_jiffies(tune_timeout); do { retval = si470x_get_register(radio, STATUSRSSI); if (retval < 0) goto stop; timed_out = time_after(jiffies, timeout); } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && (!timed_out)); if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) dev_warn(&radio->videodev->dev, "tune does not complete\n"); if (timed_out) dev_warn(&radio->videodev->dev, "tune timed out after %u ms\n", tune_timeout); stop: /* stop tuning */ radio->registers[CHANNEL] &= ~CHANNEL_TUNE; retval = si470x_set_register(radio, CHANNEL); done: return retval; }
/* * si470x_i2c_interrupt - interrupt handler */ static irqreturn_t si470x_i2c_interrupt(int irq, void *dev_id) { struct si470x_device *radio = dev_id; unsigned char regnr; unsigned char blocknum; unsigned short bler; /* rds block errors */ unsigned short rds; unsigned char tmpbuf[3]; int retval = 0; /* check Seek/Tune Complete */ retval = si470x_get_register(radio, STATUSRSSI); if (retval < 0) goto end; if (radio->registers[STATUSRSSI] & STATUSRSSI_STC) complete(&radio->completion); /* safety checks */ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) goto end; /* Update RDS registers */ for (regnr = 1; regnr < RDS_REGISTER_NUM; regnr++) { retval = si470x_get_register(radio, STATUSRSSI + regnr); if (retval < 0) goto end; } /* get rds blocks */ if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) /* No RDS group ready, better luck next time */ goto end; for (blocknum = 0; blocknum < 4; blocknum++) { switch (blocknum) { default: bler = (radio->registers[STATUSRSSI] & STATUSRSSI_BLERA) >> 9; rds = radio->registers[RDSA]; break; case 1: bler = (radio->registers[READCHAN] & READCHAN_BLERB) >> 14; rds = radio->registers[RDSB]; break; case 2: bler = (radio->registers[READCHAN] & READCHAN_BLERC) >> 12; rds = radio->registers[RDSC]; break; case 3: bler = (radio->registers[READCHAN] & READCHAN_BLERD) >> 10; rds = radio->registers[RDSD]; break; }; /* Fill the V4L2 RDS buffer */ put_unaligned_le16(rds, &tmpbuf); tmpbuf[2] = blocknum; /* offset name */ tmpbuf[2] |= blocknum << 3; /* received offset */ if (bler > max_rds_errors) tmpbuf[2] |= 0x80; /* uncorrectable errors */ else if (bler > 0) tmpbuf[2] |= 0x40; /* corrected error(s) */ /* copy RDS block to internal buffer */ memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3); radio->wr_index += 3; /* wrap write pointer */ if (radio->wr_index >= radio->buf_size) radio->wr_index = 0; /* check for overflow */ if (radio->wr_index == radio->rd_index) { /* increment and wrap read pointer */ radio->rd_index += 3; if (radio->rd_index >= radio->buf_size) radio->rd_index = 0; } } if (radio->wr_index != radio->rd_index) wake_up_interruptible(&radio->read_queue); end: return IRQ_HANDLED; }
/* * si470x_vidioc_g_tuner - get tuner attributes */ static int si470x_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { struct si470x_device *radio = video_drvdata(file); int retval = 0; /* safety checks */ if (radio->disconnected) { retval = -EIO; goto done; } if (tuner->index != 0) { retval = -EINVAL; goto done; } retval = si470x_get_register(radio, STATUSRSSI); if (retval < 0) goto done; /* driver constants */ strcpy(tuner->name, "FM"); tuner->type = V4L2_TUNER_RADIO; tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; /* range limits */ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { /* 0: 87.5 - 108 MHz (USA, Europe, default) */ default: tuner->rangelow = 87.5 * FREQ_MUL; tuner->rangehigh = 108 * FREQ_MUL; break; /* 1: 76 - 108 MHz (Japan wide band) */ case 1 : tuner->rangelow = 76 * FREQ_MUL; tuner->rangehigh = 108 * FREQ_MUL; break; /* 2: 76 - 90 MHz (Japan) */ case 2 : tuner->rangelow = 76 * FREQ_MUL; tuner->rangehigh = 90 * FREQ_MUL; break; }; /* stereo indicator == stereo (instead of mono) */ if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0) tuner->rxsubchans = V4L2_TUNER_SUB_MONO; else tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; /* mono/stereo selector */ if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0) tuner->audmode = V4L2_TUNER_MODE_STEREO; else tuner->audmode = V4L2_TUNER_MODE_MONO; /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */ /* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */ tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI); /* the ideal factor is 0xffff/75 = 873,8 */ tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10); /* automatic frequency control: -1: freq to low, 1 freq to high */ /* AFCRL does only indicate that freq. differs, not if too low/high */ tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0; done: if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": get tuner failed with %d\n", retval); return retval; }
/* * si470x_set_seek - set seek */ static int si470x_set_seek(struct si470x_device *radio, unsigned int wrap_around, unsigned int seek_upward) { int retval = 0; unsigned long timeout; bool timed_out = 0; /* start seeking */ radio->registers[POWERCFG] |= POWERCFG_SEEK; if (wrap_around == 1) radio->registers[POWERCFG] &= ~POWERCFG_SKMODE; else radio->registers[POWERCFG] |= POWERCFG_SKMODE; if (seek_upward == 1) radio->registers[POWERCFG] |= POWERCFG_SEEKUP; else radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP; retval = si470x_set_register(radio, POWERCFG); if (retval < 0) goto done; /* currently I2C driver only uses interrupt way to seek */ if (radio->stci_enabled) { INIT_COMPLETION(radio->completion); /* wait till seek operation has completed */ retval = wait_for_completion_timeout(&radio->completion, msecs_to_jiffies(seek_timeout)); if (!retval) timed_out = true; } else { /* wait till seek operation has completed */ timeout = jiffies + msecs_to_jiffies(seek_timeout); do { retval = si470x_get_register(radio, STATUSRSSI); if (retval < 0) goto stop; timed_out = time_after(jiffies, timeout); } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && (!timed_out)); } if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) dev_warn(&radio->videodev->dev, "seek does not complete\n"); if (radio->registers[STATUSRSSI] & STATUSRSSI_SF) dev_warn(&radio->videodev->dev, "seek failed / band limit reached\n"); if (timed_out) dev_warn(&radio->videodev->dev, "seek timed out after %u ms\n", seek_timeout); stop: /* stop seeking */ radio->registers[POWERCFG] &= ~POWERCFG_SEEK; retval = si470x_set_register(radio, POWERCFG); done: /* try again, if timed out */ if ((retval == 0) && timed_out) retval = -EAGAIN; return retval; }
/* * si470x_vidioc_g_tuner - get tuner attributes */ static int si470x_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { struct si470x_device *radio = video_drvdata(file); int retval = 0; mutex_lock(&radio->lock); /* safety checks */ retval = si470x_disconnect_check(radio); if (retval) goto done; if (tuner->index != 0) { retval = -EINVAL; goto done; } retval = si470x_get_register(radio, STATUSRSSI); if (retval < 0) goto done; /* driver constants */ strcpy(tuner->name, "FM"); tuner->type = V4L2_TUNER_RADIO; #if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE) tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO; #else tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; #endif /* range limits */ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { /* 0: 87.5 - 108 MHz (USA, Europe, default) */ default: tuner->rangelow = 87.5 * FREQ_MUL; tuner->rangehigh = 108 * FREQ_MUL; break; /* 1: 76 - 108 MHz (Japan wide band) */ case 1: tuner->rangelow = 76 * FREQ_MUL; tuner->rangehigh = 108 * FREQ_MUL; break; /* 2: 76 - 90 MHz (Japan) */ case 2: tuner->rangelow = 76 * FREQ_MUL; tuner->rangehigh = 90 * FREQ_MUL; break; }; /* stereo indicator == stereo (instead of mono) */ if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0) tuner->rxsubchans = V4L2_TUNER_SUB_MONO; else tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; #if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE) /* If there is a reliable method of detecting an RDS channel, then this code should check for that before setting this RDS subchannel. */ tuner->rxsubchans |= V4L2_TUNER_SUB_RDS; #endif /* mono/stereo selector */ if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0) tuner->audmode = V4L2_TUNER_MODE_STEREO; else tuner->audmode = V4L2_TUNER_MODE_MONO; /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */ /* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */ tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI); /* the ideal factor is 0xffff/75 = 873,8 */ tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10); /* automatic frequency control: -1: freq to low, 1 freq to high */ /* AFCRL does only indicate that freq. differs, not if too low/high */ tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0; done: if (retval < 0) dev_warn(&radio->videodev->dev, "get tuner failed with %d\n", retval); mutex_unlock(&radio->lock); return retval; }