/* write string property */ static int si4713_write_econtrol_string(struct si4713_device *sdev, struct v4l2_ext_control *control) { struct v4l2_queryctrl vqc; int len; s32 rval = 0; vqc.id = control->id; rval = si4713_queryctrl(&sdev->sd, &vqc); if (rval < 0) goto exit; switch (control->id) { case V4L2_CID_RDS_TX_PS_NAME: { char ps_name[MAX_RDS_PS_NAME + 1]; len = control->size - 1; if (len > MAX_RDS_PS_NAME) { rval = -ERANGE; goto exit; } rval = copy_from_user(ps_name, control->string, len); if (rval < 0) goto exit; ps_name[len] = '\0'; if (strlen(ps_name) % vqc.step) { rval = -ERANGE; goto exit; } rval = si4713_set_rds_ps_name(sdev, ps_name); } break; case V4L2_CID_RDS_TX_RADIO_TEXT: { char radio_text[MAX_RDS_RADIO_TEXT + 1]; len = control->size - 1; if (len > MAX_RDS_RADIO_TEXT) { rval = -ERANGE; goto exit; } rval = copy_from_user(radio_text, control->string, len); if (rval < 0) goto exit; radio_text[len] = '\0'; if (strlen(radio_text) % vqc.step) { rval = -ERANGE; goto exit; } rval = si4713_set_rds_radio_text(sdev, radio_text); } break; default: rval = -EINVAL; break; }; exit: return rval; }
/* si4713_s_ctrl - set the value of a control */ static int si4713_s_ctrl(struct v4l2_ctrl *ctrl) { struct si4713_device *sdev = container_of(ctrl->handler, struct si4713_device, ctrl_handler); u32 val = 0; s32 bit = 0, mask = 0; u16 property = 0; int mul = 0; unsigned long *table = NULL; int size = 0; bool force = false; int c; int ret = 0; if (ctrl->id != V4L2_CID_AUDIO_MUTE) return -EINVAL; if (ctrl->is_new) { if (ctrl->val) { ret = si4713_set_mute(sdev, ctrl->val); if (!ret) ret = si4713_set_power_state(sdev, POWER_DOWN); return ret; } ret = si4713_set_power_state(sdev, POWER_UP); if (!ret) ret = si4713_set_mute(sdev, ctrl->val); if (!ret) ret = si4713_setup(sdev); if (ret) return ret; force = true; } if (!sdev->power_state) return 0; for (c = 1; !ret && c < ctrl->ncontrols; c++) { ctrl = ctrl->cluster[c]; if (!force && !ctrl->is_new) continue; switch (ctrl->id) { case V4L2_CID_RDS_TX_PS_NAME: ret = si4713_set_rds_ps_name(sdev, ctrl->string); break; case V4L2_CID_RDS_TX_RADIO_TEXT: ret = si4713_set_rds_radio_text(sdev, ctrl->string); break; case V4L2_CID_TUNE_ANTENNA_CAPACITOR: /* don't handle this control if we force setting all * controls since in that case it will be handled by * V4L2_CID_TUNE_POWER_LEVEL. */ if (force) break; /* fall through */ case V4L2_CID_TUNE_POWER_LEVEL: ret = si4713_tx_tune_power(sdev, sdev->tune_pwr_level->val, sdev->tune_ant_cap->val); if (!ret) { /* Make sure we don't set this twice */ sdev->tune_ant_cap->is_new = false; sdev->tune_pwr_level->is_new = false; } break; default: ret = si4713_choose_econtrol_action(sdev, ctrl->id, &bit, &mask, &property, &mul, &table, &size); if (ret < 0) break; val = ctrl->val; if (mul) { val = val / mul; } else if (table) { ret = usecs_to_dev(val, table, size); if (ret < 0) break; val = ret; ret = 0; } if (mask) { ret = si4713_read_property(sdev, property, &val); if (ret < 0) break; val = set_bits(val, ctrl->val, bit, mask); } ret = si4713_write_property(sdev, property, val); if (ret < 0) break; if (mask) val = ctrl->val; break; } } return ret; }
static int si4713_setup(struct si4713_device *sdev) { struct v4l2_ext_control ctrl; struct v4l2_frequency f; struct v4l2_modulator vm; struct si4713_device *tmp; int rval = 0; tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); if (!tmp) return -ENOMEM; /* Get a local copy to avoid race */ mutex_lock(&sdev->mutex); memcpy(tmp, sdev, sizeof(*sdev)); mutex_unlock(&sdev->mutex); ctrl.id = V4L2_CID_RDS_TX_PI; ctrl.value = tmp->rds_info.pi; rval |= si4713_write_econtrol_integers(sdev, &ctrl); ctrl.id = V4L2_CID_AUDIO_COMPRESSION_THRESHOLD; ctrl.value = tmp->acomp_info.threshold; rval |= si4713_write_econtrol_integers(sdev, &ctrl); ctrl.id = V4L2_CID_AUDIO_COMPRESSION_GAIN; ctrl.value = tmp->acomp_info.gain; rval |= si4713_write_econtrol_integers(sdev, &ctrl); ctrl.id = V4L2_CID_PILOT_TONE_FREQUENCY; ctrl.value = tmp->pilot_info.frequency; rval |= si4713_write_econtrol_integers(sdev, &ctrl); ctrl.id = V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME; ctrl.value = tmp->acomp_info.attack_time; rval |= si4713_write_econtrol_integers(sdev, &ctrl); ctrl.id = V4L2_CID_PILOT_TONE_DEVIATION; ctrl.value = tmp->pilot_info.deviation; rval |= si4713_write_econtrol_integers(sdev, &ctrl); ctrl.id = V4L2_CID_AUDIO_LIMITER_DEVIATION; ctrl.value = tmp->limiter_info.deviation; rval |= si4713_write_econtrol_integers(sdev, &ctrl); ctrl.id = V4L2_CID_RDS_TX_DEVIATION; ctrl.value = tmp->rds_info.deviation; rval |= si4713_write_econtrol_integers(sdev, &ctrl); ctrl.id = V4L2_CID_RDS_TX_PTY; ctrl.value = tmp->rds_info.pty; rval |= si4713_write_econtrol_integers(sdev, &ctrl); ctrl.id = V4L2_CID_AUDIO_LIMITER_ENABLED; ctrl.value = tmp->limiter_info.enabled; rval |= si4713_write_econtrol_integers(sdev, &ctrl); ctrl.id = V4L2_CID_AUDIO_COMPRESSION_ENABLED; ctrl.value = tmp->acomp_info.enabled; rval |= si4713_write_econtrol_integers(sdev, &ctrl); ctrl.id = V4L2_CID_PILOT_TONE_ENABLED; ctrl.value = tmp->pilot_info.enabled; rval |= si4713_write_econtrol_integers(sdev, &ctrl); ctrl.id = V4L2_CID_AUDIO_LIMITER_RELEASE_TIME; ctrl.value = tmp->limiter_info.release_time; rval |= si4713_write_econtrol_integers(sdev, &ctrl); ctrl.id = V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME; ctrl.value = tmp->acomp_info.release_time; rval |= si4713_write_econtrol_integers(sdev, &ctrl); ctrl.id = V4L2_CID_TUNE_PREEMPHASIS; ctrl.value = tmp->preemphasis; rval |= si4713_write_econtrol_integers(sdev, &ctrl); ctrl.id = V4L2_CID_RDS_TX_PS_NAME; rval |= si4713_set_rds_ps_name(sdev, tmp->rds_info.ps_name); ctrl.id = V4L2_CID_RDS_TX_RADIO_TEXT; rval |= si4713_set_rds_radio_text(sdev, tmp->rds_info.radio_text); /* Device procedure needs to set frequency first */ f.frequency = tmp->frequency ? tmp->frequency : DEFAULT_FREQUENCY; f.frequency = si4713_to_v4l2(f.frequency); rval |= si4713_s_frequency(&sdev->sd, &f); ctrl.id = V4L2_CID_TUNE_POWER_LEVEL; ctrl.value = tmp->power_level; rval |= si4713_write_econtrol_tune(sdev, &ctrl); ctrl.id = V4L2_CID_TUNE_ANTENNA_CAPACITOR; ctrl.value = tmp->antenna_capacitor; rval |= si4713_write_econtrol_tune(sdev, &ctrl); vm.index = 0; if (tmp->stereo) vm.txsubchans = V4L2_TUNER_SUB_STEREO; else vm.txsubchans = V4L2_TUNER_SUB_MONO; if (tmp->rds_info.enabled) vm.txsubchans |= V4L2_TUNER_SUB_RDS; si4713_s_modulator(&sdev->sd, &vm); kfree(tmp); return rval; }