/* si4713_ioctl - deal with private ioctls (only rnl for now) */ static long si4713_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct si4713_device *sdev = to_si4713_device(sd); struct si4713_rnl *rnl = arg; u16 frequency; int rval = 0; if (!arg) return -EINVAL; switch (cmd) { case SI4713_IOC_MEASURE_RNL: frequency = v4l2_to_si4713(rnl->frequency); if (sdev->power_state) { /* Set desired measurement frequency */ rval = si4713_tx_tune_measure(sdev, frequency, 0); if (rval < 0) return rval; /* get results from tune status */ rval = si4713_update_tune_status(sdev); if (rval < 0) return rval; } rnl->rnl = sdev->tune_rnl; break; default: /* nothing */ rval = -ENOIOCTLCMD; } return rval; }
/* si4713_g_frequency - get tuner or modulator radio frequency */ static int si4713_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) { struct si4713_device *sdev = to_si4713_device(sd); int rval = 0; f->type = V4L2_TUNER_RADIO; mutex_lock(&sdev->mutex); if (sdev->power_state) { u16 freq; u8 p, a, n; rval = si4713_tx_tune_status(sdev, 0x00, &freq, &p, &a, &n); if (rval < 0) goto unlock; sdev->frequency = freq; } f->frequency = si4713_to_v4l2(sdev->frequency); unlock: mutex_unlock(&sdev->mutex); return rval; }
/* si4713_s_frequency - set tuner or modulator radio frequency */ static int si4713_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) { struct si4713_device *sdev = to_si4713_device(sd); int rval = 0; u16 frequency = v4l2_to_si4713(f->frequency); /* Check frequency range */ if (frequency < FREQ_RANGE_LOW || frequency > FREQ_RANGE_HIGH) return -EDOM; mutex_lock(&sdev->mutex); if (sdev->power_state) { rval = si4713_tx_tune_freq(sdev, frequency); if (rval < 0) goto unlock; frequency = rval; rval = 0; } sdev->frequency = frequency; f->frequency = si4713_to_v4l2(frequency); unlock: mutex_unlock(&sdev->mutex); return rval; }
/* si4713_s_ctrl - set the value of a control */ static int si4713_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { struct si4713_device *sdev = to_si4713_device(sd); int rval = 0; if (!sdev) return -ENODEV; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: if (ctrl->value) { rval = si4713_set_mute(sdev, ctrl->value); if (rval < 0) goto exit; rval = si4713_set_power_state(sdev, POWER_DOWN); } else { rval = si4713_set_power_state(sdev, POWER_UP); if (rval < 0) goto exit; rval = si4713_setup(sdev); if (rval < 0) goto exit; rval = si4713_set_mute(sdev, ctrl->value); } break; } exit: return rval; }
/* si4713_g_ctrl - get the value of a control */ static int si4713_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { struct si4713_device *sdev = to_si4713_device(sd); int rval = 0; if (!sdev) return -ENODEV; mutex_lock(&sdev->mutex); if (sdev->power_state) { rval = si4713_read_property(sdev, SI4713_TX_LINE_INPUT_MUTE, &sdev->mute); if (rval < 0) goto unlock; } switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: ctrl->value = get_mute(sdev->mute); break; } unlock: mutex_unlock(&sdev->mutex); return rval; }
/* si4713_g_modulator - get modulator attributes */ static int si4713_g_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *vm) { struct si4713_device *sdev = to_si4713_device(sd); int rval = 0; if (!sdev) { rval = -ENODEV; goto exit; } if (vm->index > 0) { rval = -EINVAL; goto exit; } strncpy(vm->name, "FM Modulator", 32); vm->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_RDS; /* Report current frequency range limits */ vm->rangelow = si4713_to_v4l2(FREQ_RANGE_LOW); vm->rangehigh = si4713_to_v4l2(FREQ_RANGE_HIGH); mutex_lock(&sdev->mutex); if (sdev->power_state) { u32 comp_en = 0; rval = si4713_read_property(sdev, SI4713_TX_COMPONENT_ENABLE, &comp_en); if (rval < 0) goto unlock; sdev->stereo = get_status_bit(comp_en, 1, 1 << 1); sdev->rds_info.enabled = get_status_bit(comp_en, 2, 1 << 2); } /* Report current audio mode: mono or stereo */ if (sdev->stereo) vm->txsubchans = V4L2_TUNER_SUB_STEREO; else vm->txsubchans = V4L2_TUNER_SUB_MONO; /* Report rds feature status */ if (sdev->rds_info.enabled) vm->txsubchans |= V4L2_TUNER_SUB_RDS; else vm->txsubchans &= ~V4L2_TUNER_SUB_RDS; unlock: mutex_unlock(&sdev->mutex); exit: return rval; }
/* si4713_remove - remove the device */ static int si4713_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct si4713_device *sdev = to_si4713_device(sd); platform_device_unregister(sdev->pd); if (sdev->power_state) si4713_set_power_state(sdev, POWER_DOWN); v4l2_device_unregister_subdev(sd); v4l2_ctrl_handler_free(sd->ctrl_handler); return 0; }
/* si4713_s_modulator - set modulator attributes */ static int si4713_s_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *vm) { struct si4713_device *sdev = to_si4713_device(sd); int rval = 0; u16 stereo, rds; u32 p; if (!sdev) return -ENODEV; if (vm->index > 0) return -EINVAL; /* Set audio mode: mono or stereo */ if (vm->txsubchans & V4L2_TUNER_SUB_STEREO) stereo = 1; else if (vm->txsubchans & V4L2_TUNER_SUB_MONO) stereo = 0; else return -EINVAL; rds = !!(vm->txsubchans & V4L2_TUNER_SUB_RDS); mutex_lock(&sdev->mutex); if (sdev->power_state) { rval = si4713_read_property(sdev, SI4713_TX_COMPONENT_ENABLE, &p); if (rval < 0) goto unlock; p = set_bits(p, stereo, 1, 1 << 1); p = set_bits(p, rds, 2, 1 << 2); rval = si4713_write_property(sdev, SI4713_TX_COMPONENT_ENABLE, p); if (rval < 0) goto unlock; } sdev->stereo = stereo; sdev->rds_info.enabled = rds; unlock: mutex_unlock(&sdev->mutex); return rval; }
/* si4713_remove - remove the device */ static int si4713_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct si4713_device *sdev = to_si4713_device(sd); if (sdev->power_state) si4713_set_power_state(sdev, POWER_DOWN); if (client->irq > 0) free_irq(client->irq, sdev); v4l2_device_unregister_subdev(sd); kfree(sdev); return 0; }
/* si4713_remove - remove the device */ static int si4713_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct si4713_device *sdev = to_si4713_device(sd); if (sdev->power_state) si4713_set_power_state(sdev, POWER_DOWN); if (client->irq > 0) free_irq(client->irq, sdev); v4l2_device_unregister_subdev(sd); v4l2_ctrl_handler_free(sd->ctrl_handler); regulator_bulk_free(ARRAY_SIZE(sdev->supplies), sdev->supplies); if (gpio_is_valid(sdev->gpio_reset)) gpio_free(sdev->gpio_reset); kfree(sdev); return 0; }
/* si4713_s_frequency - set tuner or modulator radio frequency */ static int si4713_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *f) { struct si4713_device *sdev = to_si4713_device(sd); int rval = 0; u16 frequency = v4l2_to_si4713(f->frequency); if (f->tuner) return -EINVAL; /* Check frequency range */ frequency = clamp_t(u16, frequency, FREQ_RANGE_LOW, FREQ_RANGE_HIGH); if (sdev->power_state) { rval = si4713_tx_tune_freq(sdev, frequency); if (rval < 0) return rval; frequency = rval; rval = 0; } sdev->frequency = frequency; return rval; }
/* si4713_g_frequency - get tuner or modulator radio frequency */ static int si4713_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) { struct si4713_device *sdev = to_si4713_device(sd); int rval = 0; if (f->tuner) return -EINVAL; if (sdev->power_state) { u16 freq; u8 p, a, n; rval = si4713_tx_tune_status(sdev, 0x00, &freq, &p, &a, &n); if (rval < 0) return rval; sdev->frequency = freq; } f->frequency = si4713_to_v4l2(sdev->frequency); return rval; }
/* si4713_g_ext_ctrls - get extended controls value */ static int si4713_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls) { struct si4713_device *sdev = to_si4713_device(sd); int i; if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX) return -EINVAL; for (i = 0; i < ctrls->count; i++) { int err; switch ((ctrls->controls + i)->id) { case V4L2_CID_RDS_TX_PS_NAME: case V4L2_CID_RDS_TX_RADIO_TEXT: err = si4713_read_econtrol_string(sdev, ctrls->controls + i); break; case V4L2_CID_TUNE_ANTENNA_CAPACITOR: case V4L2_CID_TUNE_POWER_LEVEL: err = si4713_read_econtrol_tune(sdev, ctrls->controls + i); break; default: err = si4713_read_econtrol_integers(sdev, ctrls->controls + i); } if (err < 0) { ctrls->error_idx = i; return err; } } return 0; }