* [PATCH 0/7] si4713/miropcm20: RDS enhancements
@ 2014-07-21 13:45 Hans Verkuil
2014-07-21 13:45 ` [PATCH 1/7] v4l2-ctrls: add new RDS TX controls Hans Verkuil
` (7 more replies)
0 siblings, 8 replies; 9+ messages in thread
From: Hans Verkuil @ 2014-07-21 13:45 UTC (permalink / raw)
To: linux-media; +Cc: Eduardo Valentin
This patch series adds a bunch of missing RDS TX controls and implements
them in the si4713 driver. It also adds back RDS support to the miropcm20
driver.
The Alternate Frequencies control is a u32 array since there can be up to
25 alternate frequencies. This was also the reason why I am only now posting
this series since it had to wait for compound control support to go in.
I've tested both drivers with my si4713 and miropcm20 boards.
Regards,
Hans
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/7] v4l2-ctrls: add new RDS TX controls
2014-07-21 13:45 [PATCH 0/7] si4713/miropcm20: RDS enhancements Hans Verkuil
@ 2014-07-21 13:45 ` Hans Verkuil
2014-07-21 13:45 ` [PATCH 2/7] DocBook/media: document the " Hans Verkuil
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Hans Verkuil @ 2014-07-21 13:45 UTC (permalink / raw)
To: linux-media; +Cc: Eduardo Valentin, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
The si4713 supports several RDS features not yet implemented in the driver.
This patch adds the missing RDS functionality to the list of RDS controls.
The ALT_FREQS control is a compound control containing an array of up
to 25 (the maximum according to the RDS standard) frequencies. To support
that the V4L2_CTRL_TYPE_U32 was added.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Cc: Eduardo Valentin <edubezval@gmail.com>
---
drivers/media/v4l2-core/v4l2-ctrls.c | 35 +++++++++++++++++++++++++++++++++++
include/media/v4l2-ctrls.h | 2 ++
include/uapi/linux/v4l2-controls.h | 9 +++++++++
include/uapi/linux/videodev2.h | 2 ++
4 files changed, 48 insertions(+)
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 1acc7aa..9a22f2c 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -805,6 +805,15 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_RDS_TX_PTY: return "RDS Program Type";
case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name";
case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text";
+ case V4L2_CID_RDS_TX_MONO_STEREO: return "RDS Stereo";
+ case V4L2_CID_RDS_TX_ARTIFICIAL_HEAD: return "RDS Artificial Head";
+ case V4L2_CID_RDS_TX_COMPRESSED: return "RDS Compressed";
+ case V4L2_CID_RDS_TX_DYNAMIC_PTY: return "RDS Dynamic PTY";
+ case V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT: return "RDS Traffic Announcement";
+ case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM: return "RDS Traffic Program";
+ case V4L2_CID_RDS_TX_MUSIC_SPEECH: return "RDS Music";
+ case V4L2_CID_RDS_TX_ALT_FREQS_ENABLE: return "RDS Enable Alt Frequencies";
+ case V4L2_CID_RDS_TX_ALT_FREQS: return "RDS Alternate Frequencies";
case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled";
case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time";
case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation";
@@ -946,6 +955,14 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_RF_TUNER_IF_GAIN_AUTO:
case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO:
case V4L2_CID_RF_TUNER_PLL_LOCK:
+ case V4L2_CID_RDS_TX_MONO_STEREO:
+ case V4L2_CID_RDS_TX_ARTIFICIAL_HEAD:
+ case V4L2_CID_RDS_TX_COMPRESSED:
+ case V4L2_CID_RDS_TX_DYNAMIC_PTY:
+ case V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT:
+ case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM:
+ case V4L2_CID_RDS_TX_MUSIC_SPEECH:
+ case V4L2_CID_RDS_TX_ALT_FREQS_ENABLE:
*type = V4L2_CTRL_TYPE_BOOLEAN;
*min = 0;
*max = *step = 1;
@@ -1089,6 +1106,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_DETECT_MD_THRESHOLD_GRID:
*type = V4L2_CTRL_TYPE_U16;
break;
+ case V4L2_CID_RDS_TX_ALT_FREQS:
+ *type = V4L2_CTRL_TYPE_U32;
+ break;
default:
*type = V4L2_CTRL_TYPE_INTEGER;
break;
@@ -1209,6 +1229,8 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx,
return ptr1.p_u8[idx] == ptr2.p_u8[idx];
case V4L2_CTRL_TYPE_U16:
return ptr1.p_u16[idx] == ptr2.p_u16[idx];
+ case V4L2_CTRL_TYPE_U32:
+ return ptr1.p_u32[idx] == ptr2.p_u32[idx];
default:
if (ctrl->is_int)
return ptr1.p_s32[idx] == ptr2.p_s32[idx];
@@ -1242,6 +1264,9 @@ static void std_init(const struct v4l2_ctrl *ctrl, u32 idx,
case V4L2_CTRL_TYPE_U16:
ptr.p_u16[idx] = ctrl->default_value;
break;
+ case V4L2_CTRL_TYPE_U32:
+ ptr.p_u32[idx] = ctrl->default_value;
+ break;
default:
idx *= ctrl->elem_size;
memset(ptr.p + idx, 0, ctrl->elem_size);
@@ -1289,6 +1314,9 @@ static void std_log(const struct v4l2_ctrl *ctrl)
case V4L2_CTRL_TYPE_U16:
pr_cont("%u", (unsigned)*ptr.p_u16);
break;
+ case V4L2_CTRL_TYPE_U32:
+ pr_cont("%u", (unsigned)*ptr.p_u32);
+ break;
default:
pr_cont("unknown type %d", ctrl->type);
break;
@@ -1335,6 +1363,8 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx,
return ROUND_TO_RANGE(ptr.p_u8[idx], u8, ctrl);
case V4L2_CTRL_TYPE_U16:
return ROUND_TO_RANGE(ptr.p_u16[idx], u16, ctrl);
+ case V4L2_CTRL_TYPE_U32:
+ return ROUND_TO_RANGE(ptr.p_u32[idx], u32, ctrl);
case V4L2_CTRL_TYPE_BOOLEAN:
ptr.p_s32[idx] = !!ptr.p_s32[idx];
@@ -1567,6 +1597,7 @@ static int check_range(enum v4l2_ctrl_type type,
/* fall through */
case V4L2_CTRL_TYPE_U8:
case V4L2_CTRL_TYPE_U16:
+ case V4L2_CTRL_TYPE_U32:
case V4L2_CTRL_TYPE_INTEGER:
case V4L2_CTRL_TYPE_INTEGER64:
if (step == 0 || min > max || def < min || def > max)
@@ -1882,6 +1913,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
case V4L2_CTRL_TYPE_U16:
elem_size = sizeof(u16);
break;
+ case V4L2_CTRL_TYPE_U32:
+ elem_size = sizeof(u32);
+ break;
default:
if (type < V4L2_CTRL_COMPOUND_TYPES)
elem_size = sizeof(s32);
@@ -3244,6 +3278,7 @@ int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl,
case V4L2_CTRL_TYPE_BITMASK:
case V4L2_CTRL_TYPE_U8:
case V4L2_CTRL_TYPE_U16:
+ case V4L2_CTRL_TYPE_U32:
if (ctrl->is_array)
return -EINVAL;
ret = check_range(ctrl->type, min, max, step, def);
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index 8c4edd6..a674bf7 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -41,6 +41,7 @@ struct poll_table_struct;
* @p_s64: Pointer to a 64-bit signed value.
* @p_u8: Pointer to a 8-bit unsigned value.
* @p_u16: Pointer to a 16-bit unsigned value.
+ * @p_u32: Pointer to a 32-bit unsigned value.
* @p_char: Pointer to a string.
* @p: Pointer to a compound value.
*/
@@ -49,6 +50,7 @@ union v4l2_ctrl_ptr {
s64 *p_s64;
u8 *p_u8;
u16 *p_u16;
+ u32 *p_u32;
char *p_char;
void *p;
};
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index db526d1..a13e6fe 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -757,6 +757,15 @@ enum v4l2_auto_focus_range {
#define V4L2_CID_RDS_TX_PTY (V4L2_CID_FM_TX_CLASS_BASE + 3)
#define V4L2_CID_RDS_TX_PS_NAME (V4L2_CID_FM_TX_CLASS_BASE + 5)
#define V4L2_CID_RDS_TX_RADIO_TEXT (V4L2_CID_FM_TX_CLASS_BASE + 6)
+#define V4L2_CID_RDS_TX_MONO_STEREO (V4L2_CID_FM_TX_CLASS_BASE + 7)
+#define V4L2_CID_RDS_TX_ARTIFICIAL_HEAD (V4L2_CID_FM_TX_CLASS_BASE + 8)
+#define V4L2_CID_RDS_TX_COMPRESSED (V4L2_CID_FM_TX_CLASS_BASE + 9)
+#define V4L2_CID_RDS_TX_DYNAMIC_PTY (V4L2_CID_FM_TX_CLASS_BASE + 10)
+#define V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT (V4L2_CID_FM_TX_CLASS_BASE + 11)
+#define V4L2_CID_RDS_TX_TRAFFIC_PROGRAM (V4L2_CID_FM_TX_CLASS_BASE + 12)
+#define V4L2_CID_RDS_TX_MUSIC_SPEECH (V4L2_CID_FM_TX_CLASS_BASE + 13)
+#define V4L2_CID_RDS_TX_ALT_FREQS_ENABLE (V4L2_CID_FM_TX_CLASS_BASE + 14)
+#define V4L2_CID_RDS_TX_ALT_FREQS (V4L2_CID_FM_TX_CLASS_BASE + 15)
#define V4L2_CID_AUDIO_LIMITER_ENABLED (V4L2_CID_FM_TX_CLASS_BASE + 64)
#define V4L2_CID_AUDIO_LIMITER_RELEASE_TIME (V4L2_CID_FM_TX_CLASS_BASE + 65)
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index b73e8cd..205ab62 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -1281,6 +1281,7 @@ struct v4l2_ext_control {
char *string;
__u8 *p_u8;
__u16 *p_u16;
+ __u32 *p_u32;
void *ptr;
};
} __attribute__ ((packed));
@@ -1313,6 +1314,7 @@ enum v4l2_ctrl_type {
V4L2_CTRL_COMPOUND_TYPES = 0x0100,
V4L2_CTRL_TYPE_U8 = 0x0100,
V4L2_CTRL_TYPE_U16 = 0x0101,
+ V4L2_CTRL_TYPE_U32 = 0x0102,
};
/* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
--
2.0.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/7] DocBook/media: document the new RDS TX controls
2014-07-21 13:45 [PATCH 0/7] si4713/miropcm20: RDS enhancements Hans Verkuil
2014-07-21 13:45 ` [PATCH 1/7] v4l2-ctrls: add new RDS TX controls Hans Verkuil
@ 2014-07-21 13:45 ` Hans Verkuil
2014-07-21 13:45 ` [PATCH 3/7] si4713: add the missing RDS functionality Hans Verkuil
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Hans Verkuil @ 2014-07-21 13:45 UTC (permalink / raw)
To: linux-media; +Cc: Eduardo Valentin, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
Document the new RDS features that will be supported by the si4713 driver.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Cc: Eduardo Valentin <edubezval@gmail.com>
---
Documentation/DocBook/media/v4l/controls.xml | 62 ++++++++++++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml
index 74f5755..3ec85f6 100644
--- a/Documentation/DocBook/media/v4l/controls.xml
+++ b/Documentation/DocBook/media/v4l/controls.xml
@@ -4051,6 +4051,68 @@ to find receivers which can scroll strings sized as 32 x N or 64 x N characters.
with steps of 32 or 64 characters. The result is it must always contain a string with size multiple of 32 or 64. </entry>
</row>
<row>
+ <entry spanname="id"><constant>V4L2_CID_RDS_TX_MONO_STEREO</constant> </entry>
+ <entry>boolean</entry>
+ </row>
+ <row><entry spanname="descr">Sets the Mono/Stereo bit of the Decoder Identification code. If set,
+then the audio was recorded as stereo.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RDS_TX_ARTIFICIAL_HEAD</constant> </entry>
+ <entry>boolean</entry>
+ </row>
+ <row><entry spanname="descr">Sets the
+<ulink url="http://en.wikipedia.org/wiki/Artificial_head">Artificial Head</ulink> bit of the Decoder
+Identification code. If set, then the audio was recorded using an artificial head.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RDS_TX_COMPRESSED</constant> </entry>
+ <entry>boolean</entry>
+ </row>
+ <row><entry spanname="descr">Sets the Compressed bit of the Decoder Identification code. If set,
+then the audio is compressed.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RDS_TX_DYNAMIC_PTY</constant> </entry>
+ <entry>boolean</entry>
+ </row>
+ <row><entry spanname="descr">Sets the Dynamic PTY bit of the Decoder Identification code. If set,
+then the PTY code is dynamically switched.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT</constant> </entry>
+ <entry>boolean</entry>
+ </row>
+ <row><entry spanname="descr">If set, then a traffic announcement is in progress.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RDS_TX_TRAFFIC_PROGRAM</constant> </entry>
+ <entry>boolean</entry>
+ </row>
+ <row><entry spanname="descr">If set, then the tuned programme carries traffic announcements.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RDS_TX_MUSIC_SPEECH</constant> </entry>
+ <entry>boolean</entry>
+ </row>
+ <row><entry spanname="descr">If set, then this channel broadcasts music. If cleared, then it
+broadcasts speech. If the transmitter doesn't make this distinction, then it should be set.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RDS_TX_ALT_FREQS_ENABLE</constant> </entry>
+ <entry>boolean</entry>
+ </row>
+ <row><entry spanname="descr">If set, then transmit alternate frequencies.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RDS_TX_ALT_FREQS</constant> </entry>
+ <entry>__u32 array</entry>
+ </row>
+ <row><entry spanname="descr">The alternate frequencies in kHz units. The RDS standard allows
+for up to 25 frequencies to be defined. Drivers may support fewer frequencies so check
+the array size.</entry>
+ </row>
+ <row>
<entry spanname="id"><constant>V4L2_CID_AUDIO_LIMITER_ENABLED</constant> </entry>
<entry>boolean</entry>
</row>
--
2.0.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 3/7] si4713: add the missing RDS functionality.
2014-07-21 13:45 [PATCH 0/7] si4713/miropcm20: RDS enhancements Hans Verkuil
2014-07-21 13:45 ` [PATCH 1/7] v4l2-ctrls: add new RDS TX controls Hans Verkuil
2014-07-21 13:45 ` [PATCH 2/7] DocBook/media: document the " Hans Verkuil
@ 2014-07-21 13:45 ` Hans Verkuil
2014-07-21 13:45 ` [PATCH 4/7] v4l2-ctrls: add RX RDS controls Hans Verkuil
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Hans Verkuil @ 2014-07-21 13:45 UTC (permalink / raw)
To: linux-media; +Cc: Eduardo Valentin, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
Not all the RDS features of the si4713 were supported. Add
the missing bits to fully support the hardware capabilities.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Cc: Eduardo Valentin <edubezval@gmail.com>
---
drivers/media/radio/si4713/si4713.c | 76 ++++++++++++++++++++++++++++++++++++-
drivers/media/radio/si4713/si4713.h | 9 +++++
2 files changed, 84 insertions(+), 1 deletion(-)
diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c
index dbe4726..b576555 100644
--- a/drivers/media/radio/si4713/si4713.c
+++ b/drivers/media/radio/si4713/si4713.c
@@ -957,6 +957,41 @@ static int si4713_choose_econtrol_action(struct si4713_device *sdev, u32 id,
*bit = 5;
*mask = 0x1F << 5;
break;
+ case V4L2_CID_RDS_TX_DYNAMIC_PTY:
+ *property = SI4713_TX_RDS_PS_MISC;
+ *bit = 15;
+ *mask = 1 << 15;
+ break;
+ case V4L2_CID_RDS_TX_COMPRESSED:
+ *property = SI4713_TX_RDS_PS_MISC;
+ *bit = 14;
+ *mask = 1 << 14;
+ break;
+ case V4L2_CID_RDS_TX_ARTIFICIAL_HEAD:
+ *property = SI4713_TX_RDS_PS_MISC;
+ *bit = 13;
+ *mask = 1 << 13;
+ break;
+ case V4L2_CID_RDS_TX_MONO_STEREO:
+ *property = SI4713_TX_RDS_PS_MISC;
+ *bit = 12;
+ *mask = 1 << 12;
+ break;
+ case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM:
+ *property = SI4713_TX_RDS_PS_MISC;
+ *bit = 10;
+ *mask = 1 << 10;
+ break;
+ case V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT:
+ *property = SI4713_TX_RDS_PS_MISC;
+ *bit = 4;
+ *mask = 1 << 4;
+ break;
+ case V4L2_CID_RDS_TX_MUSIC_SPEECH:
+ *property = SI4713_TX_RDS_PS_MISC;
+ *bit = 3;
+ *mask = 1 << 3;
+ break;
case V4L2_CID_AUDIO_LIMITER_ENABLED:
*property = SI4713_TX_ACOMP_ENABLE;
*bit = 1;
@@ -1122,6 +1157,17 @@ static int si4713_s_ctrl(struct v4l2_ctrl *ctrl)
}
break;
+ case V4L2_CID_RDS_TX_ALT_FREQS_ENABLE:
+ case V4L2_CID_RDS_TX_ALT_FREQS:
+ if (sdev->rds_alt_freqs_enable->val) {
+ val = sdev->rds_alt_freqs->p_new.p_u32[0];
+ val = val / 100 - 876 + 0xe101;
+ } else {
+ val = 0xe0e0;
+ }
+ ret = si4713_write_property(sdev, SI4713_TX_RDS_PS_AF, val);
+ break;
+
default:
ret = si4713_choose_econtrol_action(sdev, ctrl->id, &bit,
&mask, &property, &mul, &table, &size);
@@ -1355,6 +1401,17 @@ static const struct v4l2_subdev_ops si4713_subdev_ops = {
.tuner = &si4713_subdev_tuner_ops,
};
+static const struct v4l2_ctrl_config si4713_alt_freqs_ctrl = {
+ .id = V4L2_CID_RDS_TX_ALT_FREQS,
+ .type = V4L2_CTRL_TYPE_U32,
+ .min = 87600,
+ .max = 107900,
+ .step = 100,
+ .def = 87600,
+ .dims = { 1 },
+ .elem_size = sizeof(u32),
+};
+
/*
* I2C driver interface
*/
@@ -1410,6 +1467,23 @@ static int si4713_probe(struct i2c_client *client,
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_compressed = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
+ V4L2_CID_RDS_TX_COMPRESSED, 0, 1, 1, 0);
+ sdev->rds_art_head = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
+ V4L2_CID_RDS_TX_ARTIFICIAL_HEAD, 0, 1, 1, 0);
+ sdev->rds_stereo = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
+ V4L2_CID_RDS_TX_MONO_STEREO, 0, 1, 1, 1);
+ sdev->rds_tp = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
+ V4L2_CID_RDS_TX_TRAFFIC_PROGRAM, 0, 1, 1, 0);
+ sdev->rds_ta = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
+ V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT, 0, 1, 1, 0);
+ sdev->rds_ms = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
+ V4L2_CID_RDS_TX_MUSIC_SPEECH, 0, 1, 1, 1);
+ sdev->rds_dyn_pty = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
+ V4L2_CID_RDS_TX_DYNAMIC_PTY, 0, 1, 1, 0);
+ sdev->rds_alt_freqs_enable = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
+ V4L2_CID_RDS_TX_ALT_FREQS_ENABLE, 0, 1, 1, 0);
+ sdev->rds_alt_freqs = v4l2_ctrl_new_custom(hdl, &si4713_alt_freqs_ctrl, NULL);
sdev->rds_deviation = v4l2_ctrl_new_std(hdl, &si4713_ctrl_ops,
V4L2_CID_RDS_TX_DEVIATION, 0, MAX_RDS_DEVIATION,
10, DEFAULT_RDS_DEVIATION);
@@ -1476,7 +1550,7 @@ static int si4713_probe(struct i2c_client *client,
rval = hdl->error;
goto free_ctrls;
}
- v4l2_ctrl_cluster(20, &sdev->mute);
+ v4l2_ctrl_cluster(29, &sdev->mute);
sdev->sd.ctrl_handler = hdl;
if (client->irq) {
diff --git a/drivers/media/radio/si4713/si4713.h b/drivers/media/radio/si4713/si4713.h
index 4837cf6..ed700e3 100644
--- a/drivers/media/radio/si4713/si4713.h
+++ b/drivers/media/radio/si4713/si4713.h
@@ -211,6 +211,15 @@ struct si4713_device {
struct v4l2_ctrl *rds_pi;
struct v4l2_ctrl *rds_deviation;
struct v4l2_ctrl *rds_pty;
+ struct v4l2_ctrl *rds_compressed;
+ struct v4l2_ctrl *rds_art_head;
+ struct v4l2_ctrl *rds_stereo;
+ struct v4l2_ctrl *rds_ta;
+ struct v4l2_ctrl *rds_tp;
+ struct v4l2_ctrl *rds_ms;
+ struct v4l2_ctrl *rds_dyn_pty;
+ struct v4l2_ctrl *rds_alt_freqs_enable;
+ struct v4l2_ctrl *rds_alt_freqs;
struct v4l2_ctrl *compression_enabled;
struct v4l2_ctrl *compression_threshold;
struct v4l2_ctrl *compression_gain;
--
2.0.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 4/7] v4l2-ctrls: add RX RDS controls.
2014-07-21 13:45 [PATCH 0/7] si4713/miropcm20: RDS enhancements Hans Verkuil
` (2 preceding siblings ...)
2014-07-21 13:45 ` [PATCH 3/7] si4713: add the missing RDS functionality Hans Verkuil
@ 2014-07-21 13:45 ` Hans Verkuil
2014-07-21 13:45 ` [PATCH 5/7] DocBook/media: document the new RDS RX controls Hans Verkuil
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Hans Verkuil @ 2014-07-21 13:45 UTC (permalink / raw)
To: linux-media; +Cc: Eduardo Valentin, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
The radio-miropcm20 driver has firmware that decodes the RDS signals. So in that
case the RDS data becomes available in the form of controls.
Add support for these controls to the control framework, allowing the miro driver
to use them.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/media/v4l2-core/v4l2-ctrls.c | 19 +++++++++++++++++--
include/uapi/linux/v4l2-controls.h | 6 ++++++
2 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 9a22f2c..a7b4d4c 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -881,7 +881,6 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_FM_RX_CLASS: return "FM Radio Receiver Controls";
case V4L2_CID_TUNE_DEEMPHASIS: return "De-Emphasis";
case V4L2_CID_RDS_RECEPTION: return "RDS Reception";
-
case V4L2_CID_RF_TUNER_CLASS: return "RF Tuner Controls";
case V4L2_CID_RF_TUNER_LNA_GAIN_AUTO: return "LNA Gain, Auto";
case V4L2_CID_RF_TUNER_LNA_GAIN: return "LNA Gain";
@@ -892,6 +891,12 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: return "Bandwidth, Auto";
case V4L2_CID_RF_TUNER_BANDWIDTH: return "Bandwidth";
case V4L2_CID_RF_TUNER_PLL_LOCK: return "PLL Lock";
+ case V4L2_CID_RDS_RX_PTY: return "RDS Program Type";
+ case V4L2_CID_RDS_RX_PS_NAME: return "RDS PS Name";
+ case V4L2_CID_RDS_RX_RADIO_TEXT: return "RDS Radio Text";
+ case V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT: return "RDS Traffic Announcement";
+ case V4L2_CID_RDS_RX_TRAFFIC_PROGRAM: return "RDS Traffic Program";
+ case V4L2_CID_RDS_RX_MUSIC_SPEECH: return "RDS Music";
/* Detection controls */
/* Keep the order of the 'case's the same as in v4l2-controls.h! */
@@ -900,7 +905,6 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: return "MD Global Threshold";
case V4L2_CID_DETECT_MD_THRESHOLD_GRID: return "MD Threshold Grid";
case V4L2_CID_DETECT_MD_REGION_GRID: return "MD Region Grid";
-
default:
return NULL;
}
@@ -963,6 +967,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM:
case V4L2_CID_RDS_TX_MUSIC_SPEECH:
case V4L2_CID_RDS_TX_ALT_FREQS_ENABLE:
+ case V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT:
+ case V4L2_CID_RDS_RX_TRAFFIC_PROGRAM:
+ case V4L2_CID_RDS_RX_MUSIC_SPEECH:
*type = V4L2_CTRL_TYPE_BOOLEAN;
*min = 0;
*max = *step = 1;
@@ -1035,6 +1042,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
break;
case V4L2_CID_RDS_TX_PS_NAME:
case V4L2_CID_RDS_TX_RADIO_TEXT:
+ case V4L2_CID_RDS_RX_PS_NAME:
+ case V4L2_CID_RDS_RX_RADIO_TEXT:
*type = V4L2_CTRL_TYPE_STRING;
break;
case V4L2_CID_ISO_SENSITIVITY:
@@ -1166,6 +1175,12 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_DV_TX_RXSENSE:
case V4L2_CID_DV_TX_EDID_PRESENT:
case V4L2_CID_DV_RX_POWER_PRESENT:
+ case V4L2_CID_RDS_RX_PTY:
+ case V4L2_CID_RDS_RX_PS_NAME:
+ case V4L2_CID_RDS_RX_RADIO_TEXT:
+ case V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT:
+ case V4L2_CID_RDS_RX_TRAFFIC_PROGRAM:
+ case V4L2_CID_RDS_RX_MUSIC_SPEECH:
*flags |= V4L2_CTRL_FLAG_READ_ONLY;
break;
case V4L2_CID_RF_TUNER_PLL_LOCK:
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index a13e6fe..e946e43 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -910,6 +910,12 @@ enum v4l2_deemphasis {
};
#define V4L2_CID_RDS_RECEPTION (V4L2_CID_FM_RX_CLASS_BASE + 2)
+#define V4L2_CID_RDS_RX_PTY (V4L2_CID_FM_RX_CLASS_BASE + 3)
+#define V4L2_CID_RDS_RX_PS_NAME (V4L2_CID_FM_RX_CLASS_BASE + 4)
+#define V4L2_CID_RDS_RX_RADIO_TEXT (V4L2_CID_FM_RX_CLASS_BASE + 5)
+#define V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT (V4L2_CID_FM_RX_CLASS_BASE + 6)
+#define V4L2_CID_RDS_RX_TRAFFIC_PROGRAM (V4L2_CID_FM_RX_CLASS_BASE + 7)
+#define V4L2_CID_RDS_RX_MUSIC_SPEECH (V4L2_CID_FM_RX_CLASS_BASE + 8)
#define V4L2_CID_RF_TUNER_CLASS_BASE (V4L2_CTRL_CLASS_RF_TUNER | 0x900)
#define V4L2_CID_RF_TUNER_CLASS (V4L2_CTRL_CLASS_RF_TUNER | 1)
--
2.0.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 5/7] DocBook/media: document the new RDS RX controls
2014-07-21 13:45 [PATCH 0/7] si4713/miropcm20: RDS enhancements Hans Verkuil
` (3 preceding siblings ...)
2014-07-21 13:45 ` [PATCH 4/7] v4l2-ctrls: add RX RDS controls Hans Verkuil
@ 2014-07-21 13:45 ` Hans Verkuil
2014-07-21 13:45 ` [PATCH 6/7] v4l2-ctrls: add support for setting string controls Hans Verkuil
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Hans Verkuil @ 2014-07-21 13:45 UTC (permalink / raw)
To: linux-media; +Cc: Eduardo Valentin, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
Document the new RDS receiver controls. This will be used by the radio-miropcm20
driver.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
Documentation/DocBook/media/v4l/controls.xml | 51 ++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml
index 3ec85f6..9f5ffd8 100644
--- a/Documentation/DocBook/media/v4l/controls.xml
+++ b/Documentation/DocBook/media/v4l/controls.xml
@@ -5089,6 +5089,57 @@ description of this control class.</entry>
</row><row><entry spanname="descr">Enables/disables RDS
reception by the radio tuner</entry>
</row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RDS_RX_PTY</constant> </entry>
+ <entry>integer</entry>
+ </row>
+ <row><entry spanname="descr">Gets RDS Programme Type field.
+This encodes up to 31 pre-defined programme types.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RDS_RX_PS_NAME</constant> </entry>
+ <entry>string</entry>
+ </row>
+ <row><entry spanname="descr">Gets the Programme Service name (PS_NAME).
+It is intended for static display on a receiver. It is the primary aid to listeners in programme service
+identification and selection. In Annex E of <xref linkend="iec62106" />, the RDS specification,
+there is a full description of the correct character encoding for Programme Service name strings.
+Also from RDS specification, PS is usually a single eight character text. However, it is also possible
+to find receivers which can scroll strings sized as 8 x N characters. So, this control must be configured
+with steps of 8 characters. The result is it must always contain a string with size multiple of 8.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RDS_RX_RADIO_TEXT</constant> </entry>
+ <entry>string</entry>
+ </row>
+ <row><entry spanname="descr">Gets the Radio Text info. It is a textual description of
+what is being broadcasted. RDS Radio Text can be applied when broadcaster wishes to transmit longer PS names,
+programme-related information or any other text. In these cases, RadioText can be used in addition to
+<constant>V4L2_CID_RDS_RX_PS_NAME</constant>. The encoding for Radio Text strings is also fully described
+in Annex E of <xref linkend="iec62106" />. The length of Radio Text strings depends on which RDS Block is being
+used to transmit it, either 32 (2A block) or 64 (2B block). However, it is also possible
+to find receivers which can scroll strings sized as 32 x N or 64 x N characters. So, this control must be configured
+with steps of 32 or 64 characters. The result is it must always contain a string with size multiple of 32 or 64. </entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT</constant> </entry>
+ <entry>boolean</entry>
+ </row>
+ <row><entry spanname="descr">If set, then a traffic announcement is in progress.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RDS_RX_TRAFFIC_PROGRAM</constant> </entry>
+ <entry>boolean</entry>
+ </row>
+ <row><entry spanname="descr">If set, then the tuned programme carries traffic announcements.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_RDS_RX_MUSIC_SPEECH</constant> </entry>
+ <entry>boolean</entry>
+ </row>
+ <row><entry spanname="descr">If set, then this channel broadcasts music. If cleared, then it
+broadcasts speech. If the transmitter doesn't make this distinction, then it will be set.</entry>
+ </row>
<row>
<entry spanname="id"><constant>V4L2_CID_TUNE_DEEMPHASIS</constant> </entry>
<entry>enum v4l2_deemphasis</entry>
--
2.0.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 6/7] v4l2-ctrls: add support for setting string controls
2014-07-21 13:45 [PATCH 0/7] si4713/miropcm20: RDS enhancements Hans Verkuil
` (4 preceding siblings ...)
2014-07-21 13:45 ` [PATCH 5/7] DocBook/media: document the new RDS RX controls Hans Verkuil
@ 2014-07-21 13:45 ` Hans Verkuil
2014-07-21 13:45 ` [PATCH 7/7] radio-miropcm20: add RDS support Hans Verkuil
2014-07-21 13:59 ` [PATCH 0/7] si4713/miropcm20: RDS enhancements Hans Verkuil
7 siblings, 0 replies; 9+ messages in thread
From: Hans Verkuil @ 2014-07-21 13:45 UTC (permalink / raw)
To: linux-media; +Cc: Eduardo Valentin, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
Rather than always having to use a v4l2_ext_control struct to set
a control value from within a driver, switch to just setting the
new value. This is faster and it makes it possible to set more
complex types such as a string control as is added by this
patch.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/media/v4l2-core/v4l2-ctrls.c | 47 +++++++++++++++---------------------
include/media/v4l2-ctrls.h | 24 ++++++++++++++++++
2 files changed, 44 insertions(+), 27 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index a7b4d4c..848169c 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -3159,26 +3159,22 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
struct v4l2_ctrl *master = ctrl->cluster[0];
int i;
- /* Compound controls are not supported. The user_to_new() and
- * cur_to_user() calls below would need to be modified not to access
- * userspace memory when called from set_ctrl().
- */
- if (ctrl->is_ptr)
- return -EINVAL;
-
/* Reset the 'is_new' flags of the cluster */
for (i = 0; i < master->ncontrols; i++)
if (master->cluster[i])
master->cluster[i]->is_new = 0;
+ if (c)
+ user_to_new(c, ctrl);
+
/* For autoclusters with volatiles that are switched from auto to
manual mode we have to update the current volatile values since
those will become the initial manual values after such a switch. */
if (master->is_auto && master->has_volatiles && ctrl == master &&
- !is_cur_manual(master) && c->value == master->manual_mode_value)
+ !is_cur_manual(master) && ctrl->val == master->manual_mode_value)
update_from_auto_cluster(master);
- user_to_new(c, ctrl);
+ ctrl->is_new = 1;
return try_or_set_cluster(fh, master, true, ch_flags);
}
@@ -3226,40 +3222,37 @@ EXPORT_SYMBOL(v4l2_subdev_s_ctrl);
int __v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
{
- struct v4l2_ext_control c;
- int rval;
-
lockdep_assert_held(ctrl->handler->lock);
/* It's a driver bug if this happens. */
WARN_ON(!ctrl->is_int);
- c.value = val;
- rval = set_ctrl(NULL, ctrl, &c, 0);
- if (!rval)
- cur_to_user(&c, ctrl);
-
- return rval;
+ ctrl->val = val;
+ return set_ctrl(NULL, ctrl, NULL, 0);
}
EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl);
int __v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val)
{
- struct v4l2_ext_control c;
- int rval;
-
lockdep_assert_held(ctrl->handler->lock);
/* It's a driver bug if this happens. */
WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64);
- c.value64 = val;
- rval = set_ctrl(NULL, ctrl, &c, 0);
- if (!rval)
- cur_to_user(&c, ctrl);
-
- return rval;
+ *ctrl->p_new.p_s64 = val;
+ return set_ctrl(NULL, ctrl, NULL, 0);
}
EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_int64);
+int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s)
+{
+ lockdep_assert_held(ctrl->handler->lock);
+
+ /* It's a driver bug if this happens. */
+ WARN_ON(ctrl->type != V4L2_CTRL_TYPE_STRING);
+ strlcpy(ctrl->p_new.p_char, s, ctrl->maximum + 1);
+ return set_ctrl(NULL, ctrl, NULL, 0);
+}
+EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_string);
+
void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv)
{
if (ctrl == NULL)
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index a674bf7..b7cd7a6 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -739,6 +739,30 @@ static inline int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val)
return rval;
}
+/** __v4l2_ctrl_s_ctrl_string() - Unlocked variant of v4l2_ctrl_s_ctrl_string(). */
+int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s);
+
+/** v4l2_ctrl_s_ctrl_string() - Helper function to set a control's string value from within a driver.
+ * @ctrl: The control.
+ * @s: The new string.
+ *
+ * This set the control's new string safely by going through the control
+ * framework. This function will lock the control's handler, so it cannot be
+ * used from within the &v4l2_ctrl_ops functions.
+ *
+ * This function is for string type controls only.
+ */
+static inline int v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s)
+{
+ int rval;
+
+ v4l2_ctrl_lock(ctrl);
+ rval = __v4l2_ctrl_s_ctrl_string(ctrl, s);
+ v4l2_ctrl_unlock(ctrl);
+
+ return rval;
+}
+
/* Internal helper functions that deal with control events. */
extern const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops;
void v4l2_ctrl_replace(struct v4l2_event *old, const struct v4l2_event *new);
--
2.0.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 7/7] radio-miropcm20: add RDS support.
2014-07-21 13:45 [PATCH 0/7] si4713/miropcm20: RDS enhancements Hans Verkuil
` (5 preceding siblings ...)
2014-07-21 13:45 ` [PATCH 6/7] v4l2-ctrls: add support for setting string controls Hans Verkuil
@ 2014-07-21 13:45 ` Hans Verkuil
2014-07-21 13:59 ` [PATCH 0/7] si4713/miropcm20: RDS enhancements Hans Verkuil
7 siblings, 0 replies; 9+ messages in thread
From: Hans Verkuil @ 2014-07-21 13:45 UTC (permalink / raw)
To: linux-media; +Cc: Eduardo Valentin, Hans Verkuil
From: Hans Verkuil <hans.verkuil@cisco.com>
Once upon a time the radio-miropcm20 driver had RDS support. However, after
some internal kernel changes that support was removed. Now that we have a
nice RDS API I have been working on adding back this support. It has been
tested with the si4713 RDS transmitter and it is working quite nicely.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
drivers/media/radio/radio-miropcm20.c | 303 ++++++++++++++++++++++++++++++++--
1 file changed, 286 insertions(+), 17 deletions(-)
diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c
index 3d12edf..72df00e 100644
--- a/drivers/media/radio/radio-miropcm20.c
+++ b/drivers/media/radio/radio-miropcm20.c
@@ -1,20 +1,35 @@
-/* Miro PCM20 radio driver for Linux radio support
+/*
+ * Miro PCM20 radio driver for Linux radio support
* (c) 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
* Thanks to Norberto Pellici for the ACI device interface specification
* The API part is based on the radiotrack driver by M. Kirkwood
* This driver relies on the aci mixer provided by the snd-miro
* ALSA driver.
* Look there for further info...
- */
-
-/* What ever you think about the ACI, version 0x07 is not very well!
- * I can't get frequency, 'tuner status', 'tuner flags' or mute/mono
- * conditions... Robert
+ *
+ * From the original miro RDS sources:
+ *
+ * (c) 2001 Robert Siemer <Robert.Siemer@gmx.de>
+ *
+ * Many thanks to Fred Seidel <seidel@metabox.de>, the
+ * designer of the RDS decoder hardware. With his help
+ * I was able to code this driver.
+ * Thanks also to Norberto Pellicci, Dominic Mounteney
+ * <DMounteney@pinnaclesys.com> and www.teleauskunft.de
+ * for good hints on finding Fred. It was somewhat hard
+ * to locate him here in Germany... [:
+ *
+ * This code has been reintroduced and converted to use
+ * the new V4L2 RDS API by:
+ *
+ * Hans Verkuil <hans.verkuil@cisco.com>
*/
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/delay.h>
#include <linux/videodev2.h>
+#include <linux/kthread.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
@@ -22,6 +37,22 @@
#include <media/v4l2-event.h>
#include <sound/aci.h>
+#define RDS_DATASHIFT 2 /* Bit 2 */
+#define RDS_DATAMASK (1 << RDS_DATASHIFT)
+#define RDS_BUSYMASK 0x10 /* Bit 4 */
+#define RDS_CLOCKMASK 0x08 /* Bit 3 */
+#define RDS_DATA(x) (((x) >> RDS_DATASHIFT) & 1)
+
+#define RDS_STATUS 0x01
+#define RDS_STATIONNAME 0x02
+#define RDS_TEXT 0x03
+#define RDS_ALTFREQ 0x04
+#define RDS_TIMEDATE 0x05
+#define RDS_PI_CODE 0x06
+#define RDS_PTYTATP 0x07
+#define RDS_RESET 0x08
+#define RDS_RXVALUE 0x09
+
static int radio_nr = -1;
module_param(radio_nr, int, 0);
MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)");
@@ -30,6 +61,14 @@ struct pcm20 {
struct v4l2_device v4l2_dev;
struct video_device vdev;
struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *rds_pty;
+ struct v4l2_ctrl *rds_ps_name;
+ struct v4l2_ctrl *rds_radio_test;
+ struct v4l2_ctrl *rds_ta;
+ struct v4l2_ctrl *rds_tp;
+ struct v4l2_ctrl *rds_ms;
+ /* thread for periodic RDS status checking */
+ struct task_struct *kthread;
unsigned long freq;
u32 audmode;
struct snd_miro_aci *aci;
@@ -41,6 +80,103 @@ static struct pcm20 pcm20_card = {
.audmode = V4L2_TUNER_MODE_STEREO,
};
+
+static int rds_waitread(struct snd_miro_aci *aci)
+{
+ u8 byte;
+ int i = 2000;
+
+ do {
+ byte = inb(aci->aci_port + ACI_REG_RDS);
+ i--;
+ } while ((byte & RDS_BUSYMASK) && i);
+
+ /*
+ * It's magic, but without this the data that you read later on
+ * is unreliable and full of bit errors. With this 1 usec delay
+ * everything is fine.
+ */
+ udelay(1);
+ return i ? byte : -1;
+}
+
+static int rds_rawwrite(struct snd_miro_aci *aci, u8 byte)
+{
+ if (rds_waitread(aci) >= 0) {
+ outb(byte, aci->aci_port + ACI_REG_RDS);
+ return 0;
+ }
+ return -1;
+}
+
+static int rds_write(struct snd_miro_aci *aci, u8 byte)
+{
+ u8 sendbuffer[8];
+ int i;
+
+ for (i = 7; i >= 0; i--)
+ sendbuffer[7 - i] = (byte & (1 << i)) ? RDS_DATAMASK : 0;
+ sendbuffer[0] |= RDS_CLOCKMASK;
+
+ for (i = 0; i < 8; i++)
+ rds_rawwrite(aci, sendbuffer[i]);
+ return 0;
+}
+
+static int rds_readcycle_nowait(struct snd_miro_aci *aci)
+{
+ outb(0, aci->aci_port + ACI_REG_RDS);
+ return rds_waitread(aci);
+}
+
+static int rds_readcycle(struct snd_miro_aci *aci)
+{
+ if (rds_rawwrite(aci, 0) < 0)
+ return -1;
+ return rds_waitread(aci);
+}
+
+static int rds_ack(struct snd_miro_aci *aci)
+{
+ int i = rds_readcycle(aci);
+
+ if (i < 0)
+ return -1;
+ if (i & RDS_DATAMASK)
+ return 0; /* ACK */
+ return 1; /* NACK */
+}
+
+static int rds_cmd(struct snd_miro_aci *aci, u8 cmd, u8 databuffer[], u8 datasize)
+{
+ int i, j;
+
+ rds_write(aci, cmd);
+
+ /* RDS_RESET doesn't need further processing */
+ if (cmd == RDS_RESET)
+ return 0;
+ if (rds_ack(aci))
+ return -EIO;
+ if (datasize == 0)
+ return 0;
+
+ /* to be able to use rds_readcycle_nowait()
+ I have to waitread() here */
+ if (rds_waitread(aci) < 0)
+ return -1;
+
+ memset(databuffer, 0, datasize);
+
+ for (i = 0; i < 8 * datasize; i++) {
+ j = rds_readcycle_nowait(aci);
+ if (j < 0)
+ return -EIO;
+ databuffer[i / 8] |= RDS_DATA(j) << (7 - (i % 8));
+ }
+ return 0;
+}
+
static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq)
{
unsigned char freql;
@@ -54,17 +190,10 @@ static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq)
freql = freq & 0xff;
freqh = freq >> 8;
+ rds_cmd(aci, RDS_RESET, 0, 0);
return snd_aci_cmd(aci, ACI_WRITE_TUNE, freql, freqh);
}
-static const struct v4l2_file_operations pcm20_fops = {
- .owner = THIS_MODULE,
- .open = v4l2_fh_open,
- .poll = v4l2_ctrl_poll,
- .release = v4l2_fh_release,
- .unlocked_ioctl = video_ioctl2,
-};
-
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *v)
{
@@ -73,16 +202,31 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(v->driver, "Miro PCM20", sizeof(v->driver));
strlcpy(v->card, "Miro PCM20", sizeof(v->card));
snprintf(v->bus_info, sizeof(v->bus_info), "ISA:%s", dev->v4l2_dev.name);
- v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE;
v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
+static bool sanitize(char *p, int size)
+{
+ int i;
+ bool ret = true;
+
+ for (i = 0; i < size; i++) {
+ if (p[i] < 32 || p[i] >= 128) {
+ p[i] = ' ';
+ ret = false;
+ }
+ }
+ return ret;
+}
+
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
struct pcm20 *dev = video_drvdata(file);
int res;
+ u8 buf;
if (v->index)
return -EINVAL;
@@ -97,8 +241,12 @@ static int vidioc_g_tuner(struct file *file, void *priv,
res = snd_aci_cmd(dev->aci, ACI_READ_TUNERSTEREO, -1, -1);
v->rxsubchans = (res & 0x40) ? V4L2_TUNER_SUB_MONO :
V4L2_TUNER_SUB_STEREO;
- v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+ v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_CONTROLS;
v->audmode = dev->audmode;
+ res = rds_cmd(dev->aci, RDS_RXVALUE, &buf, 1);
+ if (res >= 0 && buf)
+ v->rxsubchans |= V4L2_TUNER_SUB_RDS;
return 0;
}
@@ -157,6 +305,115 @@ static int pcm20_s_ctrl(struct v4l2_ctrl *ctrl)
return -EINVAL;
}
+static int pcm20_thread(void *data)
+{
+ struct pcm20 *dev = data;
+ const unsigned no_rds_start_counter = 5;
+ const unsigned sleep_msecs = 2000;
+ unsigned no_rds_counter = no_rds_start_counter;
+
+ for (;;) {
+ char text_buffer[66];
+ u8 buf;
+ int res;
+
+ msleep_interruptible(sleep_msecs);
+
+ if (kthread_should_stop())
+ break;
+
+ res = rds_cmd(dev->aci, RDS_RXVALUE, &buf, 1);
+ if (res)
+ continue;
+ if (buf == 0) {
+ if (no_rds_counter == 0)
+ continue;
+ no_rds_counter--;
+ if (no_rds_counter)
+ continue;
+
+ /*
+ * No RDS seen for no_rds_start_counter * sleep_msecs
+ * milliseconds, clear all RDS controls to their
+ * default values.
+ */
+ v4l2_ctrl_s_ctrl_string(dev->rds_ps_name, "");
+ v4l2_ctrl_s_ctrl(dev->rds_ms, 1);
+ v4l2_ctrl_s_ctrl(dev->rds_ta, 0);
+ v4l2_ctrl_s_ctrl(dev->rds_tp, 0);
+ v4l2_ctrl_s_ctrl(dev->rds_pty, 0);
+ v4l2_ctrl_s_ctrl_string(dev->rds_radio_test, "");
+ continue;
+ }
+ no_rds_counter = no_rds_start_counter;
+
+ res = rds_cmd(dev->aci, RDS_STATUS, &buf, 1);
+ if (res)
+ continue;
+ if ((buf >> 3) & 1) {
+ res = rds_cmd(dev->aci, RDS_STATIONNAME, text_buffer, 8);
+ text_buffer[8] = 0;
+ if (!res && sanitize(text_buffer, 8))
+ v4l2_ctrl_s_ctrl_string(dev->rds_ps_name, text_buffer);
+ }
+ if ((buf >> 6) & 1) {
+ u8 pty;
+
+ res = rds_cmd(dev->aci, RDS_PTYTATP, &pty, 1);
+ if (!res) {
+ v4l2_ctrl_s_ctrl(dev->rds_ms, !!(pty & 0x01));
+ v4l2_ctrl_s_ctrl(dev->rds_ta, !!(pty & 0x02));
+ v4l2_ctrl_s_ctrl(dev->rds_tp, !!(pty & 0x80));
+ v4l2_ctrl_s_ctrl(dev->rds_pty, (pty >> 2) & 0x1f);
+ }
+ }
+ if ((buf >> 4) & 1) {
+ res = rds_cmd(dev->aci, RDS_TEXT, text_buffer, 65);
+ text_buffer[65] = 0;
+ if (!res && sanitize(text_buffer + 1, 64))
+ v4l2_ctrl_s_ctrl_string(dev->rds_radio_test, text_buffer + 1);
+ }
+ }
+ return 0;
+}
+
+static int pcm20_open(struct file *file)
+{
+ struct pcm20 *dev = video_drvdata(file);
+ int res = v4l2_fh_open(file);
+
+ if (!res && v4l2_fh_is_singular_file(file) &&
+ IS_ERR_OR_NULL(dev->kthread)) {
+ dev->kthread = kthread_run(pcm20_thread, dev, "%s",
+ dev->v4l2_dev.name);
+ if (IS_ERR(dev->kthread)) {
+ v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
+ v4l2_fh_release(file);
+ return PTR_ERR(dev->kthread);
+ }
+ }
+ return res;
+}
+
+static int pcm20_release(struct file *file)
+{
+ struct pcm20 *dev = video_drvdata(file);
+
+ if (v4l2_fh_is_singular_file(file) && !IS_ERR_OR_NULL(dev->kthread)) {
+ kthread_stop(dev->kthread);
+ dev->kthread = NULL;
+ }
+ return v4l2_fh_release(file);
+}
+
+static const struct v4l2_file_operations pcm20_fops = {
+ .owner = THIS_MODULE,
+ .open = pcm20_open,
+ .poll = v4l2_ctrl_poll,
+ .release = pcm20_release,
+ .unlocked_ioctl = video_ioctl2,
+};
+
static const struct v4l2_ioctl_ops pcm20_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_g_tuner = vidioc_g_tuner,
@@ -195,9 +452,21 @@ static int __init pcm20_init(void)
}
hdl = &dev->ctrl_handler;
- v4l2_ctrl_handler_init(hdl, 1);
+ v4l2_ctrl_handler_init(hdl, 7);
v4l2_ctrl_new_std(hdl, &pcm20_ctrl_ops,
V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+ dev->rds_pty = v4l2_ctrl_new_std(hdl, NULL,
+ V4L2_CID_RDS_RX_PTY, 0, 0x1f, 1, 0);
+ dev->rds_ps_name = v4l2_ctrl_new_std(hdl, NULL,
+ V4L2_CID_RDS_RX_PS_NAME, 0, 8, 8, 0);
+ dev->rds_radio_test = v4l2_ctrl_new_std(hdl, NULL,
+ V4L2_CID_RDS_RX_RADIO_TEXT, 0, 64, 64, 0);
+ dev->rds_ta = v4l2_ctrl_new_std(hdl, NULL,
+ V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT, 0, 1, 1, 0);
+ dev->rds_tp = v4l2_ctrl_new_std(hdl, NULL,
+ V4L2_CID_RDS_RX_TRAFFIC_PROGRAM, 0, 1, 1, 0);
+ dev->rds_ms = v4l2_ctrl_new_std(hdl, NULL,
+ V4L2_CID_RDS_RX_MUSIC_SPEECH, 0, 1, 1, 1);
v4l2_dev->ctrl_handler = hdl;
if (hdl->error) {
res = hdl->error;
--
2.0.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 0/7] si4713/miropcm20: RDS enhancements
2014-07-21 13:45 [PATCH 0/7] si4713/miropcm20: RDS enhancements Hans Verkuil
` (6 preceding siblings ...)
2014-07-21 13:45 ` [PATCH 7/7] radio-miropcm20: add RDS support Hans Verkuil
@ 2014-07-21 13:59 ` Hans Verkuil
7 siblings, 0 replies; 9+ messages in thread
From: Hans Verkuil @ 2014-07-21 13:59 UTC (permalink / raw)
To: linux-media; +Cc: Eduardo Valentin
On 07/21/2014 03:45 PM, Hans Verkuil wrote:
> This patch series adds a bunch of missing RDS TX controls and implements
> them in the si4713 driver. It also adds back RDS support to the miropcm20
> driver.
>
> The Alternate Frequencies control is a u32 array since there can be up to
> 25 alternate frequencies. This was also the reason why I am only now posting
> this series since it had to wait for compound control support to go in.
>
> I've tested both drivers with my si4713 and miropcm20 boards.
I forgot to mention that this sits on top of the ctrls patches from this
pull request:
https://patchwork.linuxtv.org/patch/24885/
Regards,
Hans
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2014-07-21 13:59 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-07-21 13:45 [PATCH 0/7] si4713/miropcm20: RDS enhancements Hans Verkuil
2014-07-21 13:45 ` [PATCH 1/7] v4l2-ctrls: add new RDS TX controls Hans Verkuil
2014-07-21 13:45 ` [PATCH 2/7] DocBook/media: document the " Hans Verkuil
2014-07-21 13:45 ` [PATCH 3/7] si4713: add the missing RDS functionality Hans Verkuil
2014-07-21 13:45 ` [PATCH 4/7] v4l2-ctrls: add RX RDS controls Hans Verkuil
2014-07-21 13:45 ` [PATCH 5/7] DocBook/media: document the new RDS RX controls Hans Verkuil
2014-07-21 13:45 ` [PATCH 6/7] v4l2-ctrls: add support for setting string controls Hans Verkuil
2014-07-21 13:45 ` [PATCH 7/7] radio-miropcm20: add RDS support Hans Verkuil
2014-07-21 13:59 ` [PATCH 0/7] si4713/miropcm20: RDS enhancements Hans Verkuil
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).