Skip to content

Commit

Permalink
Merge tag 'asoc-hdmi-codec-improvements-v2' of git://git.kernel.org/p…
Browse files Browse the repository at this point in the history
…ub/scm/linux/kernel/git/mripard/linux into asoc-5.14

Improvements to the hdmi-codec driver and ALSA infrastructure around it
to support the HDMI Channel Mapping and IEC958 controls
  • Loading branch information
Mark Brown committed Jun 14, 2021
2 parents 3ea8a74 + 2fef64e commit 116b1e1
Show file tree
Hide file tree
Showing 5 changed files with 335 additions and 89 deletions.
13 changes: 7 additions & 6 deletions Documentation/sound/kernel-api/writing-an-alsa-driver.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3508,14 +3508,15 @@ field must be set, though).

“IEC958 Playback Con Mask” is used to return the bit-mask for the IEC958
status bits of consumer mode. Similarly, “IEC958 Playback Pro Mask”
returns the bitmask for professional mode. They are read-only controls,
and are defined as MIXER controls (iface =
``SNDRV_CTL_ELEM_IFACE_MIXER``).
returns the bitmask for professional mode. They are read-only controls.

Meanwhile, “IEC958 Playback Default” control is defined for getting and
setting the current default IEC958 bits. Note that this one is usually
defined as a PCM control (iface = ``SNDRV_CTL_ELEM_IFACE_PCM``),
although in some places it's defined as a MIXER control.
setting the current default IEC958 bits.

Due to historical reasons, both variants of the Playback Mask and the
Playback Default controls can be implemented on either a
``SNDRV_CTL_ELEM_IFACE_PCM`` or a ``SNDRV_CTL_ELEM_IFACE_MIXER`` iface.
Drivers should expose the mask and default on the same iface though.

In addition, you can define the control switches to enable/disable or to
set the raw bit mode. The implementation will depend on the chip, but
Expand Down
12 changes: 11 additions & 1 deletion include/sound/hdmi-codec.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,22 @@ struct hdmi_codec_ops {

/*
* Configures HDMI-encoder for audio stream.
* Mandatory
* Having either prepare or hw_params is mandatory.
*/
int (*hw_params)(struct device *dev, void *data,
struct hdmi_codec_daifmt *fmt,
struct hdmi_codec_params *hparms);

/*
* Configures HDMI-encoder for audio stream. Can be called
* multiple times for each setup.
*
* Having either prepare or hw_params is mandatory.
*/
int (*prepare)(struct device *dev, void *data,
struct hdmi_codec_daifmt *fmt,
struct hdmi_codec_params *hparms);

/*
* Shuts down the audio stream.
* Mandatory
Expand Down
8 changes: 8 additions & 0 deletions include/sound/pcm_iec958.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@

#include <linux/types.h>

int snd_pcm_create_iec958_consumer_default(u8 *cs, size_t len);

int snd_pcm_fill_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
size_t len);

int snd_pcm_fill_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
u8 *cs, size_t len);

int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
size_t len);

Expand Down
174 changes: 132 additions & 42 deletions sound/core/pcm_iec958.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,85 @@
#include <sound/pcm_params.h>
#include <sound/pcm_iec958.h>

static int create_iec958_consumer(uint rate, uint sample_width,
u8 *cs, size_t len)
/**
* snd_pcm_create_iec958_consumer_default - create default consumer format IEC958 channel status
* @cs: channel status buffer, at least four bytes
* @len: length of channel status buffer
*
* Create the consumer format channel status data in @cs of maximum size
* @len. When relevant, the configuration-dependant bits will be set as
* unspecified.
*
* Drivers should then call einter snd_pcm_fill_iec958_consumer() or
* snd_pcm_fill_iec958_consumer_hw_params() to replace these unspecified
* bits by their actual values.
*
* Drivers may wish to tweak the contents of the buffer after creation.
*
* Returns: length of buffer, or negative error code if something failed.
*/
int snd_pcm_create_iec958_consumer_default(u8 *cs, size_t len)
{
unsigned int fs, ws;

if (len < 4)
return -EINVAL;

switch (rate) {
case 32000:
fs = IEC958_AES3_CON_FS_32000;
break;
case 44100:
fs = IEC958_AES3_CON_FS_44100;
break;
case 48000:
fs = IEC958_AES3_CON_FS_48000;
break;
case 88200:
fs = IEC958_AES3_CON_FS_88200;
break;
case 96000:
fs = IEC958_AES3_CON_FS_96000;
break;
case 176400:
fs = IEC958_AES3_CON_FS_176400;
break;
case 192000:
fs = IEC958_AES3_CON_FS_192000;
break;
default:
memset(cs, 0, len);

cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE;
cs[1] = IEC958_AES1_CON_GENERAL;
cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC;
cs[3] = IEC958_AES3_CON_CLOCK_1000PPM | IEC958_AES3_CON_FS_NOTID;

if (len > 4)
cs[4] = IEC958_AES4_CON_WORDLEN_NOTID;

return len;
}
EXPORT_SYMBOL_GPL(snd_pcm_create_iec958_consumer_default);

static int fill_iec958_consumer(uint rate, uint sample_width,
u8 *cs, size_t len)
{
if (len < 4)
return -EINVAL;

if ((cs[3] & IEC958_AES3_CON_FS) == IEC958_AES3_CON_FS_NOTID) {
unsigned int fs;

switch (rate) {
case 32000:
fs = IEC958_AES3_CON_FS_32000;
break;
case 44100:
fs = IEC958_AES3_CON_FS_44100;
break;
case 48000:
fs = IEC958_AES3_CON_FS_48000;
break;
case 88200:
fs = IEC958_AES3_CON_FS_88200;
break;
case 96000:
fs = IEC958_AES3_CON_FS_96000;
break;
case 176400:
fs = IEC958_AES3_CON_FS_176400;
break;
case 192000:
fs = IEC958_AES3_CON_FS_192000;
break;
default:
return -EINVAL;
}

cs[3] &= ~IEC958_AES3_CON_FS;
cs[3] |= fs;
}

if (len > 4) {
if (len > 4 &&
(cs[4] & IEC958_AES4_CON_WORDLEN) == IEC958_AES4_CON_WORDLEN_NOTID) {
unsigned int ws;

switch (sample_width) {
case 16:
ws = IEC958_AES4_CON_WORDLEN_20_16;
Expand All @@ -64,20 +108,57 @@ static int create_iec958_consumer(uint rate, uint sample_width,
default:
return -EINVAL;
}
}

memset(cs, 0, len);
cs[4] &= ~IEC958_AES4_CON_WORDLEN;
cs[4] |= ws;
}

cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE;
cs[1] = IEC958_AES1_CON_GENERAL;
cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC;
cs[3] = IEC958_AES3_CON_CLOCK_1000PPM | fs;
return len;
}

if (len > 4)
cs[4] = ws;
/**
* snd_pcm_fill_iec958_consumer - Fill consumer format IEC958 channel status
* @runtime: pcm runtime structure with ->rate filled in
* @cs: channel status buffer, at least four bytes
* @len: length of channel status buffer
*
* Fill the unspecified bits in an IEC958 status bits array using the
* parameters of the PCM runtime @runtime.
*
* Drivers may wish to tweak the contents of the buffer after its been
* filled.
*
* Returns: length of buffer, or negative error code if something failed.
*/
int snd_pcm_fill_iec958_consumer(struct snd_pcm_runtime *runtime,
u8 *cs, size_t len)
{
return fill_iec958_consumer(runtime->rate,
snd_pcm_format_width(runtime->format),
cs, len);
}
EXPORT_SYMBOL_GPL(snd_pcm_fill_iec958_consumer);

return len;
/**
* snd_pcm_fill_iec958_consumer_hw_params - Fill consumer format IEC958 channel status
* @params: the hw_params instance for extracting rate and sample format
* @cs: channel status buffer, at least four bytes
* @len: length of channel status buffer
*
* Fill the unspecified bits in an IEC958 status bits array using the
* parameters of the PCM hardware parameters @params.
*
* Drivers may wish to tweak the contents of the buffer after its been
* filled..
*
* Returns: length of buffer, or negative error code if something failed.
*/
int snd_pcm_fill_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
u8 *cs, size_t len)
{
return fill_iec958_consumer(params_rate(params), params_width(params), cs, len);
}
EXPORT_SYMBOL_GPL(snd_pcm_fill_iec958_consumer_hw_params);

/**
* snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status
Expand All @@ -95,9 +176,13 @@ static int create_iec958_consumer(uint rate, uint sample_width,
int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
size_t len)
{
return create_iec958_consumer(runtime->rate,
snd_pcm_format_width(runtime->format),
cs, len);
int ret;

ret = snd_pcm_create_iec958_consumer_default(cs, len);
if (ret < 0)
return ret;

return snd_pcm_fill_iec958_consumer(runtime, cs, len);
}
EXPORT_SYMBOL(snd_pcm_create_iec958_consumer);

Expand All @@ -117,7 +202,12 @@ EXPORT_SYMBOL(snd_pcm_create_iec958_consumer);
int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
u8 *cs, size_t len)
{
return create_iec958_consumer(params_rate(params), params_width(params),
cs, len);
int ret;

ret = snd_pcm_create_iec958_consumer_default(cs, len);
if (ret < 0)
return ret;

return fill_iec958_consumer(params_rate(params), params_width(params), cs, len);
}
EXPORT_SYMBOL(snd_pcm_create_iec958_consumer_hw_params);
Loading

0 comments on commit 116b1e1

Please sign in to comment.