/* * si470x_vidioc_s_frequency - set tuner or modulator radio frequency */ static int si470x_vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *freq) { 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 (freq->tuner != 0) { retval = -EINVAL; goto done; } retval = si470x_set_freq(radio, freq->frequency); done: if (retval < 0) dev_warn(&radio->videodev->dev, "set frequency failed with %d\n", retval); mutex_unlock(&radio->lock); return retval; }
/* * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek */ static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, struct v4l2_hw_freq_seek *seek) { 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 (seek->tuner != 0) { retval = -EINVAL; goto done; } retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward); done: if (retval < 0) dev_warn(&radio->videodev->dev, "set hardware frequency seek failed with %d\n", retval); mutex_unlock(&radio->lock); return retval; }
/* * si470x_vidioc_g_ctrl - get the value of a control */ static int si470x_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { 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; switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: ctrl->value = radio->registers[SYSCONFIG2] & SYSCONFIG2_VOLUME; break; case V4L2_CID_AUDIO_MUTE: ctrl->value = ((radio->registers[POWERCFG] & POWERCFG_DMUTE) == 0) ? 1 : 0; break; default: retval = -EINVAL; } done: if (retval < 0) dev_warn(&radio->videodev->dev, "get control failed with %d\n", retval); mutex_unlock(&radio->lock); return retval; }
/* * si470x_vidioc_g_frequency - get tuner or modulator radio frequency */ static int si470x_vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *freq) { struct si470x_device *radio = video_drvdata(file); int retval = 0; /* safety checks */ retval = si470x_disconnect_check(radio); if (retval) goto done; if (freq->tuner != 0) { retval = -EINVAL; goto done; } freq->type = V4L2_TUNER_RADIO; retval = si470x_get_freq(radio, &freq->frequency); done: if (retval < 0) dev_warn(&radio->videodev->dev, "get frequency failed with %d\n", retval); return retval; }
/* * si470x_vidioc_s_tuner - set tuner attributes */ static int si470x_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { struct si470x_device *radio = video_drvdata(file); int retval = -EINVAL; /* safety checks */ retval = si470x_disconnect_check(radio); if (retval) goto done; if (tuner->index != 0) goto done; /* mono/stereo selector */ switch (tuner->audmode) { case V4L2_TUNER_MODE_MONO: radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */ break; case V4L2_TUNER_MODE_STEREO: radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */ break; default: goto done; } retval = si470x_set_register(radio, POWERCFG); done: if (retval < 0) dev_warn(&radio->videodev->dev, "set tuner failed with %d\n", retval); return retval; }
/* * si470x_vidioc_s_ctrl - set the value of a control */ static int si470x_vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct si470x_device *radio = video_drvdata(file); int retval = 0; /* safety checks */ retval = si470x_disconnect_check(radio); if (retval) goto done; switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME; radio->registers[SYSCONFIG2] |= ctrl->value; retval = si470x_set_register(radio, SYSCONFIG2); break; case V4L2_CID_AUDIO_MUTE: if (ctrl->value == 1) radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; else radio->registers[POWERCFG] |= POWERCFG_DMUTE; retval = si470x_set_register(radio, POWERCFG); break; default: retval = -EINVAL; } done: if (retval < 0) dev_warn(&radio->videodev->dev, "set control failed with %d\n", retval); 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; tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO; /* 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 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); /* 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; }