|
@@ -52,8 +52,6 @@ static const char *si4713_supply_names[SI4713_NUM_SUPPLIES] = {
|
|
|
|
|
|
#define DEFAULT_RDS_PI 0x00
|
|
|
#define DEFAULT_RDS_PTY 0x00
|
|
|
-#define DEFAULT_RDS_PS_NAME ""
|
|
|
-#define DEFAULT_RDS_RADIO_TEXT DEFAULT_RDS_PS_NAME
|
|
|
#define DEFAULT_RDS_DEVIATION 0x00C8
|
|
|
#define DEFAULT_RDS_PS_REPEAT_COUNT 0x0003
|
|
|
#define DEFAULT_LIMITER_RTIME 0x1392
|
|
@@ -107,7 +105,6 @@ static const char *si4713_supply_names[SI4713_NUM_SUPPLIES] = {
|
|
|
(status & SI4713_ERR))
|
|
|
/* mute definition */
|
|
|
#define set_mute(p) ((p & 1) | ((p & 1) << 1));
|
|
|
-#define get_mute(p) (p & 0x01)
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
#define DBG_BUFFER(device, message, buffer, size) \
|
|
@@ -189,21 +186,6 @@ static int usecs_to_dev(unsigned long usecs, unsigned long const array[],
|
|
|
return rval;
|
|
|
}
|
|
|
|
|
|
-static unsigned long dev_to_usecs(int value, unsigned long const array[],
|
|
|
- int size)
|
|
|
-{
|
|
|
- int i;
|
|
|
- int rval = -EINVAL;
|
|
|
-
|
|
|
- for (i = 0; i < size / 2; i++)
|
|
|
- if (array[i * 2] == value) {
|
|
|
- rval = array[(i * 2) + 1];
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- return rval;
|
|
|
-}
|
|
|
-
|
|
|
/* si4713_handler: IRQ handler, just complete work */
|
|
|
static irqreturn_t si4713_handler(int irq, void *dev)
|
|
|
{
|
|
@@ -787,9 +769,6 @@ static int si4713_set_mute(struct si4713_device *sdev, u16 mute)
|
|
|
rval = si4713_write_property(sdev,
|
|
|
SI4713_TX_LINE_INPUT_MUTE, mute);
|
|
|
|
|
|
- if (rval >= 0)
|
|
|
- sdev->mute = get_mute(mute);
|
|
|
-
|
|
|
return rval;
|
|
|
}
|
|
|
|
|
@@ -830,7 +809,6 @@ static int si4713_set_rds_ps_name(struct si4713_device *sdev, char *ps_name)
|
|
|
return rval;
|
|
|
}
|
|
|
|
|
|
- strncpy(sdev->rds_info.ps_name, ps_name, MAX_RDS_PS_NAME);
|
|
|
return rval;
|
|
|
}
|
|
|
|
|
@@ -842,24 +820,23 @@ static int si4713_set_rds_radio_text(struct si4713_device *sdev, char *rt)
|
|
|
s8 left;
|
|
|
|
|
|
if (!sdev->power_state)
|
|
|
- goto copy;
|
|
|
+ return rval;
|
|
|
|
|
|
rval = si4713_tx_rds_buff(sdev, RDS_BLOCK_CLEAR, 0, 0, 0, &left);
|
|
|
if (rval < 0)
|
|
|
return rval;
|
|
|
|
|
|
if (!strlen(rt))
|
|
|
- goto copy;
|
|
|
+ return rval;
|
|
|
|
|
|
do {
|
|
|
/* RDS spec says that if the last block isn't used,
|
|
|
* then apply a carriage return
|
|
|
*/
|
|
|
- if (t_index < (RDS_RADIOTEXT_INDEX_MAX *
|
|
|
- RDS_RADIOTEXT_BLK_SIZE)) {
|
|
|
+ if (t_index < (RDS_RADIOTEXT_INDEX_MAX * RDS_RADIOTEXT_BLK_SIZE)) {
|
|
|
for (i = 0; i < RDS_RADIOTEXT_BLK_SIZE; i++) {
|
|
|
- if (!rt[t_index + i] || rt[t_index + i] ==
|
|
|
- RDS_CARRIAGE_RETURN) {
|
|
|
+ if (!rt[t_index + i] ||
|
|
|
+ rt[t_index + i] == RDS_CARRIAGE_RETURN) {
|
|
|
rt[t_index + i] = RDS_CARRIAGE_RETURN;
|
|
|
cr_inserted = 1;
|
|
|
break;
|
|
@@ -881,13 +858,38 @@ static int si4713_set_rds_radio_text(struct si4713_device *sdev, char *rt)
|
|
|
break;
|
|
|
} while (left > 0);
|
|
|
|
|
|
-copy:
|
|
|
- strncpy(sdev->rds_info.radio_text, rt, MAX_RDS_RADIO_TEXT);
|
|
|
+ return rval;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * si4713_update_tune_status - update properties from tx_tune_status
|
|
|
+ * command. Must be called with sdev->mutex held.
|
|
|
+ * @sdev: si4713_device structure for the device we are communicating
|
|
|
+ */
|
|
|
+static int si4713_update_tune_status(struct si4713_device *sdev)
|
|
|
+{
|
|
|
+ int rval;
|
|
|
+ u16 f = 0;
|
|
|
+ u8 p = 0, a = 0, n = 0;
|
|
|
+
|
|
|
+ rval = si4713_tx_tune_status(sdev, 0x00, &f, &p, &a, &n);
|
|
|
+
|
|
|
+ if (rval < 0)
|
|
|
+ goto exit;
|
|
|
+
|
|
|
+/* TODO: check that power_level and antenna_capacitor really are not
|
|
|
+ changed by the hardware. If they are, then these controls should become
|
|
|
+ volatiles.
|
|
|
+ sdev->power_level = p;
|
|
|
+ sdev->antenna_capacitor = a;*/
|
|
|
+ sdev->tune_rnl = n;
|
|
|
+
|
|
|
+exit:
|
|
|
return rval;
|
|
|
}
|
|
|
|
|
|
static int si4713_choose_econtrol_action(struct si4713_device *sdev, u32 id,
|
|
|
- u32 **shadow, s32 *bit, s32 *mask, u16 *property, int *mul,
|
|
|
+ s32 *bit, s32 *mask, u16 *property, int *mul,
|
|
|
unsigned long **table, int *size)
|
|
|
{
|
|
|
s32 rval = 0;
|
|
@@ -897,277 +899,76 @@ static int si4713_choose_econtrol_action(struct si4713_device *sdev, u32 id,
|
|
|
case V4L2_CID_RDS_TX_PI:
|
|
|
*property = SI4713_TX_RDS_PI;
|
|
|
*mul = 1;
|
|
|
- *shadow = &sdev->rds_info.pi;
|
|
|
break;
|
|
|
case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD:
|
|
|
*property = SI4713_TX_ACOMP_THRESHOLD;
|
|
|
*mul = 1;
|
|
|
- *shadow = &sdev->acomp_info.threshold;
|
|
|
break;
|
|
|
case V4L2_CID_AUDIO_COMPRESSION_GAIN:
|
|
|
*property = SI4713_TX_ACOMP_GAIN;
|
|
|
*mul = 1;
|
|
|
- *shadow = &sdev->acomp_info.gain;
|
|
|
break;
|
|
|
case V4L2_CID_PILOT_TONE_FREQUENCY:
|
|
|
*property = SI4713_TX_PILOT_FREQUENCY;
|
|
|
*mul = 1;
|
|
|
- *shadow = &sdev->pilot_info.frequency;
|
|
|
break;
|
|
|
case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME:
|
|
|
*property = SI4713_TX_ACOMP_ATTACK_TIME;
|
|
|
*mul = ATTACK_TIME_UNIT;
|
|
|
- *shadow = &sdev->acomp_info.attack_time;
|
|
|
break;
|
|
|
case V4L2_CID_PILOT_TONE_DEVIATION:
|
|
|
*property = SI4713_TX_PILOT_DEVIATION;
|
|
|
*mul = 10;
|
|
|
- *shadow = &sdev->pilot_info.deviation;
|
|
|
break;
|
|
|
case V4L2_CID_AUDIO_LIMITER_DEVIATION:
|
|
|
*property = SI4713_TX_AUDIO_DEVIATION;
|
|
|
*mul = 10;
|
|
|
- *shadow = &sdev->limiter_info.deviation;
|
|
|
break;
|
|
|
case V4L2_CID_RDS_TX_DEVIATION:
|
|
|
*property = SI4713_TX_RDS_DEVIATION;
|
|
|
*mul = 1;
|
|
|
- *shadow = &sdev->rds_info.deviation;
|
|
|
break;
|
|
|
|
|
|
case V4L2_CID_RDS_TX_PTY:
|
|
|
*property = SI4713_TX_RDS_PS_MISC;
|
|
|
*bit = 5;
|
|
|
*mask = 0x1F << 5;
|
|
|
- *shadow = &sdev->rds_info.pty;
|
|
|
break;
|
|
|
case V4L2_CID_AUDIO_LIMITER_ENABLED:
|
|
|
*property = SI4713_TX_ACOMP_ENABLE;
|
|
|
*bit = 1;
|
|
|
*mask = 1 << 1;
|
|
|
- *shadow = &sdev->limiter_info.enabled;
|
|
|
break;
|
|
|
case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
|
|
|
*property = SI4713_TX_ACOMP_ENABLE;
|
|
|
*bit = 0;
|
|
|
*mask = 1 << 0;
|
|
|
- *shadow = &sdev->acomp_info.enabled;
|
|
|
break;
|
|
|
case V4L2_CID_PILOT_TONE_ENABLED:
|
|
|
*property = SI4713_TX_COMPONENT_ENABLE;
|
|
|
*bit = 0;
|
|
|
*mask = 1 << 0;
|
|
|
- *shadow = &sdev->pilot_info.enabled;
|
|
|
break;
|
|
|
|
|
|
case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME:
|
|
|
*property = SI4713_TX_LIMITER_RELEASE_TIME;
|
|
|
*table = limiter_times;
|
|
|
*size = ARRAY_SIZE(limiter_times);
|
|
|
- *shadow = &sdev->limiter_info.release_time;
|
|
|
break;
|
|
|
case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME:
|
|
|
*property = SI4713_TX_ACOMP_RELEASE_TIME;
|
|
|
*table = acomp_rtimes;
|
|
|
*size = ARRAY_SIZE(acomp_rtimes);
|
|
|
- *shadow = &sdev->acomp_info.release_time;
|
|
|
break;
|
|
|
case V4L2_CID_TUNE_PREEMPHASIS:
|
|
|
*property = SI4713_TX_PREEMPHASIS;
|
|
|
*table = preemphasis_values;
|
|
|
*size = ARRAY_SIZE(preemphasis_values);
|
|
|
- *shadow = &sdev->preemphasis;
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
rval = -EINVAL;
|
|
|
- }
|
|
|
-
|
|
|
- return rval;
|
|
|
-}
|
|
|
-
|
|
|
-static int si4713_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc);
|
|
|
-
|
|
|
-/* 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 < 0 || len > MAX_RDS_PS_NAME) {
|
|
|
- rval = -ERANGE;
|
|
|
- goto exit;
|
|
|
- }
|
|
|
- rval = copy_from_user(ps_name, control->string, len);
|
|
|
- if (rval) {
|
|
|
- rval = -EFAULT;
|
|
|
- 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 < 0 || len > MAX_RDS_RADIO_TEXT) {
|
|
|
- rval = -ERANGE;
|
|
|
- goto exit;
|
|
|
- }
|
|
|
- rval = copy_from_user(radio_text, control->string, len);
|
|
|
- if (rval) {
|
|
|
- rval = -EFAULT;
|
|
|
- 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;
|
|
|
-}
|
|
|
-
|
|
|
-static int validate_range(struct v4l2_subdev *sd,
|
|
|
- struct v4l2_ext_control *control)
|
|
|
-{
|
|
|
- struct v4l2_queryctrl vqc;
|
|
|
- int rval;
|
|
|
-
|
|
|
- vqc.id = control->id;
|
|
|
- rval = si4713_queryctrl(sd, &vqc);
|
|
|
- if (rval < 0)
|
|
|
- goto exit;
|
|
|
-
|
|
|
- if (control->value < vqc.minimum || control->value > vqc.maximum)
|
|
|
- rval = -ERANGE;
|
|
|
-
|
|
|
-exit:
|
|
|
- return rval;
|
|
|
-}
|
|
|
-
|
|
|
-/* properties which use tx_tune_power*/
|
|
|
-static int si4713_write_econtrol_tune(struct si4713_device *sdev,
|
|
|
- struct v4l2_ext_control *control)
|
|
|
-{
|
|
|
- s32 rval = 0;
|
|
|
- u8 power, antcap;
|
|
|
-
|
|
|
- rval = validate_range(&sdev->sd, control);
|
|
|
- if (rval < 0)
|
|
|
- return rval;
|
|
|
-
|
|
|
- switch (control->id) {
|
|
|
- case V4L2_CID_TUNE_POWER_LEVEL:
|
|
|
- power = control->value;
|
|
|
- antcap = sdev->antenna_capacitor;
|
|
|
- break;
|
|
|
- case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
|
|
|
- power = sdev->power_level;
|
|
|
- antcap = control->value;
|
|
|
break;
|
|
|
- default:
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-
|
|
|
- if (sdev->power_state)
|
|
|
- rval = si4713_tx_tune_power(sdev, power, antcap);
|
|
|
-
|
|
|
- if (rval == 0) {
|
|
|
- sdev->power_level = power;
|
|
|
- sdev->antenna_capacitor = antcap;
|
|
|
- }
|
|
|
-
|
|
|
- return rval;
|
|
|
-}
|
|
|
-
|
|
|
-static int si4713_write_econtrol_integers(struct si4713_device *sdev,
|
|
|
- struct v4l2_ext_control *control)
|
|
|
-{
|
|
|
- s32 rval;
|
|
|
- u32 *shadow = NULL, val = 0;
|
|
|
- s32 bit = 0, mask = 0;
|
|
|
- u16 property = 0;
|
|
|
- int mul = 0;
|
|
|
- unsigned long *table = NULL;
|
|
|
- int size = 0;
|
|
|
-
|
|
|
- rval = validate_range(&sdev->sd, control);
|
|
|
- if (rval < 0)
|
|
|
- return rval;
|
|
|
-
|
|
|
- rval = si4713_choose_econtrol_action(sdev, control->id, &shadow, &bit,
|
|
|
- &mask, &property, &mul, &table, &size);
|
|
|
- if (rval < 0)
|
|
|
- return rval;
|
|
|
-
|
|
|
- val = control->value;
|
|
|
- if (mul) {
|
|
|
- val = control->value / mul;
|
|
|
- } else if (table) {
|
|
|
- rval = usecs_to_dev(control->value, table, size);
|
|
|
- if (rval < 0)
|
|
|
- return rval;
|
|
|
- val = rval;
|
|
|
- rval = 0;
|
|
|
- }
|
|
|
-
|
|
|
- if (sdev->power_state) {
|
|
|
- if (mask) {
|
|
|
- rval = si4713_read_property(sdev, property, &val);
|
|
|
- if (rval < 0)
|
|
|
- return rval;
|
|
|
- val = set_bits(val, control->value, bit, mask);
|
|
|
- }
|
|
|
-
|
|
|
- rval = si4713_write_property(sdev, property, val);
|
|
|
- if (rval < 0)
|
|
|
- return rval;
|
|
|
- if (mask)
|
|
|
- val = control->value;
|
|
|
- }
|
|
|
-
|
|
|
- if (mul) {
|
|
|
- *shadow = val * mul;
|
|
|
- } else if (table) {
|
|
|
- rval = dev_to_usecs(val, table, size);
|
|
|
- if (rval < 0)
|
|
|
- return rval;
|
|
|
- *shadow = rval;
|
|
|
- rval = 0;
|
|
|
- } else {
|
|
|
- *shadow = val;
|
|
|
}
|
|
|
|
|
|
return rval;
|
|
@@ -1181,110 +982,25 @@ static int si4713_s_modulator(struct v4l2_subdev *sd, const struct v4l2_modulato
|
|
|
*/
|
|
|
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 */
|
|
|
- memcpy(tmp, sdev, sizeof(*sdev));
|
|
|
-
|
|
|
- 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);
|
|
|
+ int rval;
|
|
|
|
|
|
/* Device procedure needs to set frequency first */
|
|
|
f.tuner = 0;
|
|
|
- f.frequency = tmp->frequency ? tmp->frequency : DEFAULT_FREQUENCY;
|
|
|
+ f.frequency = sdev->frequency ? sdev->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);
|
|
|
+ rval = si4713_s_frequency(&sdev->sd, &f);
|
|
|
|
|
|
vm.index = 0;
|
|
|
- if (tmp->stereo)
|
|
|
+ if (sdev->stereo)
|
|
|
vm.txsubchans = V4L2_TUNER_SUB_STEREO;
|
|
|
else
|
|
|
vm.txsubchans = V4L2_TUNER_SUB_MONO;
|
|
|
- if (tmp->rds_info.enabled)
|
|
|
+ if (sdev->rds_enabled)
|
|
|
vm.txsubchans |= V4L2_TUNER_SUB_RDS;
|
|
|
si4713_s_modulator(&sdev->sd, &vm);
|
|
|
|
|
|
- kfree(tmp);
|
|
|
-
|
|
|
return rval;
|
|
|
}
|
|
|
|
|
@@ -1308,406 +1024,116 @@ static int si4713_initialize(struct si4713_device *sdev)
|
|
|
if (rval < 0)
|
|
|
return rval;
|
|
|
|
|
|
- sdev->rds_info.pi = DEFAULT_RDS_PI;
|
|
|
- sdev->rds_info.pty = DEFAULT_RDS_PTY;
|
|
|
- sdev->rds_info.deviation = DEFAULT_RDS_DEVIATION;
|
|
|
- strlcpy(sdev->rds_info.ps_name, DEFAULT_RDS_PS_NAME, MAX_RDS_PS_NAME);
|
|
|
- strlcpy(sdev->rds_info.radio_text, DEFAULT_RDS_RADIO_TEXT,
|
|
|
- MAX_RDS_RADIO_TEXT);
|
|
|
- sdev->rds_info.enabled = 1;
|
|
|
-
|
|
|
- sdev->limiter_info.release_time = DEFAULT_LIMITER_RTIME;
|
|
|
- sdev->limiter_info.deviation = DEFAULT_LIMITER_DEV;
|
|
|
- sdev->limiter_info.enabled = 1;
|
|
|
-
|
|
|
- sdev->pilot_info.deviation = DEFAULT_PILOT_DEVIATION;
|
|
|
- sdev->pilot_info.frequency = DEFAULT_PILOT_FREQUENCY;
|
|
|
- sdev->pilot_info.enabled = 1;
|
|
|
-
|
|
|
- sdev->acomp_info.release_time = DEFAULT_ACOMP_RTIME;
|
|
|
- sdev->acomp_info.attack_time = DEFAULT_ACOMP_ATIME;
|
|
|
- sdev->acomp_info.threshold = DEFAULT_ACOMP_THRESHOLD;
|
|
|
- sdev->acomp_info.gain = DEFAULT_ACOMP_GAIN;
|
|
|
- sdev->acomp_info.enabled = 1;
|
|
|
|
|
|
sdev->frequency = DEFAULT_FREQUENCY;
|
|
|
- sdev->preemphasis = DEFAULT_PREEMPHASIS;
|
|
|
- sdev->mute = DEFAULT_MUTE;
|
|
|
- sdev->power_level = DEFAULT_POWER_LEVEL;
|
|
|
- sdev->antenna_capacitor = 0;
|
|
|
sdev->stereo = 1;
|
|
|
sdev->tune_rnl = DEFAULT_TUNE_RNL;
|
|
|
-
|
|
|
- return rval;
|
|
|
-}
|
|
|
-
|
|
|
-/* read string property */
|
|
|
-static int si4713_read_econtrol_string(struct si4713_device *sdev,
|
|
|
- struct v4l2_ext_control *control)
|
|
|
-{
|
|
|
- s32 rval = 0;
|
|
|
-
|
|
|
- switch (control->id) {
|
|
|
- case V4L2_CID_RDS_TX_PS_NAME:
|
|
|
- if (strlen(sdev->rds_info.ps_name) + 1 > control->size) {
|
|
|
- control->size = MAX_RDS_PS_NAME + 1;
|
|
|
- rval = -ENOSPC;
|
|
|
- goto exit;
|
|
|
- }
|
|
|
- rval = copy_to_user(control->string, sdev->rds_info.ps_name,
|
|
|
- strlen(sdev->rds_info.ps_name) + 1);
|
|
|
- if (rval)
|
|
|
- rval = -EFAULT;
|
|
|
- break;
|
|
|
-
|
|
|
- case V4L2_CID_RDS_TX_RADIO_TEXT:
|
|
|
- if (strlen(sdev->rds_info.radio_text) + 1 > control->size) {
|
|
|
- control->size = MAX_RDS_RADIO_TEXT + 1;
|
|
|
- rval = -ENOSPC;
|
|
|
- goto exit;
|
|
|
- }
|
|
|
- rval = copy_to_user(control->string, sdev->rds_info.radio_text,
|
|
|
- strlen(sdev->rds_info.radio_text) + 1);
|
|
|
- if (rval)
|
|
|
- rval = -EFAULT;
|
|
|
- break;
|
|
|
-
|
|
|
- default:
|
|
|
- rval = -EINVAL;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
-exit:
|
|
|
- return rval;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * si4713_update_tune_status - update properties from tx_tune_status
|
|
|
- * command.
|
|
|
- * @sdev: si4713_device structure for the device we are communicating
|
|
|
- */
|
|
|
-static int si4713_update_tune_status(struct si4713_device *sdev)
|
|
|
-{
|
|
|
- int rval;
|
|
|
- u16 f = 0;
|
|
|
- u8 p = 0, a = 0, n = 0;
|
|
|
-
|
|
|
- rval = si4713_tx_tune_status(sdev, 0x00, &f, &p, &a, &n);
|
|
|
-
|
|
|
- if (rval < 0)
|
|
|
- goto exit;
|
|
|
-
|
|
|
- sdev->power_level = p;
|
|
|
- sdev->antenna_capacitor = a;
|
|
|
- sdev->tune_rnl = n;
|
|
|
-
|
|
|
-exit:
|
|
|
- return rval;
|
|
|
-}
|
|
|
-
|
|
|
-/* properties which use tx_tune_status */
|
|
|
-static int si4713_read_econtrol_tune(struct si4713_device *sdev,
|
|
|
- struct v4l2_ext_control *control)
|
|
|
-{
|
|
|
- s32 rval = 0;
|
|
|
-
|
|
|
- if (sdev->power_state) {
|
|
|
- rval = si4713_update_tune_status(sdev);
|
|
|
- if (rval < 0)
|
|
|
- return rval;
|
|
|
- }
|
|
|
-
|
|
|
- switch (control->id) {
|
|
|
- case V4L2_CID_TUNE_POWER_LEVEL:
|
|
|
- control->value = sdev->power_level;
|
|
|
- break;
|
|
|
- case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
|
|
|
- control->value = sdev->antenna_capacitor;
|
|
|
- break;
|
|
|
- default:
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-
|
|
|
- return rval;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
-static int si4713_read_econtrol_integers(struct si4713_device *sdev,
|
|
|
- struct v4l2_ext_control *control)
|
|
|
+/* si4713_s_ctrl - set the value of a control */
|
|
|
+static int si4713_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
|
{
|
|
|
- s32 rval;
|
|
|
- u32 *shadow = NULL, val = 0;
|
|
|
+ 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;
|
|
|
|
|
|
- rval = si4713_choose_econtrol_action(sdev, control->id, &shadow, &bit,
|
|
|
- &mask, &property, &mul, &table, &size);
|
|
|
- if (rval < 0)
|
|
|
- return rval;
|
|
|
-
|
|
|
- if (sdev->power_state) {
|
|
|
- rval = si4713_read_property(sdev, property, &val);
|
|
|
- if (rval < 0)
|
|
|
- return rval;
|
|
|
-
|
|
|
- /* Keep negative values for threshold */
|
|
|
- if (control->id == V4L2_CID_AUDIO_COMPRESSION_THRESHOLD)
|
|
|
- *shadow = (s16)val;
|
|
|
- else if (mask)
|
|
|
- *shadow = get_status_bit(val, bit, mask);
|
|
|
- else if (mul)
|
|
|
- *shadow = val * mul;
|
|
|
- else
|
|
|
- *shadow = dev_to_usecs(val, table, size);
|
|
|
- }
|
|
|
-
|
|
|
- control->value = *shadow;
|
|
|
-
|
|
|
- return rval;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * Video4Linux Subdev Interface
|
|
|
- */
|
|
|
-/* si4713_s_ext_ctrls - set extended controls value */
|
|
|
-static int si4713_s_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)
|
|
|
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
|
|
|
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_write_econtrol_string(sdev,
|
|
|
- ctrls->controls + i);
|
|
|
- break;
|
|
|
- case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
|
|
|
- case V4L2_CID_TUNE_POWER_LEVEL:
|
|
|
- err = si4713_write_econtrol_tune(sdev,
|
|
|
- ctrls->controls + i);
|
|
|
- break;
|
|
|
- default:
|
|
|
- err = si4713_write_econtrol_integers(sdev,
|
|
|
- ctrls->controls + i);
|
|
|
- }
|
|
|
-
|
|
|
- if (err < 0) {
|
|
|
- ctrls->error_idx = i;
|
|
|
- return err;
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/* 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 (!sdev->power_state)
|
|
|
+ return 0;
|
|
|
|
|
|
- if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX)
|
|
|
- return -EINVAL;
|
|
|
+ for (c = 1; !ret && c < ctrl->ncontrols; c++) {
|
|
|
+ ctrl = ctrl->cluster[c];
|
|
|
|
|
|
- for (i = 0; i < ctrls->count; i++) {
|
|
|
- int err;
|
|
|
+ if (!force && !ctrl->is_new)
|
|
|
+ continue;
|
|
|
|
|
|
- switch ((ctrls->controls + i)->id) {
|
|
|
+ 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:
|
|
|
- err = si4713_read_econtrol_string(sdev,
|
|
|
- ctrls->controls + i);
|
|
|
+ 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:
|
|
|
- err = si4713_read_econtrol_tune(sdev,
|
|
|
- ctrls->controls + i);
|
|
|
+ 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:
|
|
|
- err = si4713_read_econtrol_integers(sdev,
|
|
|
- ctrls->controls + i);
|
|
|
- }
|
|
|
-
|
|
|
- if (err < 0) {
|
|
|
- ctrls->error_idx = i;
|
|
|
- return err;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/* si4713_queryctrl - enumerate control items */
|
|
|
-static int si4713_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
|
|
|
-{
|
|
|
- int rval = 0;
|
|
|
-
|
|
|
- switch (qc->id) {
|
|
|
- /* User class controls */
|
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
|
- rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, DEFAULT_MUTE);
|
|
|
- break;
|
|
|
- /* FM_TX class controls */
|
|
|
- case V4L2_CID_RDS_TX_PI:
|
|
|
- rval = v4l2_ctrl_query_fill(qc, 0, 0xFFFF, 1, DEFAULT_RDS_PI);
|
|
|
- break;
|
|
|
- case V4L2_CID_RDS_TX_PTY:
|
|
|
- rval = v4l2_ctrl_query_fill(qc, 0, 31, 1, DEFAULT_RDS_PTY);
|
|
|
- break;
|
|
|
- case V4L2_CID_RDS_TX_DEVIATION:
|
|
|
- rval = v4l2_ctrl_query_fill(qc, 0, MAX_RDS_DEVIATION,
|
|
|
- 10, DEFAULT_RDS_DEVIATION);
|
|
|
- break;
|
|
|
- case V4L2_CID_RDS_TX_PS_NAME:
|
|
|
- /*
|
|
|
- * Report step as 8. From RDS spec, psname
|
|
|
- * should be 8. But there are receivers which scroll strings
|
|
|
- * sized as 8xN.
|
|
|
- */
|
|
|
- rval = v4l2_ctrl_query_fill(qc, 0, MAX_RDS_PS_NAME, 8, 0);
|
|
|
- break;
|
|
|
- case V4L2_CID_RDS_TX_RADIO_TEXT:
|
|
|
- /*
|
|
|
- * Report step as 32 (2A block). From RDS spec,
|
|
|
- * radio text should be 32 for 2A block. But there are receivers
|
|
|
- * which scroll strings sized as 32xN. Setting default to 32.
|
|
|
- */
|
|
|
- rval = v4l2_ctrl_query_fill(qc, 0, MAX_RDS_RADIO_TEXT, 32, 0);
|
|
|
- break;
|
|
|
|
|
|
- case V4L2_CID_AUDIO_LIMITER_ENABLED:
|
|
|
- rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
|
|
|
- break;
|
|
|
- case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME:
|
|
|
- rval = v4l2_ctrl_query_fill(qc, 250, MAX_LIMITER_RELEASE_TIME,
|
|
|
- 50, DEFAULT_LIMITER_RTIME);
|
|
|
- break;
|
|
|
- case V4L2_CID_AUDIO_LIMITER_DEVIATION:
|
|
|
- rval = v4l2_ctrl_query_fill(qc, 0, MAX_LIMITER_DEVIATION,
|
|
|
- 10, DEFAULT_LIMITER_DEV);
|
|
|
- break;
|
|
|
-
|
|
|
- case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
|
|
|
- rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
|
|
|
- break;
|
|
|
- case V4L2_CID_AUDIO_COMPRESSION_GAIN:
|
|
|
- rval = v4l2_ctrl_query_fill(qc, 0, MAX_ACOMP_GAIN, 1,
|
|
|
- DEFAULT_ACOMP_GAIN);
|
|
|
- break;
|
|
|
- case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD:
|
|
|
- rval = v4l2_ctrl_query_fill(qc, MIN_ACOMP_THRESHOLD,
|
|
|
- MAX_ACOMP_THRESHOLD, 1,
|
|
|
- DEFAULT_ACOMP_THRESHOLD);
|
|
|
- break;
|
|
|
- case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME:
|
|
|
- rval = v4l2_ctrl_query_fill(qc, 0, MAX_ACOMP_ATTACK_TIME,
|
|
|
- 500, DEFAULT_ACOMP_ATIME);
|
|
|
- break;
|
|
|
- case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME:
|
|
|
- rval = v4l2_ctrl_query_fill(qc, 100000, MAX_ACOMP_RELEASE_TIME,
|
|
|
- 100000, DEFAULT_ACOMP_RTIME);
|
|
|
- break;
|
|
|
-
|
|
|
- case V4L2_CID_PILOT_TONE_ENABLED:
|
|
|
- rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
|
|
|
- break;
|
|
|
- case V4L2_CID_PILOT_TONE_DEVIATION:
|
|
|
- rval = v4l2_ctrl_query_fill(qc, 0, MAX_PILOT_DEVIATION,
|
|
|
- 10, DEFAULT_PILOT_DEVIATION);
|
|
|
- break;
|
|
|
- case V4L2_CID_PILOT_TONE_FREQUENCY:
|
|
|
- rval = v4l2_ctrl_query_fill(qc, 0, MAX_PILOT_FREQUENCY,
|
|
|
- 1, DEFAULT_PILOT_FREQUENCY);
|
|
|
- break;
|
|
|
-
|
|
|
- case V4L2_CID_TUNE_PREEMPHASIS:
|
|
|
- rval = v4l2_ctrl_query_fill(qc, V4L2_PREEMPHASIS_DISABLED,
|
|
|
- V4L2_PREEMPHASIS_75_uS, 1,
|
|
|
- V4L2_PREEMPHASIS_50_uS);
|
|
|
- break;
|
|
|
- case V4L2_CID_TUNE_POWER_LEVEL:
|
|
|
- rval = v4l2_ctrl_query_fill(qc, 0, 120, 1, DEFAULT_POWER_LEVEL);
|
|
|
- break;
|
|
|
- case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
|
|
|
- rval = v4l2_ctrl_query_fill(qc, 0, 191, 1, 0);
|
|
|
- break;
|
|
|
- default:
|
|
|
- rval = -EINVAL;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- 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;
|
|
|
-
|
|
|
- if (sdev->power_state) {
|
|
|
- rval = si4713_read_property(sdev, SI4713_TX_LINE_INPUT_MUTE,
|
|
|
- &sdev->mute);
|
|
|
-
|
|
|
- if (rval < 0)
|
|
|
- return rval;
|
|
|
- }
|
|
|
-
|
|
|
- switch (ctrl->id) {
|
|
|
- case V4L2_CID_AUDIO_MUTE:
|
|
|
- ctrl->value = get_mute(sdev->mute);
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- 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;
|
|
|
+ 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;
|
|
|
+ }
|
|
|
|
|
|
- rval = si4713_setup(sdev);
|
|
|
- if (rval < 0)
|
|
|
- goto exit;
|
|
|
+ if (mask) {
|
|
|
+ ret = si4713_read_property(sdev, property, &val);
|
|
|
+ if (ret < 0)
|
|
|
+ break;
|
|
|
+ val = set_bits(val, ctrl->val, bit, mask);
|
|
|
+ }
|
|
|
|
|
|
- rval = si4713_set_mute(sdev, ctrl->value);
|
|
|
+ ret = si4713_write_property(sdev, property, val);
|
|
|
+ if (ret < 0)
|
|
|
+ break;
|
|
|
+ if (mask)
|
|
|
+ val = ctrl->val;
|
|
|
+ break;
|
|
|
}
|
|
|
- break;
|
|
|
}
|
|
|
|
|
|
-exit:
|
|
|
- return rval;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/* si4713_ioctl - deal with private ioctls (only rnl for now) */
|
|
@@ -1746,15 +1172,6 @@ static long si4713_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
|
|
return rval;
|
|
|
}
|
|
|
|
|
|
-static const struct v4l2_subdev_core_ops si4713_subdev_core_ops = {
|
|
|
- .queryctrl = si4713_queryctrl,
|
|
|
- .g_ext_ctrls = si4713_g_ext_ctrls,
|
|
|
- .s_ext_ctrls = si4713_s_ext_ctrls,
|
|
|
- .g_ctrl = si4713_g_ctrl,
|
|
|
- .s_ctrl = si4713_s_ctrl,
|
|
|
- .ioctl = si4713_ioctl,
|
|
|
-};
|
|
|
-
|
|
|
/* si4713_g_modulator - get modulator attributes */
|
|
|
static int si4713_g_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *vm)
|
|
|
{
|
|
@@ -1784,7 +1201,6 @@ static int si4713_g_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *vm)
|
|
|
return rval;
|
|
|
|
|
|
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 */
|
|
@@ -1794,7 +1210,7 @@ static int si4713_g_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *vm)
|
|
|
vm->txsubchans = V4L2_TUNER_SUB_MONO;
|
|
|
|
|
|
/* Report rds feature status */
|
|
|
- if (sdev->rds_info.enabled)
|
|
|
+ if (sdev->rds_enabled)
|
|
|
vm->txsubchans |= V4L2_TUNER_SUB_RDS;
|
|
|
else
|
|
|
vm->txsubchans &= ~V4L2_TUNER_SUB_RDS;
|
|
@@ -1842,7 +1258,7 @@ static int si4713_s_modulator(struct v4l2_subdev *sd, const struct v4l2_modulato
|
|
|
}
|
|
|
|
|
|
sdev->stereo = stereo;
|
|
|
- sdev->rds_info.enabled = rds;
|
|
|
+ sdev->rds_enabled = rds;
|
|
|
|
|
|
return rval;
|
|
|
}
|
|
@@ -1897,6 +1313,14 @@ static int si4713_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequenc
|
|
|
return rval;
|
|
|
}
|
|
|
|
|
|
+static const struct v4l2_ctrl_ops si4713_ctrl_ops = {
|
|
|
+ .s_ctrl = si4713_s_ctrl,
|
|
|
+};
|
|
|
+
|
|
|
+static const struct v4l2_subdev_core_ops si4713_subdev_core_ops = {
|
|
|
+ .ioctl = si4713_ioctl,
|
|
|
+};
|
|
|
+
|
|
|
static const struct v4l2_subdev_tuner_ops si4713_subdev_tuner_ops = {
|
|
|
.g_frequency = si4713_g_frequency,
|
|
|
.s_frequency = si4713_s_frequency,
|
|
@@ -1918,6 +1342,7 @@ static int si4713_probe(struct i2c_client *client,
|
|
|
{
|
|
|
struct si4713_device *sdev;
|
|
|
struct si4713_platform_data *pdata = client->dev.platform_data;
|
|
|
+ struct v4l2_ctrl_handler *hdl;
|
|
|
int rval, i;
|
|
|
|
|
|
sdev = kzalloc(sizeof *sdev, GFP_KERNEL);
|
|
@@ -1953,6 +1378,82 @@ static int si4713_probe(struct i2c_client *client,
|
|
|
|
|
|
init_completion(&sdev->work);
|
|
|
|
|
|
+ hdl = &sdev->ctrl_handler;
|
|
|
+ v4l2_ctrl_handler_init(hdl, 20);
|
|
|
+ sdev->mute = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, DEFAULT_MUTE);
|
|
|
+
|
|
|
+ sdev->rds_pi = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_RDS_TX_PI, 0, 0xffff, 1, DEFAULT_RDS_PI);
|
|
|
+ sdev->rds_pty = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_RDS_TX_PTY, 0, 31, 1, DEFAULT_RDS_PTY);
|
|
|
+ sdev->rds_deviation = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_RDS_TX_DEVIATION, 0, MAX_RDS_DEVIATION,
|
|
|
+ 10, DEFAULT_RDS_DEVIATION);
|
|
|
+ /*
|
|
|
+ * Report step as 8. From RDS spec, psname
|
|
|
+ * should be 8. But there are receivers which scroll strings
|
|
|
+ * sized as 8xN.
|
|
|
+ */
|
|
|
+ sdev->rds_ps_name = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_RDS_TX_PS_NAME, 0, MAX_RDS_PS_NAME, 8, 0);
|
|
|
+ /*
|
|
|
+ * Report step as 32 (2A block). From RDS spec,
|
|
|
+ * radio text should be 32 for 2A block. But there are receivers
|
|
|
+ * which scroll strings sized as 32xN. Setting default to 32.
|
|
|
+ */
|
|
|
+ sdev->rds_radio_text = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_RDS_TX_RADIO_TEXT, 0, MAX_RDS_RADIO_TEXT, 32, 0);
|
|
|
+
|
|
|
+ sdev->limiter_enabled = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_AUDIO_LIMITER_ENABLED, 0, 1, 1, 1);
|
|
|
+ sdev->limiter_release_time = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_AUDIO_LIMITER_RELEASE_TIME, 250,
|
|
|
+ MAX_LIMITER_RELEASE_TIME, 10, DEFAULT_LIMITER_RTIME);
|
|
|
+ sdev->limiter_deviation = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_AUDIO_LIMITER_DEVIATION, 0,
|
|
|
+ MAX_LIMITER_DEVIATION, 10, DEFAULT_LIMITER_DEV);
|
|
|
+
|
|
|
+ sdev->compression_enabled = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_AUDIO_COMPRESSION_ENABLED, 0, 1, 1, 1);
|
|
|
+ sdev->compression_gain = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_AUDIO_COMPRESSION_GAIN, 0, MAX_ACOMP_GAIN, 1,
|
|
|
+ DEFAULT_ACOMP_GAIN);
|
|
|
+ sdev->compression_threshold = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_AUDIO_COMPRESSION_THRESHOLD, MIN_ACOMP_THRESHOLD,
|
|
|
+ MAX_ACOMP_THRESHOLD, 1,
|
|
|
+ DEFAULT_ACOMP_THRESHOLD);
|
|
|
+ sdev->compression_attack_time = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME, 0,
|
|
|
+ MAX_ACOMP_ATTACK_TIME, 500, DEFAULT_ACOMP_ATIME);
|
|
|
+ sdev->compression_release_time = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME, 100000,
|
|
|
+ MAX_ACOMP_RELEASE_TIME, 100000, DEFAULT_ACOMP_RTIME);
|
|
|
+
|
|
|
+ sdev->pilot_tone_enabled = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_PILOT_TONE_ENABLED, 0, 1, 1, 1);
|
|
|
+ sdev->pilot_tone_deviation = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_PILOT_TONE_DEVIATION, 0, MAX_PILOT_DEVIATION,
|
|
|
+ 10, DEFAULT_PILOT_DEVIATION);
|
|
|
+ sdev->pilot_tone_freq = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_PILOT_TONE_FREQUENCY, 0, MAX_PILOT_FREQUENCY,
|
|
|
+ 1, DEFAULT_PILOT_FREQUENCY);
|
|
|
+
|
|
|
+ sdev->tune_preemphasis = v4l2_ctrl_new_std_menu(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_TUNE_PREEMPHASIS,
|
|
|
+ V4L2_PREEMPHASIS_75_uS, 0, V4L2_PREEMPHASIS_50_uS);
|
|
|
+ sdev->tune_pwr_level = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_TUNE_POWER_LEVEL, 0, 120, 1, DEFAULT_POWER_LEVEL);
|
|
|
+ sdev->tune_ant_cap = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
|
|
|
+ V4L2_CID_TUNE_ANTENNA_CAPACITOR, 0, 191, 1, 0);
|
|
|
+
|
|
|
+ if (hdl->error) {
|
|
|
+ rval = hdl->error;
|
|
|
+ goto free_ctrls;
|
|
|
+ }
|
|
|
+ v4l2_ctrl_cluster(20, &sdev->mute);
|
|
|
+ sdev->sd.ctrl_handler = hdl;
|
|
|
+
|
|
|
if (client->irq) {
|
|
|
rval = request_irq(client->irq,
|
|
|
si4713_handler, IRQF_TRIGGER_FALLING | IRQF_DISABLED,
|
|
@@ -1977,6 +1478,8 @@ static int si4713_probe(struct i2c_client *client,
|
|
|
free_irq:
|
|
|
if (client->irq)
|
|
|
free_irq(client->irq, sdev);
|
|
|
+free_ctrls:
|
|
|
+ v4l2_ctrl_handler_free(hdl);
|
|
|
put_reg:
|
|
|
regulator_bulk_free(ARRAY_SIZE(sdev->supplies), sdev->supplies);
|
|
|
free_gpio:
|
|
@@ -2001,6 +1504,7 @@ static int si4713_remove(struct i2c_client *client)
|
|
|
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);
|