diff mbox series

[RFC] v4l-utils: add support for v4l-audioX devices

Message ID a4d502be-42aa-43d1-b816-a2c545509d9f@xs4all.nl
State New
Headers show
Series [RFC] v4l-utils: add support for v4l-audioX devices | expand

Commit Message

Hans Verkuil Nov. 7, 2023, 9:46 a.m. UTC
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@xs4all.nl>
---
diff mbox series

Patch

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])