mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Hans Verkuil <hverkuil+cisco@kernel.org>
To: Hermes.wu@ite.com.tw, Mauro Carvalho Chehab <mchehab@kernel.org>,
	Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>
Cc: linux-media@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, Mark Brown <broonie@kernel.org>,
	Liam Girdwood <lgirdwood@gmail.com>,
	linux-sound@vger.kernel.org, linux-api@vger.kernel.org
Subject: Re: [PATCH 5/5] media: i2c: it6625: capture compressed and HBR audio, not just LPCM
Date: Wed, 11 Nov 2026 13:16:38 +0100	[thread overview]
Message-ID: <38706010-b949-4f70-b45c-6800f7fcf942@kernel.org> (raw)
In-Reply-To: <20261103-upstream-it6625-alsa-i2s-v1-5-bb91e2879b01@ite.com.tw>

On 03/11/2026 07:50, Hermes Wu via B4 Relay wrote:
> From: Hermes Wu <Hermes.wu@ite.com.tw>
> 
> The capture DAI accepts only LPCM. The bridge also outputs compressed
> IEC 61937 streams (AC-3, DTS, ...) and high bit rate (HBR) streams
> (Dolby TrueHD/MAT, DTS-HD Master Audio) on the same I2S interface, but
> these are rejected.
> 
> Add it6625_audio_xport(), which derives the I2S frame rate and slot
> count from the stream's coding type and sampling rate. LPCM is
> unchanged. A compressed stream is carried in 2 slots at its own
> sampling rate, except at the HBR rates of 768 kHz and 705.6 kHz, where
> one IEC 61937 stream is spread over 8 slots on 4 data lines at a quarter
> of that rate. The bridge firmware also reports the HBR coding type for
> some compressed streams at ordinary rates, such as 48 kHz DTS, so the
> sampling rate, not the coding type, selects the layout. A compressed
> stream at 768 kHz or 705.6 kHz is rejected when fewer than 4 data lines
> are connected.
> 
> Use the derived rate and channel count in the eligibility check, the
> startup constraints and the hw_params comparison. DSD is still
> rejected. The driver does not parse the compressed data: userspace
> receives the IEC 61937 words as they are.
> 
> Add a read-only "Audio Type" menu control, V4L2_CID_IT6625_AUDIO_TYPE,
> so that userspace can tell LPCM, compressed, HBR and DSD streams apart
> without parsing the audio InfoFrame, and document it.
> 
> Signed-off-by: Hermes Wu <Hermes.wu@ite.com.tw>
> ---
>  .../userspace-api/media/drivers/it6625.rst         |  30 ++++++
>  drivers/media/i2c/it6625.c                         | 118 ++++++++++++++++++---
>  include/uapi/linux/it6625.h                        |  25 +++++
>  3 files changed, 157 insertions(+), 16 deletions(-)
> 
> diff --git a/Documentation/userspace-api/media/drivers/it6625.rst b/Documentation/userspace-api/media/drivers/it6625.rst
> index efd970e8124979abffb6f9ba3ea1e1bc193820b3..1c4d319f88946ef79e0d847ba0f8adf5b2132210 100644
> --- a/Documentation/userspace-api/media/drivers/it6625.rst
> +++ b/Documentation/userspace-api/media/drivers/it6625.rst
> @@ -16,3 +16,33 @@ them are read-only and describe the audio of the HDMI input.
>  ``V4L2_CID_IT6625_AUDIO_PRESENT (boolean)``
>  -------------------------------------------
>      Whether the bridge currently receives HDMI audio.
> +
> +``V4L2_CID_IT6625_AUDIO_TYPE (menu)``
> +-------------------------------------
> +    The coding type of the HDMI audio stream, as last read successfully from
> +    the bridge. The value is kept when audio goes away or reading the audio
> +    status fails, so it describes the current stream only while
> +    ``V4L2_CID_IT6625_AUDIO_PRESENT`` is set and
> +    ``V4L2_CID_IT6625_AUDIO_SAMPLING_RATE`` is not 0.
> +
> +.. flat-table::
> +    :header-rows:  0
> +    :stub-columns: 0
> +    :widths:       1 4
> +
> +    * - ``V4L2_IT6625_AUDIO_TYPE_LPCM``
> +      - Linear PCM.
> +    * - ``V4L2_IT6625_AUDIO_TYPE_NLPCM``
> +      - Compressed audio in IEC 61937 format.
> +    * - ``V4L2_IT6625_AUDIO_TYPE_HBR``
> +      - High bit rate compressed audio in IEC 61937 format. The bridge also
> +        reports this type for some compressed streams at ordinary sampling
> +        rates.
> +    * - ``V4L2_IT6625_AUDIO_TYPE_DSD``
> +      - One-bit audio. It cannot be captured.

This too should become a standard DV audio control.

Regards,

	Hans

> +
> +Compressed and high bit rate streams are captured as raw IEC 61937 data. The
> +capture layout follows the sampling rate, not the coding type: at 768 kHz and
> +705.6 kHz the stream is captured as 8 channels at a quarter of that rate,
> +which needs 4 connected I2S data lines; at other rates it is captured as 2
> +channels at the sampling rate.
> diff --git a/drivers/media/i2c/it6625.c b/drivers/media/i2c/it6625.c
> index f1bd42ad15eda1a2bd22dac00ba11a5b1c790e71..f9cd15dc24960cf2caf98576ecccd3f803dbfd86 100644
> --- a/drivers/media/i2c/it6625.c
> +++ b/drivers/media/i2c/it6625.c
> @@ -101,6 +101,9 @@ MODULE_PARM_DESC(debug, "debug level (0-3)");
>  #define REG_HDMI_AUDIO_INFO3 0x6D
>  #define M_AUD_TYPE  GENMASK(1, 0)
>  #define AUD_TYPE_LPCM 0
> +#define AUD_TYPE_NLPCM 1
> +#define AUD_TYPE_HBR 2
> +#define AUD_TYPE_DSD 3
>  #define B_AUD_MS  BIT(2)
>  #define B_AUD_3D  BIT(3)
>  
> @@ -119,6 +122,9 @@ MODULE_PARM_DESC(debug, "debug level (0-3)");
>  #define IT6625_AUDIO_MAX_LANES 8
>  #define IT6625_AUDIO_MAX_CHANNELS 8
>  #define IT6625_AUDIO_MAX_RATE 192000
> +/* HBR: one IEC 61937 stream spread over 4 lanes (8 slots) per frame */
> +#define IT6625_AUDIO_HBR_LANES 4
> +#define IT6625_AUDIO_HBR_CHANNELS 8
>  /* status re-check period while a capture stream is open */
>  #define IT6625_AUDIO_REFRESH_MS 200
>  /* re-checks after an event that left present audio without a valid rate */
> @@ -322,6 +328,7 @@ struct it6625 {
>  	struct v4l2_ctrl *ctrl_5v_detect;
>  	struct v4l2_ctrl *ctrl_audio_sampling_rate;
>  	struct v4l2_ctrl *ctrl_audio_present;
> +	struct v4l2_ctrl *ctrl_audio_type;
>  	struct v4l2_ctrl *ctrl_link_freq;
>  
>  	struct delayed_work hpd_delayed_work;
> @@ -1069,6 +1076,9 @@ static int it6625_audio_update_ctrls(struct it6625 *it6625)
>  	if (!ret)
>  		ret = __v4l2_ctrl_s_ctrl(it6625->ctrl_audio_present,
>  					 info.present);
> +	if (!ret)
> +		ret = __v4l2_ctrl_s_ctrl(it6625->ctrl_audio_type,
> +					 info.stream.type);
>  
>  	v4l2_ctrl_unlock(it6625->ctrl_audio_present);
>  
> @@ -2124,6 +2134,25 @@ static const struct v4l2_ctrl_config it6625_ctrl_audio_present = {
>  	.flags = V4L2_CTRL_FLAG_READ_ONLY,
>  };
>  
> +static const char * const it6625_audio_type_menu[] = {
> +	[V4L2_IT6625_AUDIO_TYPE_LPCM] = "LPCM",
> +	[V4L2_IT6625_AUDIO_TYPE_NLPCM] = "NLPCM",
> +	[V4L2_IT6625_AUDIO_TYPE_HBR] = "HBR",
> +	[V4L2_IT6625_AUDIO_TYPE_DSD] = "DSD",
> +	NULL,
> +};
> +
> +static const struct v4l2_ctrl_config it6625_ctrl_audio_type = {
> +	.id = V4L2_CID_IT6625_AUDIO_TYPE,
> +	.name = "Audio Type",
> +	.type = V4L2_CTRL_TYPE_MENU,
> +	.min = V4L2_IT6625_AUDIO_TYPE_LPCM,
> +	.max = V4L2_IT6625_AUDIO_TYPE_DSD,
> +	.def = V4L2_IT6625_AUDIO_TYPE_LPCM,
> +	.qmenu = it6625_audio_type_menu,
> +	.flags = V4L2_CTRL_FLAG_READ_ONLY,
> +};
> +
>  static int it6625_v4l2_init_controls(struct v4l2_subdev *sd)
>  {
>  	struct it6625 *it6625 = sd_to_6625(sd);
> @@ -2131,7 +2160,7 @@ static int it6625_v4l2_init_controls(struct v4l2_subdev *sd)
>  	bool cphy_3trio = it6625->bus_type == V4L2_MBUS_CSI2_CPHY &&
>  			   it6625->csi_lanes == 3;
>  
> -	v4l2_ctrl_handler_init(hdl, 4);
> +	v4l2_ctrl_handler_init(hdl, 5);
>  
>  	it6625->ctrl_5v_detect =
>  		v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_DV_RX_POWER_PRESENT,
> @@ -2143,6 +2172,8 @@ static int it6625_v4l2_init_controls(struct v4l2_subdev *sd)
>  				     NULL);
>  	it6625->ctrl_audio_present =
>  		v4l2_ctrl_new_custom(hdl, &it6625_ctrl_audio_present, NULL);
> +	it6625->ctrl_audio_type =
> +		v4l2_ctrl_new_custom(hdl, &it6625_ctrl_audio_type, NULL);
>  	it6625->ctrl_link_freq =
>  		v4l2_ctrl_new_int_menu(hdl, NULL, V4L2_CID_LINK_FREQ, 0, 0,
>  				       cphy_3trio ? it6626_cphy_3trio_link_freq :
> @@ -2186,26 +2217,74 @@ static unsigned int it6625_audio_max_channels(const struct it6625 *it6625)
>  	return min(2U * it6625->audio_lanes, IT6625_AUDIO_MAX_CHANNELS);
>  }
>  
> +/*
> + * I2S frame rate and slot count that carry this stream. Pure, callable in
> + * atomic context; returns -ENODEV for a stream the I2S output can't carry.
> + *
> + * LPCM goes out as is. A compressed IEC 61937 stream at up to 192 kHz
> + * takes 2 slots at its own sampling rate, whatever channel count the
> + * source signals. An HBR stream (AUD_FS 768 or 705.6 kHz) is one IEC 61937
> + * stream at 4x the frame rate spread over 4 lanes: 8 slots at a quarter of
> + * that rate.
> + *
> + * The firmware doesn't keep AUD_TYPE to its documented meaning: it reports
> + * HBR for a plain 48 kHz DTS stream too. So the rate, not NLPCM vs. HBR,
> + * selects the layout of a compressed stream.
> + */
> +static int it6625_audio_xport(const struct it6625 *it6625,
> +			      const struct it6625_audio_stream *s,
> +			      u32 *rate, unsigned int *channels)
> +{
> +	u32 fs = it6625_audio_fs_to_rate(s->fs_code);
> +
> +	if (!fs)
> +		return -ENODEV;
> +
> +	switch (s->type) {
> +	case AUD_TYPE_LPCM:
> +		*rate = fs;
> +		*channels = s->channels;
> +		break;
> +	case AUD_TYPE_NLPCM:
> +	case AUD_TYPE_HBR:
> +		if (fs == 768000 || fs == 705600) {
> +			*rate = fs / IT6625_AUDIO_HBR_LANES;
> +			*channels = IT6625_AUDIO_HBR_CHANNELS;
> +		} else {
> +			*rate = fs;
> +			*channels = 2;
> +		}
> +		break;
> +	default:
> +		return -ENODEV;
> +	}
> +
> +	if (*rate > IT6625_AUDIO_MAX_RATE)
> +		return -ENODEV;
> +
> +	if (*channels < 2 || *channels % 2 ||
> +	    *channels > it6625_audio_max_channels(it6625))
> +		return -ENODEV;
> +
> +	return 0;
> +}
> +
>  /* can this audio status be captured? Pure check, callable in atomic context. */
>  static int it6625_audio_check(const struct it6625 *it6625,
>  			      const struct it6625_audio_info *info)
>  {
>  	const struct it6625_audio_stream *s = &info->stream;
> -	u32 rate = it6625_audio_fs_to_rate(s->fs_code);
> +	unsigned int channels;
> +	u32 rate;
>  
>  	if (!info->read_ok || !info->vid_stable || !info->present ||
>  	    info->avmute)
>  		return -ENODEV;
>  
> -	if (s->out_if != AUD_OUT_IF_I2S || s->type != AUD_TYPE_LPCM ||
> -	    s->multi_stream || s->is_3d)
> -		return -ENODEV;
> -
> -	if (!rate || rate > IT6625_AUDIO_MAX_RATE)
> +	if (s->out_if != AUD_OUT_IF_I2S || s->multi_stream || s->is_3d)
>  		return -ENODEV;
>  
> -	if (s->channels < 2 || s->channels % 2 ||
> -	    s->channels > it6625_audio_max_channels(it6625))
> +	if (it6625_audio_xport(it6625, s, &rate, &channels))
>  		return -ENODEV;
>  
>  	if (!it6625_audio_wl_bits(s->i2s_wl))
> @@ -2281,8 +2360,9 @@ static int it6625_dai_startup(struct snd_pcm_substream *substream,
>  	struct it6625 *it6625 = dai_to_6625(dai);
>  	struct snd_pcm_runtime *runtime = substream->runtime;
>  	struct it6625_audio_info info;
> +	unsigned int channels;
>  	unsigned long flags;
> -	u32 gen;
> +	u32 gen, rate;
>  	int ret;
>  
>  	it6625_audio_refresh(it6625);
> @@ -2298,13 +2378,16 @@ static int it6625_dai_startup(struct snd_pcm_substream *substream,
>  	if (ret)
>  		return ret;
>  
> +	/* the check above passed, so this can't fail */
> +	it6625_audio_xport(it6625, &info.stream, &rate, &channels);
> +
>  	ret = snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_RATE,
> -					   it6625_audio_fs_to_rate(info.stream.fs_code));
> +					   rate);
>  	if (ret < 0)
>  		return ret;
>  
>  	ret = snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS,
> -					   info.stream.channels);
> +					   channels);
>  	if (ret < 0)
>  		return ret;
>  
> @@ -2378,18 +2461,21 @@ static int it6625_dai_hw_params(struct snd_pcm_substream *substream,
>  				struct snd_soc_dai *dai)
>  {
>  	struct it6625 *it6625 = dai_to_6625(dai);
> -	const struct it6625_audio_stream *s;
> +	unsigned int channels;
>  	unsigned long flags;
> +	u32 rate;
>  	int ret;
>  
>  	it6625_audio_refresh(it6625);
>  
>  	spin_lock_irqsave(&it6625->audio_lock, flags);
>  	ret = it6625_audio_validate_locked(it6625);
> -	s = &it6625->audio_accepted;
> +	if (!ret)
> +		ret = it6625_audio_xport(it6625, &it6625->audio_accepted,
> +					 &rate, &channels);
>  	if (!ret &&
> -	    (params_rate(params) != it6625_audio_fs_to_rate(s->fs_code) ||
> -	     params_channels(params) != s->channels ||
> +	    (params_rate(params) != rate ||
> +	     params_channels(params) != channels ||
>  	     params_format(params) != SNDRV_PCM_FORMAT_S32_LE))
>  		ret = -EINVAL;
>  	spin_unlock_irqrestore(&it6625->audio_lock, flags);
> diff --git a/include/uapi/linux/it6625.h b/include/uapi/linux/it6625.h
> index a895658aaf6a80d3fb624cdece33c5135161c72d..9ab85378ac77d1e06bc87964c1066d35ad177c15 100644
> --- a/include/uapi/linux/it6625.h
> +++ b/include/uapi/linux/it6625.h
> @@ -22,4 +22,29 @@
>   */
>  #define V4L2_CID_IT6625_AUDIO_PRESENT		(V4L2_CID_USER_IT6625_BASE + 1)
>  
> +/*
> + * Coding type of the HDMI audio stream, as last successfully read from the
> + * bridge. Read-only menu. It is kept, not cleared, when audio goes away or
> + * a status read fails, so it describes the current stream only while
> + * V4L2_CID_IT6625_AUDIO_PRESENT is set and
> + * V4L2_CID_IT6625_AUDIO_SAMPLING_RATE is non-zero.
> + *
> + * LPCM:  linear PCM, captured as is.
> + * NLPCM, HBR: IEC 61937 compressed stream, as reported by the bridge
> + *        firmware (which also reports HBR for plain 48 kHz DTS). The
> + *        capture layout follows V4L2_CID_IT6625_AUDIO_SAMPLING_RATE: at
> + *        768/705.6 kHz (TrueHD/MAT, DTS-HD MA) 8 channels on 4 I2S lanes
> + *        at a quarter of that rate, otherwise (AC-3, DTS, ...) 2 channels
> + *        at that rate.
> + * DSD:   one-bit audio; not capturable.
> + */
> +#define V4L2_CID_IT6625_AUDIO_TYPE		(V4L2_CID_USER_IT6625_BASE + 2)
> +
> +enum v4l2_it6625_audio_type {
> +	V4L2_IT6625_AUDIO_TYPE_LPCM = 0,
> +	V4L2_IT6625_AUDIO_TYPE_NLPCM = 1,
> +	V4L2_IT6625_AUDIO_TYPE_HBR = 2,
> +	V4L2_IT6625_AUDIO_TYPE_DSD = 3,
> +};
> +
>  #endif /* _UAPI_LINUX_IT6625_H */
> 


      reply	other threads:[~2026-11-11 12:16 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-03  6:50 [PATCH 0/5] media: i2c: it6625: add HDMI audio capture over I2S Hermes Wu via B4 Relay
2026-11-03  6:50 ` [PATCH 1/5] dt-bindings: media: ite,it6625: add audio DAI properties Hermes Wu via B4 Relay
2026-11-04 14:24   ` Krzysztof Kozlowski
2026-11-05  1:50     ` Hermes.Wu
2026-11-03  6:50 ` [PATCH 2/5] media: Documentation: it6625: document the audio controls Hermes Wu via B4 Relay
2026-11-11 12:12   ` Hans Verkuil
2026-11-03  6:50 ` [PATCH 3/5] media: i2c: it6625: decode audio status fields with FIELD_GET() Hermes Wu via B4 Relay
2026-11-03  6:50 ` [PATCH 4/5] media: i2c: it6625: add I2S audio capture DAI Hermes Wu via B4 Relay
2026-11-03  6:50 ` [PATCH 5/5] media: i2c: it6625: capture compressed and HBR audio, not just LPCM Hermes Wu via B4 Relay
2026-11-11 12:16   ` Hans Verkuil [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=38706010-b949-4f70-b45c-6800f7fcf942@kernel.org \
    --to=hverkuil+cisco@kernel.org \
    --cc=Hermes.wu@ite.com.tw \
    --cc=broonie@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=lgirdwood@gmail.com \
    --cc=linux-api@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=mchehab@kernel.org \
    --cc=robh@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®