This patch adds support for v4l-audio devices to v4l2-ctl, v4l2-compliance and the test-media script. This is RFC quality: when media controller support is added to the m2m audio drivers, this patch will need to be updated. And it also need to be split into smaller pieces, but I will wait with that until vim2m-audio uses the media controller. Signed-off-by: Hans Verkuil <hverkuil-cisco@xxxxxxxxx> --- diff --git a/contrib/test/test-media b/contrib/test/test-media index afe20760..c2a71b89 100755 --- a/contrib/test/test-media +++ b/contrib/test/test-media @@ -7,6 +7,7 @@ vidtv=0 vivid=0 vim2m=0 +vim2m_audio=0 vimc=0 vicodec=0 cec=0 @@ -53,13 +54,14 @@ if [ -z "$1" ]; then echo Test Targets: echo "vivid: test the vivid driver" echo "vim2m: test the vim2m driver" + echo "vim2m-audio: test the vim2m-audio driver" echo "vimc: test the vimc driver" echo "vicodec: test the vicodec driver" echo "vidtv: test the vidtv driver" echo "cec: adds the vivid CEC compliance tests, except for the CEC standby/wakeup tests." echo "cec-pwr: adds the vivid CEC compliance tests, including the CEC standby/wakeup tests." - echo "all: equals 'vivid vim2m vimc vicodec vidtv cec cec-pwr'" - echo "mc: equals 'vivid vim2m vimc vicodec vidtv'" + echo "all: equals 'vivid vim2m vim2m-audio vimc vicodec vidtv cec cec-pwr'" + echo "mc: equals 'vivid vim2m vim2m-audio vimc vicodec vidtv'" exit 0 fi @@ -116,6 +118,7 @@ while [ ! -z "$1" ]; do vidtv=1 vivid=1 vim2m=1 + vim2m_audio=1 vimc=1 vicodec=1 cec=1 @@ -124,6 +127,7 @@ while [ ! -z "$1" ]; do mc) vivid=1 vim2m=1 + vim2m_audio=1 vimc=1 vicodec=1 vidtv=1 @@ -137,6 +141,9 @@ while [ ! -z "$1" ]; do vim2m) vim2m=1 ;; + vim2m-audio) + vim2m_audio=1 + ;; vimc) vimc=1 ;; @@ -421,6 +428,83 @@ if [ $vim2m -eq 1 -a $setup -eq 0 ]; then echo fi + +if [ $vim2m_audio -eq 1 ]; then + rmmod vim2m-audio 2&>/dev/null + modprobe vim2m-audio + sleep $modprobe_time + dmesg -n notice + + if ! $v4l2_ctl -z platform:vim2m-audio ; then + echo "FAIL: the vim2m-audio module failed to load" | tee -a $tmp + echo "Grand Total for vim2m-audio: Succeeded: 0, Failed: 1, Warnings: 0" | tee -a $tmp + echo "Final Summary: 1, Succeeded: 0, Failed: 1, Warnings: 0" + rmmod vivid + exit 0 + fi +fi + +if [ $vim2m_audio -eq 1 -a $setup -eq 0 ]; then + echo + echo vim2m-audio compliance tests | tee /dev/kmsg + echo + date + stdbuf -oL $v4l2_compliance -A0 -z platform:vivid-002 -e vivid-002-vid-cap -s10 -P -a 2>&1 | tee -a $tmp + echo + echo + echo + echo + echo + echo + echo + echo + echo + echo unbind vim2m-audio | tee /dev/kmsg + echo + echo -n vim2m-audio.0 >/sys/bus/platform/drivers/vim2m-audio/unbind + sleep $unbind_time + echo + echo rebind vim2m-audio | tee /dev/kmsg + echo + echo -n vim2m-audio.0 >/sys/bus/platform/drivers/vim2m-audio/bind + sleep 1 + echo + echo second unbind vim2m-audio | tee /dev/kmsg + echo + for i in `$v4l2_ctl -z platform:vim2m-audio --list-devices`; do + let "t = 1 + $RANDOM / 4096" + echo $i: sleep ${t}s + sleep $t <$i & + done + sleep 1 + echo + echo -n vim2m-audio.0 >/sys/bus/platform/drivers/vim2m-audio/unbind + sleep $reunbind_time + echo + echo rmmod vim2m-audio | tee /dev/kmsg + echo + rmmod vim2m-audio + sleep $rmmod_time + if [ $kmemleak -eq 1 ]; then + echo + echo kmemleak results for vim2m-audio: + echo + echo scan >/sys/kernel/debug/kmemleak + cat /sys/kernel/debug/kmemleak + echo + echo end of kmemleak results + echo clear >/sys/kernel/debug/kmemleak + fi + echo + echo + echo + echo + echo + echo + echo + echo +fi + if [ $vimc -eq 1 ]; then rmmod vimc 2&>/dev/null modprobe vimc diff --git a/utils/common/cv4l-helpers.h b/utils/common/cv4l-helpers.h index 91a04146..235368ec 100644 --- a/utils/common/cv4l-helpers.h +++ b/utils/common/cv4l-helpers.h @@ -78,6 +78,13 @@ public: bool has_rds_out() const { return v4l_has_rds_out(this); } bool has_sdr_cap() const { return v4l_has_sdr_cap(this); } bool has_sdr_out() const { return v4l_has_sdr_out(this); } + bool has_touch() const { return v4l_has_touch(this); } + bool has_meta_cap() const { return v4l_has_meta_cap(this); } + bool has_meta_out() const { return v4l_has_meta_out(this); } + bool has_audio_cap() const { return v4l_has_audio_cap(this); } + bool has_audio_out() const { return v4l_has_audio_out(this); } + bool has_audio_m2m() const { return v4l_has_audio_m2m(this); } + bool has_m2m() const { return v4l_has_m2m(this); } bool has_hwseek() const { return v4l_has_hwseek(this); } bool has_rw() const { return v4l_has_rw(this); } bool has_streaming() const { return v4l_has_streaming(this); } diff --git a/utils/common/v4l-helpers.h b/utils/common/v4l-helpers.h index 5a256603..a01b3e48 100644 --- a/utils/common/v4l-helpers.h +++ b/utils/common/v4l-helpers.h @@ -404,11 +404,26 @@ static inline bool v4l_has_touch(const struct v4l_fd *f) return v4l_g_caps(f) & V4L2_CAP_TOUCH; } +static inline bool v4l_has_audio_cap(const struct v4l_fd *f) +{ + return v4l_g_caps(f) & V4L2_CAP_AUDIO_M2M; +} + +static inline bool v4l_has_audio_out(const struct v4l_fd *f) +{ + return v4l_g_caps(f) & V4L2_CAP_AUDIO_M2M; +} + static inline bool v4l_has_audio_m2m(const struct v4l_fd *f) { return v4l_g_caps(f) & V4L2_CAP_AUDIO_M2M; } +static inline bool v4l_has_m2m(const struct v4l_fd *f) +{ + return v4l_has_vid_m2m(f) || v4l_has_audio_m2m(f); +} + static inline bool v4l_has_hwseek(const struct v4l_fd *f) { return v4l_g_caps(f) & V4L2_CAP_HW_FREQ_SEEK; @@ -454,6 +469,10 @@ static inline __u32 v4l_determine_type(const struct v4l_fd *f) return V4L2_BUF_TYPE_META_CAPTURE; if (v4l_has_meta_out(f)) return V4L2_BUF_TYPE_META_OUTPUT; + if (v4l_has_audio_cap(f)) + return V4L2_BUF_TYPE_AUDIO_CAPTURE; + if (v4l_has_audio_out(f)) + return V4L2_BUF_TYPE_AUDIO_OUTPUT; return 0; } @@ -706,6 +725,10 @@ static inline void v4l_format_s_pixelformat(struct v4l2_format *fmt, __u32 pixel case V4L2_BUF_TYPE_META_OUTPUT: fmt->fmt.meta.dataformat = pixelformat; break; + case V4L2_BUF_TYPE_AUDIO_CAPTURE: + case V4L2_BUF_TYPE_AUDIO_OUTPUT: + fmt->fmt.audio.audioformat = pixelformat; + break; } } @@ -727,6 +750,9 @@ static inline __u32 v4l_format_g_pixelformat(const struct v4l2_format *fmt) case V4L2_BUF_TYPE_META_CAPTURE: case V4L2_BUF_TYPE_META_OUTPUT: return fmt->fmt.meta.dataformat; + case V4L2_BUF_TYPE_AUDIO_CAPTURE: + case V4L2_BUF_TYPE_AUDIO_OUTPUT: + return fmt->fmt.audio.audioformat; default: return 0; } @@ -1068,6 +1094,9 @@ v4l_format_g_sizeimage(const struct v4l2_format *fmt, unsigned plane) case V4L2_BUF_TYPE_META_CAPTURE: case V4L2_BUF_TYPE_META_OUTPUT: return plane ? 0 : fmt->fmt.meta.buffersize; + case V4L2_BUF_TYPE_AUDIO_CAPTURE: + case V4L2_BUF_TYPE_AUDIO_OUTPUT: + return plane ? 0 : fmt->fmt.audio.buffersize; default: return 0; } @@ -1192,12 +1221,22 @@ static inline bool v4l_type_is_meta(unsigned type) type == V4L2_BUF_TYPE_META_OUTPUT; } +static inline bool v4l_type_is_audio(unsigned type) +{ + return type == V4L2_BUF_TYPE_AUDIO_CAPTURE || + type == V4L2_BUF_TYPE_AUDIO_OUTPUT; +} + static inline unsigned v4l_type_invert(unsigned type) { if (v4l_type_is_planar(type)) return v4l_type_is_output(type) ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + if (v4l_type_is_audio(type)) + return v4l_type_is_output(type) ? + V4L2_BUF_TYPE_AUDIO_CAPTURE : + V4L2_BUF_TYPE_AUDIO_OUTPUT; return v4l_type_is_output(type) ? V4L2_BUF_TYPE_VIDEO_CAPTURE : V4L2_BUF_TYPE_VIDEO_OUTPUT; diff --git a/utils/v4l2-compliance/v4l2-compliance.h b/utils/v4l2-compliance/v4l2-compliance.h index bba56b12..1e83fba3 100644 --- a/utils/v4l2-compliance/v4l2-compliance.h +++ b/utils/v4l2-compliance/v4l2-compliance.h @@ -102,7 +102,7 @@ using frmsizes_count_map = std::map<__u32, unsigned>; struct base_node; -#define V4L2_BUF_TYPE_LAST V4L2_BUF_TYPE_META_OUTPUT +#define V4L2_BUF_TYPE_LAST V4L2_BUF_TYPE_AUDIO_OUTPUT struct base_node { bool is_video; diff --git a/utils/v4l2-compliance/v4l2-test-buffers.cpp b/utils/v4l2-compliance/v4l2-test-buffers.cpp index 6d592c9b..d5c8d17c 100644 --- a/utils/v4l2-compliance/v4l2-test-buffers.cpp +++ b/utils/v4l2-compliance/v4l2-test-buffers.cpp @@ -235,12 +235,14 @@ public: if (v4l_type_is_output(g_type())) fill_output_buf(fill_bytesused); err = node->qbuf(*this); - if (err == 0 && - v4l_type_is_video(g_type()) && v4l_type_is_output(g_type())) { - fail_on_test(g_field() == V4L2_FIELD_ANY); + if (err) + return err; + if (v4l_type_is_output(g_type())) { + if (v4l_type_is_video(g_type())) + fail_on_test(g_field() == V4L2_FIELD_ANY); buffer_info[g_timestamp()] = buf; } - return err; + return 0; } int qbuf(node *node, const cv4l_queue &q) { diff --git a/utils/v4l2-compliance/v4l2-test-formats.cpp b/utils/v4l2-compliance/v4l2-test-formats.cpp index c92e9658..adec678a 100644 --- a/utils/v4l2-compliance/v4l2-test-formats.cpp +++ b/utils/v4l2-compliance/v4l2-test-formats.cpp @@ -451,6 +451,7 @@ static int testFormatsType(struct node *node, int ret, unsigned type, struct v4 struct v4l2_sliced_vbi_format &sliced = fmt.fmt.sliced; struct v4l2_sdr_format &sdr = fmt.fmt.sdr; struct v4l2_meta_format &meta = fmt.fmt.meta; + struct v4l2_audio_format &audio = fmt.fmt.audio; unsigned min_data_samples; unsigned min_sampling_rate; v4l2_std_id std; @@ -595,6 +596,13 @@ static int testFormatsType(struct node *node, int ret, unsigned type, struct v4 meta.dataformat, fcc2s(meta.dataformat).c_str(), type); fail_on_test(meta.buffersize == 0); break; + case V4L2_BUF_TYPE_AUDIO_CAPTURE: + case V4L2_BUF_TYPE_AUDIO_OUTPUT: + if (map.find(audio.audioformat) == map.end()) + return fail("audioformat %08x (%s) for buftype %d not reported by ENUM_FMT\n", + audio.audioformat, fcc2s(audio.audioformat).c_str(), type); + fail_on_test(audio.buffersize == 0); + break; case V4L2_BUF_TYPE_PRIVATE: break; } @@ -709,6 +717,9 @@ static bool matchFormats(const struct v4l2_format &f1, const struct v4l2_format case V4L2_BUF_TYPE_META_CAPTURE: case V4L2_BUF_TYPE_META_OUTPUT: return !memcmp(&f1.fmt.meta, &f2.fmt.meta, sizeof(f1.fmt.meta)); + case V4L2_BUF_TYPE_AUDIO_CAPTURE: + case V4L2_BUF_TYPE_AUDIO_OUTPUT: + return !memcmp(&f1.fmt.audio, &f2.fmt.audio, sizeof(f1.fmt.audio)); } return false; @@ -788,6 +799,10 @@ int testTryFormats(struct node *node) case V4L2_BUF_TYPE_META_OUTPUT: pixelformat = fmt.fmt.meta.dataformat; break; + case V4L2_BUF_TYPE_AUDIO_CAPTURE: + case V4L2_BUF_TYPE_AUDIO_OUTPUT: + pixelformat = fmt.fmt.audio.audioformat; + break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: pixelformat = fmt.fmt.pix_mp.pixelformat; @@ -866,6 +881,9 @@ static int testM2MFormats(struct node *node) fail_on_test(node->g_fmt(fmt_out, out_type)); fail_on_test(node->g_fmt(fmt_cap, cap_type)); + if (node->has_audio_m2m()) + return 0; + /* * JPEG codec have fixed colorspace, so these tests * are different compared to other m2m devices. @@ -1138,6 +1156,10 @@ int testSetFormats(struct node *node) case V4L2_BUF_TYPE_META_OUTPUT: pixelformat = fmt_set.fmt.meta.dataformat; break; + case V4L2_BUF_TYPE_AUDIO_CAPTURE: + case V4L2_BUF_TYPE_AUDIO_OUTPUT: + pixelformat = fmt_set.fmt.audio.audioformat; + break; case V4L2_BUF_TYPE_SDR_CAPTURE: case V4L2_BUF_TYPE_SDR_OUTPUT: pixelformat = fmt_set.fmt.sdr.pixelformat; diff --git a/utils/v4l2-ctl/v4l2-ctl-streaming.cpp b/utils/v4l2-ctl/v4l2-ctl-streaming.cpp index ffa36164..18dd2c9b 100644 --- a/utils/v4l2-ctl/v4l2-ctl-streaming.cpp +++ b/utils/v4l2-ctl/v4l2-ctl-streaming.cpp @@ -2156,7 +2156,7 @@ static FILE *open_input_file(cv4l_fd &fd, __u32 type) static void streaming_set_out(cv4l_fd &fd, cv4l_fd &exp_fd) { - __u32 type = fd.has_vid_m2m() ? v4l_type_invert(fd.g_type()) : fd.g_type(); + __u32 type = fd.has_m2m() ? v4l_type_invert(fd.g_type()) : fd.g_type(); cv4l_queue q(type, out_memory); cv4l_queue exp_q(exp_fd.g_type(), V4L2_MEMORY_MMAP); int fd_flags = fcntl(fd.g_fd(), F_GETFL); @@ -2713,7 +2713,7 @@ static void streaming_set_m2m(cv4l_fd &fd, cv4l_fd &exp_fd) fd.g_fmt(fmt[OUT], out.g_type()); fd.g_fmt(fmt[CAP], in.g_type()); - if (!fd.has_vid_m2m()) { + if (!fd.has_m2m()) { fprintf(stderr, "unsupported m2m stream type\n"); return; } @@ -2763,7 +2763,7 @@ static void streaming_set_cap2out(cv4l_fd &fd, cv4l_fd &out_fd) bool use_poll = options[OptStreamPoll]; bool use_dmabuf = options[OptStreamDmaBuf] || options[OptStreamOutDmaBuf]; bool use_userptr = options[OptStreamUser] && options[OptStreamOutUser]; - __u32 out_type = out_fd.has_vid_m2m() ? v4l_type_invert(out_fd.g_type()) : out_fd.g_type(); + __u32 out_type = out_fd.has_m2m() ? v4l_type_invert(out_fd.g_type()) : out_fd.g_type(); cv4l_queue in(fd.g_type(), memory); cv4l_queue out(out_type, out_memory); fps_timestamps fps_ts[2]; @@ -3002,7 +3002,7 @@ void streaming_list(cv4l_fd &fd, cv4l_fd &out_fd) list_buffers(fd, fd.g_type()); if (options[OptListBuffersOut]) - list_buffers(*p_out_fd, p_out_fd->has_vid_m2m() ? + list_buffers(*p_out_fd, p_out_fd->has_m2m() ? v4l_type_invert(p_out_fd->g_type()) : p_out_fd->g_type()); if (options[OptStreamBufCaps])