summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sound/soc/fsl/imx-cdnhdmi.c215
1 files changed, 195 insertions, 20 deletions
diff --git a/sound/soc/fsl/imx-cdnhdmi.c b/sound/soc/fsl/imx-cdnhdmi.c
index 0bd7278fe60b..a40230ee1f7b 100644
--- a/sound/soc/fsl/imx-cdnhdmi.c
+++ b/sound/soc/fsl/imx-cdnhdmi.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017-2018 NXP
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
@@ -21,9 +21,8 @@
#include <sound/control.h>
#include <sound/pcm_params.h>
#include <sound/soc-dapm.h>
-#include "../../../drivers/mxc/hdp/API_Audio.h"
+#include <sound/hdmi-codec.h>
#include "../../../drivers/gpu/drm/imx/hdp/imx-hdp.h"
-#include "../../../drivers/video/fbdev/mxc/API_AFE_t28hpc_hdmitx.h"
#define SUPPORT_RATE_NUM 10
#define SUPPORT_CHANNEL_NUM 10
@@ -34,6 +33,13 @@ struct imx_cdnhdmi_data {
int protocol;
u32 support_rates[SUPPORT_RATE_NUM];
u32 support_rates_num;
+ u32 support_channels[SUPPORT_CHANNEL_NUM];
+ u32 support_channels_num;
+ u32 edid_rates[SUPPORT_RATE_NUM];
+ u32 edid_rates_count;
+ u32 edid_channels[SUPPORT_CHANNEL_NUM];
+ u32 edid_channels_count;
+ uint8_t eld[MAX_ELD_BYTES];
};
static int imx_cdnhdmi_startup(struct snd_pcm_substream *substream)
@@ -44,7 +50,6 @@ static int imx_cdnhdmi_startup(struct snd_pcm_substream *substream)
struct imx_cdnhdmi_data *data = snd_soc_card_get_drvdata(card);
static struct snd_pcm_hw_constraint_list constraint_rates;
static struct snd_pcm_hw_constraint_list constraint_channels;
- static u32 support_channels[SUPPORT_CHANNEL_NUM];
int ret;
constraint_rates.list = data->support_rates;
@@ -55,11 +60,8 @@ static int imx_cdnhdmi_startup(struct snd_pcm_substream *substream)
if (ret)
return ret;
- support_channels[0] = 2;
- support_channels[1] = 4;
- support_channels[2] = 8;
- constraint_channels.list = support_channels;
- constraint_channels.count = 3;
+ constraint_channels.list = data->support_channels;
+ constraint_channels.count = data->support_channels_num;
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
&constraint_channels);
@@ -76,10 +78,6 @@ static int imx_cdnhdmi_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_card *card = rtd->card;
struct device *dev = card->dev;
- unsigned int sample_rate = params_rate(params);
- unsigned int channels = params_channels(params);
- unsigned int width = params_physical_width(params);
- struct imx_cdnhdmi_data *data = snd_soc_card_get_drvdata(card);
int ret;
/* set cpu DAI configuration */
@@ -104,11 +102,6 @@ static int imx_cdnhdmi_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- if (data->protocol == 1)
- imx_hdp_audio(AUDIO_TYPE_I2S, sample_rate, channels, width);
- else
- imx_hdmi_audio(AUDIO_TYPE_I2S, sample_rate, channels, width);
-
return 0;
}
@@ -117,6 +110,181 @@ static struct snd_soc_ops imx_cdnhdmi_ops = {
.hw_params = imx_cdnhdmi_hw_params,
};
+static const unsigned int eld_rates[] = {
+ 32000,
+ 44100,
+ 48000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
+};
+
+static unsigned int sad_max_channels(const u8 *sad)
+{
+ return 1 + (sad[0] & 7);
+}
+
+static int get_edid_info(struct snd_soc_card *card)
+{
+ struct snd_soc_pcm_runtime *rtd = list_first_entry(
+ &card->rtd_list, struct snd_soc_pcm_runtime, list);
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct hdmi_codec_pdata *hcd = codec->dev->platform_data;
+ struct imx_cdnhdmi_data *data = snd_soc_card_get_drvdata(card);
+ int i, j, ret;
+ const u8 *sad;
+ unsigned int channel_max = 0;
+ unsigned int rate_mask = 0;
+ unsigned int rate_mask_eld = 0;
+
+ ret = hcd->ops->get_eld(codec->dev->parent, hcd->data,
+ data->eld, sizeof(data->eld));
+ sad = drm_eld_sad(data->eld);
+ if (sad) {
+ for (j = 0; j < data->support_rates_num; j++) {
+ for (i = 0; i < ARRAY_SIZE(eld_rates); i++)
+ if (eld_rates[i] == data->support_rates[j])
+ rate_mask |= BIT(i);
+ }
+
+ for (i = drm_eld_sad_count(data->eld); i > 0; i--, sad += 3) {
+ if (rate_mask & sad[1])
+ channel_max = max(channel_max, sad_max_channels(sad));
+
+ if (sad_max_channels(sad) >= 2)
+ rate_mask_eld |= sad[1];
+ }
+ }
+
+ rate_mask = rate_mask & rate_mask_eld;
+
+ data->edid_rates_count = 0;
+ data->edid_channels_count = 0;
+
+ for (i = 0; i < ARRAY_SIZE(eld_rates); i++) {
+ if (rate_mask & BIT(i)) {
+ data->edid_rates[data->edid_rates_count] = eld_rates[i];
+ data->edid_rates_count++;
+ }
+ }
+
+ for (i = 0; i < data->support_channels_num; i++) {
+ if (data->support_channels[i] <= channel_max) {
+ data->edid_channels[data->edid_channels_count]
+ = data->support_channels[i];
+ data->edid_channels_count++;
+ }
+ }
+
+ return 0;
+}
+
+static int imx_cdnhdmi_channels_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+ struct imx_cdnhdmi_data *data = snd_soc_card_get_drvdata(card);
+
+ get_edid_info(card);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = data->edid_channels_count;
+
+ return 0;
+}
+
+static int imx_cdnhdmi_channels_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+ struct imx_cdnhdmi_data *data = snd_soc_card_get_drvdata(card);
+ int i;
+
+ get_edid_info(card);
+
+ for (i = 0 ; i < data->edid_channels_count ; i++)
+ uvalue->value.integer.value[i] = data->edid_channels[i];
+
+ return 0;
+}
+
+static int imx_cdnhdmi_rates_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+ struct imx_cdnhdmi_data *data = snd_soc_card_get_drvdata(card);
+
+ get_edid_info(card);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = data->edid_rates_count;
+
+ return 0;
+}
+
+static int imx_cdnhdmi_rates_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+ struct imx_cdnhdmi_data *data = snd_soc_card_get_drvdata(card);
+ int i;
+
+ get_edid_info(card);
+
+ for (i = 0 ; i < data->edid_rates_count; i++)
+ uvalue->value.integer.value[i] = data->edid_rates[i];
+
+ return 0;
+}
+
+static int imx_cdnhdmi_formats_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 3;
+
+ return 0;
+}
+
+static int imx_cdnhdmi_formats_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ uvalue->value.integer.value[0] = 16;
+ uvalue->value.integer.value[1] = 24;
+ uvalue->value.integer.value[2] = 32;
+
+ return 0;
+}
+
+static struct snd_kcontrol_new imx_cdnhdmi_ctrls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "HDMI Support Channels",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = imx_cdnhdmi_channels_info,
+ .get = imx_cdnhdmi_channels_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "HDMI Support Rates",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = imx_cdnhdmi_rates_info,
+ .get = imx_cdnhdmi_rates_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "HDMI Support Formats",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = imx_cdnhdmi_formats_info,
+ .get = imx_cdnhdmi_formats_get,
+ },
+};
+
static int imx_cdnhdmi_probe(struct platform_device *pdev)
{
struct device_node *cpu_np, *cdnhdmi_np = NULL;
@@ -163,13 +331,18 @@ static int imx_cdnhdmi_probe(struct platform_device *pdev)
data->support_rates_num = 4;
}
+ data->support_channels[0] = 2;
+ data->support_channels[1] = 4;
+ data->support_channels[2] = 8;
+ data->support_channels_num = 3;
+
of_property_read_u32(pdev->dev.of_node, "protocol",
&data->protocol);
data->dai.name = "imx8 hdmi";
data->dai.stream_name = "imx8 hdmi";
- data->dai.codec_dai_name = "snd-soc-dummy-dai";
- data->dai.codec_name = "snd-soc-dummy";
+ data->dai.codec_dai_name = "hdmi-hifi.0";
+ data->dai.codec_name = "hdmi-audio-codec";
data->dai.cpu_dai_name = dev_name(&cpu_pdev->dev);
data->dai.platform_of_node = cpu_np;
data->dai.ops = &imx_cdnhdmi_ops;
@@ -186,6 +359,8 @@ static int imx_cdnhdmi_probe(struct platform_device *pdev)
goto fail;
data->card.num_links = 1;
data->card.dai_link = &data->dai;
+ data->card.controls = imx_cdnhdmi_ctrls,
+ data->card.num_controls = ARRAY_SIZE(imx_cdnhdmi_ctrls),
platform_set_drvdata(pdev, &data->card);
snd_soc_card_set_drvdata(&data->card, data);