Commit ecd3ee3d authored by Maxime Ripard's avatar Maxime Ripard

Merge tag 'asoc-hdmi-codec-improvements-v2' of...

Merge tag 'asoc-hdmi-codec-improvements-v2' of git://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux into drm-misc-next

Improvements to the hdmi-codec driver and ALSA infrastructure around it
to support the HDMI Channel Mapping and IEC958 controls
Signed-off-by: default avatarMaxime Ripard <maxime@cerno.tech>

# -----BEGIN PGP SIGNATURE-----
#
# iHUEABYIAB0WIQRcEzekXsqa64kGDp7j7w1vZxhRxQUCYMHitQAKCRDj7w1vZxhR
# xQ40AP49z0mUifkpbyUvYwdmrUVlg2JEWSTOWaH3tp0kke/dBQEA1vYxdMimhFu3
# SYKXxgtvlT7vL48vNYBxGbNuGQvzJw4=
# =IWna
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 10 Jun 2021 12:00:21 PM CEST
# gpg:                using EDDSA key 5C1337A45ECA9AEB89060E9EE3EF0D6F671851C5
# gpg: Good signature from "Maxime Ripard <maxime.ripard@anandra.org>" [unknown]
# gpg:                 aka "Maxime Ripard <mripard@kernel.org>" [unknown]
# gpg:                 aka "Maxime Ripard (Work Address) <maxime@cerno.tech>" [unknown]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: BE56 75C3 7E81 8C8B 5764  241C 254B CFC5 6BF6 CE8D
#      Subkey fingerprint: 5C13 37A4 5ECA 9AEB 8906  0E9E E3EF 0D6F 6718 51C5
From: Maxime Ripard <maxime@cerno.tech>
Link: https://patchwork.freedesktop.org/patch/msgid/20210610122550.jnriewchqspdcrwk@gilmour
parents bacbab58 2fef64ee
...@@ -3508,14 +3508,15 @@ field must be set, though). ...@@ -3508,14 +3508,15 @@ field must be set, though).
“IEC958 Playback Con Mask” is used to return the bit-mask for the IEC958 “IEC958 Playback Con Mask” is used to return the bit-mask for the IEC958
status bits of consumer mode. Similarly, “IEC958 Playback Pro Mask” status bits of consumer mode. Similarly, “IEC958 Playback Pro Mask”
returns the bitmask for professional mode. They are read-only controls, returns the bitmask for professional mode. They are read-only controls.
and are defined as MIXER controls (iface =
``SNDRV_CTL_ELEM_IFACE_MIXER``).
Meanwhile, “IEC958 Playback Default” control is defined for getting and Meanwhile, “IEC958 Playback Default” control is defined for getting and
setting the current default IEC958 bits. Note that this one is usually setting the current default IEC958 bits.
defined as a PCM control (iface = ``SNDRV_CTL_ELEM_IFACE_PCM``),
although in some places it's defined as a MIXER control. 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 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 set the raw bit mode. The implementation will depend on the chip, but
......
...@@ -65,12 +65,22 @@ struct hdmi_codec_ops { ...@@ -65,12 +65,22 @@ struct hdmi_codec_ops {
/* /*
* Configures HDMI-encoder for audio stream. * Configures HDMI-encoder for audio stream.
* Mandatory * Having either prepare or hw_params is mandatory.
*/ */
int (*hw_params)(struct device *dev, void *data, int (*hw_params)(struct device *dev, void *data,
struct hdmi_codec_daifmt *fmt, struct hdmi_codec_daifmt *fmt,
struct hdmi_codec_params *hparms); 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. * Shuts down the audio stream.
* Mandatory * Mandatory
......
...@@ -4,6 +4,14 @@ ...@@ -4,6 +4,14 @@
#include <linux/types.h> #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, int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
size_t len); size_t len);
......
...@@ -9,41 +9,85 @@ ...@@ -9,41 +9,85 @@
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/pcm_iec958.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) if (len < 4)
return -EINVAL; return -EINVAL;
switch (rate) { memset(cs, 0, len);
case 32000:
fs = IEC958_AES3_CON_FS_32000; cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE;
break; cs[1] = IEC958_AES1_CON_GENERAL;
case 44100: cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC;
fs = IEC958_AES3_CON_FS_44100; cs[3] = IEC958_AES3_CON_CLOCK_1000PPM | IEC958_AES3_CON_FS_NOTID;
break;
case 48000: if (len > 4)
fs = IEC958_AES3_CON_FS_48000; cs[4] = IEC958_AES4_CON_WORDLEN_NOTID;
break;
case 88200: return len;
fs = IEC958_AES3_CON_FS_88200; }
break; EXPORT_SYMBOL_GPL(snd_pcm_create_iec958_consumer_default);
case 96000:
fs = IEC958_AES3_CON_FS_96000; static int fill_iec958_consumer(uint rate, uint sample_width,
break; u8 *cs, size_t len)
case 176400: {
fs = IEC958_AES3_CON_FS_176400; if (len < 4)
break;
case 192000:
fs = IEC958_AES3_CON_FS_192000;
break;
default:
return -EINVAL; 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) { switch (sample_width) {
case 16: case 16:
ws = IEC958_AES4_CON_WORDLEN_20_16; ws = IEC958_AES4_CON_WORDLEN_20_16;
...@@ -64,20 +108,57 @@ static int create_iec958_consumer(uint rate, uint sample_width, ...@@ -64,20 +108,57 @@ static int create_iec958_consumer(uint rate, uint sample_width,
default: default:
return -EINVAL; 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; return len;
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;
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 * snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status
...@@ -95,9 +176,13 @@ static int create_iec958_consumer(uint rate, uint sample_width, ...@@ -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, int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
size_t len) size_t len)
{ {
return create_iec958_consumer(runtime->rate, int ret;
snd_pcm_format_width(runtime->format),
cs, len); 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); EXPORT_SYMBOL(snd_pcm_create_iec958_consumer);
...@@ -117,7 +202,12 @@ EXPORT_SYMBOL(snd_pcm_create_iec958_consumer); ...@@ -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, int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
u8 *cs, size_t len) u8 *cs, size_t len)
{ {
return create_iec958_consumer(params_rate(params), params_width(params), int ret;
cs, len);
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); EXPORT_SYMBOL(snd_pcm_create_iec958_consumer_hw_params);
...@@ -277,6 +277,7 @@ struct hdmi_codec_priv { ...@@ -277,6 +277,7 @@ struct hdmi_codec_priv {
bool busy; bool busy;
struct snd_soc_jack *jack; struct snd_soc_jack *jack;
unsigned int jack_status; unsigned int jack_status;
u8 iec_status[5];
}; };
static const struct snd_soc_dapm_widget hdmi_widgets[] = { static const struct snd_soc_dapm_widget hdmi_widgets[] = {
...@@ -385,6 +386,47 @@ static int hdmi_codec_chmap_ctl_get(struct snd_kcontrol *kcontrol, ...@@ -385,6 +386,47 @@ static int hdmi_codec_chmap_ctl_get(struct snd_kcontrol *kcontrol,
return 0; return 0;
} }
static int hdmi_codec_iec958_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
uinfo->count = 1;
return 0;
}
static int hdmi_codec_iec958_default_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
memcpy(ucontrol->value.iec958.status, hcp->iec_status,
sizeof(hcp->iec_status));
return 0;
}
static int hdmi_codec_iec958_default_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
memcpy(hcp->iec_status, ucontrol->value.iec958.status,
sizeof(hcp->iec_status));
return 0;
}
static int hdmi_codec_iec958_mask_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
memset(ucontrol->value.iec958.status, 0xff,
sizeof_field(struct hdmi_codec_priv, iec_status));
return 0;
}
static int hdmi_codec_startup(struct snd_pcm_substream *substream, static int hdmi_codec_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
...@@ -439,6 +481,42 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream, ...@@ -439,6 +481,42 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
mutex_unlock(&hcp->lock); mutex_unlock(&hcp->lock);
} }
static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai,
unsigned int sample_width,
unsigned int sample_rate,
unsigned int channels,
struct hdmi_codec_params *hp)
{
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
int idx;
/* Select a channel allocation that matches with ELD and pcm channels */
idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels);
if (idx < 0) {
dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
idx);
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
return idx;
}
memset(hp, 0, sizeof(*hp));
hdmi_audio_infoframe_init(&hp->cea);
hp->cea.channels = channels;
hp->cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
hp->cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
hp->cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
hp->cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
hp->sample_width = sample_width;
hp->sample_rate = sample_rate;
hp->channels = channels;
hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
return 0;
}
static int hdmi_codec_hw_params(struct snd_pcm_substream *substream, static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
...@@ -453,46 +531,73 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream, ...@@ -453,46 +531,73 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
.dig_subframe = { 0 }, .dig_subframe = { 0 },
} }
}; };
int ret, idx; int ret;
if (!hcp->hcd.ops->hw_params)
return 0;
dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__, dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
params_width(params), params_rate(params), params_width(params), params_rate(params),
params_channels(params)); params_channels(params));
ret = snd_pcm_create_iec958_consumer_hw_params(params, hp.iec.status, ret = hdmi_codec_fill_codec_params(dai,
sizeof(hp.iec.status)); params_width(params),
params_rate(params),
params_channels(params),
&hp);
if (ret < 0)
return ret;
memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status));
ret = snd_pcm_fill_iec958_consumer_hw_params(params, hp.iec.status,
sizeof(hp.iec.status));
if (ret < 0) { if (ret < 0) {
dev_err(dai->dev, "Creating IEC958 channel status failed %d\n", dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
ret); ret);
return ret; return ret;
} }
hdmi_audio_infoframe_init(&hp.cea);
hp.cea.channels = params_channels(params);
hp.cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
/* Select a channel allocation that matches with ELD and pcm channels */
idx = hdmi_codec_get_ch_alloc_table_idx(hcp, hp.cea.channels);
if (idx < 0) {
dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
idx);
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
return idx;
}
hp.cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
hp.sample_width = params_width(params);
hp.sample_rate = params_rate(params);
hp.channels = params_channels(params);
cf->bit_fmt = params_format(params); cf->bit_fmt = params_format(params);
return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data, return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data,
cf, &hp); cf, &hp);
} }
static int hdmi_codec_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
struct hdmi_codec_daifmt *cf = dai->playback_dma_data;
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int channels = runtime->channels;
unsigned int width = snd_pcm_format_width(runtime->format);
unsigned int rate = runtime->rate;
struct hdmi_codec_params hp;
int ret;
if (!hcp->hcd.ops->prepare)
return 0;
dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
width, rate, channels);
ret = hdmi_codec_fill_codec_params(dai, width, rate, channels, &hp);
if (ret < 0)
return ret;
memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status));
ret = snd_pcm_fill_iec958_consumer(runtime, hp.iec.status,
sizeof(hp.iec.status));
if (ret < 0) {
dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
ret);
return ret;
}
cf->bit_fmt = runtime->format;
return hcp->hcd.ops->prepare(dai->dev->parent, hcp->hcd.data,
cf, &hp);
}
static int hdmi_codec_i2s_set_fmt(struct snd_soc_dai *dai, static int hdmi_codec_i2s_set_fmt(struct snd_soc_dai *dai,
unsigned int fmt) unsigned int fmt)
{ {
...@@ -584,6 +689,7 @@ static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = { ...@@ -584,6 +689,7 @@ static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = {
.startup = hdmi_codec_startup, .startup = hdmi_codec_startup,
.shutdown = hdmi_codec_shutdown, .shutdown = hdmi_codec_shutdown,
.hw_params = hdmi_codec_hw_params, .hw_params = hdmi_codec_hw_params,
.prepare = hdmi_codec_prepare,
.set_fmt = hdmi_codec_i2s_set_fmt, .set_fmt = hdmi_codec_i2s_set_fmt,
.mute_stream = hdmi_codec_mute, .mute_stream = hdmi_codec_mute,
}; };
...@@ -620,21 +726,37 @@ static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = { ...@@ -620,21 +726,37 @@ static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = {
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
struct snd_kcontrol_new hdmi_codec_controls[] = {
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
.info = hdmi_codec_iec958_info,
.get = hdmi_codec_iec958_mask_get,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
.info = hdmi_codec_iec958_info,
.get = hdmi_codec_iec958_default_get,
.put = hdmi_codec_iec958_default_put,
},
{
.access = (SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE),
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "ELD",
.info = hdmi_eld_ctl_info,
.get = hdmi_eld_ctl_get,
},
};
static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd, static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct snd_soc_dai_driver *drv = dai->driver; struct snd_soc_dai_driver *drv = dai->driver;
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
struct snd_kcontrol *kctl; unsigned int i;
struct snd_kcontrol_new hdmi_eld_ctl = {
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "ELD",
.info = hdmi_eld_ctl_info,
.get = hdmi_eld_ctl_get,
.device = rtd->pcm->device,
};
int ret; int ret;
ret = snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK, ret = snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK,
...@@ -651,12 +773,21 @@ static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd, ...@@ -651,12 +773,21 @@ static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd,
hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps; hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps;
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
/* add ELD ctl with the device number corresponding to the PCM stream */ for (i = 0; i < ARRAY_SIZE(hdmi_codec_controls); i++) {
kctl = snd_ctl_new1(&hdmi_eld_ctl, dai->component); struct snd_kcontrol *kctl;
if (!kctl)
return -ENOMEM; /* add ELD ctl with the device number corresponding to the PCM stream */
kctl = snd_ctl_new1(&hdmi_codec_controls[i], dai->component);
if (!kctl)
return -ENOMEM;
return snd_ctl_add(rtd->card->snd_card, kctl); kctl->id.device = rtd->pcm->device;
ret = snd_ctl_add(rtd->card->snd_card, kctl);
if (ret < 0)
return ret;
}
return 0;
} }
static int hdmi_dai_probe(struct snd_soc_dai *dai) static int hdmi_dai_probe(struct snd_soc_dai *dai)
...@@ -849,7 +980,8 @@ static int hdmi_codec_probe(struct platform_device *pdev) ...@@ -849,7 +980,8 @@ static int hdmi_codec_probe(struct platform_device *pdev)
} }
dai_count = hcd->i2s + hcd->spdif; dai_count = hcd->i2s + hcd->spdif;
if (dai_count < 1 || !hcd->ops || !hcd->ops->hw_params || if (dai_count < 1 || !hcd->ops ||
(!hcd->ops->hw_params && !hcd->ops->prepare) ||
!hcd->ops->audio_shutdown) { !hcd->ops->audio_shutdown) {
dev_err(dev, "%s: Invalid parameters\n", __func__); dev_err(dev, "%s: Invalid parameters\n", __func__);
return -EINVAL; return -EINVAL;
...@@ -862,6 +994,11 @@ static int hdmi_codec_probe(struct platform_device *pdev) ...@@ -862,6 +994,11 @@ static int hdmi_codec_probe(struct platform_device *pdev)
hcp->hcd = *hcd; hcp->hcd = *hcd;
mutex_init(&hcp->lock); mutex_init(&hcp->lock);
ret = snd_pcm_create_iec958_consumer_default(hcp->iec_status,
sizeof(hcp->iec_status));
if (ret < 0)
return ret;
daidrv = devm_kcalloc(dev, dai_count, sizeof(*daidrv), GFP_KERNEL); daidrv = devm_kcalloc(dev, dai_count, sizeof(*daidrv), GFP_KERNEL);
if (!daidrv) if (!daidrv)
return -ENOMEM; return -ENOMEM;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment