From: Raja Mani <raja_mani@xxxxxx> Add FM TX API calls in FM V4L2 module which will internally link to V4L2 subsystem. It includes , 1) Support for set/get modulator attributes and extended control. 2) fm_v4l2_vidioc_s_ctrl () + added support for V4L2_CID_TUNE_POWER_LEVEL, V4L2_CID_TUNE_PREEMPHASIS, V4L2_CID_FM_BAND, V4L2_CID_TUNE_ANTENNA_CAPACITOR 3) fm_v4l2_vidioc_g_ctrl () + added support for V4L2_CID_TUNE_PREEMPHASIS, V4L2_CID_FM_BAND Signed-off-by: Raja Mani <raja_mani@xxxxxx> Signed-off-by: Pramodh AG <pramodh_ag@xxxxxx> --- drivers/staging/ti-st/fmdrv_v4l2.c | 243 ++++++++++++++++++++++++++++++++--- 1 files changed, 222 insertions(+), 21 deletions(-) diff --git a/drivers/staging/ti-st/fmdrv_v4l2.c b/drivers/staging/ti-st/fmdrv_v4l2.c index c546a0f..d76344e 100644 --- a/drivers/staging/ti-st/fmdrv_v4l2.c +++ b/drivers/staging/ti-st/fmdrv_v4l2.c @@ -30,8 +30,7 @@ #include "fmdrv_v4l2.h" #include "fmdrv_common.h" #include "fmdrv_rx.h" -/* TODO: Enable when FM TX is supported */ -/* #include "fmdrv_tx.h" */ +#include "fmdrv_tx.h" #ifndef DEBUG #ifdef pr_info @@ -134,9 +133,8 @@ static ssize_t fm_v4l2_fops_write(struct file *file, const char __user * buf, ret, rds.text_type, rds.text, rds.af_freq); fmdev = video_drvdata(file); - /* TODO: Enable when FM TX is supported */ - /* fm_tx_set_radio_text(fmdev, rds.text, rds.text_type); */ - /* fm_tx_set_af(fmdev, rds.af_freq); */ + fm_tx_set_radio_text(fmdev, rds.text, rds.text_type); + fm_tx_set_af(fmdev, rds.af_freq); return 0; } @@ -257,6 +255,9 @@ static int fm_v4l2_vidioc_g_ctrl(struct file *file, void *priv, int ret = -EINVAL; unsigned short curr_vol; unsigned char curr_mute_mode; + unsigned char region; + unsigned char afreq; + short threshold; struct fmdrv_ops *fmdev; fmdev = video_drvdata(file); @@ -274,16 +275,48 @@ static int fm_v4l2_vidioc_g_ctrl(struct file *file, void *priv, goto exit; ctrl->value = curr_vol; break; + case V4L2_CID_FM_BAND: + if (fmdev->curr_fmmode != FM_MODE_RX) + break; + ret = fm_rx_get_region(fmdev, ®ion); + if (ret < 0) + break; + if (region == FM_BAND_EUROPE_US) + ctrl->value = V4L2_FM_BAND_OTHER; + else + ctrl->value = V4L2_FM_BAND_JAPAN; + break; + case V4L2_CID_RSSI_THRESHOLD: + ret = fm_rx_get_rssi_threshold(fmdev, &threshold); + if (ret == 0) + ctrl->value = threshold; + break; + case V4L2_CID_TUNE_AF: + ret = fm_rx_get_af_switch(fmdev, &afreq); + if (ret == 0) + ctrl->value = afreq; + break; + case V4L2_CID_TUNE_PREEMPHASIS: + ctrl->value = fmdev->tx_data.preemph; + break; } exit: return ret; } +/* Change the value of specified control. + * V4L2_CID_TUNE_POWER_LEVEL: Application will specify power level value in + * units of dB/uV, whereas range and step are specific to FM chip. For TI's WL + * chips, convert application specified power level value to chip specific + * value by substracting it with 122. Refer to TI FM data sheet for details. + */ static int fm_v4l2_vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { int ret = -EINVAL; + unsigned int emph_filter; + unsigned char region; struct fmdrv_ops *fmdev; fmdev = video_drvdata(file); @@ -291,17 +324,61 @@ static int fm_v4l2_vidioc_s_ctrl(struct file *file, void *priv, switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: /* set mute */ ret = fmc_set_mute_mode(fmdev, (unsigned char)ctrl->value); - if (ret < 0) - goto exit; break; case V4L2_CID_AUDIO_VOLUME: /* set volume */ ret = fm_rx_set_volume(fmdev, (unsigned short)ctrl->value); - if (ret < 0) - goto exit; + break; + case V4L2_CID_TUNE_POWER_LEVEL: /* set TX power level - ext control */ + if (ctrl->value >= FM_PWR_LVL_LOW && + ctrl->value <= FM_PWR_LVL_HIGH) { + ctrl->value = FM_PWR_LVL_HIGH - ctrl->value; + ret = fm_tx_set_pwr_lvl(fmdev, + (unsigned char)ctrl->value); + } else + ret = -ERANGE; + break; + case V4L2_CID_FM_BAND: + if (ctrl->value < V4L2_FM_BAND_OTHER || + ctrl->value > V4L2_FM_BAND_JAPAN) { + ret = -ERANGE; + break; + } + if (ctrl->value == V4L2_FM_BAND_OTHER) + region = FM_BAND_EUROPE_US; + else + region = FM_BAND_JAPAN; + ret = fmc_set_region(fmdev, region); + break; + case V4L2_CID_RSSI_THRESHOLD: + ret = fm_rx_set_rssi_threshold(fmdev, (short)ctrl->value); + break; + case V4L2_CID_TUNE_AF: + ret = fm_rx_set_af_switch(fmdev, (unsigned char)ctrl->value); + break; + case V4L2_CID_TUNE_PREEMPHASIS: + if (ctrl->value < V4L2_PREEMPHASIS_DISABLED || + ctrl->value > V4L2_PREEMPHASIS_75_uS) { + ret = -EINVAL; + break; + } + if (ctrl->value == V4L2_PREEMPHASIS_DISABLED) + emph_filter = FM_TX_PREEMPH_OFF; + else if (ctrl->value == V4L2_PREEMPHASIS_50_uS) + emph_filter = FM_TX_PREEMPH_50US; + else + emph_filter = FM_TX_PREEMPH_75US; + ret = fm_tx_set_preemph_filter(fmdev, emph_filter); + break; + case V4L2_CID_TUNE_ANTENNA_CAPACITOR: + if (ctrl->value == FM_TX_ANT_IMP_50 || + ctrl->value == FM_TX_ANT_IMP_200 || + ctrl->value == FM_TX_ANT_IMP_500) + ret = fm_tx_set_ant_imp(fmdev, ctrl->value); + else + ret = -EINVAL; break; } -exit: return ret; } @@ -324,7 +401,7 @@ static int fm_v4l2_vidioc_s_audio(struct file *file, void *priv, return 0; } -/* Get tuner attributes. If current mode is NOT RX, set to RX */ +/* Get tuner attributes. If current mode is NOT RX, return error */ static int fm_v4l2_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { @@ -340,12 +417,8 @@ static int fm_v4l2_vidioc_g_tuner(struct file *file, void *priv, fmdev = video_drvdata(file); if (fmdev->curr_fmmode != FM_MODE_RX) { - ret = fmc_set_mode(fmdev, FM_MODE_RX); - if (ret < 0) { - pr_err("(fmdrv): Failed to set RX mode; unable to " \ - "read tuner attributes\n"); - goto exit; - } + ret = -EPERM; + goto exit; } ret = fm_rx_get_currband_lowhigh_freq(fmdev, &bottom_frequency, @@ -364,11 +437,12 @@ static int fm_v4l2_vidioc_g_tuner(struct file *file, void *priv, strcpy(tuner->name, "FM"); tuner->type = V4L2_TUNER_RADIO; /* Store rangelow and rangehigh freq in unit of 62.5 KHz */ - tuner->rangelow = (bottom_frequency * 10000) / 625; - tuner->rangehigh = (top_frequency * 10000) / 625; + tuner->rangelow = bottom_frequency * 16; + tuner->rangehigh = top_frequency * 16; tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO | ((fmdev->rx.rds.flag == FM_RDS_ENABLE) ? V4L2_TUNER_SUB_RDS : 0); - tuner->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS; + tuner->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | + V4L2_TUNER_CAP_LOW; tuner->audmode = (stereo_mono_mode ? V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO); @@ -440,6 +514,8 @@ static int fm_v4l2_vidioc_g_frequency(struct file *file, void *priv, ret = fmc_get_frequency(fmdev, &freq->frequency); if (ret < 0) return ret; + + freq->frequency *= 16; /* unit of 62.5 */ return 0; } @@ -451,6 +527,13 @@ static int fm_v4l2_vidioc_s_frequency(struct file *file, void *priv, struct fmdrv_ops *fmdev; fmdev = video_drvdata(file); + + /* + * As per V4L2 specifications user sends the frequency + * in units of 62.5. + */ + freq->frequency = (unsigned int)(freq->frequency / 16); + ret = fmc_set_frequency(fmdev, freq->frequency); if (ret < 0) return ret; @@ -482,6 +565,120 @@ exit: return ret; } +static int fm_v4l2_vidioc_g_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ext_ctrls) +{ + int index; + int ret = -EINVAL; + struct v4l2_control ctrl; + + if (V4L2_CTRL_CLASS_FM_TX == ext_ctrls->ctrl_class) { + for (index = 0; index < ext_ctrls->count; index++) { + ctrl.id = ext_ctrls->controls[index].id; + ctrl.value = ext_ctrls->controls[index].value; + ret = fm_v4l2_vidioc_g_ctrl(file, priv, &ctrl); + if (ret < 0) { + ext_ctrls->error_idx = index; + break; + } + ext_ctrls->controls[index].value = ctrl.value; + } + } + return ret; +} + +static int fm_v4l2_vidioc_s_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ext_ctrls) +{ + int index; + int ret = -EINVAL; + struct v4l2_control ctrl; + + if (V4L2_CTRL_CLASS_FM_TX == ext_ctrls->ctrl_class) { + for (index = 0; index < ext_ctrls->count; index++) { + ctrl.id = ext_ctrls->controls[index].id; + ctrl.value = ext_ctrls->controls[index].value; + ret = fm_v4l2_vidioc_s_ctrl(file, priv, &ctrl); + if (ret < 0) { + ext_ctrls->error_idx = index; + break; + } + ext_ctrls->controls[index].value = ctrl.value; + } + } + return ret; +} + +/* Get modulator attributes. If mode is not TX, return no attributes. */ +static int fm_v4l2_vidioc_g_modulator(struct file *file, void *priv, + struct v4l2_modulator *mod) +{ + int ret = -EPERM; + struct fmdrv_ops *fmdev; + + if (mod->index != 0) { + ret = -EINVAL; + goto exit; + } + + fmdev = video_drvdata(file); + if (fmdev->curr_fmmode != FM_MODE_TX) + goto exit; + + mod->txsubchans = ((fmdev->tx_data.aud_mode == FM_STEREO_MODE) ? + V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO) | + ((fmdev->tx_data.rds.flag == FM_RDS_ENABLE) ? V4L2_TUNER_SUB_RDS : 0); + + mod->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS; + ret = 0; +exit: + return ret; +} + +/* Set modulator attributes. If mode is not TX, set to TX. */ +static int fm_v4l2_vidioc_s_modulator(struct file *file, void *priv, + struct v4l2_modulator *mod) +{ + unsigned char rds_mode; + unsigned short aud_mode; + int ret; + struct fmdrv_ops *fmdev; + + if (mod->index != 0) { + ret = -EINVAL; + goto exit; + } + + fmdev = video_drvdata(file); + if (fmdev->curr_fmmode != FM_MODE_TX) { + ret = fmc_set_mode(fmdev, FM_MODE_TX); + if (ret != 0) { + pr_err("(fmdrv): Failed to set TX mode; unable to " \ + "set modulator attributes\n"); + goto exit; + } + } + + aud_mode = (mod->txsubchans & V4L2_TUNER_SUB_STEREO) ? + FM_STEREO_MODE : FM_MONO_MODE; + rds_mode = (mod->txsubchans & V4L2_TUNER_SUB_RDS) ? + FM_RDS_ENABLE : FM_RDS_DISABLE; + + ret = fm_tx_set_stereo_mono(fmdev, aud_mode); + if (ret < 0) { + pr_err("(fmdrv): Failed to set mono/stereo mode for TX\n"); + goto exit; + } + + ret = fm_tx_set_rds_mode(fmdev, rds_mode); + if (ret < 0) { + pr_err("(fmdrv): Failed to set rds mode for TX\n"); + goto exit; + } +exit: + return ret; +} + static const struct v4l2_file_operations fm_drv_fops = { .owner = THIS_MODULE, .read = fm_v4l2_fops_read, @@ -497,13 +694,17 @@ static const struct v4l2_ioctl_ops fm_drv_ioctl_ops = { .vidioc_queryctrl = fm_v4l2_vidioc_queryctrl, .vidioc_g_ctrl = fm_v4l2_vidioc_g_ctrl, .vidioc_s_ctrl = fm_v4l2_vidioc_s_ctrl, + .vidioc_g_ext_ctrls = fm_v4l2_vidioc_g_ext_ctrls, + .vidioc_s_ext_ctrls = fm_v4l2_vidioc_s_ext_ctrls, .vidioc_g_audio = fm_v4l2_vidioc_g_audio, .vidioc_s_audio = fm_v4l2_vidioc_s_audio, .vidioc_g_tuner = fm_v4l2_vidioc_g_tuner, .vidioc_s_tuner = fm_v4l2_vidioc_s_tuner, .vidioc_g_frequency = fm_v4l2_vidioc_g_frequency, .vidioc_s_frequency = fm_v4l2_vidioc_s_frequency, - .vidioc_s_hw_freq_seek = fm_v4l2_vidioc_s_hw_freq_seek + .vidioc_s_hw_freq_seek = fm_v4l2_vidioc_s_hw_freq_seek, + .vidioc_g_modulator = fm_v4l2_vidioc_g_modulator, + .vidioc_s_modulator = fm_v4l2_vidioc_s_modulator }; /* V4L2 RADIO device parent structure */ -- 1.5.6.3 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html